Merge pull request #2199 from tool4ever/canthappen

ReplacementLayer.CantHappen
This commit is contained in:
Anthony Calosa
2023-01-05 16:53:00 +08:00
committed by GitHub
14 changed files with 93 additions and 51 deletions

View File

@@ -164,8 +164,8 @@ public class GameAction {
} }
if (!found) { if (!found) {
c.clearControllers(); c.clearControllers();
if (c.removeChangedState()) { if (cause != null) {
c.updateStateForView(); unanimateOnAbortedChange(cause, c);
} }
return c; return c;
} }
@@ -365,7 +365,18 @@ public class GameAction {
copied.getOwner().removeInboundToken(copied); copied.getOwner().removeInboundToken(copied);
if (repres == ReplacementResult.Prevented) { if (repres == ReplacementResult.Prevented) {
if (game.getStack().isResolving(c) && !zoneTo.is(ZoneType.Graveyard)) { c.clearEtbCounters();
c.clearControllers();
if (cause != null) {
unanimateOnAbortedChange(cause, c);
if (cause.hasParam("Transformed") || cause.hasParam("FaceDown")) {
c.setBackSide(false);
c.changeToState(CardStateName.Original);
}
unattachCardLeavingBattlefield(c);
}
if (c.isInZone(ZoneType.Stack) && !zoneTo.is(ZoneType.Graveyard)) {
return moveToGraveyard(c, cause, params); return moveToGraveyard(c, cause, params);
} }
@@ -373,10 +384,8 @@ public class GameAction {
copied.clearDelved(); copied.clearDelved();
copied.clearConvoked(); copied.clearConvoked();
copied.clearExploited(); copied.clearExploited();
} } else if (toBattlefield && !c.isInPlay()) {
// was replaced with another Zone Change // was replaced with another Zone Change
if (toBattlefield && !c.isInPlay()) {
if (c.removeChangedState()) { if (c.removeChangedState()) {
c.updateStateForView(); c.updateStateForView();
} }
@@ -2560,4 +2569,17 @@ public class GameAction {
} }
return false; return false;
} }
private static void unanimateOnAbortedChange(final SpellAbility cause, final Card c) {
if (cause.hasParam("AnimateSubAbility")) {
long unanimateTimestamp = Long.valueOf(cause.getAdditionalAbility("AnimateSubAbility").getSVar("unanimateTimestamp"));
c.removeChangedCardKeywords(unanimateTimestamp, 0);
c.removeChangedCardTypes(unanimateTimestamp, 0);
c.removeChangedName(unanimateTimestamp, 0);
c.removeNewPT(unanimateTimestamp, 0);
if (c.removeChangedCardTraits(unanimateTimestamp, 0)) {
c.updateStateForView();
}
}
}
} }

View File

@@ -136,10 +136,10 @@ public class AnimateAllEffect extends AnimateEffectBase {
CardCollectionView list; CardCollectionView list;
if (!sa.usesTargeting() && !sa.hasParam("Defined")) { if (sa.usesTargeting() || sa.hasParam("Defined")) {
list = game.getCardsIn(ZoneType.Battlefield);
} else {
list = getTargetPlayers(sa).getCardsIn(ZoneType.Battlefield); list = getTargetPlayers(sa).getCardsIn(ZoneType.Battlefield);
} else {
list = game.getCardsIn(ZoneType.Battlefield);
} }
list = CardLists.getValidCards(list, valid, sa.getActivatingPlayer(), host, sa); list = CardLists.getValidCards(list, valid, sa.getActivatingPlayer(), host, sa);
@@ -155,6 +155,7 @@ public class AnimateAllEffect extends AnimateEffectBase {
game.fireEvent(new GameEventCardStatsChanged(c)); game.fireEvent(new GameEventCardStatsChanged(c));
if (!permanent) {
final GameCommand unanimate = new GameCommand() { final GameCommand unanimate = new GameCommand() {
private static final long serialVersionUID = -5861759814760561373L; private static final long serialVersionUID = -5861759814760561373L;
@@ -166,7 +167,6 @@ public class AnimateAllEffect extends AnimateEffectBase {
} }
}; };
if (!permanent) {
addUntilCommand(sa, unanimate); addUntilCommand(sa, unanimate);
} }
} }

View File

@@ -171,9 +171,11 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
// need LKI before Animate does apply // need LKI before Animate does apply
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c)); moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
final SpellAbility animate = sa.getAdditionalAbility("AnimateSubAbility");
source.addRemembered(c); source.addRemembered(c);
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility")); AbilityUtils.resolve(animate);
source.removeRemembered(c); source.removeRemembered(c);
animate.setSVar("unanimateTimestamp", String.valueOf(game.getTimestamp()));
} }
if (sa.hasParam("Tapped")) { if (sa.hasParam("Tapped")) {
c.setTapped(true); c.setTapped(true);

View File

@@ -575,6 +575,17 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
movedCard = game.getAction().moveToLibrary(gameCard, libraryPosition, sa); movedCard = game.getAction().moveToLibrary(gameCard, libraryPosition, sa);
} else { } else {
if (destination.equals(ZoneType.Battlefield)) { if (destination.equals(ZoneType.Battlefield)) {
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
if (sa.isReplacementAbility()) {
ReplacementEffect re = sa.getReplacementEffect();
moveParams.put(AbilityKey.ReplacementEffect, re);
if (ReplacementType.Moved.equals(re.getMode()) && sa.getReplacingObject(AbilityKey.CardLKI) != null) {
moveParams.put(AbilityKey.CardLKI, sa.getReplacingObject(AbilityKey.CardLKI));
}
}
if (sa.hasParam("Tapped") || sa.isNinjutsu()) { if (sa.hasParam("Tapped") || sa.isNinjutsu()) {
gameCard.setTapped(true); gameCard.setTapped(true);
} }
@@ -583,6 +594,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
} }
if (sa.hasParam("Transformed")) { if (sa.hasParam("Transformed")) {
if (gameCard.isDoubleFaced()) { if (gameCard.isDoubleFaced()) {
// need LKI before Animate does apply
if (!moveParams.containsKey(AbilityKey.CardLKI)) {
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(gameCard));
}
gameCard.changeCardState("Transform", null, sa); gameCard.changeCardState("Transform", null, sa);
} else { } else {
// If it can't Transform, don't change zones. // If it can't Transform, don't change zones.
@@ -650,26 +665,17 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
} }
} }
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
if (sa.isReplacementAbility()) {
ReplacementEffect re = sa.getReplacementEffect();
moveParams.put(AbilityKey.ReplacementEffect, re);
if (ReplacementType.Moved.equals(re.getMode()) && sa.getReplacingObject(AbilityKey.CardLKI) != null) {
moveParams.put(AbilityKey.CardLKI, sa.getReplacingObject(AbilityKey.CardLKI));
}
}
if (sa.hasAdditionalAbility("AnimateSubAbility")) { if (sa.hasAdditionalAbility("AnimateSubAbility")) {
// need LKI before Animate does apply // need LKI before Animate does apply
if (!moveParams.containsKey(AbilityKey.CardLKI)) { if (!moveParams.containsKey(AbilityKey.CardLKI)) {
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(gameCard)); moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(gameCard));
} }
final SpellAbility animate = sa.getAdditionalAbility("AnimateSubAbility");
hostCard.addRemembered(gameCard); hostCard.addRemembered(gameCard);
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility")); AbilityUtils.resolve(animate);
hostCard.removeRemembered(gameCard); hostCard.removeRemembered(gameCard);
animate.setSVar("unanimateTimestamp", String.valueOf(game.getTimestamp()));
} }
// need to be facedown before it hits the battlefield in case of Replacement Effects or Trigger // need to be facedown before it hits the battlefield in case of Replacement Effects or Trigger
@@ -1310,9 +1316,11 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
// need LKI before Animate does apply // need LKI before Animate does apply
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c)); moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
final SpellAbility animate = sa.getAdditionalAbility("AnimateSubAbility");
source.addRemembered(c); source.addRemembered(c);
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility")); AbilityUtils.resolve(animate);
source.removeRemembered(c); source.removeRemembered(c);
animate.setSVar("unanimateTimestamp", String.valueOf(game.getTimestamp()));
} }
if (sa.hasParam("GainControl")) { if (sa.hasParam("GainControl")) {
final String g = sa.getParam("GainControl"); final String g = sa.getParam("GainControl");
@@ -1331,6 +1339,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
} }
if (sa.hasParam("Transformed")) { if (sa.hasParam("Transformed")) {
if (c.isDoubleFaced()) { if (c.isDoubleFaced()) {
// need LKI before Animate does apply
if (!moveParams.containsKey(AbilityKey.CardLKI)) {
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
}
c.changeCardState("Transform", null, sa); c.changeCardState("Transform", null, sa);
} else { } else {
// If it can't Transform, don't change zones. // If it can't Transform, don't change zones.
@@ -1390,7 +1402,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
movedCard.setTimestamp(ts); movedCard.setTimestamp(ts);
if (sa.hasParam("AttachAfter") && movedCard.isAttachment()) { if (sa.hasParam("AttachAfter") && movedCard.isAttachment() && movedCard.isInPlay()) {
CardCollection list = AbilityUtils.getDefinedCards(source, sa.getParam("AttachAfter"), sa); CardCollection list = AbilityUtils.getDefinedCards(source, sa.getParam("AttachAfter"), sa);
if (list.isEmpty()) { if (list.isEmpty()) {
list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("AttachAfter"), c.getController(), c, sa); list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("AttachAfter"), c.getController(), c, sa);

View File

@@ -417,13 +417,13 @@ public class DigEffect extends SpellAbilityEffect {
} }
if (sa.hasAdditionalAbility("AnimateSubAbility")) { if (sa.hasAdditionalAbility("AnimateSubAbility")) {
// need LKI before Animate does apply // need LKI before Animate does apply
if (!moveParams.containsKey(AbilityKey.CardLKI)) {
moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c)); moveParams.put(AbilityKey.CardLKI, CardUtil.getLKICopy(c));
}
final SpellAbility animate = sa.getAdditionalAbility("AnimateSubAbility");
host.addRemembered(c); host.addRemembered(c);
AbilityUtils.resolve(sa.getAdditionalAbility("AnimateSubAbility")); AbilityUtils.resolve(animate);
host.removeRemembered(c); host.removeRemembered(c);
animate.setSVar("unanimateTimestamp", String.valueOf(game.getTimestamp()));
} }
c = game.getAction().moveTo(zone, c, sa, moveParams); c = game.getAction().moveTo(zone, c, sa, moveParams);
if (destZone1.equals(ZoneType.Battlefield)) { if (destZone1.equals(ZoneType.Battlefield)) {

View File

@@ -6,6 +6,7 @@ package forge.game.replacement;
* *
*/ */
public enum ReplacementLayer { public enum ReplacementLayer {
CantHappen, // 614.17
Control, // 616.1b Control, // 616.1b
Copy, // 616.1c Copy, // 616.1c
Transform, // 616.1d Transform, // 616.1d

View File

@@ -1,7 +1,7 @@
Name:Grafdigger's Cage Name:Grafdigger's Cage
ManaCost:1 ManaCost:1
Types:Artifact Types:Artifact
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard,Library | Destination$ Battlefield | ValidLKI$ Creature.Other | Prevent$ True | Description$ Creature cards in graveyards and libraries can't enter the battlefield. R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard,Library | Destination$ Battlefield | ValidLKI$ Creature.Other | Prevent$ True | Layer$ CantHappen | Description$ Creature cards in graveyards and libraries can't enter the battlefield.
S:Mode$ CantBeCast | Origin$ Graveyard,Library | Description$ Players can't cast spells from graveyards or libraries. S:Mode$ CantBeCast | Origin$ Graveyard,Library | Description$ Players can't cast spells from graveyards or libraries.
SVar:NonStackingEffect:True SVar:NonStackingEffect:True
AI:RemoveDeck:Random AI:RemoveDeck:Random

View File

@@ -3,8 +3,9 @@ ManaCost:1 W B
Types:Legendary Creature Rat Pilot Types:Legendary Creature Rat Pilot
PT:4/3 PT:4/3
T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigReturn | TriggerDescription$ At the beginning of combat on your turn, return target Vehicle card from your graveyard to the battlefield. It gains haste. Return it to its owner's hand at the beginning of your next end step. T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigReturn | TriggerDescription$ At the beginning of combat on your turn, return target Vehicle card from your graveyard to the battlefield. It gains haste. Return it to its owner's hand at the beginning of your next end step.
SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Select target Vehicle card in your graveyard | ValidTgts$ Vehicle.YouOwn | AnimateSubAbility$ Animate SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Select target Vehicle card in your graveyard | ValidTgts$ Vehicle.YouOwn | SubAbility$ Animate | RememberChanged$ True | AtEOT$ Hand
SVar:Animate:DB$ Animate | Keywords$ Haste | Defined$ Remembered | Duration$ Permanent | AtEOT$ Hand SVar:Animate:DB$ Animate | Keywords$ Haste | Defined$ Remembered | Duration$ Permanent | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
DeckHas:Ability$Graveyard DeckHas:Ability$Graveyard
DeckNeeds:Type$Vehicle DeckNeeds:Type$Vehicle
Oracle:At the beginning of combat on your turn, return target Vehicle card from your graveyard to the battlefield. It gains haste. Return it to its owner's hand at the beginning of your next end step. Oracle:At the beginning of combat on your turn, return target Vehicle card from your graveyard to the battlefield. It gains haste. Return it to its owner's hand at the beginning of your next end step.

View File

@@ -5,7 +5,7 @@ PT:3/3
K:Vigilance K:Vigilance
K:Menace K:Menace
K:Lifelink K:Lifelink
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard | Destination$ Battlefield | ValidLKI$ Creature.Other | Prevent$ True | Description$ Creature cards in graveyards can't enter the battlefield. R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard | Destination$ Battlefield | ValidLKI$ Creature.Other | Prevent$ True | Layer$ CantHappen | Description$ Creature cards in graveyards can't enter the battlefield.
S:Mode$ CantBeCast | Origin$ Graveyard | Description$ Players can't cast spells from graveyards. S:Mode$ CantBeCast | Origin$ Graveyard | Description$ Players can't cast spells from graveyards.
SVar:NonStackingEffect:True SVar:NonStackingEffect:True
Oracle:Vigilance, menace, lifelink\nCreature cards in graveyards can't enter the battlefield.\nPlayers can't cast spells from graveyards. Oracle:Vigilance, menace, lifelink\nCreature cards in graveyards can't enter the battlefield.\nPlayers can't cast spells from graveyards.

View File

@@ -5,8 +5,9 @@ PT:3/4
K:Flying K:Flying
K:Haste K:Haste
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigChange | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks, return target creature card from your graveyard to the battlefield tapped and attacking. It gains "When you don't control a legendary Vampire, exile this creature." T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigChange | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks, return target creature card from your graveyard to the battlefield tapped and attacking. It gains "When you don't control a legendary Vampire, exile this creature."
SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Creature.YouOwn | Tapped$ True | Attacking$ True | AnimateSubAbility$ DBAnimate SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Creature.YouOwn | Tapped$ True | Attacking$ True | RememberChanged$ True | SubAbility$ DBAnimate
SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Duration$ Permanent | Triggers$ TrigOlivia SVar:DBAnimate:DB$ Animate | Defined$ Remembered | Duration$ Permanent | Triggers$ TrigOlivia | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:TrigOlivia:Mode$ Always | TriggerZones$ Battlefield | IsPresent$ Vampire.YouCtrl+Legendary | PresentCompare$ EQ0 | Execute$ TrigExile | TriggerDescription$ When you don't control a legendary Vampire, exile this creature. SVar:TrigOlivia:Mode$ Always | TriggerZones$ Battlefield | IsPresent$ Vampire.YouCtrl+Legendary | PresentCompare$ EQ0 | Execute$ TrigExile | TriggerDescription$ When you don't control a legendary Vampire, exile this creature.
SVar:TrigExile:DB$ ChangeZone | Defined$ Self | Origin$ Battlefield | Destination$ Exile SVar:TrigExile:DB$ ChangeZone | Defined$ Self | Origin$ Battlefield | Destination$ Exile
SVar:HasAttackEffect:TRUE SVar:HasAttackEffect:TRUE

View File

@@ -2,6 +2,7 @@ Name:Swords to Plowshares
ManaCost:W ManaCost:W
Types:Instant Types:Instant
A:SP$ ChangeZone | ValidTgts$ Creature | Origin$ Battlefield | Destination$ Exile | RememberLKI$ True | SubAbility$ DBGainLife | SpellDescription$ Exile target creature. A:SP$ ChangeZone | ValidTgts$ Creature | Origin$ Battlefield | Destination$ Exile | RememberLKI$ True | SubAbility$ DBGainLife | SpellDescription$ Exile target creature.
SVar:DBGainLife:DB$ GainLife | Defined$ RememberedController | LifeAmount$ X | SpellDescription$ Its controller gains life equal to its power. SVar:DBGainLife:DB$ GainLife | Defined$ RememberedController | LifeAmount$ X | SubAbility$ DBCleanup | SpellDescription$ Its controller gains life equal to its power.
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:RememberedLKI$CardPower SVar:X:RememberedLKI$CardPower
Oracle:Exile target creature. Its controller gains life equal to its power. Oracle:Exile target creature. Its controller gains life equal to its power.

View File

@@ -1,7 +1,7 @@
Name:Weathered Runestone Name:Weathered Runestone
ManaCost:2 ManaCost:2
Types:Artifact Types:Artifact
R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard,Library | Destination$ Battlefield | ValidLKI$ Permanent.nonland | Prevent$ True | Description$ Nonland permanent cards in graveyards and libraries can't enter the battlefield. R:Event$ Moved | ActiveZones$ Battlefield | Origin$ Graveyard,Library | Destination$ Battlefield | ValidLKI$ Permanent.nonland | Prevent$ True | Layer$ CantHappen | Description$ Nonland permanent cards in graveyards and libraries can't enter the battlefield.
S:Mode$ CantBeCast | Origin$ Graveyard,Library | Description$ Players can't cast spells from graveyards or libraries. S:Mode$ CantBeCast | Origin$ Graveyard,Library | Description$ Players can't cast spells from graveyards or libraries.
SVar:NonStackingEffect:True SVar:NonStackingEffect:True
AI:RemoveDeck:Random AI:RemoveDeck:Random

View File

@@ -2,7 +2,7 @@ Name:Worms of the Earth
ManaCost:2 B B B ManaCost:2 B B B
Types:Enchantment Types:Enchantment
S:Mode$ CantPlayLand | Description$ Players can't play lands. S:Mode$ CantPlayLand | Description$ Players can't play lands.
R:Event$ Moved | ActiveZones$ Battlefield | Destination$ Battlefield | ValidCard$ Land | Prevent$ True | Description$ Lands can't enter the battlefield. R:Event$ Moved | ActiveZones$ Battlefield | Destination$ Battlefield | ValidCard$ Land | Prevent$ True | Layer$ CantHappen | Description$ Lands can't enter the battlefield.
T:Mode$ Phase | Phase$ Upkeep | TriggerZones$ Battlefield | Execute$ RepeatAbility | TriggerDescription$ At the beginning of each upkeep, any player may sacrifice two lands or have CARDNAME deal 5 damage to that player. If a player does either, destroy CARDNAME. T:Mode$ Phase | Phase$ Upkeep | TriggerZones$ Battlefield | Execute$ RepeatAbility | TriggerDescription$ At the beginning of each upkeep, any player may sacrifice two lands or have CARDNAME deal 5 damage to that player. If a player does either, destroy CARDNAME.
SVar:RepeatAbility:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBChoose SVar:RepeatAbility:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBChoose
SVar:DBChoose:DB$ GenericChoice | Defined$ Player.IsRemembered | Choices$ SacTwoLands,DealDmg | AILogic$ PayUnlessCost SVar:DBChoose:DB$ GenericChoice | Defined$ Player.IsRemembered | Choices$ SacTwoLands,DealDmg | AILogic$ PayUnlessCost

View File

@@ -114,9 +114,11 @@ public class HumanPlay {
final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa); final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa);
if (!req.playAbility(true, false, false)) { if (!req.playAbility(true, false, false)) {
if (flippedToCast && !castFaceDown) {
// need to get the changed card if able
Card rollback = p.getGame().getCardState(sa.getHostCard()); Card rollback = p.getGame().getCardState(sa.getHostCard());
if (castFaceDown) {
rollback.setFaceDown(false);
} else if (flippedToCast) {
// need to get the changed card if able
rollback.turnFaceDown(true); rollback.turnFaceDown(true);
//need to set correct imagekey when forcing facedown //need to set correct imagekey when forcing facedown
rollback.setImageKey(ImageKeys.getTokenKey(isforetold ? ImageKeys.FORETELL_IMAGE : ImageKeys.HIDDEN_CARD)); rollback.setImageKey(ImageKeys.getTokenKey(isforetold ? ImageKeys.FORETELL_IMAGE : ImageKeys.HIDDEN_CARD));