diff --git a/forge-ai/src/main/java/forge/ai/ability/TokenAi.java b/forge-ai/src/main/java/forge/ai/ability/TokenAi.java index 7b2a02ebf11..4a193850e01 100644 --- a/forge-ai/src/main/java/forge/ai/ability/TokenAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/TokenAi.java @@ -8,7 +8,6 @@ import forge.game.GameEntity; import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; -import forge.game.ability.effects.TokenEffect; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardLists; @@ -61,20 +60,9 @@ public class TokenAi extends SpellAbilityAi { private void readParameters(final SpellAbility mapParams) { this.tokenAmount = mapParams.getParamOrDefault("TokenAmount", "1"); - TokenEffect effect = new TokenEffect(); - - this.actualToken = effect.loadTokenPrototype(mapParams); + this.actualToken = TokenInfo.getProtoType(mapParams.getParam("TokenScript"), mapParams); if (actualToken == null) { - String[] keywords; - - if (mapParams.hasParam("TokenKeywords")) { - // TODO: Change this Split to a semicolon or something else - keywords = mapParams.getParam("TokenKeywords").split("<>"); - } else { - keywords = new String[0]; - } - this.tokenPower = mapParams.getParam("TokenPower"); this.tokenToughness = mapParams.getParam("TokenToughness"); } else { @@ -394,6 +382,7 @@ public class TokenAi extends SpellAbilityAi { * @param sa Token SpellAbility * @return token creature created by ability */ + @Deprecated public static Card spawnToken(Player ai, SpellAbility sa) { return spawnToken(ai, sa, false); } @@ -406,9 +395,17 @@ public class TokenAi extends SpellAbilityAi { * @return token creature created by ability */ // TODO Is this just completely copied from TokenEffect? Let's just call that thing + @Deprecated public static Card spawnToken(Player ai, SpellAbility sa, boolean notNull) { final Card host = sa.getHostCard(); + Card result = TokenInfo.getProtoType(sa.getParam("TokenScript"), sa); + + if (result != null) { + result.setController(ai, 0); + return result; + } + String[] tokenKeywords = sa.hasParam("TokenKeywords") ? sa.getParam("TokenKeywords").split("<>") : new String[0]; String tokenPower = sa.getParam("TokenPower"); String tokenToughness = sa.getParam("TokenToughness"); diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index c0edb324c83..38394c3ea30 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -147,13 +147,6 @@ public class GameAction { } } - // Cards returned from exile face-down must be reset to their original state, otherwise - // all sort of funky shenanigans may happen later (e.g. their ETB replacement effects are set - // up on the wrong card state etc.). - if (wasFacedown && (fromBattlefield || (toHand && zoneFrom.is(ZoneType.Exile)))) { - c.setState(CardStateName.Original, true); - c.runFaceupCommands(); - } // Clean up the temporary Dash SVar when the Dashed card leaves the battlefield // Clean up the temporary AtEOT SVar @@ -191,6 +184,14 @@ public class GameAction { lastKnownInfo = CardUtil.getLKICopy(c); } + // Cards returned from exile face-down must be reset to their original state, otherwise + // all sort of funky shenanigans may happen later (e.g. their ETB replacement effects are set + // up on the wrong card state etc.). + if (wasFacedown && (fromBattlefield || (toHand && zoneFrom.is(ZoneType.Exile)))) { + c.setState(CardStateName.Original, true); + c.runFaceupCommands(); + } + if (!c.isToken()) { if (c.isCloned()) { c.switchStates(CardStateName.Original, CardStateName.Cloner, false); @@ -215,15 +216,15 @@ public class GameAction { c.updateStateForView(); } - if (fromBattlefield && c.getCurrentStateName() != CardStateName.Original) { + copied = CardFactory.copyCard(c, false); + + if (fromBattlefield && copied.getCurrentStateName() != CardStateName.Original) { // when a card leaves the battlefield, ensure it's in its original state // (we need to do this on the object before copying it, or it won't work correctly e.g. // on Transformed objects) - c.setState(CardStateName.Original, false); + copied.setState(CardStateName.Original, false); } - copied = CardFactory.copyCard(c, false); - copied.setUnearthed(c.isUnearthed()); copied.setTapped(false); @@ -250,26 +251,25 @@ public class GameAction { // special rule for Worms of the Earth if (toBattlefield && game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLandBattlefield)) { // something that is already a Land cant enter the battlefield - if (c.isLand()) { - return c; - } - // check if something would be a land - Card noLandLKI = CardUtil.getLKICopy(c); - // this check needs to check if this card would be on the battlefield - noLandLKI.setLastKnownZone(zoneTo); + Card noLandLKI = c; + if (!c.isLand()) { + // check if something would be a land + noLandLKI = CardUtil.getLKICopy(c); + // this check needs to check if this card would be on the battlefield + noLandLKI.setLastKnownZone(zoneTo); - CardCollection preList = new CardCollection(noLandLKI); - checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList); + CardCollection preList = new CardCollection(noLandLKI); + checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList); - // fake etb counters thing, then if something changed, - // need to apply checkStaticAbilities again - if(!noLandLKI.isLand()) { - if (noLandLKI.putEtbCounters()) { - // counters are added need to check again - checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList); + // fake etb counters thing, then if something changed, + // need to apply checkStaticAbilities again + if(!noLandLKI.isLand()) { + if (noLandLKI.putEtbCounters()) { + // counters are added need to check again + checkStaticAbilities(false, Sets.newHashSet(noLandLKI), preList); + } } } - if(noLandLKI.isLand()) { // if it isn't on the Stack, it stays in that Zone if (!c.getZone().is(ZoneType.Stack)) { @@ -278,6 +278,7 @@ public class GameAction { // if something would only be a land when entering the battlefield and not before // put it into the graveyard instead zoneTo = c.getOwner().getZone(ZoneType.Graveyard); + // reset facedown copied.setState(CardStateName.Original, false); copied.setManifested(false); @@ -285,6 +286,26 @@ public class GameAction { // not to battlefield anymore! toBattlefield = false; + + if (copied.isCloned()) { + copied.switchStates(CardStateName.Original, CardStateName.Cloner, false); + copied.setState(CardStateName.Original, false); + copied.clearStates(CardStateName.Cloner, false); + if (copied.isFlipCard()) { + copied.clearStates(CardStateName.Flipped, false); + } + if (copied.getStates().contains(CardStateName.OriginalText)) { + copied.clearStates(CardStateName.OriginalText, false); + copied.removeSVar("GainingTextFrom"); + copied.removeSVar("GainingTextFromTimestamp"); + } + } + + if (copied.getCurrentStateName() != CardStateName.Original) { + copied.setState(CardStateName.Original, false); + } + + copied.updateStateForView(); } } diff --git a/forge-game/src/main/java/forge/game/card/token/TokenInfo.java b/forge-game/src/main/java/forge/game/card/token/TokenInfo.java index 86f319baf80..c7a06f64fd5 100644 --- a/forge-game/src/main/java/forge/game/card/token/TokenInfo.java +++ b/forge-game/src/main/java/forge/game/card/token/TokenInfo.java @@ -226,6 +226,10 @@ public class TokenInfo { } static public Card getProtoType(final String script, final SpellAbility sa) { + // script might be null, or sa might be null + if (script == null || sa == null) { + return null; + } final Card host = sa.getHostCard(); final Game game = host.getGame(); diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerChangesZone.java b/forge-game/src/main/java/forge/game/trigger/TriggerChangesZone.java index 85259cd4a5a..a4aa12b4a72 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerChangesZone.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerChangesZone.java @@ -100,8 +100,8 @@ public class TriggerChangesZone extends Trigger { if (hasParam("ValidCard")) { Card moved = (Card) runParams2.get("Card"); final Game game = getHostCard().getGame(); - boolean isDiesTrig = "Battlefield".equals(getParam("Origin")) - && "Graveyard".equals(getParam("Destination")); + boolean leavesBattlefield = "Battlefield".equals(getParam("Origin")); + boolean isDiesTrig = leavesBattlefield && "Graveyard".equals(getParam("Destination")); if (isDiesTrig) { moved = game.getChangeZoneLKIInfo(moved); @@ -111,6 +111,11 @@ public class TriggerChangesZone extends Trigger { getHostCard(), null)) { return false; } + + // if it is a die trigger, and the hostcard is the moved one, but it doesn't has the trigger + if (leavesBattlefield && moved.equals(getHostCard()) && !moved.hasTrigger(this)) { + return false; + } } if (hasParam("ValidCause")) {