CardFactoryUtil: Disguise Keyword

This commit is contained in:
Hans Mackowiak
2024-01-20 22:13:02 +01:00
committed by Chris H
parent 6639da3f20
commit ff3ea852b7
14 changed files with 215 additions and 81 deletions

View File

@@ -310,6 +310,9 @@ public abstract class GameState {
if (c.isManifested()) { if (c.isManifested()) {
newText.append(":Manifested"); newText.append(":Manifested");
} }
if (c.isCloaked()) {
newText.append(":Cloaked");
}
} }
if (c.getCurrentStateName().equals(CardStateName.Transformed)) { if (c.getCurrentStateName().equals(CardStateName.Transformed)) {
newText.append("|Transformed"); newText.append("|Transformed");
@@ -1280,6 +1283,9 @@ public abstract class GameState {
if (info.endsWith("Manifested")) { if (info.endsWith("Manifested")) {
c.setManifested(true); c.setManifested(true);
} }
if (info.endsWith("Cloaked")) {
c.setCloaked(true);
}
} else if (info.startsWith("Transformed")) { } else if (info.startsWith("Transformed")) {
c.setState(CardStateName.Transformed, true); c.setState(CardStateName.Transformed, true);
c.setBackSide(true); c.setBackSide(true);

View File

@@ -389,6 +389,9 @@ public class GameCopier {
if (c.isManifested()) { if (c.isManifested()) {
newCard.setManifested(true); newCard.setManifested(true);
} }
if (c.isCloaked()) {
newCard.setCloaked(true);
}
} }
if (c.isMonstrous()) { if (c.isMonstrous()) {
newCard.setMonstrous(true); newCard.setMonstrous(true);

View File

@@ -23,7 +23,9 @@ public final class ImageKeys {
public static final String HIDDEN_CARD = "hidden"; public static final String HIDDEN_CARD = "hidden";
public static final String MORPH_IMAGE = "morph"; public static final String MORPH_IMAGE = "morph";
public static final String DISGUISED_IMAGE = "disguised";
public static final String MANIFEST_IMAGE = "manifest"; public static final String MANIFEST_IMAGE = "manifest";
public static final String CLOAKED_IMAGE = "cloaked";
public static final String FORETELL_IMAGE = "foretell"; public static final String FORETELL_IMAGE = "foretell";
public static final String BACKFACE_POSTFIX = "$alt"; public static final String BACKFACE_POSTFIX = "$alt";
@@ -406,7 +408,7 @@ public final class ImageKeys {
CardEdition ed = StaticData.instance().getEditions().get(setFolder); CardEdition ed = StaticData.instance().getEditions().get(setFolder);
if (ed != null && !editionAlias.containsKey(setFolder)) { if (ed != null && !editionAlias.containsKey(setFolder)) {
String alias = ed.getAlias(); String alias = ed.getAlias();
Set aliasSet = new HashSet<>(); Set<String> aliasSet = new HashSet<>();
if (alias != null) { if (alias != null) {
if (!alias.equalsIgnoreCase(setFolder)) if (!alias.equalsIgnoreCase(setFolder))
aliasSet.add(alias); aliasSet.add(alias);

View File

@@ -189,7 +189,7 @@ public class GameAction {
if (c.isSplitCard()) { if (c.isSplitCard()) {
boolean resetToOriginal = false; boolean resetToOriginal = false;
if (c.isManifested()) { if (c.isManifested() || c.isCloaked()) {
if (fromBattlefield) { if (fromBattlefield) {
// Make sure the card returns from the battlefield as the original card with two halves // Make sure the card returns from the battlefield as the original card with two halves
resetToOriginal = true; resetToOriginal = true;
@@ -351,7 +351,7 @@ public class GameAction {
ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.Moved, repParams); ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.Moved, repParams);
if (repres != ReplacementResult.NotReplaced && repres != ReplacementResult.Updated) { if (repres != ReplacementResult.NotReplaced && repres != ReplacementResult.Updated) {
// reset failed manifested Cards back to original // reset failed manifested Cards back to original
if (c.isManifested() && !c.isInPlay()) { if ((c.isManifested() || c.isCloaked()) && !c.isInPlay()) {
c.forceTurnFaceUp(); c.forceTurnFaceUp();
} }

View File

@@ -323,7 +323,7 @@ public class PlayEffect extends SpellAbilityEffect {
if (sa.hasParam("CastFaceDown")) { if (sa.hasParam("CastFaceDown")) {
// For Illusionary Mask effect // For Illusionary Mask effect
tgtSA = CardFactoryUtil.abilityMorphDown(tgtCard.getCurrentState(), false); tgtSA = CardFactoryUtil.abilityCastFaceDown(tgtCard.getCurrentState(), false, "Morph");
} else { } else {
tgtSA = controller.getController().getAbilityToPlay(tgtCard, sas); tgtSA = controller.getController().getAbilityToPlay(tgtCard, sas);
} }

View File

@@ -161,10 +161,8 @@ public class SetStateEffect extends SpellAbilityEffect {
} }
boolean hasTransformed = false; boolean hasTransformed = false;
if (sa.isMorphUp()) { if (sa.isTurnFaceUp()) {
hasTransformed = gameCard.turnFaceUp(sa); hasTransformed = gameCard.turnFaceUp(sa);
} else if (sa.isManifestUp()) {
hasTransformed = gameCard.turnFaceUp(true, true, sa);
} else if ("Specialize".equals(mode)) { } else if ("Specialize".equals(mode)) {
hasTransformed = gameCard.changeCardState(mode, host.getChosenColor(), sa); hasTransformed = gameCard.changeCardState(mode, host.getChosenColor(), sa);
host.setChosenColors(null); host.setChosenColors(null);
@@ -182,6 +180,12 @@ public class SetStateEffect extends SpellAbilityEffect {
} else if (sa.isManifestUp()) { } else if (sa.isManifestUp()) {
String sb = p + " has unmanifested " + gameCard.getName(); String sb = p + " has unmanifested " + gameCard.getName();
game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb); game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb);
} else if (sa.isDisguiseUp()) {
String sb = p + " has undisguised " + gameCard.getName();
game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb);
} else if (sa.isCloakUp()) {
String sb = p + " has uncloaked " + gameCard.getName();
game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, sb);
} else if (hiddenAgenda) { } else if (hiddenAgenda) {
if (gameCard.hasKeyword("Double agenda")) { if (gameCard.hasKeyword("Double agenda")) {
String sb = p + " has revealed " + gameCard.getName() + " with the chosen names: " + String sb = p + " has revealed " + gameCard.getName() + " with the chosen names: " +

View File

@@ -221,6 +221,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
private boolean suspected = false; private boolean suspected = false;
private boolean manifested; private boolean manifested;
private boolean cloaked;
private boolean foretold; private boolean foretold;
private boolean foretoldCostByEffect; private boolean foretoldCostByEffect;
@@ -516,6 +517,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
// Cleared tests, about to change states // Cleared tests, about to change states
if (currentStateName.equals(CardStateName.FaceDown) && state.equals(CardStateName.Original)) { if (currentStateName.equals(CardStateName.FaceDown) && state.equals(CardStateName.Original)) {
this.setManifested(false); this.setManifested(false);
this.setCloaked(false);
} }
currentStateName = state; currentStateName = state;
@@ -732,6 +734,32 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
return c; return c;
} }
public Card cloak(Player p, SpellAbility sa, Map<AbilityKey, Object> params) {
// Turn Face Down (even if it's DFC).
// Sometimes cards are manifested while already being face down
if (!turnFaceDown(true) && !isFaceDown()) {
return null;
}
// Just in case you aren't the controller, now you are!
setController(p, game.getNextTimestamp());
// Mark this card as "cloaked"
setCloaked(true);
// give it Ward:2
getFaceDownState().addIntrinsicKeyword("Ward:2", true);
// Move to p's battlefield
Card c = game.getAction().moveToPlay(this, p, sa, params);
if (c.isInPlay()) {
c.setCloaked(true);
c.turnFaceDown(true);
c.updateStateForView();
}
return c;
}
public boolean turnFaceDown() { public boolean turnFaceDown() {
return turnFaceDown(false); return turnFaceDown(false);
} }
@@ -762,15 +790,11 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
public boolean turnFaceUp(SpellAbility cause) { public boolean turnFaceUp(SpellAbility cause) {
return turnFaceUp(false, true, cause); return turnFaceUp(true, cause);
} }
public boolean turnFaceUp(boolean manifestPaid, boolean runTriggers, SpellAbility cause) { public boolean turnFaceUp(boolean runTriggers, SpellAbility cause) {
if (isFaceDown()) { if (!isFaceDown()) {
if (manifestPaid && isManifested() && !getRules().getType().isCreature()) {
// If we've manifested a non-creature and we're demanifesting disallow it
// Unless this creature also has a Morph ability
return false; return false;
} }
@@ -813,8 +837,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
return retResult; return retResult;
} }
return false;
}
public boolean wasTurnedFaceUpThisTurn() { public boolean wasTurnedFaceUpThisTurn() {
return turnedFaceUpThisTurn; return turnedFaceUpThisTurn;
@@ -2143,6 +2165,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} else if (keyword.startsWith("Ripple")) { } else if (keyword.startsWith("Ripple")) {
sbLong.append(TextUtil.fastReplace(keyword, ":", " ")).append("\r\n"); sbLong.append(TextUtil.fastReplace(keyword, ":", " ")).append("\r\n");
} else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph") } else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph")
|| keyword.startsWith("Disguise")
|| keyword.startsWith("Escape") || keyword.startsWith("Foretell:") || keyword.startsWith("Escape") || keyword.startsWith("Foretell:")
|| keyword.startsWith("Madness:")|| keyword.startsWith("Recover") || keyword.startsWith("Madness:")|| keyword.startsWith("Recover")
|| keyword.startsWith("Reconfigure") || keyword.startsWith("Squad") || keyword.startsWith("Reconfigure") || keyword.startsWith("Squad")
@@ -2615,6 +2638,9 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
if (manifested) { if (manifested) {
sb.append("Manifested\r\n"); sb.append("Manifested\r\n");
} }
if (cloaked) {
sb.append("Cloaked\r\n");
}
String keywordText = keywordsToText(getUnhiddenKeywords(state)); String keywordText = keywordsToText(getUnhiddenKeywords(state));
sb.append(keywordText).append(keywordText.length() > 0 ? linebreak : ""); sb.append(keywordText).append(keywordText.length() > 0 ? linebreak : "");
@@ -3172,7 +3198,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
return false; return false;
} }
for (SpellAbility sa : getSpellAbilities()) { for (SpellAbility sa : getSpellAbilities()) {
if (!(sa instanceof SpellPermanent && sa.isBasicSpell()) && !sa.isMorphUp()) { if (!(sa instanceof SpellPermanent && sa.isBasicSpell()) && !sa.isMorphUp() && !sa.isDisguiseUp()) {
return false; return false;
} }
} }
@@ -3206,7 +3232,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
if (isInPlay()) { if (isInPlay()) {
if ((null == mana || false == mana) && isFaceDown() && state.getView().getState() == CardStateName.FaceDown) { if ((null == mana || false == mana) && isFaceDown() && state.getView().getState() == CardStateName.FaceDown) {
for (SpellAbility sa : getState(CardStateName.Original).getNonManaAbilities()) { for (SpellAbility sa : getState(CardStateName.Original).getNonManaAbilities()) {
if (sa.isManifestUp() || sa.isMorphUp()) { if (sa.isTurnFaceUp()) {
list.add(sa); list.add(sa);
} }
} }
@@ -4425,6 +4451,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
perpetual.add(p); perpetual.add(p);
} }
@SuppressWarnings("unchecked")
public final void executePerpetual(Map<String, Object> p) { public final void executePerpetual(Map<String, Object> p) {
final String category = (String) p.get("Category"); final String category = (String) p.get("Category");
if (category.equals("NewPT")) { if (category.equals("NewPT")) {
@@ -6269,6 +6296,13 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
this.manifested = manifested; this.manifested = manifested;
} }
public final boolean isCloaked() {
return cloaked;
}
public final void setCloaked(final boolean cloaked) {
this.cloaked = cloaked;
}
public final boolean isForetold() { public final boolean isForetold() {
// in exile and foretold // in exile and foretold
if (this.isInZone(ZoneType.Exile)) { if (this.isInZone(ZoneType.Exile)) {
@@ -7061,10 +7095,13 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
} }
if (isInPlay() && isFaceDown() && isManifested()) { if (isInPlay() && isFaceDown() && oState.getType().isCreature())
ManaCost cost = oState.getManaCost(); {
if (oState.getType().isCreature()) { if (isManifested()) {
abilities.add(CardFactoryUtil.abilityManifestFaceUp(this, cost)); abilities.add(oState.getManifestUp());
}
if (isCloaked()) {
abilities.add(oState.getCloakUp());
} }
} }
@@ -7387,7 +7424,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
public void forceTurnFaceUp() { public void forceTurnFaceUp() {
getGame().getTriggerHandler().suppressMode(TriggerType.TurnFaceUp); getGame().getTriggerHandler().suppressMode(TriggerType.TurnFaceUp);
turnFaceUp(false, false, null); turnFaceUp(false, null);
getGame().getTriggerHandler().clearSuppression(TriggerType.TurnFaceUp); getGame().getTriggerHandler().clearSuppression(TriggerType.TurnFaceUp);
} }

View File

@@ -102,7 +102,7 @@ public class CardFactoryUtil {
* *
* @return a {@link forge.game.spellability.SpellAbility} object. * @return a {@link forge.game.spellability.SpellAbility} object.
*/ */
public static SpellAbility abilityMorphDown(final CardState cardState, final boolean intrinsic) { public static SpellAbility abilityCastFaceDown(final CardState cardState, final boolean intrinsic, String key) {
final Spell morphDown = new Spell(cardState.getCard(), new Cost(ManaCost.THREE, false)) { final Spell morphDown = new Spell(cardState.getCard(), new Cost(ManaCost.THREE, false)) {
private static final long serialVersionUID = -1438810964807867610L; private static final long serialVersionUID = -1438810964807867610L;
@@ -111,13 +111,16 @@ public class CardFactoryUtil {
if (!hostCard.isFaceDown()) { if (!hostCard.isFaceDown()) {
hostCard.setOriginalStateAsFaceDown(); hostCard.setOriginalStateAsFaceDown();
} }
CardFactoryUtil.setFaceDownState(hostCard, this);
final Game game = hostCard.getGame(); final Game game = hostCard.getGame();
Map<AbilityKey, Object> moveParams = AbilityKey.newMap(); CardZoneTable table = new CardZoneTable(game.copyLastStateBattlefield(), game.copyLastStateBattlefield());
moveParams.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield()); Map<AbilityKey, Object> params = AbilityKey.newMap();
moveParams.put(AbilityKey.LastStateGraveyard, game.copyLastStateGraveyard()); params.put(AbilityKey.LastStateBattlefield, table.getLastStateBattlefield());
params.put(AbilityKey.LastStateGraveyard, table.getLastStateGraveyard());
params.put(AbilityKey.InternalTriggerTable, table);
hostCard.getGame().getAction().moveToPlay(hostCard, this, moveParams); hostCard.getGame().getAction().moveToPlay(hostCard, this, params);
} }
@Override @Override
@@ -138,9 +141,8 @@ public class CardFactoryUtil {
morphDown.setCardState(cardState); morphDown.setCardState(cardState);
morphDown.setDescription("(You may cast this card face down as a 2/2 creature for {3}.)"); morphDown.setDescription("(You may cast this card face down as a 2/2 creature for {3}.)");
morphDown.setStackDescription("Morph - Creature 2/2"); morphDown.setStackDescription(key + " - Creature 2/2");
morphDown.setCastFaceDown(true); morphDown.setCastFaceDown(true);
morphDown.setBasicSpell(false);
morphDown.setIntrinsic(intrinsic); morphDown.setIntrinsic(intrinsic);
@@ -187,18 +189,48 @@ public class CardFactoryUtil {
return morphUp; return morphUp;
} }
public static SpellAbility abilityDisguiseUp(final CardState cardState, final String costStr, final boolean intrinsic) {
Cost cost = new Cost(costStr, true);
StringBuilder sbCost = new StringBuilder("Disguise");
sbCost.append(" ");
if (!cost.isOnlyManaCost()) {
sbCost.append("");
}
sbCost.append(cost.toString());
public static SpellAbility abilityManifestFaceUp(final Card sourceCard, final ManaCost manaCost) { StringBuilder sb = new StringBuilder();
sb.append("ST$ SetState | Cost$ ").append(costStr).append(" | CostDesc$ ").append(sbCost);
sb.append(" | DisguiseUp$ True | Secondary$ True | IsPresent$ Card.Self+faceDown");
sb.append(" | Mode$ TurnFaceUp | SpellDescription$ (Turn this face up any time for its disguise cost.)");
final SpellAbility morphUp = AbilityFactory.getAbility(sb.toString(), cardState);
// if Cost has X in cost, need to check source for an SVar for this
if (cost.hasXInAnyCostPart() && cardState.hasSVar("X")) {
morphUp.setSVar("X", cardState.getSVar("X"));
}
final StringBuilder sbStack = new StringBuilder();
sbStack.append(cardState.getName()).append(" - turn this card face up.");
morphUp.setStackDescription(sbStack.toString());
morphUp.setIntrinsic(intrinsic);
return morphUp;
}
public static SpellAbility abilityTurnFaceUp(final CardState sourceCard, String key, String desc) {
final ManaCost manaCost = sourceCard.getManaCost();
String costDesc = manaCost.toString(); String costDesc = manaCost.toString();
// Cost need to be set later // Cost need to be set later
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("ST$ SetState | Cost$ 0 | CostDesc$ Unmanifest ").append(costDesc); sb.append("ST$ SetState | Cost$ 0 | ").append("PrecostDesc$ ").append(desc).append(" | CostDesc$ ").append(costDesc);
sb.append(" | ManifestUp$ True | Secondary$ True | PresentDefined$ Self | IsPresent$ Card.faceDown+manifested"); sb.append(" | ").append(key).append("$ True | Secondary$ True | Mode$ TurnFaceUp | SpellDescription$ (Turn this face up any time for its mana cost.)");
sb.append(" | Mode$ TurnFaceUp | SpellDescription$ (Turn this face up any time for its mana cost.)");
final SpellAbility manifestUp = AbilityFactory.getAbility(sb.toString(), sourceCard); final SpellAbility manifestUp = AbilityFactory.getAbility(sb.toString(), sourceCard);
manifestUp.setPayCosts(new Cost(manaCost, true)); manifestUp.setPayCosts(new Cost(manaCost, true));
manifestUp.rebuiltDescription();
final StringBuilder sbStack = new StringBuilder(); final StringBuilder sbStack = new StringBuilder();
sbStack.append(sourceCard.getName()).append(" - turn this card face up."); sbStack.append(sourceCard.getName()).append(" - turn this card face up.");
@@ -2760,6 +2792,13 @@ public class CardFactoryUtil {
newSA.setAlternativeCost(AlternativeCost.Dash); newSA.setAlternativeCost(AlternativeCost.Dash);
newSA.setIntrinsic(intrinsic); newSA.setIntrinsic(intrinsic);
inst.addSpellAbility(newSA); inst.addSpellAbility(newSA);
} else if (keyword.startsWith("Disguise")) {
final String[] k = keyword.split(":");
SpellAbility faceDown = abilityCastFaceDown(card, intrinsic, "Disguise");
faceDown.putParam("FaceDownKeyword", "Ward:2");
inst.addSpellAbility(faceDown);
inst.addSpellAbility(abilityDisguiseUp(card, k[1], intrinsic));
} else if (keyword.startsWith("Disturb")) { } else if (keyword.startsWith("Disturb")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
final Cost disturbCost = new Cost(k[1], true); final Cost disturbCost = new Cost(k[1], true);
@@ -3128,12 +3167,12 @@ public class CardFactoryUtil {
} else if (keyword.startsWith("Morph")) { } else if (keyword.startsWith("Morph")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
inst.addSpellAbility(abilityMorphDown(card, intrinsic)); inst.addSpellAbility(abilityCastFaceDown(card, intrinsic, "Morph"));
inst.addSpellAbility(abilityMorphUp(card, k[1], false, intrinsic)); inst.addSpellAbility(abilityMorphUp(card, k[1], false, intrinsic));
} else if (keyword.startsWith("Megamorph")) { } else if (keyword.startsWith("Megamorph")) {
final String[] k = keyword.split(":"); final String[] k = keyword.split(":");
inst.addSpellAbility(abilityMorphDown(card, intrinsic)); inst.addSpellAbility(abilityCastFaceDown(card, intrinsic, "Morph"));
inst.addSpellAbility(abilityMorphUp(card, k[1], true, intrinsic)); inst.addSpellAbility(abilityMorphUp(card, k[1], true, intrinsic));
} else if (keyword.startsWith("More Than Meets the Eye")) { } else if (keyword.startsWith("More Than Meets the Eye")) {
final String[] n = keyword.split(":"); final String[] n = keyword.split(":");
@@ -4018,9 +4057,12 @@ public class CardFactoryUtil {
if (sa.hasParam("FaceDownSetType")) { if (sa.hasParam("FaceDownSetType")) {
faceDown.setType(new CardType(Arrays.asList(sa.getParam("FaceDownSetType").split(" & ")), false)); faceDown.setType(new CardType(Arrays.asList(sa.getParam("FaceDownSetType").split(" & ")), false));
} }
if (sa.hasParam("FaceDownKeyword")) {
faceDown.addIntrinsicKeywords(Arrays.asList(sa.getParam("FaceDownKeyword").split(" & ")));
}
if (sa.hasParam("FaceDownPower") || sa.hasParam("FaceDownToughness") if (sa.hasParam("FaceDownPower") || sa.hasParam("FaceDownToughness")
|| sa.hasParam("FaceDownSetType")) { || sa.hasParam("FaceDownSetType") || sa.hasParam("FaceDownKeyword")) {
final GameCommand unanimate = new GameCommand() { final GameCommand unanimate = new GameCommand() {
private static final long serialVersionUID = 8853789549297846163L; private static final long serialVersionUID = 8853789549297846163L;

View File

@@ -1124,10 +1124,14 @@ public class CardProperty {
if (card.isPhasedOut()) { if (card.isPhasedOut()) {
return false; return false;
} }
} else if (property.startsWith("manifested")) { } else if (property.equals("manifested")) {
if (!card.isManifested()) { if (!card.isManifested()) {
return false; return false;
} }
} else if (property.equals("cloaked")) {
if (!card.isCloaked()) {
return false;
}
} else if (property.startsWith("DrawnThisTurn")) { } else if (property.startsWith("DrawnThisTurn")) {
if (!card.getDrawnThisTurn()) { if (!card.getDrawnThisTurn()) {
return false; return false;

View File

@@ -86,6 +86,9 @@ public class CardState extends GameObject implements IHasSVars {
private ReplacementEffect battleTypeRep; private ReplacementEffect battleTypeRep;
private ReplacementEffect sagaRep; private ReplacementEffect sagaRep;
private SpellAbility manifestUp;
private SpellAbility cloakUp;
public CardState(Card card, CardStateName name) { public CardState(Card card, CardStateName name) {
this(card.getView().createAlternateState(name), card); this(card.getView().createAlternateState(name), card);
} }
@@ -759,4 +762,16 @@ public class CardState extends GameObject implements IHasSVars {
return n; return n;
} }
public SpellAbility getManifestUp() {
if (this.manifestUp == null) {
manifestUp = CardFactoryUtil.abilityTurnFaceUp(this, "ManifestUp", "Unmanifest");
}
return manifestUp;
}
public SpellAbility getCloakUp() {
if (this.cloakUp == null) {
cloakUp = CardFactoryUtil.abilityTurnFaceUp(this, "CloakUp", "Uncloak");
}
return cloakUp;
}
} }

View File

@@ -145,6 +145,9 @@ public class CardView extends GameEntityView {
public boolean isManifested() { public boolean isManifested() {
return get(TrackableProperty.Manifested); return get(TrackableProperty.Manifested);
} }
public boolean isCloaked() {
return get(TrackableProperty.Cloaked);
}
public boolean isFlipCard() { public boolean isFlipCard() {
return get(TrackableProperty.FlipCard); return get(TrackableProperty.FlipCard);
@@ -948,6 +951,7 @@ public class CardView extends GameEntityView {
set(TrackableProperty.Facedown, c.isFaceDown()); set(TrackableProperty.Facedown, c.isFaceDown());
set(TrackableProperty.Foretold, c.isForetold()); set(TrackableProperty.Foretold, c.isForetold());
set(TrackableProperty.Manifested, c.isManifested()); set(TrackableProperty.Manifested, c.isManifested());
set(TrackableProperty.Cloaked, c.isCloaked());
set(TrackableProperty.Adventure, c.isAdventureCard()); set(TrackableProperty.Adventure, c.isAdventureCard());
set(TrackableProperty.DoubleFaced, c.isDoubleFaced()); set(TrackableProperty.DoubleFaced, c.isDoubleFaced());
set(TrackableProperty.Modal, c.isModal()); set(TrackableProperty.Modal, c.isModal());
@@ -1230,8 +1234,13 @@ public class CardView extends GameEntityView {
if (getCard().getZone() == ZoneType.Exile) { if (getCard().getZone() == ZoneType.Exile) {
return ImageKeys.getTokenKey(getCard().isForeTold() ? ImageKeys.FORETELL_IMAGE : ImageKeys.HIDDEN_CARD); return ImageKeys.getTokenKey(getCard().isForeTold() ? ImageKeys.FORETELL_IMAGE : ImageKeys.HIDDEN_CARD);
} }
return ImageKeys.getTokenKey(getCard().isManifested() ? ImageKeys.MANIFEST_IMAGE if (getCard().isManifested()) {
: getType().getCreatureTypes().isEmpty() ? isCreature() ? ImageKeys.MORPH_IMAGE : ImageKeys.HIDDEN_CARD return ImageKeys.getTokenKey(ImageKeys.MANIFEST_IMAGE);
} else if (getCard().isCloaked()) {
return ImageKeys.getTokenKey(ImageKeys.CLOAKED_IMAGE);
}
return ImageKeys.getTokenKey(getType().getCreatureTypes().isEmpty() ? isCreature() ? ImageKeys.MORPH_IMAGE : ImageKeys.HIDDEN_CARD
: getType().getCreatureTypes().toString().toLowerCase().replace(" ", "_").replace("[", "").replace("]","")); : getType().getCreatureTypes().toString().toLowerCase().replace(" ", "_").replace("[", "").replace("]",""));
} }
if (canBeShownToAny(viewers)) { if (canBeShownToAny(viewers)) {

View File

@@ -61,6 +61,7 @@ public enum Keyword {
DETHRONE("Dethrone", SimpleKeyword.class, false, "Whenever this creature attacks the player with the most life or tied for the most life, put a +1/+1 counter on it."), DETHRONE("Dethrone", SimpleKeyword.class, false, "Whenever this creature attacks the player with the most life or tied for the most life, put a +1/+1 counter on it."),
DEVOUR("Devour", KeywordWithAmount.class, false, "As this creature enters the battlefield, you may sacrifice any number of creatures. This creature enters the battlefield with {%d:+1/+1 counter} on it for each creature sacrificed this way."), DEVOUR("Devour", KeywordWithAmount.class, false, "As this creature enters the battlefield, you may sacrifice any number of creatures. This creature enters the battlefield with {%d:+1/+1 counter} on it for each creature sacrificed this way."),
DEVOID("Devoid", SimpleKeyword.class, true, "This card has no color."), DEVOID("Devoid", SimpleKeyword.class, true, "This card has no color."),
DISGUISE("Disguise", KeywordWithCost.class, false, "You may cast this card face down for {3} as a 2/2 creature with ward {2}. Turn it face up any time for its disguise cost."),
DISTURB("Disturb", KeywordWithCost.class, false, "You may cast this card from your graveyard transformed for its disturb cost."), DISTURB("Disturb", KeywordWithCost.class, false, "You may cast this card from your graveyard transformed for its disturb cost."),
DOCTORS_COMPANION("Doctor's companion", Partner.class, true, "You can have two commanders if the other is the Doctor."), DOCTORS_COMPANION("Doctor's companion", Partner.class, true, "You can have two commanders if the other is the Doctor."),
DOUBLE_STRIKE("Double Strike", SimpleKeyword.class, true, "This creature deals both first-strike and regular combat damage."), DOUBLE_STRIKE("Double Strike", SimpleKeyword.class, true, "This creature deals both first-strike and regular combat damage."),

View File

@@ -519,9 +519,16 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
public boolean isAbility() { return true; } public boolean isAbility() { return true; }
public boolean isActivatedAbility() { return false; } public boolean isActivatedAbility() { return false; }
public boolean isTurnFaceUp() {
return isMorphUp() || isDisguiseUp() || isManifestUp() || isCloakUp();
}
public boolean isMorphUp() { public boolean isMorphUp() {
return this.hasParam("MorphUp"); return this.hasParam("MorphUp");
} }
public boolean isDisguiseUp() {
return this.hasParam("DisguiseUp");
}
public boolean isCastFaceDown() { public boolean isCastFaceDown() {
return false; return false;
@@ -530,6 +537,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
public boolean isManifestUp() { public boolean isManifestUp() {
return hasParam("ManifestUp"); return hasParam("ManifestUp");
} }
public boolean isCloakUp() {
return hasParam("CloakUp");
}
public boolean isCycling() { public boolean isCycling() {
return isAlternativeCost(AlternativeCost.Cycling); return isAlternativeCost(AlternativeCost.Cycling);
@@ -1060,7 +1070,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
} }
public boolean isBasicSpell() { public boolean isBasicSpell() {
return basicSpell && this.altCost == null && getRootAbility().optionalCosts.isEmpty(); return basicSpell && !isCastFaceDown() && this.altCost == null && getRootAbility().optionalCosts.isEmpty();
} }
public void setBasicSpell(final boolean basicSpell0) { public void setBasicSpell(final boolean basicSpell0) {
basicSpell = basicSpell0; basicSpell = basicSpell0;

View File

@@ -32,6 +32,7 @@ public enum TrackableProperty {
Facedown(TrackableTypes.BooleanType), Facedown(TrackableTypes.BooleanType),
Foretold(TrackableTypes.BooleanType), Foretold(TrackableTypes.BooleanType),
Manifested(TrackableTypes.BooleanType), Manifested(TrackableTypes.BooleanType),
Cloaked(TrackableTypes.BooleanType),
Modal(TrackableTypes.BooleanType), Modal(TrackableTypes.BooleanType),
Adventure(TrackableTypes.BooleanType), Adventure(TrackableTypes.BooleanType),
DoubleFaced(TrackableTypes.BooleanType), DoubleFaced(TrackableTypes.BooleanType),