Codechanges and lots of fixes related to playing other player's cards from exile.

- Cards now keep track of who's allowed to cast them, fixing possible issues in multiplayer.
- Face-down cards can now be properly looked at when allowed to by a static ability.
- Improve support for casting a face-down exiled card in general.
- Add Shared Fate (including AI support).
This commit is contained in:
elcnesh
2014-12-03 08:49:49 +00:00
parent 4ae2687478
commit ea23bb33b2
28 changed files with 251 additions and 93 deletions

2
.gitattributes vendored
View File

@@ -446,6 +446,7 @@ forge-game/src/main/java/forge/game/card/CardDamageHistory.java -text
forge-game/src/main/java/forge/game/card/CardFactory.java svneol=native#text/plain
forge-game/src/main/java/forge/game/card/CardFactoryUtil.java svneol=native#text/plain
forge-game/src/main/java/forge/game/card/CardLists.java svneol=native#text/plain
forge-game/src/main/java/forge/game/card/CardPlayOption.java -text
forge-game/src/main/java/forge/game/card/CardPowerToughness.java svneol=native#text/plain
forge-game/src/main/java/forge/game/card/CardPredicates.java svneol=native#text/plain
forge-game/src/main/java/forge/game/card/CardShields.java -text
@@ -12181,6 +12182,7 @@ forge-gui/res/cardsfolder/s/sharding_sphinx.txt svneol=native#text/plain
forge-gui/res/cardsfolder/s/shardless_agent.txt -text
forge-gui/res/cardsfolder/s/shared_animosity.txt -text
forge-gui/res/cardsfolder/s/shared_discovery.txt svneol=native#text/plain
forge-gui/res/cardsfolder/s/shared_fate.txt -text
forge-gui/res/cardsfolder/s/shared_trauma.txt -text
forge-gui/res/cardsfolder/s/shared_triumph.txt svneol=native#text/plain
forge-gui/res/cardsfolder/s/sharpened_pitchfork.txt -text

View File

@@ -34,6 +34,7 @@ import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import forge.card.CardStateName;
import forge.card.CardType;
import forge.card.MagicColor;
import forge.card.CardType.Supertype;
@@ -335,7 +336,7 @@ public class AiController {
sa.setActivatingPlayer(player);
//add alternative costs as additional spell abilities
newAbilities.add(sa);
newAbilities.addAll(GameActionUtil.getAlternativeCosts(sa));
newAbilities.addAll(GameActionUtil.getAlternativeCosts(sa, player));
}
final ArrayList<SpellAbility> result = new ArrayList<SpellAbility>();
@@ -352,6 +353,11 @@ public class AiController {
for (final SpellAbility sa : c.getSpellAbilities()) {
spellAbilities.add(sa);
}
if (c.isFaceDown() && c.isInZone(ZoneType.Exile) && c.mayPlay(player) != null) {
for (final SpellAbility sa : c.getState(CardStateName.Original).getSpellAbilities()) {
spellAbilities.add(sa);
}
}
}
return spellAbilities;
}
@@ -392,11 +398,15 @@ public class AiController {
});
final CardCollection landsNotInHand = new CardCollection(player.getCardsIn(ZoneType.Graveyard));
landsNotInHand.addAll(game.getCardsIn(ZoneType.Exile));
if (!player.getCardsIn(ZoneType.Library).isEmpty()) {
landsNotInHand.add(player.getCardsIn(ZoneType.Library).get(0));
}
for (final Card crd : landsNotInHand) {
if (crd.isLand() && crd.hasKeyword("May be played")) {
if (!(crd.isLand() || (crd.isFaceDown() && crd.getState(CardStateName.Original).getType().isLand()))) {
continue;
}
if (crd.hasKeyword("May be played") || crd.mayPlay(player) != null) {
landList.add(crd);
}
}

View File

@@ -29,6 +29,7 @@ import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPlayOption;
import forge.game.card.CardPredicates;
import forge.game.cost.Cost;
import forge.game.mana.ManaCostBeingPaid;
@@ -143,20 +144,37 @@ public final class GameActionUtil {
/**
* <p>
* getAlternativeCosts.
* Find the alternative costs to a {@link SpellAbility}.
* </p>
*
* @param sa
* a SpellAbility.
* @return an ArrayList<SpellAbility>.
* get alternative costs as additional spell abilities
* a {@link SpellAbility}.
* @param activator
* the {@link Player} for which to calculate available
* @return a {@link List} of {@link SpellAbility} objects, each representing
* a possible alternative cost the provided activator can use to pay
* the provided {@link SpellAbility}.
*/
public static final ArrayList<SpellAbility> getAlternativeCosts(SpellAbility sa) {
ArrayList<SpellAbility> alternatives = new ArrayList<SpellAbility>();
Card source = sa.getHostCard();
public static final List<SpellAbility> getAlternativeCosts(final SpellAbility sa, final Player activator) {
final List<SpellAbility> alternatives = new ArrayList<SpellAbility>();
if (!sa.isBasicSpell()) {
return alternatives;
}
final Card source = sa.getHostCard();
final CardPlayOption playOption = source.mayPlay(activator);
if (sa.isSpell() && playOption != null && playOption.isWithoutManaCost()) {
final SpellAbility newSA = sa.copy();
final SpellAbilityRestriction sar = new SpellAbilityRestriction();
sar.setVariables(sa.getRestrictions());
sar.setZone(null);
newSA.setRestrictions(sar);
newSA.setBasicSpell(false);
newSA.setPayCosts(newSA.getPayCosts().copyWithNoMana());
newSA.setDescription(sa.getDescription() + " (without paying its mana cost)");
alternatives.add(newSA);
}
for (final String keyword : source.getKeywords()) {
if (sa.isSpell() && keyword.startsWith("Flashback")) {
final SpellAbility flashback = sa.copy();
@@ -183,18 +201,6 @@ public final class GameActionUtil {
newSA.setDescription(sa.getDescription() + " (without paying its mana cost)");
alternatives.add(newSA);
}
if (sa.isSpell() && keyword.equals("May be played by your opponent without paying its mana cost")) {
final SpellAbility newSA = sa.copy();
SpellAbilityRestriction sar = new SpellAbilityRestriction();
sar.setVariables(sa.getRestrictions());
sar.setZone(null);
sar.setOpponentOnly(true);
newSA.setRestrictions(sar);
newSA.setBasicSpell(false);
newSA.setPayCosts(newSA.getPayCosts().copyWithNoMana());
newSA.setDescription(sa.getDescription() + " (without paying its mana cost)");
alternatives.add(newSA);
}
if (sa.isSpell() && keyword.startsWith("May be played without paying its mana cost and as though it has flash")) {
final SpellAbility newSA = sa.copy();
SpellAbilityRestriction sar = new SpellAbilityRestriction();

View File

@@ -92,7 +92,7 @@ public class StaticEffects {
boolean setPT = false;
String[] addHiddenKeywords = null;
String addColors = null;
boolean removeMayLookAt = false;
boolean removeMayLookAt = false, removeMayPlay = false;
if (params.containsKey("ChangeColorWordsTo")) {
changeColorWordsTo = params.get("ChangeColorWordsTo");
@@ -158,6 +158,9 @@ public class StaticEffects {
if (params.containsKey("MayLookAt")) {
removeMayLookAt = true;
}
if (params.containsKey("MayPlay")) {
removeMayPlay = true;
}
if (params.containsKey("IgnoreEffectCost")) {
for (final SpellAbility s : se.getSource().getSpellAbilities()) {
@@ -254,6 +257,9 @@ public class StaticEffects {
if (removeMayLookAt) {
affectedCard.setMayLookAt(controller, false);
}
if (removeMayPlay) {
affectedCard.removeMayPlay(controller);
}
}
se.clearTimestamps();
return affectedCards;

View File

@@ -112,8 +112,6 @@ public class EffectEffect extends SpellAbilityEffect {
eff.setImmutable(true);
eff.setEffectSource(hostCard);
// Effects should be Orange or something probably
final Card e = eff;
// Grant SVars first in order to give references to granted abilities

View File

@@ -136,6 +136,8 @@ public class Card extends GameEntity implements Comparable<Card>, IIdentifiable
// if this card is an Aura, what Entity is it enchanting?
private GameEntity enchanting = null;
private final Map<Player, CardPlayOption> mayPlay = Maps.newTreeMap();
// changes by AF animate and continuous static effects - timestamp is the key of maps
private Map<Long, CardChangedType> changedCardTypes = new ConcurrentSkipListMap<Long, CardChangedType>();
private Map<Long, KeywordsChange> changedCardKeywords = new ConcurrentSkipListMap<Long, KeywordsChange>();
@@ -1503,10 +1505,22 @@ public class Card extends GameEntity implements Comparable<Card>, IIdentifiable
public String getAbilityText() {
return getAbilityText(currentState);
}
public String getAbilityText(CardState state) {
public String getAbilityText(final CardState state) {
final CardTypeView type = state.getType();
final StringBuilder sb = new StringBuilder();
if (!mayPlay.isEmpty()) {
sb.append("May be played by: ");
sb.append(Lang.joinHomogenous(mayPlay.entrySet(), new Function<Entry<Player, CardPlayOption>, String>() {
@Override public String apply(final Entry<Player, CardPlayOption> entry) {
return entry.getKey().toString() + entry.getValue().toString();
}
}));
sb.append("\r\n");
}
if (type.isInstant() || type.isSorcery()) {
final StringBuilder sb = abilityTextInstantSorcery(state);
sb.append(abilityTextInstantSorcery(state));
if (haunting != null) {
sb.append("Haunting: ").append(haunting);
@@ -1520,8 +1534,6 @@ public class Card extends GameEntity implements Comparable<Card>, IIdentifiable
return sb.toString().replaceAll("CARDNAME", state.getName());
}
final StringBuilder sb = new StringBuilder();
if (monstrous) {
sb.append("Monstrous\r\n");
}
@@ -2141,6 +2153,17 @@ public class Card extends GameEntity implements Comparable<Card>, IIdentifiable
view.setPlayerMayLook(player, mayLookAt, temp);
}
public final CardPlayOption mayPlay(final Player player) {
return mayPlay.get(player);
}
public final void setMayPlay(final Player player, final boolean withoutManaCost, final boolean ignoreColor) {
final CardPlayOption option = this.mayPlay.get(player);
this.mayPlay.put(player, option == null ? new CardPlayOption(withoutManaCost, ignoreColor) : option.add(withoutManaCost, ignoreColor));
}
public final void removeMayPlay(final Player player) {
this.mayPlay.remove(player);
}
public final CardCollectionView getEquippedBy(boolean allowModify) {
return CardCollection.getView(equippedBy, allowModify);
}
@@ -6246,13 +6269,18 @@ public class Card extends GameEntity implements Comparable<Card>, IIdentifiable
return null;
}
public List<SpellAbility> getAllPossibleAbilities(Player player, boolean removeUnplayable) {
public List<SpellAbility> getAllPossibleAbilities(final Player player, final boolean removeUnplayable) {
// this can only be called by the Human
final List<SpellAbility> abilities = new ArrayList<SpellAbility>();
for (SpellAbility sa : getSpellAbilities()) {
//add alternative costs as additional spell abilities
abilities.add(sa);
abilities.addAll(GameActionUtil.getAlternativeCosts(sa));
abilities.addAll(GameActionUtil.getAlternativeCosts(sa, player));
}
if (isFaceDown() && isInZone(ZoneType.Exile) && mayPlay(player) != null) {
for (final SpellAbility sa : getState(CardStateName.Original).getSpellAbilities()) {
abilities.add(sa);
}
}
for (int i = abilities.size() - 1; i >= 0; i--) {
@@ -6266,7 +6294,7 @@ public class Card extends GameEntity implements Comparable<Card>, IIdentifiable
}
}
if (isLand() && player.canPlayLand(this)) {
if (getState(CardStateName.Original).getType().isLand() && player.canPlayLand(this)) {
Ability.PLAY_LAND_SURROGATE.setHostCard(this);
abilities.add(Ability.PLAY_LAND_SURROGATE);
}

View File

@@ -0,0 +1,36 @@
package forge.game.card;
import org.apache.commons.lang3.StringUtils;
public final class CardPlayOption {
private final boolean withoutManaCost, ignoreManaCostColor;
public CardPlayOption(final boolean withoutManaCost, final boolean ignoreManaCostColor) {
this.withoutManaCost = withoutManaCost;
this.ignoreManaCostColor = ignoreManaCostColor;
}
public CardPlayOption add(final boolean withoutManaCost, final boolean ignoreManaCostColor) {
return new CardPlayOption(isWithoutManaCost() || withoutManaCost, isIgnoreManaCostColor() || ignoreManaCostColor);
}
public boolean isWithoutManaCost() {
return withoutManaCost;
}
public boolean isIgnoreManaCostColor() {
return ignoreManaCostColor;
}
@Override
public String toString() {
if (isWithoutManaCost()) {
return " (without paying its mana cost)";
}
if (isIgnoreManaCostColor()) {
return " (may spend mana as though it were mana of any color to cast it)";
}
return StringUtils.EMPTY;
}
}

View File

@@ -1,9 +1,12 @@
package forge.game.card;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Iterables;
import forge.ImageKeys;
import forge.card.CardStateName;
import forge.card.CardEdition;
@@ -102,6 +105,9 @@ public class CardView extends GameEntityView {
void updateZone(Card c) {
set(TrackableProperty.Zone, c.getZone() == null ? null : c.getZone().getZoneType());
}
public boolean isInZone(final Iterable<ZoneType> zones) {
return Iterables.contains(zones, getZone());
}
public boolean isCloned() {
return get(TrackableProperty.Cloned);
@@ -397,11 +403,17 @@ public class CardView extends GameEntityView {
}
//if viewer is controlled by another player, also check if face can be shown to that player
PlayerView mindSlaveMaster = viewer.getMindSlaveMaster();
final PlayerView mindSlaveMaster = viewer.getMindSlaveMaster();
if (mindSlaveMaster != null && canFaceDownBeShownTo(mindSlaveMaster)) {
return true;
}
return !getController().isOpponentOf(viewer) || getCurrentState().getOpponentMayLook();
if (isInZone(EnumSet.of(ZoneType.Battlefield, ZoneType.Stack, ZoneType.Sideboard)) && getController().equals(viewer)) {
return true;
}
if (getController().isOpponentOf(viewer) && getCurrentState().getOpponentMayLook()) {
return true;
}
return false;
}
public CardView getEquipping() {

View File

@@ -1386,6 +1386,9 @@ public class Player extends GameEntity implements Comparable<Player> {
// Dakkon Blackblade Avatar will use a similar effect
if (canPlayLand(land, ignoreZoneAndTiming)) {
land.setController(this, 0);
if (land.isFaceDown()) {
land.turnFaceUp();
}
game.getAction().moveTo(getZone(ZoneType.Battlefield), land);
// play a sound
@@ -1407,7 +1410,7 @@ public class Player extends GameEntity implements Comparable<Player> {
public final boolean canPlayLand(final Card land) {
return canPlayLand(land, false);
}
public final boolean canPlayLand(Card land, final boolean ignoreZoneAndTiming) {
public final boolean canPlayLand(final Card land, final boolean ignoreZoneAndTiming) {
if (!ignoreZoneAndTiming && !canCastSorcery()) {
return false;
}
@@ -1423,14 +1426,13 @@ public class Player extends GameEntity implements Comparable<Player> {
}
if (land != null && !ignoreZoneAndTiming) {
if (land.getOwner() != this && !land.hasKeyword("May be played by your opponent"))
return false;
if (land.getOwner() == this && land.hasKeyword("May be played by your opponent") && !land.hasKeyword("May be played"))
final boolean mayPlay = land.mayPlay(this) != null;
if (land.getOwner() != this && !mayPlay) {
return false;
}
final Zone zone = game.getZoneOf(land);
if (zone != null && (zone.is(ZoneType.Battlefield) || (!zone.is(ZoneType.Hand) && !land.hasStartOfKeyword("May be played")))) {
if (zone != null && (zone.is(ZoneType.Battlefield) || (!zone.is(ZoneType.Hand) && !(mayPlay || land.hasStartOfKeyword("May be played"))))) {
return false;
}
}

View File

@@ -17,6 +17,7 @@
*/
package forge.game.spellability;
import forge.card.CardStateName;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.card.CardCollection;
@@ -91,7 +92,7 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
}
// for uncastables like lotus bloom, check if manaCost is blank (except for morph spells)
if (!isCastFaceDown() && isBasicSpell() && card.getManaCost().isNoCost()) {
if (!isCastFaceDown() && isBasicSpell() && card.getState(card.isFaceDown() ? CardStateName.Original : card.getCurrentStateName()).getManaCost().isNoCost()) {
return false;
}

View File

@@ -200,9 +200,8 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
return true;
}
Player activator = sa.getActivatingPlayer();
Zone cardZone = activator.getGame().getZoneOf(c);
final Player activator = sa.getActivatingPlayer();
final Zone cardZone = activator.getGame().getZoneOf(c);
if (cardZone == null || !cardZone.is(this.getZone())) {
// If Card is not in the default activating zone, do some additional checks
// Not a Spell, or on Battlefield, return false
@@ -211,12 +210,12 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
return false;
}
if (cardZone.is(ZoneType.Stack)) {
return false;
return false;
}
if (c.hasKeyword("May be played") && activator.equals(c.getController())) {
if (c.mayPlay(activator) != null) {
return true;
}
if (c.hasKeyword("May be played by your opponent") && !activator.equals(c.getController())) {
if (c.hasKeyword("May be played") && activator.equals(c.getController())) {
return true;
}
return false;
@@ -296,7 +295,7 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
return true;
}
if (sa.isSpell() && activator.isOpponentOf(c.getController()) && c.hasKeyword("May be played by your opponent")) {
if (sa.isSpell() && c.mayPlay(activator) != null) {
return true;
}

View File

@@ -257,9 +257,8 @@ public class StaticAbility extends CardTraitBase {
return in;
}
// apply the ability if it has the right mode
/**
* Apply ability.
* Apply ability if it has the right mode.
*
* @param mode
* the mode

View File

@@ -113,6 +113,7 @@ public class StaticAbilityContinuous {
boolean removeSubTypes = false;
boolean removeCreatureTypes = false;
boolean controllerMayLookAt = false;
boolean controllerMayPlay = false, mayPlayWithoutManaCost = false, mayPlayIgnoreColor = false;
//Global rules changes
if (params.containsKey("GlobalRule")) {
@@ -328,6 +329,14 @@ public class StaticAbilityContinuous {
if (params.containsKey("MayLookAt")) {
controllerMayLookAt = true;
}
if (params.containsKey("MayPlay")) {
controllerMayPlay = true;
if (params.containsKey("MayPlayWithoutManaCost")) {
mayPlayWithoutManaCost = true;
} else if (params.containsKey("MayPlayIgnoreColor")) {
mayPlayIgnoreColor = true;
}
}
if (params.containsKey("IgnoreEffectCost")) {
String cost = params.get("IgnoreEffectCost");
@@ -558,8 +567,11 @@ public class StaticAbilityContinuous {
if (controllerMayLookAt) {
affectedCard.setMayLookAt(controller, true);
}
if (controllerMayPlay) {
affectedCard.setMayPlay(controller, mayPlayWithoutManaCost, mayPlayIgnoreColor);
}
}
return affectedCards;
}

View File

@@ -39,14 +39,17 @@ import forge.game.zone.ZoneType;
import java.util.*;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimaps;
public class TriggerHandler {
private final ArrayList<TriggerType> suppressedModes = new ArrayList<TriggerType>();
private final ArrayList<Trigger> activeTriggers = new ArrayList<Trigger>();
private final List<TriggerType> suppressedModes = Collections.synchronizedList(new ArrayList<TriggerType>());
private final List<Trigger> activeTriggers = Collections.synchronizedList(new ArrayList<Trigger>());
private final ArrayList<Trigger> delayedTriggers = new ArrayList<Trigger>();
private final ArrayListMultimap<Player, Trigger> playerDefinedDelayedTriggers = ArrayListMultimap.create();
private final List<TriggerWaiting> waitingTriggers = new ArrayList<TriggerWaiting>();
private final List<Trigger> delayedTriggers = Collections.synchronizedList(new ArrayList<Trigger>());
private final ListMultimap<Player, Trigger> playerDefinedDelayedTriggers = Multimaps.synchronizedListMultimap(ArrayListMultimap.<Player, Trigger>create());
private final List<TriggerWaiting> waitingTriggers = Collections.synchronizedList(new ArrayList<TriggerWaiting>());
private final Game game;
public TriggerHandler(Game gameState) {
@@ -268,7 +271,7 @@ public class TriggerHandler {
boolean checkStatics = false;
// Static triggers
for (final Trigger t : activeTriggers) {
for (final Trigger t : Lists.newArrayList(activeTriggers)) {
if (t.isStatic() && canRunTrigger(t, mode, runParams)) {
runSingleTrigger(t, runParams);
checkStatics = true;

View File

@@ -216,9 +216,11 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
if (sp.isSpell()) {
source.setController(activator, 0);
Spell spell = (Spell) sp;
final Spell spell = (Spell) sp;
if (spell.isCastFaceDown()) {
source.turnFaceDown();
} else if (source.isFaceDown()) {
source.turnFaceUp();
}
}

View File

@@ -38,8 +38,8 @@ public enum TrackableProperty {
ChosenDirection(TrackableTypes.EnumType(Direction.class)),
Remembered(TrackableTypes.StringType),
NamedCard(TrackableTypes.StringType),
PlayerMayLook(TrackableTypes.PlayerViewCollectionType),
PlayerMayLookTemp(TrackableTypes.PlayerViewCollectionType),
PlayerMayLook(TrackableTypes.PlayerViewCollectionType, false),
PlayerMayLookTemp(TrackableTypes.PlayerViewCollectionType, false),
Equipping(TrackableTypes.CardViewType),
EquippedBy(TrackableTypes.CardViewCollectionType),
Enchanting(TrackableTypes.GameEntityViewType),

View File

@@ -7,9 +7,8 @@ T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage
SVar:TrigExile:AB$ Mill | Cost$ 0 | Defined$ TriggeredTarget | NumCards$ 1 | Destination$ Exile | RememberMilled$ True | SubAbility$ DBGainLife
SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ X | References$ X | SubAbility$ DBEffect
SVar:X:Remembered$CardManaCost
SVar:DBEffect:DB$ Effect | StaticAbilities$ STPlay,STPlay2 | Triggers$ TriggerCastDoM | SVars$ TrigRemoveSelf | RememberObjects$ Remembered | SubAbility$ DBCleanup
SVar:STPlay:Mode$ Continuous | EffectZone$ Command | Affected$ Card.IsRemembered+nonLand+YouOwn | AddHiddenKeyword$ May be played & May spend mana as though it were mana of any color to cast CARDNAME | AffectedZone$ Exile | Description$ Until end of turn, you may cast that card and you may spend mana as though it were mana of any color to cast it.
SVar:STPlay2:Mode$ Continuous | EffectZone$ Command | Affected$ Card.IsRemembered+nonLand+OppOwn | AddHiddenKeyword$ May be played by your opponent & May spend mana as though it were mana of any color to cast CARDNAME | AffectedZone$ Exile
SVar:DBEffect:DB$ Effect | StaticAbilities$ STPlay | Triggers$ TriggerCastDoM | SVars$ TrigRemoveSelf | RememberObjects$ Remembered | SubAbility$ DBCleanup
SVar:STPlay:Mode$ Continuous | MayPlay$ True | MayPlayIgnoreColor$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonLand | AffectedZone$ Exile | Description$ Until end of turn, you may cast that card and you may spend mana as though it were mana of any color to cast it.
SVar:TriggerCastDoM:Mode$ SpellCast | ValidCard$ Card.IsRemembered | TriggerZones$ Command | Execute$ TrigRemoveSelf | Static$ True
SVar:TrigRemoveSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True

View File

@@ -4,8 +4,11 @@ Types:Creature Vampire Wizard
PT:3/3
K:Flying
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigChangeZone | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, that player exiles a card from his or her hand. You may play that card for as long as it remains exiled.
SVar:TrigChangeZone:AB$ ChangeZone | Cost$ 0 | Origin$ Hand | Destination$ Exile | DefinedPlayer$ TriggeredTarget | Chooser$ TriggeredTarget | ChangeType$ Card | ChangeNum$ 1 | RememberChanged$ True | SubAbility$ DBPump
SVar:DBPump:DB$ Pump | Defined$ Remembered | KW$ May be played by your opponent | PumpZone$ Exile | Permanent$ True | SubAbility$ DBCleanup
SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | DefinedPlayer$ TriggeredTarget | Chooser$ TriggeredTarget | ChangeType$ Card | ChangeNum$ 1 | RememberChanged$ True | SubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | StaticAbilities$ STPlay | Triggers$ TCleanup | RememberObjects$ Remembered | SVars$ DBExileSelf | SubAbility$ DBCleanup
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play a card for as long as it remains exiled.
SVar:TCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | Execute$ DBExileSelf | Static$ True
SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
A:AB$ Regenerate | Cost$ Sac<1/Human> | SpellDescription$ Regenerate CARDNAME.
SVar:RemAIDeck:True

View File

@@ -5,7 +5,8 @@ PT:4/4
A:AB$ Pump | Cost$ 1 | ValidTgts$ Creature | TgtZone$ Graveyard | TgtPrompt$ Select target creature card | PumpZone$ Graveyard | SubAbility$ PumpYour | RememberObjects$ Targeted | SpellDescription$ You may cast target creature card in a graveyard this turn. When you cast that card this turn, CARDNAME gains all activated abilities of that card until end of turn.
SVar:PumpYour:DB$ Pump | Defined$ Remembered | ConditionCheckSVar$ SelectKW | ConditionSVarCompare$ EQ1 | KW$ HIDDEN May be played | PumpZone$ Graveyard | SubAbility$ PumpTheir | References$ SelectKW
SVar:PumpTheir:DB$ Pump | Defined$ Remembered | ConditionCheckSVar$ SelectKW | ConditionSVarCompare$ EQ0 | KW$ HIDDEN May be played by your opponent | PumpZone$ Graveyard | SubAbility$ FXCast | References$ SelectKW
SVar:FXCast:DB$ Effect | Name$ Havengul Lich Delayed Trigger | Triggers$ DTCast | SVars$ StealAbs,STSteal,CleanupDT | RememberObjects$ Targeted
SVar:FXCast:DB$ Effect | Name$ Havengul Lich Delayed Trigger | StaticAbilities$ STPlay | Triggers$ DTCast | SVars$ StealAbs,STSteal,CleanupDT | RememberObjects$ Targeted
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Graveyard | Description$ Until end of turn, you may cast a creature card in a graveyard.
SVar:DTCast:Mode$ SpellCast | ValidCard$ Card.IsRemembered | Execute$ StealAbs | TriggerDescription$ When you cast that card this turn, Havengul Lich gains all activated abilities of that card until end of turn.
SVar:StealAbs:DB$ Effect | Name$ Havengul Lich effect | RememberObjects$ TriggeredCard | StaticAbilities$ STSteal | SubAbility$ CleanupDT
SVar:STSteal:Mode$ Continuous | Affected$ EffectSource | EffectZone$ Command | GainsAbilitiesOf$ Creature.IsRemembered | GainsAbilitiesOfZones$ Library,Hand,Stack,Battlefield,Graveyard,Exile,Command

View File

@@ -5,9 +5,8 @@ PT:3/3
K:Morph:4 U U
T:Mode$ TurnFaceUp | ValidCard$ Card.Self | Execute$ TrigCounter | TriggerZones$ Battlefield | TriggerDescription$ When CARDNAME is turned face up, counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard. You may cast that card without paying its mana cost as long as it remains exiled.
SVar:TrigCounter:AB$ Counter | Cost$ 0 | TargetType$ Spell | TgtPrompt$ Select target spell | ValidTgts$ Card | RememberCountered$ True | ForgetOtherTargets$ True | Destination$ Exile | SubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | Name$ Kheru Spellsnatcher Effect | RememberObjects$ Remembered | StaticAbilities$ PlayOpp,PlayYou | Duration$ Permanent | Triggers$ TrigCleanup | SVars$ DBCleanup | References$ PlayOpp,PlayYou,TrigCleanup,DBCleanup
SVar:PlayOpp:Mode$ Continuous | EffectZone$ Command | Affected$ Card.IsRemembered+OppOwn | AffectedZone$ Exile | AddHiddenKeyword$ May be played by your opponent without paying its mana cost | Description$ You may play cards exiled with Kheru Spellsnatcher.
SVar:PlayYou:Mode$ Continuous | EffectZone$ Command | Affected$ Card.IsRemembered+YouOwn | AffectedZone$ Exile | AddHiddenKeyword$ May be played without paying its mana cost
SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ STPlay | Duration$ Permanent | Triggers$ TrigCleanup | SVars$ DBCleanup | References$ PlayOpp,PlayYou,TrigCleanup,DBCleanup
SVar:STPlay:Mode$ Continuous | MayPlay$ True | MayPlayWithoutManaCost$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may cast cards without paying their mana cost as long as they remain exiled.
SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBCleanup | Static$ True
SVar:DBCleanup:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
SVar:RemAIDeck:True

View File

@@ -3,9 +3,12 @@ ManaCost:3 U
Types:Creature Faerie Rogue
PT:1/4
K:Flying
A:AB$ Mill | Cost$ 1 U Q | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | NumCards$ 1 | Destination$ Exile | RememberMilled$ True | SubAbility$ DBPump | SpellDescription$ Target opponent exiles the top card of his or her library. Until end of turn, you may play that card.
SVar:DBPump:DB$ Pump | Defined$ Remembered | KW$ May be played by your opponent | PumpZone$ Exile | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
A:AB$ Mill | Cost$ 1 U Q | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | NumCards$ 1 | Destination$ Exile | RememberMilled$ True | SubAbility$ DBEffect | SpellDescription$ Target opponent exiles the top card of his or her library. Until end of turn, you may play that card.
SVar:DBEffect:DB$ Effect | Duration$ EndOfTurn | RememberObjects$ Remembered | StaticAbilities$ STPlay | Triggers$ TrigCleanup | SVars$ DBExileSelf | SubAbility$ DBCleanup
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play a card this turn.
SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBExileSelf | Static$ True
SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ Truek
SVar:RemAIDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/knacksaw_clique.jpg
Oracle:Flying\n{1}{U}, {Q}: Target opponent exiles the top card of his or her library. Until end of turn, you may play that card. ({Q} is the untap symbol.)

View File

@@ -2,9 +2,11 @@ Name:Muse Vessel
ManaCost:4
Types:Artifact
A:AB$ ChangeZone | Cost$ 3 T | ValidTgts$ Player | TgtPrompt$ Select target player | SorcerySpeed$ True | Origin$ Hand | Destination$ Exile | ChangeType$ Card | ChangeNum$ 1 | RememberChanged$ True | Chooser$ Targeted | Hidden$ True | IsCurse$ True | Mandatory$ True | SpellDescription$ Target player exiles a card from his or her hand. Activate this ability only any time you could cast a sorcery.
A:AB$ ChooseCard | Cost$ 1 | ChoiceZone$ Exile | Choices$ Card.IsRemembered | Amount$ 1 | ChoiceTitle$ Choose a card exiled with Muse Vessel | SubAbility$ DBPlayYouOwn | AILogic$ Never | SpellDescription$ Choose a card exiled with CARDNAME. You may play that card this turn.
SVar:DBPlayYouOwn:DB$ Pump | Defined$ ChosenCard | KW$ HIDDEN May be played | PumpZone$ Exile | ConditionDefined$ ChosenCard | ConditionPresent$ Card.YouOwn | ConditionCompare$ EQ1 | SubAbility$ DBPlayOppOwn
SVar:DBPlayOppOwn:DB$ Pump | Defined$ ChosenCard | KW$ HIDDEN May be played by your opponent | PumpZone$ Exile | ConditionDefined$ ChosenCard | ConditionPresent$ Card.OppOwn | ConditionCompare$ EQ1
A:AB$ ChooseCard | Cost$ 1 | ChoiceZone$ Exile | Choices$ Card.IsRemembered | Amount$ 1 | ChoiceTitle$ Choose a card exiled with Muse Vessel | SubAbility$ DBEffect | AILogic$ Never | SpellDescription$ Choose a card exiled with CARDNAME. You may play that card this turn.
SVar:DBEffect:DB$ Effect | Duration$ EndOfTurn | StaticAbilities$ STPlay | Triggers$ TrigCleanup | SVars$ DBExileSelf | RememberObjects$ ChosenCard | SubAbility$ DBCleanup
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play a card this turn.
SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBExileSelf | Static$ True
SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered | Execute$ DBForget
SVar:DBForget:DB$ Pump | Defined$ TriggeredCard | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup

View File

@@ -5,8 +5,7 @@ PT:2/3
K:Flying
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigMill | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, that player exiles the top card of his or her library.
SVar:TrigMill:AB$ Mill | Cost$ 0 | Defined$ TriggeredTarget | NumCards$ 1 | Destination$ Exile | RememberMilled$ True
S:Mode$ Continuous | Affected$ Card.IsRemembered+OppOwn | AffectedZone$ Exile | AddHiddenKeyword$ May be played by your opponent | Description$ You may play cards exiled with CARDNAME.
S:Mode$ Continuous | Affected$ Card.IsRemembered+YouOwn | AffectedZone$ Exile | AddHiddenKeyword$ May be played
S:Mode$ Continuous | MayPlay$ True | Affected$ Card.IsRemembered+OppOwn | AffectedZone$ Exile | Description$ You may play cards exiled with CARDNAME.
T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered | Execute$ DBForget
SVar:DBForget:DB$ Pump | Defined$ TriggeredCard | ForgetObjects$ TriggeredCard
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | Static$ True | ValidCard$ Card.Self | Execute$ DBCleanup

View File

@@ -1,8 +1,11 @@
Name:Ornate Kanzashi
ManaCost:5
Types:Artifact
A:AB$ Mill | Cost$ 2 T | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | NumCards$ 1 | Destination$ Exile | RememberMilled$ True | SubAbility$ DBPump | SpellDescription$ Target opponent exiles the top card of his or her library. You may play that card this turn.
SVar:DBPump:DB$ Pump | Defined$ Remembered | KW$ May be played by your opponent | PumpZone$ Exile | SubAbility$ DBCleanup
A:AB$ Mill | Cost$ 2 T | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | NumCards$ 1 | Destination$ Exile | RememberMilled$ True | SubAbility$ DBEffect | SpellDescription$ Target opponent exiles the top card of his or her library. You may play that card this turn.
SVar:DBEffect:DB$ Effect | Duration$ EndOfTurn | RememberObjects$ Remembered | StaticAbilities$ STPlay | Triggers$ TrigCleanup | SVars$ DBExileSelf | SubAbility$ DBCleanup
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play a card this turn.
SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBExileSelf | Static$ True
SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:RemAIDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/ornate_kanzashi.jpg

View File

@@ -6,9 +6,9 @@ SVar:DBChangeZone:DB$ ChangeZoneAll | ChangeType$ Instant.IsRemembered,Sorcery.I
SVar:DBForgetOther:DB$ Cleanup | ClearRemembered$ True | ConditionCheckSVar$ X | ConditionSVarCompare$ EQ0 | SubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | StaticAbilities$ MischiefPlay | Triggers$ TrigEOT,TrigChangesZone | SVars$ MischiefCleanup,MischiefReturn | RememberObjects$ Remembered | Permanent$ True | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:MischiefPlay:Mode$ Continuous | EffectZone$ Command | Affected$ Card.IsRemembered+OppOwn | AffectedZone$ Exile | AddHiddenKeyword$ May be played by your opponent without paying its mana cost | Description$ You may play that exiled card.
SVar:MischiefPlay:Mode$ Continuous | MayPlay$ True | MayPlayWithoutManaCost$ True | EffectZone$ Command | Affected$ Card.IsRemembered+OppOwn | AffectedZone$ Exile | Description$ You may cast a card without paying its mana cost as long as it remains exiled.
SVar:TrigEOT:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Command | Execute$ MischiefReturn | TriggerDescription$ At the beginning of the next end step, if you haven't cast it, return it to its owner's hand.
SVar:MischiefReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Hand |
SVar:MischiefReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Hand
SVar:TrigChangesZone:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCard$ Card.IsRemembered | Execute$ MischiefCleanup
SVar:MischiefCleanup:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
SVar:X:Count$ValidExile Instant.IsRemembered,Sorcery.IsRemembered

View File

@@ -1,8 +1,12 @@
Name:Praetor's Grasp
ManaCost:1 B B
Types:Sorcery
A:SP$ ChangeZone | Cost$ 1 B B | Origin$ Library | Destination$ Exile | ValidTgts$ Opponent | ChangeType$ Card | ChangeNum$ 1 | IsCurse$ True | RememberChanged$ True | SubAbility$ DBPump | StackDescription$ SpellDescription | SpellDescription$ Search target opponent's library for a card and exile it face down. Then that player shuffles his or her library. You may look at and play that card for as long as it remains exiled.
SVar:DBPump:DB$ Pump | Defined$ Remembered | KW$ Your opponent may look at this card. & May be played by your opponent | PumpZone$ Exile | Permanent$ True
A:SP$ ChangeZone | Cost$ 1 B B | Origin$ Library | Destination$ Exile | ExileFaceDown$ True | ValidTgts$ Opponent | ChangeType$ Card | ChangeNum$ 1 | IsCurse$ True | RememberChanged$ True | SubAbility$ DBEffect | StackDescription$ SpellDescription | SpellDescription$ Search target opponent's library for a card and exile it face down. Then that player shuffles his or her library. You may look at and play that card for as long as it remains exiled.
SVar:DBEffect:DB$ Effect | MayLookAt$ True | MayPlay$ True | RememberObjects$ Remembered | StaticAbilities$ STPlay | Duration$ Permanent | Triggers$ TrigCleanup | SVars$ DBExileSelf | SubAbility$ DBCleanup
SVar:STPlay:Mode$ Continuous | MayLookAt$ True | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may look at and play a card as long as it remains exiled.
SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBExileSelf | Static$ True
SVar:DBExileSelf:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:RemAIDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/praetors_grasp.jpg
Oracle:Search target opponent's library for a card and exile it face down. Then that player shuffles his or her library. You may look at and play that card for as long as it remains exiled.

View File

@@ -0,0 +1,18 @@
Name:Shared Fate
ManaCost:4 U
Types:Enchantment
Text:If a player would draw a card, that player exiles the top card of an opponent's library face down instead. Each player may look at and play cards he or she exiled with CARDNAME.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigEffects | Static$ True
#Create an effect for each player. The effect contains both Shared Fate's abilities.
SVar:TrigEffects:DB$ RepeatEach | RepeatPlayers$ Each | RepeatSubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | EffectOwner$ Remembered | StaticAbilities$ STPlay | Triggers$ TrigCleanup | ReplacementEffects$ RDraw | SVars$ DBChooseOpp,DBExile,DBCleanup | Duration$ UntilHostLeavesPlay
SVar:RDraw:Event$ Draw | ActiveZones$ Command | ValidPlayer$ You | ReplaceWith$ DBChooseOpp | Description$ If you would draw a card, exile the top card of an opponent's library face down instead.
SVar:DBChooseOpp:DB$ ChoosePlayer | ChoiceTitle$ Choose an opponent whose top library card to exile | Choices$ Player.Opponent | AILogic$ Curse | SubAbility$ DBExile
SVar:DBExile:DB$ Mill | NumCards$ 1 | Destination$ Exile | ExileFaceDown$ True | Defined$ Player.Chosen | RememberMilled$ True
SVar:STPlay:Mode$ Continuous | MayLookAt$ True | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may look at and play cards exiled with Shared Fate.
SVar:TrigCleanup:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | TriggerZones$ Command | Execute$ DBCleanup | Static$ True
SVar:DBCleanup:DB$ Cleanup | ForgetDefined$ TriggeredCard
SVar:RemAIDeck:True
SVar:RemRandomDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/shared_fate.jpg
Oracle:If a player would draw a card, that player exiles the top card of an opponent's library face down instead.\nEach player may look at and play cards he or she exiled with Shared Fate.

View File

@@ -17,14 +17,20 @@
*/
package forge.player;
import java.util.Collections;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Iterables;
import forge.card.CardStateName;
import forge.card.CardType;
import forge.card.MagicColor;
import forge.game.Game;
import forge.game.GameObject;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardPlayOption;
import forge.game.cost.CostPartMana;
import forge.game.cost.CostPayment;
import forge.game.mana.ManaPool;
@@ -37,10 +43,6 @@ import forge.game.spellability.TargetRestrictions;
import forge.game.zone.Zone;
import forge.util.FCollection;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
/**
* <p>
* SpellAbility_Requirements class.
@@ -66,17 +68,26 @@ public class HumanPlaySpellAbility {
// used to rollback
Zone fromZone = null;
CardStateName fromState = null;
int zonePosition = 0;
final ManaPool manapool = human.getManaPool();
final Card c = ability.getHostCard();
boolean manaConversion = (ability.isSpell() && c.hasKeyword("May spend mana as though it were mana of any color to cast CARDNAME"));
final CardPlayOption option = c.mayPlay(human);
boolean manaConversion = (ability.isSpell() && (c.hasKeyword("May spend mana as though it were mana of any color to cast CARDNAME")
|| (option != null && option.isIgnoreManaCostColor())));
boolean playerManaConversion = human.hasManaConversion()
&& human.getController().confirmAction(ability, null, "Do you want to spend mana as though it were mana of any color to pay the cost?");
if (ability instanceof Spell && !c.isCopiedSpell()) {
fromZone = game.getZoneOf(c);
fromState = c.getCurrentStateName();
if (fromZone != null) {
zonePosition = fromZone.getCards().indexOf(c);
zonePosition = fromZone.getCards().indexOf(c);
}
// Turn face-down card face up (except case of morph spell)
if (ability instanceof Spell && !((Spell) ability).isCastFaceDown() && fromState == CardStateName.FaceDown) {
c.turnFaceUp();
}
ability.setHostCard(game.getAction().moveToStack(c));
}
@@ -100,7 +111,7 @@ public class HumanPlaySpellAbility {
if (!prerequisitesMet) {
if (!ability.isTrigger()) {
rollbackAbility(fromZone, zonePosition);
rollbackAbility(fromZone, fromState, zonePosition);
if (ability.getHostCard().isMadness()) {
// if a player failed to play madness cost, move the card to graveyard
game.getAction().moveToGraveyard(c);
@@ -125,8 +136,7 @@ public class HumanPlaySpellAbility {
if (skipStack) {
AbilityUtils.resolve(ability);
}
else {
} else {
enusureAbilityHasDescription(ability);
game.getStack().addAndUnfreeze(ability);
}
@@ -182,13 +192,14 @@ public class HumanPlaySpellAbility {
}
}
private void rollbackAbility(Zone fromZone, int zonePosition) {
private void rollbackAbility(final Zone fromZone, final CardStateName fromState, final int zonePosition) {
// cancel ability during target choosing
final Game game = ability.getActivatingPlayer().getGame();
if (fromZone != null) { // and not a copy
// add back to where it came from
game.getAction().moveTo(fromZone, ability.getHostCard(), zonePosition >= 0 ? Integer.valueOf(zonePosition) : null);
ability.getHostCard().setState(fromState, true);
}
clearTargets(ability);
@@ -255,7 +266,7 @@ public class HumanPlaySpellAbility {
for (String aVar : announce.split(",")) {
String varName = aVar.trim();
if ("CreatureType".equals(varName)) {
String choice = pc.chooseSomeType("Creature", ability, CardType.Constant.CREATURE_TYPES, new ArrayList<String>());
final String choice = pc.chooseSomeType("Creature", ability, CardType.Constant.CREATURE_TYPES, Collections.<String>emptyList());
ability.getHostCard().setChosenType(choice);
}
if ("ChooseNumber".equals(varName)) {