Resolve "ZNR: Modal Double Faced Cards"

This commit is contained in:
Hans Mackowiak
2020-09-14 19:13:54 +00:00
committed by Sol
parent 5ea03022cd
commit c8d69cbbb9
17 changed files with 198 additions and 114 deletions

View File

@@ -1856,7 +1856,7 @@ public class ComputerUtilCard {
public static AiPlayDecision checkNeedsToPlayReqs(final Card card, final SpellAbility sa) { public static AiPlayDecision checkNeedsToPlayReqs(final Card card, final SpellAbility sa) {
Game game = card.getGame(); Game game = card.getGame();
boolean isRightSplit = sa != null && sa.isRightSplit(); boolean isRightSplit = sa != null && sa.getCardState() != null;
String needsToPlayName = isRightSplit ? "SplitNeedsToPlay" : "NeedsToPlay"; String needsToPlayName = isRightSplit ? "SplitNeedsToPlay" : "NeedsToPlay";
String needsToPlayVarName = isRightSplit ? "SplitNeedsToPlayVar" : "NeedsToPlayVar"; String needsToPlayVarName = isRightSplit ? "SplitNeedsToPlayVar" : "NeedsToPlayVar";

View File

@@ -9,7 +9,8 @@ public enum CardSplitType
Meld(FaceSelectionMethod.USE_ACTIVE_FACE, CardStateName.Meld), Meld(FaceSelectionMethod.USE_ACTIVE_FACE, CardStateName.Meld),
Split(FaceSelectionMethod.COMBINE, CardStateName.RightSplit), Split(FaceSelectionMethod.COMBINE, CardStateName.RightSplit),
Flip(FaceSelectionMethod.USE_PRIMARY_FACE, CardStateName.Flipped), 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) { CardSplitType(FaceSelectionMethod calcMode, CardStateName stateName) {
method = calcMode; method = calcMode;

View File

@@ -10,6 +10,7 @@ public enum CardStateName {
LeftSplit, LeftSplit,
RightSplit, RightSplit,
Adventure, Adventure,
Modal
; ;

View File

@@ -532,7 +532,12 @@ public class BoosterGenerator {
Predicate<PaperCard> toAdd = null; Predicate<PaperCard> toAdd = null;
if (operator.equalsIgnoreCase(BoosterSlots.DUAL_FACED_CARD)) { 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); 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.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; } else if (operator.equalsIgnoreCase(BoosterSlots.BASIC_LAND)) { toAdd = IPaperCard.Predicates.Presets.IS_BASIC_LAND;

View File

@@ -81,7 +81,7 @@ public class ImageUtil {
public static boolean hasBackFacePicture(PaperCard cp) { public static boolean hasBackFacePicture(PaperCard cp) {
CardSplitType cst = cp.getRules().getSplitType(); 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) { public static String getNameToUse(PaperCard cp, boolean backFace) {

View File

@@ -338,28 +338,14 @@ public class Card extends GameEntity implements Comparable<Card> {
public CardStateName getAlternateStateName() { public CardStateName getAlternateStateName() {
if (hasAlternateState()) { if (hasAlternateState()) {
if (isSplitCard()) { if (isSplitCard()) {
if (currentStateName == CardStateName.RightSplit) { return currentStateName == CardStateName.RightSplit ? CardStateName.LeftSplit : CardStateName.RightSplit;
return CardStateName.LeftSplit; } else if (getRules() != null) {
} CardStateName changedState = getRules().getSplitType().getChangedStateName();
else { if (currentStateName != changedState) {
return CardStateName.RightSplit; return changedState;
} }
} }
else if (isFlipCard() && currentStateName != CardStateName.Flipped) { return CardStateName.Original;
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;
}
} }
else if (isFaceDown()) { else if (isFaceDown()) {
return CardStateName.Original; return CardStateName.Original;
@@ -642,7 +628,7 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
public boolean turnFaceDown(boolean override) { public boolean turnFaceDown(boolean override) {
if (override || (!isDoubleFaced() && !isMeldable())) { if (override || !hasBackSide()) {
facedown = true; facedown = true;
if (setState(CardStateName.FaceDown, true)) { if (setState(CardStateName.FaceDown, true)) {
runFacedownCommands(); runFacedownCommands();
@@ -738,6 +724,10 @@ public class Card extends GameEntity implements Comparable<Card> {
return getName(currentState); return getName(currentState);
} }
public final String getName(CardStateName stateName) {
return getName(getState(stateName));
}
public final String getName(CardState state) { public final String getName(CardState state) {
if (changedCardNames.isEmpty()) { if (changedCardNames.isEmpty()) {
return state.getName(); return state.getName();
@@ -798,6 +788,14 @@ public class Card extends GameEntity implements Comparable<Card> {
return getRules() != null && getRules().getSplitType() == CardSplitType.Meld; 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() { public final boolean isFlipCard() {
return hasState(CardStateName.Flipped); return hasState(CardStateName.Flipped);
} }
@@ -813,6 +811,9 @@ public class Card extends GameEntity implements Comparable<Card> {
public final boolean isBackSide() { public final boolean isBackSide() {
return backside; return backside;
} }
public final void setBackSide(boolean value) {
backside = value;
}
public boolean isCloned() { public boolean isCloned() {
return !clonedStates.isEmpty(); return !clonedStates.isEmpty();
@@ -3380,13 +3381,13 @@ public class Card extends GameEntity implements Comparable<Card> {
public final CardStateName getFaceupCardStateName() { public final CardStateName getFaceupCardStateName() {
if (isFlipped() && hasState(CardStateName.Flipped)) { if (isFlipped() && hasState(CardStateName.Flipped)) {
return CardStateName.Flipped; return CardStateName.Flipped;
} else if (backside && isDoubleFaced() && hasState(CardStateName.Transformed)) { } else if (backside && hasBackSide()) {
return CardStateName.Transformed; CardStateName stateName = getRules().getSplitType().getChangedStateName();
} else if (backside && isMeldable() && hasState(CardStateName.Meld)) { if (hasState(stateName)) {
return CardStateName.Meld; return stateName;
} else { }
return CardStateName.Original;
} }
return CardStateName.Original;
} }
private final CardCloneStates getLastClonedState() { private final CardCloneStates getLastClonedState() {
@@ -6068,20 +6069,13 @@ public class Card extends GameEntity implements Comparable<Card> {
} }
public void setSplitStateToPlayAbility(final SpellAbility sa) { public void setSplitStateToPlayAbility(final SpellAbility sa) {
if (isAdventureCard()) { CardStateName stateName = sa.getCardState();
if (sa.isAdventure()) { if (hasState(stateName)) {
setState(CardStateName.Adventure, true); 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<Card> {
} }
} }
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; return abilities;
} }

View File

@@ -240,8 +240,8 @@ public class CardFactory {
c.setState(CardStateName.Flipped, false); c.setState(CardStateName.Flipped, false);
c.setImageKey(cp.getImageKey(true)); c.setImageKey(cp.getImageKey(true));
} }
else if (c.isDoubleFaced() && cp instanceof PaperCard) { else if (c.hasBackSide() && cp instanceof PaperCard && cardRules != null) {
c.setState(CardStateName.Transformed, false); c.setState(cardRules.getSplitType().getChangedStateName(), false);
c.setImageKey(cp.getImageKey(true)); c.setImageKey(cp.getImageKey(true));
} }
else if (c.isSplitCard()) { else if (c.isSplitCard()) {
@@ -251,14 +251,9 @@ public class CardFactory {
c.setRarity(cp.getRarity()); c.setRarity(cp.getRarity());
c.setState(CardStateName.RightSplit, false); c.setState(CardStateName.RightSplit, false);
c.setImageKey(originalPicture); c.setImageKey(originalPicture);
} else if (c.isMeldable() && cp instanceof PaperCard) {
c.setState(CardStateName.Meld, false);
c.setImageKey(cp.getImageKey(true));
} else if (c.isAdventureCard()) { } else if (c.isAdventureCard()) {
c.setState(CardStateName.Adventure, false); c.setState(CardStateName.Adventure, false);
c.setImageKey(originalPicture); c.setImageKey(originalPicture);
c.setSetCode(cp.getEdition());
c.setRarity(cp.getRarity());
} }
c.setSetCode(cp.getEdition()); c.setSetCode(cp.getEdition());
@@ -272,7 +267,7 @@ public class CardFactory {
private static void buildAbilities(final Card card) { private static void buildAbilities(final Card card) {
for (final CardStateName state : card.getStates()) { 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. continue; // Ignore FaceDown for DFC since they have none.
} }
card.setState(state, false); card.setState(state, false);
@@ -281,11 +276,7 @@ public class CardFactory {
// ************** Link to different CardFactories ******************* // ************** Link to different CardFactories *******************
if (state == CardStateName.LeftSplit || state == CardStateName.RightSplit) { if (state == CardStateName.LeftSplit || state == CardStateName.RightSplit) {
for (final SpellAbility sa : card.getSpellAbilities()) { for (final SpellAbility sa : card.getSpellAbilities()) {
if (state == CardStateName.LeftSplit) { sa.setCardState(state);
sa.setLeftSplit();
} else {
sa.setRightSplit();
}
} }
CardFactoryUtil.setupKeywordedAbilities(card); CardFactoryUtil.setupKeywordedAbilities(card);
final CardState original = card.getState(CardStateName.Original); final CardState original = card.getState(CardStateName.Original);

View File

@@ -4817,7 +4817,7 @@ public class CardFactoryUtil {
if (sa == null) { if (sa == null) {
return; return;
} }
sa.setAdventure(true); sa.setCardState(CardStateName.Adventure);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("Event$ Moved | ValidCard$ Card.Self | Origin$ Stack | ExcludeDestination$ Exile "); sb.append("Event$ Moved | ValidCard$ Card.Self | Origin$ Stack | ExcludeDestination$ Exile ");

View File

@@ -1769,7 +1769,7 @@ public class Player extends GameEntity implements Comparable<Player> {
return false; return false;
} }
public final void playLandNoCheck(final Card land) { public final Card playLandNoCheck(final Card land) {
land.setController(this, 0); land.setController(this, 0);
if (land.isFaceDown()) { if (land.isFaceDown()) {
land.turnFaceUp(null); land.turnFaceUp(null);
@@ -1785,6 +1785,7 @@ public class Player extends GameEntity implements Comparable<Player> {
game.getTriggerHandler().runTrigger(TriggerType.LandPlayed, AbilityKey.mapFromCard(land), false); game.getTriggerHandler().runTrigger(TriggerType.LandPlayed, AbilityKey.mapFromCard(land), false);
game.getStack().unfreezeStack(); game.getStack().unfreezeStack();
addLandPlayedThisTurn(); addLandPlayedThisTurn();
return c;
} }
public final boolean canPlayLand(final Card land) { public final boolean canPlayLand(final Card land) {

View File

@@ -17,10 +17,15 @@
*/ */
package forge.game.spellability; package forge.game.spellability;
import org.apache.commons.lang3.ObjectUtils;
import forge.card.CardStateName;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardUtil;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.staticability.StaticAbility; import forge.game.staticability.StaticAbility;
import forge.game.zone.ZoneType;
public class LandAbility extends Ability { public class LandAbility extends Ability {
@@ -32,25 +37,58 @@ public class LandAbility extends Ability {
public LandAbility(Card sourceCard) { public LandAbility(Card sourceCard) {
this(sourceCard, sourceCard.getController(), null); this(sourceCard, sourceCard.getController(), null);
} }
public boolean canPlay(Card newHost) {
final Player p = getActivatingPlayer();
return p.canPlayLand(newHost, false, this);
}
@Override @Override
public boolean canPlay() { public boolean canPlay() {
final Card land = this.getHostCard(); Card land = this.getHostCard();
final Player p = this.getActivatingPlayer(); 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); return p.canPlayLand(land, false, this);
} }
@Override @Override
public void resolve() { public void resolve() {
getActivatingPlayer().playLandNoCheck(getHostCard()); getHostCard().setSplitStateToPlayAbility(this);
final Card result = getActivatingPlayer().playLandNoCheck(getHostCard());
// increase mayplay used // increase mayplay used
if (getMayPlay() != null) { if (getMayPlay() != null) {
getMayPlay().incMayPlayTurn(); 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 @Override
public String toUnsuppressedString() { public String toUnsuppressedString() {
StringBuilder sb = new StringBuilder("Play land"); StringBuilder sb = new StringBuilder("Play land");
if (getHostCard().isModal()) {
sb.append(" (").append(getHostCard().getName(ObjectUtils.defaultIfNull(getCardState(), CardStateName.Original))).append(")");
}
StaticAbility sta = getMayPlay(); StaticAbility sta = getMayPlay();
if (sta != null) { if (sta != null) {
Card source = sta.getHostCard(); Card source = sta.getHostCard();
@@ -69,5 +107,5 @@ public class LandAbility extends Ability {
} }
return sb.toString(); return sb.toString();
} }
} }

View File

@@ -227,39 +227,19 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable
} }
source.turnFaceDownNoUpdate(); source.turnFaceDownNoUpdate();
lkicheck = true; lkicheck = true;
} else if (isAdventure()) { } else if (getCardState() != null) {
if (!source.isLKI()) { if (!source.isLKI()) {
source = CardUtil.getLKICopy(source); source = CardUtil.getLKICopy(source);
} }
CardStateName stateName = getCardState();
source.setState(CardStateName.Adventure, false); if (!source.hasState(stateName)) {
source.addAlternateState(stateName, false);
// need to reset CMC source.getState(stateName).copyFrom(getHostCard().getState(stateName), true);
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);
} }
if (isRightSplit()) { source.setState(stateName, false);
if (!source.hasState(CardStateName.RightSplit)) { if (getHostCard().hasBackSide()) {
source.addAlternateState(CardStateName.RightSplit, false); source.setBackSide(getHostCard().getRules().getSplitType().getChangedStateName().equals(stateName));
source.getState(CardStateName.RightSplit).copyFrom(
getHostCard().getState(CardStateName.RightSplit), true);
}
source.setState(CardStateName.RightSplit, false);
} }
// need to reset CMC // need to reset CMC

View File

@@ -21,6 +21,8 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import forge.card.CardStateName;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.game.*; import forge.game.*;
import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityFactory;
@@ -113,9 +115,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
private boolean basicLandAbility = false; private boolean basicLandAbility = false;
private boolean adventure = false; private CardStateName stateName = null;
private SplitSide splitSide = null;
enum SplitSide { LEFT, RIGHT }
private int totalManaSpent = 0; private int totalManaSpent = 0;
@@ -840,26 +840,15 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
mayPlay = sta; mayPlay = sta;
} }
public boolean isLeftSplit() { public CardStateName getCardState() {
return splitSide == SplitSide.LEFT; return stateName;
} }
public boolean isRightSplit() { public void setCardState(CardStateName stateName0) {
return splitSide == SplitSide.RIGHT; this.stateName = stateName0;
}
public void setNoSplit() {
splitSide = null;
}
public void setLeftSplit() {
splitSide = SplitSide.LEFT;
}
public void setRightSplit() {
splitSide = SplitSide.RIGHT;
} }
public boolean isAdventure() { public boolean isAdventure() {
return this.adventure; return this.stateName == CardStateName.Adventure;
}
public void setAdventure(boolean adventure) {
this.adventure = adventure;
} }
public SpellAbility copy() { public SpellAbility copy() {

View File

@@ -21,7 +21,6 @@ import forge.game.Game;
import forge.game.GameType; import forge.game.GameType;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.card.*; import forge.game.card.*;
import forge.game.combat.Combat;
import forge.game.cost.IndividualCostPaymentInstance; import forge.game.cost.IndividualCostPaymentInstance;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; 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. // 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; return false;
} }
@@ -310,8 +309,6 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
*/ */
public final boolean checkActivatorRestrictions(final Card c, final SpellAbility sa) { public final boolean checkActivatorRestrictions(final Card c, final SpellAbility sa) {
Player activator = sa.getActivatingPlayer(); Player activator = sa.getActivatingPlayer();
final Game game = activator.getGame();
final Combat combat = game.getPhaseHandler().getCombat();
if (sa.isSpell()) { if (sa.isSpell()) {
// Spells should always default to "controller" but use mayPlay check. // Spells should always default to "controller" but use mayPlay check.

View File

@@ -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}.

View File

@@ -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 dont, it enters the battlefield tapped.\nAdd {W}.

View File

@@ -0,0 +1,7 @@
Name:Angel Warrior
ManaCost:no cost
Types:Creature Angel Warrior
Colors:white
PT:4/4
K:Flying
Oracle:Flying

View File

@@ -220,6 +220,7 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
case Flipped: case Flipped:
case Transformed: case Transformed:
case Meld: case Meld:
case Modal:
return true; return true;
default: default:
return false; return false;