From c8d69cbbb9a3e65fcff06e6aae73f54cdd7fa440 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Mon, 14 Sep 2020 19:13:54 +0000 Subject: [PATCH] Resolve "ZNR: Modal Double Faced Cards" --- .../main/java/forge/ai/ComputerUtilCard.java | 2 +- .../main/java/forge/card/CardSplitType.java | 3 +- .../main/java/forge/card/CardStateName.java | 1 + .../item/generation/BoosterGenerator.java | 7 +- .../src/main/java/forge/util/ImageUtil.java | 2 +- .../src/main/java/forge/game/card/Card.java | 120 ++++++++++++------ .../java/forge/game/card/CardFactory.java | 17 +-- .../java/forge/game/card/CardFactoryUtil.java | 2 +- .../main/java/forge/game/player/Player.java | 3 +- .../forge/game/spellability/LandAbility.java | 44 ++++++- .../java/forge/game/spellability/Spell.java | 36 ++---- .../forge/game/spellability/SpellAbility.java | 29 ++--- .../spellability/SpellAbilityRestriction.java | 5 +- ...branchloft_pathway_boulderloft_pathway.txt | 14 ++ ...emerias_call_emeria_shattered_skyclave.txt | 19 +++ .../w_4_4_angel_warrior_flying.txt | 7 + .../java/forge/match/AbstractGuiGame.java | 1 + 17 files changed, 198 insertions(+), 114 deletions(-) create mode 100644 forge-gui/res/cardsfolder/upcoming/ZNR/branchloft_pathway_boulderloft_pathway.txt create mode 100644 forge-gui/res/cardsfolder/upcoming/ZNR/emerias_call_emeria_shattered_skyclave.txt create mode 100644 forge-gui/res/tokenscripts/w_4_4_angel_warrior_flying.txt diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index b7e96d35549..69b9191e6b4 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -1856,7 +1856,7 @@ public class ComputerUtilCard { public static AiPlayDecision checkNeedsToPlayReqs(final Card card, final SpellAbility sa) { Game game = card.getGame(); - boolean isRightSplit = sa != null && sa.isRightSplit(); + boolean isRightSplit = sa != null && sa.getCardState() != null; String needsToPlayName = isRightSplit ? "SplitNeedsToPlay" : "NeedsToPlay"; String needsToPlayVarName = isRightSplit ? "SplitNeedsToPlayVar" : "NeedsToPlayVar"; diff --git a/forge-core/src/main/java/forge/card/CardSplitType.java b/forge-core/src/main/java/forge/card/CardSplitType.java index f1e53ed0898..43670c7bafe 100644 --- a/forge-core/src/main/java/forge/card/CardSplitType.java +++ b/forge-core/src/main/java/forge/card/CardSplitType.java @@ -9,7 +9,8 @@ public enum CardSplitType Meld(FaceSelectionMethod.USE_ACTIVE_FACE, CardStateName.Meld), Split(FaceSelectionMethod.COMBINE, CardStateName.RightSplit), Flip(FaceSelectionMethod.USE_PRIMARY_FACE, CardStateName.Flipped), - Adventure(FaceSelectionMethod.USE_PRIMARY_FACE, CardStateName.Adventure); + Adventure(FaceSelectionMethod.USE_PRIMARY_FACE, CardStateName.Adventure), + Modal(FaceSelectionMethod.USE_ACTIVE_FACE, CardStateName.Modal); CardSplitType(FaceSelectionMethod calcMode, CardStateName stateName) { method = calcMode; diff --git a/forge-core/src/main/java/forge/card/CardStateName.java b/forge-core/src/main/java/forge/card/CardStateName.java index 70b857dc5c8..90149dde340 100644 --- a/forge-core/src/main/java/forge/card/CardStateName.java +++ b/forge-core/src/main/java/forge/card/CardStateName.java @@ -10,6 +10,7 @@ public enum CardStateName { LeftSplit, RightSplit, Adventure, + Modal ; diff --git a/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java b/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java index 831ba7f21f6..8f5d18715d6 100644 --- a/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java +++ b/forge-core/src/main/java/forge/item/generation/BoosterGenerator.java @@ -532,7 +532,12 @@ public class BoosterGenerator { Predicate toAdd = null; if (operator.equalsIgnoreCase(BoosterSlots.DUAL_FACED_CARD)) { - toAdd = Predicates.compose(Predicates.or(CardRulesPredicates.splitType(CardSplitType.Transform), CardRulesPredicates.splitType(CardSplitType.Meld)), + toAdd = Predicates.compose( + Predicates.or( + CardRulesPredicates.splitType(CardSplitType.Transform), + CardRulesPredicates.splitType(CardSplitType.Meld), + CardRulesPredicates.splitType(CardSplitType.Modal) + ), PaperCard.FN_GET_RULES); } else if (operator.equalsIgnoreCase(BoosterSlots.LAND)) { toAdd = Predicates.compose(CardRulesPredicates.Presets.IS_LAND, PaperCard.FN_GET_RULES); } else if (operator.equalsIgnoreCase(BoosterSlots.BASIC_LAND)) { toAdd = IPaperCard.Predicates.Presets.IS_BASIC_LAND; diff --git a/forge-core/src/main/java/forge/util/ImageUtil.java b/forge-core/src/main/java/forge/util/ImageUtil.java index 4b2e515f8fd..89bd4039cbe 100644 --- a/forge-core/src/main/java/forge/util/ImageUtil.java +++ b/forge-core/src/main/java/forge/util/ImageUtil.java @@ -81,7 +81,7 @@ public class ImageUtil { public static boolean hasBackFacePicture(PaperCard cp) { CardSplitType cst = cp.getRules().getSplitType(); - return cst == CardSplitType.Transform || cst == CardSplitType.Flip || cst == CardSplitType.Meld; + return cst == CardSplitType.Transform || cst == CardSplitType.Flip || cst == CardSplitType.Meld || cst == CardSplitType.Modal; } public static String getNameToUse(PaperCard cp, boolean backFace) { diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index c98fc98df5f..1ec7fbe8a83 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -338,28 +338,14 @@ public class Card extends GameEntity implements Comparable { public CardStateName getAlternateStateName() { if (hasAlternateState()) { if (isSplitCard()) { - if (currentStateName == CardStateName.RightSplit) { - return CardStateName.LeftSplit; - } - else { - return CardStateName.RightSplit; + return currentStateName == CardStateName.RightSplit ? CardStateName.LeftSplit : CardStateName.RightSplit; + } else if (getRules() != null) { + CardStateName changedState = getRules().getSplitType().getChangedStateName(); + if (currentStateName != changedState) { + return changedState; } } - else if (isFlipCard() && currentStateName != CardStateName.Flipped) { - return CardStateName.Flipped; - } - else if (isDoubleFaced() && currentStateName != CardStateName.Transformed) { - return CardStateName.Transformed; - } - else if (isMeldable() && currentStateName != CardStateName.Meld) { - return CardStateName.Meld; - } - else if (this.isAdventureCard() && currentStateName != CardStateName.Adventure) { - return CardStateName.Adventure; - } - else { - return CardStateName.Original; - } + return CardStateName.Original; } else if (isFaceDown()) { return CardStateName.Original; @@ -642,7 +628,7 @@ public class Card extends GameEntity implements Comparable { } public boolean turnFaceDown(boolean override) { - if (override || (!isDoubleFaced() && !isMeldable())) { + if (override || !hasBackSide()) { facedown = true; if (setState(CardStateName.FaceDown, true)) { runFacedownCommands(); @@ -738,6 +724,10 @@ public class Card extends GameEntity implements Comparable { return getName(currentState); } + public final String getName(CardStateName stateName) { + return getName(getState(stateName)); + } + public final String getName(CardState state) { if (changedCardNames.isEmpty()) { return state.getName(); @@ -798,6 +788,14 @@ public class Card extends GameEntity implements Comparable { return getRules() != null && getRules().getSplitType() == CardSplitType.Meld; } + public final boolean isModal() { + return getRules() != null && getRules().getSplitType() == CardSplitType.Modal; + } + + public final boolean hasBackSide() { + return isDoubleFaced() || isMeldable() || isModal(); + } + public final boolean isFlipCard() { return hasState(CardStateName.Flipped); } @@ -813,6 +811,9 @@ public class Card extends GameEntity implements Comparable { public final boolean isBackSide() { return backside; } + public final void setBackSide(boolean value) { + backside = value; + } public boolean isCloned() { return !clonedStates.isEmpty(); @@ -3380,13 +3381,13 @@ public class Card extends GameEntity implements Comparable { public final CardStateName getFaceupCardStateName() { if (isFlipped() && hasState(CardStateName.Flipped)) { return CardStateName.Flipped; - } else if (backside && isDoubleFaced() && hasState(CardStateName.Transformed)) { - return CardStateName.Transformed; - } else if (backside && isMeldable() && hasState(CardStateName.Meld)) { - return CardStateName.Meld; - } else { - return CardStateName.Original; + } else if (backside && hasBackSide()) { + CardStateName stateName = getRules().getSplitType().getChangedStateName(); + if (hasState(stateName)) { + return stateName; + } } + return CardStateName.Original; } private final CardCloneStates getLastClonedState() { @@ -6068,20 +6069,13 @@ public class Card extends GameEntity implements Comparable { } public void setSplitStateToPlayAbility(final SpellAbility sa) { - if (isAdventureCard()) { - if (sa.isAdventure()) { - setState(CardStateName.Adventure, true); + CardStateName stateName = sa.getCardState(); + if (hasState(stateName)) { + setState(stateName, true); + // need to set backSide value according to the SplitType + if (hasBackSide()) { + setBackSide(getRules().getSplitType().getChangedStateName().equals(stateName)); } - return; - } - if (!isSplitCard()) { - return; // just in case - } - // Split card support - if (sa.isLeftSplit()) { - setState(CardStateName.LeftSplit, true); - } else if (sa.isRightSplit()) { - setState(CardStateName.RightSplit, true); } } @@ -6167,6 +6161,52 @@ public class Card extends GameEntity implements Comparable { } } + if (isModal() && hasState(CardStateName.Modal)) { + if (getState(CardStateName.Modal).getType().isLand() && !getLastKnownZone().is(ZoneType.Battlefield)) { + LandAbility la = new LandAbility(this, player, null); + la.setCardState(CardStateName.Modal); + + Card source = CardUtil.getLKICopy(this); + boolean lkicheck = true; + + // if Card is Facedown, need to check if MayPlay still applies + if (isFaceDown()) { + source.forceTurnFaceUp(); + } + + source.setSplitStateToPlayAbility(la); + + if (la.canPlay(source)) { + abilities.add(la); + } + + if (lkicheck) { + // double freeze tracker, so it doesn't update view + game.getTracker().freeze(); + CardCollection preList = new CardCollection(source); + game.getAction().checkStaticAbilities(false, Sets.newHashSet(source), preList); + } + + // extra for MayPlay + for (CardPlayOption o : source.mayPlay(player)) { + la = new LandAbility(this, player, o.getAbility()); + la.setCardState(CardStateName.Modal); + if (la.canPlay(source)) { + abilities.add(la); + } + } + + // reset static abilities + if (lkicheck) { + game.getAction().checkStaticAbilities(false); + // clear delayed changes, this check should not have updated the view + game.getTracker().clearDelayed(); + // need to unfreeze tracker + game.getTracker().unfreeze(); + } + } + } + return abilities; } diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index 74bd7d6ad75..bfcb56b02a8 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -240,8 +240,8 @@ public class CardFactory { c.setState(CardStateName.Flipped, false); c.setImageKey(cp.getImageKey(true)); } - else if (c.isDoubleFaced() && cp instanceof PaperCard) { - c.setState(CardStateName.Transformed, false); + else if (c.hasBackSide() && cp instanceof PaperCard && cardRules != null) { + c.setState(cardRules.getSplitType().getChangedStateName(), false); c.setImageKey(cp.getImageKey(true)); } else if (c.isSplitCard()) { @@ -251,14 +251,9 @@ public class CardFactory { c.setRarity(cp.getRarity()); c.setState(CardStateName.RightSplit, false); c.setImageKey(originalPicture); - } else if (c.isMeldable() && cp instanceof PaperCard) { - c.setState(CardStateName.Meld, false); - c.setImageKey(cp.getImageKey(true)); } else if (c.isAdventureCard()) { c.setState(CardStateName.Adventure, false); c.setImageKey(originalPicture); - c.setSetCode(cp.getEdition()); - c.setRarity(cp.getRarity()); } c.setSetCode(cp.getEdition()); @@ -272,7 +267,7 @@ public class CardFactory { private static void buildAbilities(final Card card) { for (final CardStateName state : card.getStates()) { - if (card.isDoubleFaced() && state == CardStateName.FaceDown) { + if (card.hasBackSide() && state == CardStateName.FaceDown) { continue; // Ignore FaceDown for DFC since they have none. } card.setState(state, false); @@ -281,11 +276,7 @@ public class CardFactory { // ************** Link to different CardFactories ******************* if (state == CardStateName.LeftSplit || state == CardStateName.RightSplit) { for (final SpellAbility sa : card.getSpellAbilities()) { - if (state == CardStateName.LeftSplit) { - sa.setLeftSplit(); - } else { - sa.setRightSplit(); - } + sa.setCardState(state); } CardFactoryUtil.setupKeywordedAbilities(card); final CardState original = card.getState(CardStateName.Original); diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index f8014d61432..fce27c3824a 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -4817,7 +4817,7 @@ public class CardFactoryUtil { if (sa == null) { return; } - sa.setAdventure(true); + sa.setCardState(CardStateName.Adventure); StringBuilder sb = new StringBuilder(); sb.append("Event$ Moved | ValidCard$ Card.Self | Origin$ Stack | ExcludeDestination$ Exile "); diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index cf1b5f9ed12..3637fc98ca2 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -1769,7 +1769,7 @@ public class Player extends GameEntity implements Comparable { return false; } - public final void playLandNoCheck(final Card land) { + public final Card playLandNoCheck(final Card land) { land.setController(this, 0); if (land.isFaceDown()) { land.turnFaceUp(null); @@ -1785,6 +1785,7 @@ public class Player extends GameEntity implements Comparable { game.getTriggerHandler().runTrigger(TriggerType.LandPlayed, AbilityKey.mapFromCard(land), false); game.getStack().unfreezeStack(); addLandPlayedThisTurn(); + return c; } public final boolean canPlayLand(final Card land) { diff --git a/forge-game/src/main/java/forge/game/spellability/LandAbility.java b/forge-game/src/main/java/forge/game/spellability/LandAbility.java index 8b1ba1557ca..b6ee1a29cd2 100644 --- a/forge-game/src/main/java/forge/game/spellability/LandAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/LandAbility.java @@ -17,10 +17,15 @@ */ package forge.game.spellability; +import org.apache.commons.lang3.ObjectUtils; + +import forge.card.CardStateName; import forge.game.card.Card; +import forge.game.card.CardUtil; import forge.game.cost.Cost; import forge.game.player.Player; import forge.game.staticability.StaticAbility; +import forge.game.zone.ZoneType; public class LandAbility extends Ability { @@ -32,25 +37,58 @@ public class LandAbility extends Ability { public LandAbility(Card sourceCard) { this(sourceCard, sourceCard.getController(), null); } + + public boolean canPlay(Card newHost) { + final Player p = getActivatingPlayer(); + return p.canPlayLand(newHost, false, this); + } + @Override public boolean canPlay() { - final Card land = this.getHostCard(); + Card land = this.getHostCard(); final Player p = this.getActivatingPlayer(); + if (this.getCardState() != null) { + if (!land.isLKI()) { + land = CardUtil.getLKICopy(land); + } + CardStateName stateName = getCardState(); + if (!land.hasState(stateName)) { + land.addAlternateState(stateName, false); + land.getState(stateName).copyFrom(getHostCard().getState(stateName), true); + } + + land.setState(stateName, false); + + // need to reset CMC + land.setLKICMC(-1); + land.setLKICMC(land.getCMC()); + } + return p.canPlayLand(land, false, this); } @Override public void resolve() { - getActivatingPlayer().playLandNoCheck(getHostCard()); + getHostCard().setSplitStateToPlayAbility(this); + final Card result = getActivatingPlayer().playLandNoCheck(getHostCard()); // increase mayplay used if (getMayPlay() != null) { getMayPlay().incMayPlayTurn(); } + // if land isn't in battlefield try to reset the card state + if (result != null && !result.isInZone(ZoneType.Battlefield)) { + result.setState(CardStateName.Original, true); + } } @Override public String toUnsuppressedString() { StringBuilder sb = new StringBuilder("Play land"); + + if (getHostCard().isModal()) { + sb.append(" (").append(getHostCard().getName(ObjectUtils.defaultIfNull(getCardState(), CardStateName.Original))).append(")"); + } + StaticAbility sta = getMayPlay(); if (sta != null) { Card source = sta.getHostCard(); @@ -69,5 +107,5 @@ public class LandAbility extends Ability { } return sb.toString(); } - + } \ No newline at end of file diff --git a/forge-game/src/main/java/forge/game/spellability/Spell.java b/forge-game/src/main/java/forge/game/spellability/Spell.java index 2de48e94e87..f491d7bbbf4 100644 --- a/forge-game/src/main/java/forge/game/spellability/Spell.java +++ b/forge-game/src/main/java/forge/game/spellability/Spell.java @@ -227,39 +227,19 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable } source.turnFaceDownNoUpdate(); lkicheck = true; - } else if (isAdventure()) { + } else if (getCardState() != null) { if (!source.isLKI()) { source = CardUtil.getLKICopy(source); } - - source.setState(CardStateName.Adventure, false); - - // need to reset CMC - source.setLKICMC(-1); - source.setLKICMC(source.getCMC()); - lkicheck = true; - } else if (source.isSplitCard() && (isLeftSplit() || isRightSplit())) { - if (!source.isLKI()) { - source = CardUtil.getLKICopy(source); - } - if (isLeftSplit()) { - if (!source.hasState(CardStateName.LeftSplit)) { - source.addAlternateState(CardStateName.LeftSplit, false); - source.getState(CardStateName.LeftSplit).copyFrom( - getHostCard().getState(CardStateName.LeftSplit), true); - } - - source.setState(CardStateName.LeftSplit, false); + CardStateName stateName = getCardState(); + if (!source.hasState(stateName)) { + source.addAlternateState(stateName, false); + source.getState(stateName).copyFrom(getHostCard().getState(stateName), true); } - if (isRightSplit()) { - if (!source.hasState(CardStateName.RightSplit)) { - source.addAlternateState(CardStateName.RightSplit, false); - source.getState(CardStateName.RightSplit).copyFrom( - getHostCard().getState(CardStateName.RightSplit), true); - } - - source.setState(CardStateName.RightSplit, false); + source.setState(stateName, false); + if (getHostCard().hasBackSide()) { + source.setBackSide(getHostCard().getRules().getSplitType().getChangedStateName().equals(stateName)); } // need to reset CMC diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index f8cd61a35f1..95db0fad1e5 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -21,6 +21,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; + +import forge.card.CardStateName; import forge.card.mana.ManaCost; import forge.game.*; import forge.game.ability.AbilityFactory; @@ -113,9 +115,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private boolean basicLandAbility = false; - private boolean adventure = false; - private SplitSide splitSide = null; - enum SplitSide { LEFT, RIGHT } + private CardStateName stateName = null; private int totalManaSpent = 0; @@ -840,26 +840,15 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit mayPlay = sta; } - public boolean isLeftSplit() { - return splitSide == SplitSide.LEFT; + public CardStateName getCardState() { + return stateName; } - public boolean isRightSplit() { - return splitSide == SplitSide.RIGHT; - } - public void setNoSplit() { - splitSide = null; - } - public void setLeftSplit() { - splitSide = SplitSide.LEFT; - } - public void setRightSplit() { - splitSide = SplitSide.RIGHT; + public void setCardState(CardStateName stateName0) { + this.stateName = stateName0; } + public boolean isAdventure() { - return this.adventure; - } - public void setAdventure(boolean adventure) { - this.adventure = adventure; + return this.stateName == CardStateName.Adventure; } public SpellAbility copy() { diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityRestriction.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityRestriction.java index d718f6c9be4..7b93328fd04 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityRestriction.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityRestriction.java @@ -21,7 +21,6 @@ import forge.game.Game; import forge.game.GameType; import forge.game.ability.AbilityUtils; import forge.game.card.*; -import forge.game.combat.Combat; import forge.game.cost.IndividualCostPaymentInstance; import forge.game.phase.PhaseType; import forge.game.player.Player; @@ -255,7 +254,7 @@ public class SpellAbilityRestriction extends SpellAbilityVariables { } // TODO: this is an exception for Aftermath. Needs to be somehow generalized. - if (this.getZone() != ZoneType.Graveyard && sa.isAftermath() && sa.isRightSplit()) { + if (this.getZone() != ZoneType.Graveyard && sa.isAftermath() && sa.getCardState() != null) { return false; } @@ -310,8 +309,6 @@ public class SpellAbilityRestriction extends SpellAbilityVariables { */ public final boolean checkActivatorRestrictions(final Card c, final SpellAbility sa) { Player activator = sa.getActivatingPlayer(); - final Game game = activator.getGame(); - final Combat combat = game.getPhaseHandler().getCombat(); if (sa.isSpell()) { // Spells should always default to "controller" but use mayPlay check. diff --git a/forge-gui/res/cardsfolder/upcoming/ZNR/branchloft_pathway_boulderloft_pathway.txt b/forge-gui/res/cardsfolder/upcoming/ZNR/branchloft_pathway_boulderloft_pathway.txt new file mode 100644 index 00000000000..68bee0458ae --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/ZNR/branchloft_pathway_boulderloft_pathway.txt @@ -0,0 +1,14 @@ +Name:Branchloft Pathway +ManaCost:no cost +Types:Land +A:AB$ Mana | Cost$ T | Produced$ G | SpellDescription$ Add {G}. +AlternateMode:Modal +Oracle:Add {G}. + +ALTERNATE + +Name:Boulderloft Pathway +ManaCost:no cost +Types:Land +A:AB$ Mana | Cost$ T | Produced$ W | SpellDescription$ Add {W}. +Oracle:Add {W}. diff --git a/forge-gui/res/cardsfolder/upcoming/ZNR/emerias_call_emeria_shattered_skyclave.txt b/forge-gui/res/cardsfolder/upcoming/ZNR/emerias_call_emeria_shattered_skyclave.txt new file mode 100644 index 00000000000..2926048059b --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/ZNR/emerias_call_emeria_shattered_skyclave.txt @@ -0,0 +1,19 @@ +Name:Emeria's Call +ManaCost:4 W W W +Types:Sorcery +A:SP$ Token | Cost$ 4 W W W | TokenAmount$ 2 | TokenScript$ w_4_4_angel_warrior_flying | SubAbility$ DBPumpAll | SpellDescription$ Create two 4/4 white Angel Warrior creature tokens with flying. Non-Angel creatures you control gain indestructible until your next turn. +SVar:DBPumpAll:DB$ PumpAll | ValidCards$ Creature.nonAngel+YouCtrl | KW$ Indestructible | UntilYourNextTurn$ True +AlternateMode:Modal +DeckHas:Ability$Token +Oracle:Create two 4/4 white Angel Warrior creature tokens with flying. Non-Angel creatures you control gain indestructible until your next turn. + +ALTERNATE + +Name:Emeria, Shattered Skyclave +ManaCost:no cost +Types:Land +K:ETBReplacement:Other:DBTap +SVar:DBTap:DB$ Tap | ETB$ True | Defined$ Self | UnlessCost$ PayLife<3> | UnlessPayer$ You | UnlessAI$ Shockland | StackDescription$ enters the battlefield tapped. | SpellDescription$ As CARDNAME enters the battlefield, you may pay 3 life. If you don't, it enters the battlefield tapped. +A:AB$ Mana | Cost$ T | Produced$ W | SpellDescription$ Add {W}. +Oracle:As Emeria, Shattered Skyclave enters the battlefield, you may pay 3 life. If you don’t, it enters the battlefield tapped.\nAdd {W}. + diff --git a/forge-gui/res/tokenscripts/w_4_4_angel_warrior_flying.txt b/forge-gui/res/tokenscripts/w_4_4_angel_warrior_flying.txt new file mode 100644 index 00000000000..c6da271178a --- /dev/null +++ b/forge-gui/res/tokenscripts/w_4_4_angel_warrior_flying.txt @@ -0,0 +1,7 @@ +Name:Angel Warrior +ManaCost:no cost +Types:Creature Angel Warrior +Colors:white +PT:4/4 +K:Flying +Oracle:Flying diff --git a/forge-gui/src/main/java/forge/match/AbstractGuiGame.java b/forge-gui/src/main/java/forge/match/AbstractGuiGame.java index d2715a4e985..d2c762b7840 100644 --- a/forge-gui/src/main/java/forge/match/AbstractGuiGame.java +++ b/forge-gui/src/main/java/forge/match/AbstractGuiGame.java @@ -220,6 +220,7 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards { case Flipped: case Transformed: case Meld: + case Modal: return true; default: return false;