diff --git a/forge-ai/src/main/java/forge/ai/GameState.java b/forge-ai/src/main/java/forge/ai/GameState.java index d441ab10721..f7defad4a36 100644 --- a/forge-ai/src/main/java/forge/ai/GameState.java +++ b/forge-ai/src/main/java/forge/ai/GameState.java @@ -1250,7 +1250,7 @@ public abstract class GameState { } if (cardsWithoutETBTrigs.contains(c)) { - p.getGame().getAction().moveTo(ZoneType.Battlefield, c, null); + p.getGame().getAction().moveTo(ZoneType.Battlefield, c, null, null); } else { p.getZone(ZoneType.Hand).add(c); p.getGame().getAction().moveToPlay(c, null, null); diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index f9cd5adae81..4d93d16a262 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -26,6 +26,7 @@ import java.util.Map; import java.util.Set; import forge.util.*; + import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; @@ -202,6 +203,16 @@ public class GameAction { lastKnownInfo = (Card) cause.getReplacingObject(AbilityKey.CardLKI); } } + CardCollectionView lastBattlefield = null; + if (params != null) { + lastBattlefield = (CardCollectionView) params.get(AbilityKey.LastStateBattlefield); + } + if (lastBattlefield == null && cause != null) { + lastBattlefield = cause.getLastStateBattlefield(); + } + if (lastBattlefield == null) { + lastBattlefield = game.getLastStateBattlefield(); + } if (c.isSplitCard()) { boolean resetToOriginal = false; @@ -254,7 +265,6 @@ public class GameAction { // if from Battlefield to Graveyard and Card does exist in LastStateBattlefield // use that instead if (fromBattlefield) { - CardCollectionView lastBattlefield = game.getLastStateBattlefield(); int idx = lastBattlefield.indexOf(c); if (idx != -1) { lastKnownInfo = lastBattlefield.get(idx); @@ -577,7 +587,10 @@ public class GameAction { } game.getTriggerHandler().clearActiveTriggers(copied, null); - game.getTriggerHandler().registerActiveLTBTrigger(lastKnownInfo); + // register all LTB trigger from last state battlefield + for (Card lki : lastBattlefield) { + game.getTriggerHandler().registerActiveLTBTrigger(lki); + } game.getTriggerHandler().registerActiveTrigger(copied, false); table.replaceCounterEffect(game, null, true); @@ -705,7 +718,7 @@ public class GameAction { } public final Card moveTo(final Zone zoneTo, Card c, SpellAbility cause) { - return moveTo(zoneTo, c, cause, null); + return moveTo(zoneTo, c, cause, AbilityKey.newMap()); } public final Card moveTo(final Zone zoneTo, Card c, SpellAbility cause, Map params) { // FThreads.assertExecutedByEdt(false); // This code must never be executed from EDT, @@ -713,14 +726,14 @@ public class GameAction { return moveTo(zoneTo, c, null, cause, params); } public final Card moveTo(final Zone zoneTo, Card c, Integer position, SpellAbility cause) { - return moveTo(zoneTo, c, position, cause, null); + return moveTo(zoneTo, c, position, cause, AbilityKey.newMap()); } - public final Card moveTo(final ZoneType name, final Card c, SpellAbility cause) { - return moveTo(name, c, 0, cause); + public final Card moveTo(final ZoneType name, final Card c, SpellAbility cause, Map params) { + return moveTo(name, c, 0, cause, params); } public final Card moveTo(final ZoneType name, final Card c, final int libPosition, SpellAbility cause) { - return moveTo(name, c, libPosition, cause, null); + return moveTo(name, c, libPosition, cause, AbilityKey.newMap()); } public final Card moveTo(final ZoneType name, final Card c, final int libPosition, SpellAbility cause, Map params) { // Call specific functions to set PlayerZone, then move onto moveTo @@ -786,7 +799,7 @@ public class GameAction { if (maingameCard.getZone().is(ZoneType.Stack)) { game.getMaingame().getStack().remove(maingameCard); } - game.getMaingame().getAction().moveTo(ZoneType.Subgame, maingameCard, null); + game.getMaingame().getAction().moveTo(ZoneType.Subgame, maingameCard, null, params); } } @@ -816,7 +829,10 @@ public class GameAction { } public final Card moveToStack(final Card c, SpellAbility cause) { - return moveToStack(c, cause, null); + Map params = AbilityKey.newMap(); + params.put(AbilityKey.LastStateBattlefield, game.getLastStateBattlefield()); + params.put(AbilityKey.LastStateGraveyard, game.getLastStateGraveyard()); + return moveToStack(c, cause, params); } public final Card moveToStack(final Card c, SpellAbility cause, Map params) { Card result = moveTo(game.getStackZone(), c, cause, params); @@ -833,7 +849,7 @@ public class GameAction { } public final Card moveToGraveyard(final Card c, SpellAbility cause) { - return moveToGraveyard(c, cause, null); + return moveToGraveyard(c, cause, AbilityKey.newMap()); } public final Card moveToGraveyard(final Card c, SpellAbility cause, Map params) { final PlayerZone grave = c.getOwner().getZone(ZoneType.Graveyard); @@ -842,7 +858,7 @@ public class GameAction { } public final Card moveToHand(final Card c, SpellAbility cause) { - return moveToHand(c, cause, null); + return moveToHand(c, cause, AbilityKey.newMap()); } public final Card moveToHand(final Card c, SpellAbility cause, Map params) { final PlayerZone hand = c.getOwner().getZone(ZoneType.Hand); @@ -859,14 +875,14 @@ public class GameAction { } public final Card moveToBottomOfLibrary(final Card c, SpellAbility cause) { - return moveToBottomOfLibrary(c, cause, null); + return moveToBottomOfLibrary(c, cause, AbilityKey.newMap()); } public final Card moveToBottomOfLibrary(final Card c, SpellAbility cause, Map params) { return moveToLibrary(c, -1, cause, params); } public final Card moveToLibrary(final Card c, SpellAbility cause) { - return moveToLibrary(c, cause, null); + return moveToLibrary(c, cause, AbilityKey.newMap()); } public final Card moveToLibrary(final Card c, SpellAbility cause, Map params) { return moveToLibrary(c, 0, cause, params); @@ -1229,7 +1245,12 @@ public class GameAction { // do this multiple times, sometimes creatures/permanents will survive when they shouldn't boolean orderedDesCreats = false; boolean orderedNoRegCreats = false; + boolean orderedSacrificeList = false; CardCollection cardsToUpdateLKI = new CardCollection(); + + Map mapParams = AbilityKey.newMap(); + mapParams.put(AbilityKey.LastStateBattlefield, game.getLastStateBattlefield()); + mapParams.put(AbilityKey.LastStateGraveyard, game.getLastStateGraveyard()); for (int q = 0; q < 9; q++) { checkStaticAbilities(false, affectedCards, CardCollection.EMPTY); boolean checkAgain = false; @@ -1253,16 +1274,14 @@ public class GameAction { } } } - CardCollection noRegCreats = null; + CardCollection noRegCreats = new CardCollection(); CardCollection desCreats = null; CardCollection unAttachList = new CardCollection(); + CardCollection sacrificeList = new CardCollection(); for (final Card c : game.getCardsIn(ZoneType.Battlefield)) { if (c.isCreature()) { // Rule 704.5f - Put into grave (no regeneration) for toughness <= 0 if (c.getNetToughness() <= 0) { - if (noRegCreats == null) { - noRegCreats = new CardCollection(); - } noRegCreats.add(c); checkAgain = true; } else if (c.hasKeyword("CARDNAME can't be destroyed by lethal damage unless lethal damage dealt by a single source is marked on it.")) { @@ -1306,7 +1325,7 @@ public class GameAction { } } - checkAgain |= stateBasedAction_Saga(c, table); + checkAgain |= stateBasedAction_Saga(c, sacrificeList); checkAgain |= stateBasedAction704_attach(c, unAttachList); // Attachment checkAgain |= stateBasedAction704_5r(c); // annihilate +1/+1 counters with -1/-1 ones @@ -1336,9 +1355,6 @@ public class GameAction { // cleanup aura if (c.isAura() && c.isInPlay() && !c.isEnchanting()) { - if (noRegCreats == null) { - noRegCreats = new CardCollection(); - } noRegCreats.add(c); checkAgain = true; } @@ -1351,28 +1367,46 @@ public class GameAction { // cleanup aura if (u.isAura() && u.isInPlay() && !u.isEnchanting()) { - if (noRegCreats == null) { - noRegCreats = new CardCollection(); - } noRegCreats.add(u); checkAgain = true; } } + for (Player p : game.getPlayers()) { + if (handleLegendRule(p, noRegCreats)) { + checkAgain = true; + } + + if ((game.getRules().hasAppliedVariant(GameType.Commander) + || game.getRules().hasAppliedVariant(GameType.Brawl) + || game.getRules().hasAppliedVariant(GameType.Planeswalker)) && !checkAgain) { + for (final Card c : p.getCardsIn(ZoneType.Graveyard).threadSafeIterable()) { + checkAgain |= stateBasedAction903_9a(c); + } + for (final Card c : p.getCardsIn(ZoneType.Exile).threadSafeIterable()) { + checkAgain |= stateBasedAction903_9a(c); + } + } + + if (handlePlaneswalkerRule(p, noRegCreats)) { + checkAgain = true; + } + } + // 704.5m World rule + checkAgain |= handleWorldRule(noRegCreats); // only check static abilities once after destroying all the creatures // (e.g. helpful for Erebos's Titan and another creature dealing lethal damage to each other simultaneously) setHoldCheckingStaticAbilities(true); - if (noRegCreats != null) { - if (noRegCreats.size() > 1 && !orderedNoRegCreats) { - noRegCreats = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, noRegCreats, ZoneType.Graveyard, null); - orderedNoRegCreats = true; - } - for (Card c : noRegCreats) { - c.updateWasDestroyed(true); - sacrificeDestroy(c, null, table, null); - } + if (noRegCreats.size() > 1 && !orderedNoRegCreats) { + noRegCreats = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, noRegCreats, ZoneType.Graveyard, null); + orderedNoRegCreats = true; } + for (Card c : noRegCreats) { + c.updateWasDestroyed(true); + sacrificeDestroy(c, null, table, mapParams); + } + if (desCreats != null) { if (desCreats.size() > 1 && !orderedDesCreats) { desCreats = CardLists.filter(desCreats, CardPredicates.Presets.CAN_BE_DESTROYED); @@ -1382,39 +1416,24 @@ public class GameAction { orderedDesCreats = true; } for (Card c : desCreats) { - destroy(c, null, true, table, null); + destroy(c, null, true, table, mapParams); } } + + if (sacrificeList.size() > 1 && !orderedSacrificeList) { + sacrificeList = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, sacrificeList, ZoneType.Graveyard, null); + orderedSacrificeList = true; + } + for (Card c : sacrificeList) { + c.updateWasDestroyed(true); + sacrifice(c, null, true, table, mapParams); + } setHoldCheckingStaticAbilities(false); if (game.getTriggerHandler().runWaitingTriggers()) { checkAgain = true; } - for (Player p : game.getPlayers()) { - if (handleLegendRule(p, table)) { - checkAgain = true; - } - - if ((game.getRules().hasAppliedVariant(GameType.Commander) - || game.getRules().hasAppliedVariant(GameType.Brawl) - || game.getRules().hasAppliedVariant(GameType.Planeswalker)) && !checkAgain) { - Iterable cards = p.getCardsIn(ZoneType.Graveyard).threadSafeIterable(); - for (final Card c : cards) { - checkAgain |= stateBasedAction903_9a(c); - } - cards = p.getCardsIn(ZoneType.Exile).threadSafeIterable(); - for (final Card c : cards) { - checkAgain |= stateBasedAction903_9a(c); - } - } - - if (handlePlaneswalkerRule(p, table)) { - checkAgain = true; - } - } - // 704.5m World rule - checkAgain |= handleWorldRule(table); if (game.getCombat() != null) { game.getCombat().removeAbsentCombatants(); @@ -1459,7 +1478,7 @@ public class GameAction { game.runSBACheckedCommands(); } - private boolean stateBasedAction_Saga(Card c, CardZoneTable table) { + private boolean stateBasedAction_Saga(Card c, CardCollection sacrificeList) { boolean checkAgain = false; if (!c.getType().hasSubtype("Saga")) { return false; @@ -1472,7 +1491,7 @@ public class GameAction { } if (!game.getStack().hasSourceOnStack(c, SpellAbilityPredicates.isChapter())) { // needs to be effect, because otherwise it might be a cost? - sacrifice(c, null, true, table, null); + sacrificeList.add(c); checkAgain = true; } return checkAgain; @@ -1653,57 +1672,28 @@ public class GameAction { game.getStack().clearSimultaneousStack(); } - private boolean handlePlaneswalkerRule(Player p, CardZoneTable table) { + private boolean handlePlaneswalkerRule(Player p, CardCollection noRegCreats) { // get all Planeswalkers final List list = p.getPlaneswalkersInPlay(); boolean recheck = false; - //final Multimap uniqueWalkers = ArrayListMultimap.create(); // Not used as of Ixalan - for (Card c : list) { if (c.getCounters(CounterEnumType.LOYALTY) <= 0) { - //for animation - c.updateWasDestroyed(true); - sacrificeDestroy(c, null, table, null); - // Play the Destroy sound - game.fireEvent(new GameEventCardDestroyed()); + noRegCreats.add(c); recheck = true; } - - /* -- Not used as of Ixalan -- - for (final String type : c.getType()) { - if (CardType.isAPlaneswalkerType(type)) { - uniqueWalkers.put(type, c); - } - }*/ } - /* -- Not used as of Ixalan -- - for (String key : uniqueWalkers.keySet()) { - Collection duplicates = uniqueWalkers.get(key); - if (duplicates.size() < 2) { - continue; - } - - recheck = true; - - Card toKeep = p.getController().chooseSingleEntityForEffect(new CardCollection(duplicates), new AbilitySub(ApiType.InternalLegendaryRule, null, null, null), "You have multiple planeswalkers of type \""+key+"\"in play.\n\nChoose one to stay on battlefield (the rest will be moved to graveyard)"); - for (Card c: duplicates) { - if (c != toKeep) { - moveToGraveyard(c, null); - } - } - } - */ return recheck; } - private boolean handleLegendRule(Player p, CardZoneTable table) { + private boolean handleLegendRule(Player p, CardCollection noRegCreats) { final List a = CardLists.getType(p.getCardsIn(ZoneType.Battlefield), "Legendary"); if (a.isEmpty() || game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule)) { return false; } boolean recheck = false; + // TODO legend rule exception into static ability List yamazaki = CardLists.getKeyword(a, "Legend rule doesn't apply to CARDNAME."); a.removeAll(yamazaki); @@ -1715,6 +1705,8 @@ public class GameAction { } } + // TODO handle Spy Kit + for (String name : uniqueLegends.keySet()) { Collection cc = uniqueLegends.get(name); if (cc.size() < 2) { @@ -1725,20 +1717,14 @@ public class GameAction { Card toKeep = p.getController().chooseSingleEntityForEffect(new CardCollection(cc), new SpellAbility.EmptySa(ApiType.InternalLegendaryRule, new Card(-1, game), p), "You have multiple legendary permanents named \""+name+"\" in play.\n\nChoose the one to stay on battlefield (the rest will be moved to graveyard)", null); - for (Card c: cc) { - if (c != toKeep) { - //for animation - c.updateWasDestroyed(true); - sacrificeDestroy(c, null, table, null); - } - } - game.fireEvent(new GameEventCardDestroyed()); + cc.remove(toKeep); + noRegCreats.addAll(cc); } return recheck; } - private boolean handleWorldRule(CardZoneTable table) { + private boolean handleWorldRule(CardCollection noRegCreats) { final List worlds = CardLists.getType(game.getCardsIn(ZoneType.Battlefield), "World"); if (worlds.size() <= 1) { return false; @@ -1762,12 +1748,7 @@ public class GameAction { worlds.removeAll(toKeep); } - for (Card c : worlds) { - //for animation - c.updateWasDestroyed(true); - sacrificeDestroy(c, null, table, null); - game.fireEvent(new GameEventCardDestroyed()); - } + noRegCreats.addAll(worlds); return true; } diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index 81fbbc77dfd..0370d3fcfac 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -691,7 +691,7 @@ public final class GameActionUtil { // TODO: Add targeting to the effect so it knows who it's dealing with game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getAction().moveTo(ZoneType.Command, eff, null); + game.getAction().moveTo(ZoneType.Command, eff, null, null); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); return eff; diff --git a/forge-game/src/main/java/forge/game/Match.java b/forge-game/src/main/java/forge/game/Match.java index b7468054fd4..32329b57858 100644 --- a/forge-game/src/main/java/forge/game/Match.java +++ b/forge-game/src/main/java/forge/game/Match.java @@ -7,6 +7,7 @@ import forge.deck.CardPool; import forge.deck.Deck; import forge.deck.DeckFormat; import forge.deck.DeckSection; +import forge.game.ability.AbilityKey; import forge.game.card.Card; import forge.game.card.CardCollectionView; import forge.game.event.Event; @@ -80,7 +81,7 @@ public class Match { Multimap list = game.chooseCardsForAnte(rules.getMatchAnteRarity()); for (Entry kv : list.entries()) { Player p = kv.getKey(); - game.getAction().moveTo(ZoneType.Ante, kv.getValue(), null); + game.getAction().moveTo(ZoneType.Ante, kv.getValue(), null, AbilityKey.newMap()); game.getGameLog().add(GameLogEntryType.ANTE, p + " anted " + kv.getValue()); } game.fireEvent(new GameEventAnteCardsSelected(list)); @@ -303,7 +304,7 @@ public class Match { // Create an effect that lets you cast your companion from your sideboard if (companion != null) { PlayerZone commandZone = player.getZone(ZoneType.Command); - companion = game.getAction().moveTo(ZoneType.Command, companion, null); + companion = game.getAction().moveTo(ZoneType.Command, companion, null, AbilityKey.newMap()); commandZone.add(Player.createCompanionEffect(game, companion)); player.updateZoneForView(commandZone); diff --git a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java index 9dbed5e6b24..27d34292b56 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -435,7 +435,7 @@ public abstract class SpellAbilityEffect { // TODO: Add targeting to the effect so it knows who it's dealing with game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getAction().moveTo(ZoneType.Command, eff, sa); + game.getAction().moveTo(ZoneType.Command, eff, sa, null); eff.updateStateForView(); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); } @@ -553,7 +553,7 @@ public abstract class SpellAbilityEffect { // TODO: Add targeting to the effect so it knows who it's dealing with game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getAction().moveTo(ZoneType.Command, eff, sa); + game.getAction().moveTo(ZoneType.Command, eff, sa, null); eff.updateStateForView(); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); } @@ -685,13 +685,13 @@ public abstract class SpellAbilityEffect { }; } - protected static void discard(SpellAbility sa, CardZoneTable table, final boolean effect, Map discardedMap) { + protected static void discard(SpellAbility sa, CardZoneTable table, final boolean effect, Map discardedMap, Map params) { Set discarders = discardedMap.keySet(); for (Player p : discarders) { final CardCollection discardedByPlayer = new CardCollection(); for (Card card : Lists.newArrayList(discardedMap.get(p))) { // without copying will get concurrent modification exception if (card == null) { continue; } - if (p.discard(card, sa, effect, table) != null) { + if (p.discard(card, sa, effect, table, params) != null) { discardedByPlayer.add(card); if (sa.hasParam("RememberDiscarded")) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/AddTurnEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AddTurnEffect.java index 71cd3c5b321..34861a8e750 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AddTurnEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AddTurnEffect.java @@ -4,6 +4,7 @@ import java.util.List; import forge.game.Game; import forge.game.ability.AbilityFactory; +import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -84,7 +85,7 @@ public class AddTurnEffect extends SpellAbilityEffect { eff.addStaticAbility(stEffect); game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getAction().moveTo(ZoneType.Command, eff, sa); + game.getAction().moveTo(ZoneType.Command, eff, sa, AbilityKey.newMap()); eff.updateStateForView(); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/BalanceEffect.java b/forge-game/src/main/java/forge/game/ability/effects/BalanceEffect.java index 0622cbd6f8a..a726d0a47ee 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/BalanceEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/BalanceEffect.java @@ -49,6 +49,8 @@ public class BalanceEffect extends SpellAbilityEffect { } Map params = AbilityKey.newMap(); + params.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); + params.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); CardZoneTable table = new CardZoneTable(); for (int i = 0; i < players.size(); i++) { Player p = players.get(i); @@ -67,7 +69,7 @@ public class BalanceEffect extends SpellAbilityEffect { } if (zone.equals(ZoneType.Hand)) { - discard(sa, table, true, discardedMap); + discard(sa, table, true, discardedMap, params); } table.triggerChangesZoneAll(game, sa); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index c76f0ae1c7a..351367581a6 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -620,7 +620,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { } } - Map moveParams = Maps.newEnumMap(AbilityKey.class); + Map moveParams = AbilityKey.newMap(); moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); if (sa.isReplacementAbility()) { @@ -699,7 +699,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect { gameCard.setExiledWith(host); gameCard.setExiledBy(host.getController()); } - movedCard = game.getAction().moveTo(destination, gameCard, sa); + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); + moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); + movedCard = game.getAction().moveTo(destination, gameCard, sa, moveParams); if (ZoneType.Hand.equals(destination) && ZoneType.Command.equals(originZone.getZoneType())) { StringBuilder sb = new StringBuilder(); sb.append(movedCard.getName()).append(" has moved from Command Zone to ").append(player).append("'s hand."); @@ -1210,7 +1213,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { for (final Card c : chosenCards) { Card movedCard = null; final Zone originZone = game.getZoneOf(c); - Map moveParams = Maps.newEnumMap(AbilityKey.class); + Map moveParams = AbilityKey.newMap(); moveParams.put(AbilityKey.FoundSearchingLibrary, searchedLibrary); moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffectBase.java b/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffectBase.java index a0499a41be6..cdecd9ce083 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffectBase.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffectBase.java @@ -6,6 +6,7 @@ import forge.GameCommand; import forge.game.Game; import forge.game.GameObject; import forge.game.ability.AbilityFactory; +import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -65,7 +66,7 @@ public abstract class DamagePreventEffectBase extends SpellAbilityEffect { } game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getAction().moveTo(ZoneType.Command, eff, sa); + game.getAction().moveTo(ZoneType.Command, eff, sa, AbilityKey.newMap()); eff.updateStateForView(); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DiscardEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DiscardEffect.java index f66fe752f83..fce8c6cf64c 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DiscardEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DiscardEffect.java @@ -9,6 +9,7 @@ import com.google.common.collect.Maps; import forge.game.Game; import forge.game.GameActionUtil; +import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -287,7 +288,11 @@ public class DiscardEffect extends SpellAbilityEffect { discardedMap.put(p, toBeDiscarded); } - discard(sa, table, true, discardedMap); + Map params = AbilityKey.newMap(); + params.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); + params.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); + + discard(sa, table, true, discardedMap, params); // run trigger if something got milled table.triggerChangesZoneAll(game, sa); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DrawEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DrawEffect.java index bd14ab704cb..f14adfc3cc2 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DrawEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DrawEffect.java @@ -1,7 +1,9 @@ package forge.game.ability.effects; import java.util.List; +import java.util.Map; +import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -52,6 +54,9 @@ public class DrawEffect extends SpellAbilityEffect { final boolean upto = sa.hasParam("Upto"); final boolean optional = sa.hasParam("OptionalDecider") || upto; + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); for (final Player p : getDefinedPlayersOrTargeted(sa)) { // TODO can this be removed? @@ -80,7 +85,7 @@ public class DrawEffect extends SpellAbilityEffect { actualNum = p.getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblHowManyCardDoYouWantDraw"), 0, actualNum); } - final CardCollectionView drawn = p.drawCards(actualNum, sa); + final CardCollectionView drawn = p.drawCards(actualNum, sa, moveParams); if (sa.hasParam("Reveal")) { if (sa.getParam("Reveal").equals("All")) { p.getGame().getAction().reveal(drawn, p, false); diff --git a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java index 66d595cab19..58af067e77e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java @@ -2,6 +2,7 @@ package forge.game.ability.effects; import java.util.EnumSet; import java.util.List; +import java.util.Map; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @@ -12,6 +13,7 @@ import forge.card.CardRarity; import forge.game.Game; import forge.game.GameObject; import forge.game.ability.AbilityFactory; +import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -136,6 +138,10 @@ public class EffectEffect extends SpellAbilityEffect { image = hostCard.getImageKey(); } + Map params = AbilityKey.newMap(); + params.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); + params.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); + for (Player controller : effectOwner) { final Card eff = createEffect(sa, controller, name, image); eff.setSetCode(sa.getHostCard().getSetCode()); @@ -322,7 +328,7 @@ public class EffectEffect extends SpellAbilityEffect { // TODO: Add targeting to the effect so it knows who it's dealing with game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getAction().moveTo(ZoneType.Command, eff, sa); + game.getAction().moveTo(ZoneType.Command, eff, sa, params); eff.updateStateForView(); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); //if (effectTriggers != null) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/EncodeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/EncodeEffect.java index 314468cad29..05beb40e92d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/EncodeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/EncodeEffect.java @@ -1,7 +1,10 @@ package forge.game.ability.effects; +import java.util.Map; + import forge.game.Game; import forge.game.GameLogEntryType; +import forge.game.ability.AbilityKey; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.card.CardCollectionView; @@ -49,8 +52,12 @@ public class EncodeEffect extends SpellAbilityEffect { return; } + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); + // move host card to exile - Card movedCard = game.getAction().moveTo(ZoneType.Exile, host, sa); + Card movedCard = game.getAction().moveTo(ZoneType.Exile, host, sa, moveParams); // choose a creature Card choice = player.getController().chooseSingleEntityForEffect(choices, sa, Localizer.getInstance().getMessage("lblChooseACreatureYouControlToEncode") + " ", true, null); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java index 3299cef13a1..36170ceab0d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java @@ -1,6 +1,7 @@ package forge.game.ability.effects; import java.util.List; +import java.util.Map; import com.google.common.collect.Lists; @@ -48,6 +49,9 @@ public class ExploreEffect extends SpellAbilityEffect { GameEntityCounterTable table = new GameEntityCounterTable(); final CardZoneTable triggerList = new CardZoneTable(); + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); for (final Card c : getTargetCards(sa)) { // revealed land card boolean revealedLand = false; @@ -59,7 +63,7 @@ public class ExploreEffect extends SpellAbilityEffect { final Card r = top.getFirst(); final Zone originZone = game.getZoneOf(r); if (r.isLand()) { - movedCard = game.getAction().moveTo(ZoneType.Hand, r, sa); + movedCard = game.getAction().moveTo(ZoneType.Hand, r, sa, moveParams); revealedLand = true; } else { // TODO find better way to choose optional send away @@ -67,7 +71,7 @@ public class ExploreEffect extends SpellAbilityEffect { ZoneType.Graveyard, Lists.newArrayList(ZoneType.Library), sa, top, null, Localizer.getInstance().getMessage("lblPutThisCardToYourGraveyard"), true, pl); if (choosen != null) { - movedCard = game.getAction().moveTo(ZoneType.Graveyard, choosen, sa); + movedCard = game.getAction().moveTo(ZoneType.Graveyard, choosen, sa, moveParams); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/FogEffect.java b/forge-game/src/main/java/forge/game/ability/effects/FogEffect.java index 7db93850abe..335e8eeb3b7 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/FogEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/FogEffect.java @@ -2,6 +2,7 @@ package forge.game.ability.effects; import forge.GameCommand; import forge.game.Game; +import forge.game.ability.AbilityKey; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.replacement.ReplacementEffect; @@ -32,7 +33,7 @@ public class FogEffect extends SpellAbilityEffect { eff.addReplacementEffect(re); game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getAction().moveTo(ZoneType.Command, eff, sa); + game.getAction().moveTo(ZoneType.Command, eff, sa, AbilityKey.newMap()); eff.updateStateForView(); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); diff --git a/forge-game/src/main/java/forge/game/ability/effects/LearnEffect.java b/forge-game/src/main/java/forge/game/ability/effects/LearnEffect.java index 207b8606f54..68d89b805d2 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/LearnEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/LearnEffect.java @@ -1,6 +1,9 @@ package forge.game.ability.effects; +import java.util.Map; + import forge.game.Game; +import forge.game.ability.AbilityKey; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.card.CardZoneTable; @@ -18,8 +21,11 @@ public class LearnEffect extends SpellAbilityEffect { final Card source = sa.getHostCard(); final Game game = source.getGame(); CardZoneTable table = new CardZoneTable(); + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); for (Player p : getTargetPlayers(sa)) { - p.learnLesson(sa, table); + p.learnLesson(sa, table, moveParams); } table.triggerChangesZoneAll(game, sa); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/MakeCardEffect.java b/forge-game/src/main/java/forge/game/ability/effects/MakeCardEffect.java index a36c38d4507..5d32044542b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/MakeCardEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/MakeCardEffect.java @@ -1,7 +1,10 @@ package forge.game.ability.effects; +import java.util.Map; + import forge.StaticData; import forge.game.Game; +import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -13,6 +16,9 @@ import forge.game.zone.ZoneType; public class MakeCardEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); for (final Player player : getTargetPlayers(sa)) { final Card source = sa.getHostCard(); final Game game = player.getGame(); @@ -43,13 +49,13 @@ public class MakeCardEffect extends SpellAbilityEffect { if (!sa.hasParam("NotToken")) { card.setTokenCard(true); } - game.getAction().moveTo(ZoneType.None, card, sa); + game.getAction().moveTo(ZoneType.None, card, sa, moveParams); cards.add(card); amount--; } for (final Card c : cards) { - game.getAction().moveTo(zone, c, sa); + game.getAction().moveTo(zone, c, sa, moveParams); if (sa.hasParam("RememberMade")) { sa.getHostCard().addRemembered(c); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/MillEffect.java b/forge-game/src/main/java/forge/game/ability/effects/MillEffect.java index d9c2ff553fa..21bb6d86db3 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/MillEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/MillEffect.java @@ -1,7 +1,10 @@ package forge.game.ability.effects; +import java.util.Map; + import forge.game.Game; import forge.game.GameLogEntryType; +import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -35,6 +38,9 @@ public class MillEffect extends SpellAbilityEffect { } final CardZoneTable table = new CardZoneTable(); + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); for (final Player p : getTargetPlayers(sa)) { if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { @@ -45,7 +51,7 @@ public class MillEffect extends SpellAbilityEffect { continue; } } - final CardCollectionView milled = p.mill(numCards, destination, bottom, sa, table); + final CardCollectionView milled = p.mill(numCards, destination, bottom, sa, table, moveParams); // Reveal the milled cards, so players don't have to manually inspect the // graveyard to figure out which ones were milled. if (!facedown && reveal) { // do not reveal when exiling face down diff --git a/forge-game/src/main/java/forge/game/ability/effects/PlaneswalkEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PlaneswalkEffect.java index dc3bc6970ed..caac7f2b2c3 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PlaneswalkEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PlaneswalkEffect.java @@ -18,9 +18,9 @@ public class PlaneswalkEffect extends SpellAbilityEffect { } if (sa.hasParam("Defined")) { CardCollectionView destinations = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa); - sa.getActivatingPlayer().planeswalkTo(destinations); + sa.getActivatingPlayer().planeswalkTo(sa, destinations); } else { - sa.getActivatingPlayer().planeswalk(); + sa.getActivatingPlayer().planeswalk(sa); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java index 3b20b103075..dc6f8f64b63 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java @@ -19,6 +19,7 @@ import forge.StaticData; import forge.card.CardRulesPredicates; import forge.game.Game; import forge.game.ability.AbilityFactory; +import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -216,6 +217,11 @@ public class PlayEffect extends SpellAbilityEffect { boolean singleOption = tgtCards.size() == 1 && amount == 1 && optional; Map params = hasTotalCMCLimit ? new HashMap<>() : null; + + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); + while (!tgtCards.isEmpty() && amount > 0 && totalCMCLimit >= 0) { if (hasTotalCMCLimit) { // filter out cards with mana value greater than limit @@ -376,12 +382,12 @@ public class PlayEffect extends SpellAbilityEffect { // can't be done later if (sa.hasParam("ReplaceGraveyard")) { - addReplaceGraveyardEffect(tgtCard, sa, sa.getParam("ReplaceGraveyard")); + addReplaceGraveyardEffect(tgtCard, sa, sa.getParam("ReplaceGraveyard"), moveParams); } // For Illusionary Mask effect if (sa.hasParam("ReplaceIlluMask")) { - addIllusionaryMaskReplace(tgtCard, sa); + addIllusionaryMaskReplace(tgtCard, sa, moveParams); } tgtSA.setSVar("IsCastFromPlayEffect", "True"); @@ -420,7 +426,7 @@ public class PlayEffect extends SpellAbilityEffect { } // end resolve - protected void addReplaceGraveyardEffect(Card c, SpellAbility sa, String zone) { + protected void addReplaceGraveyardEffect(Card c, SpellAbility sa, String zone, Map moveParams) { final Card hostCard = sa.getHostCard(); final Game game = hostCard.getGame(); final Player controller = sa.getActivatingPlayer(); @@ -461,12 +467,12 @@ public class PlayEffect extends SpellAbilityEffect { // TODO: Add targeting to the effect so it knows who it's dealing with game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getAction().moveTo(ZoneType.Command, eff, sa); + game.getAction().moveTo(ZoneType.Command, eff, sa, moveParams); eff.updateStateForView(); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); } - protected void addIllusionaryMaskReplace(Card c, SpellAbility sa) { + protected void addIllusionaryMaskReplace(Card c, SpellAbility sa, Map moveParams) { final Card hostCard = sa.getHostCard(); final Game game = hostCard.getGame(); final Player controller = sa.getActivatingPlayer(); @@ -497,7 +503,7 @@ public class PlayEffect extends SpellAbilityEffect { addExileOnCounteredTrigger(eff); game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getAction().moveTo(ZoneType.Command, eff, sa); + game.getAction().moveTo(ZoneType.Command, eff, sa, moveParams); eff.updateStateForView(); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java index 82bbb917667..2ed822bb1d4 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RegenerateBaseEffect.java @@ -3,6 +3,7 @@ package forge.game.ability.effects; import forge.GameCommand; import forge.game.Game; import forge.game.ability.AbilityFactory; +import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -71,7 +72,7 @@ public abstract class RegenerateBaseEffect extends SpellAbilityEffect { c.addShield(eff); } game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getAction().moveTo(ZoneType.Command, eff, sa); + game.getAction().moveTo(ZoneType.Command, eff, sa, AbilityKey.newMap()); eff.updateStateForView(); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); diff --git a/forge-game/src/main/java/forge/game/ability/effects/SetInMotionEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SetInMotionEffect.java index 2943b46b509..f471abd7c01 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SetInMotionEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SetInMotionEffect.java @@ -41,7 +41,7 @@ public class SetInMotionEffect extends SpellAbilityEffect { } game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getAction().moveTo(ZoneType.Command, controller.getActiveScheme(), null); + game.getAction().moveTo(ZoneType.Command, controller.getActiveScheme(), null, AbilityKey.newMap()); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); // Run triggers diff --git a/forge-game/src/main/java/forge/game/ability/effects/SkipPhaseEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SkipPhaseEffect.java index 43d6c2b5631..a589a52d2c4 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SkipPhaseEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SkipPhaseEffect.java @@ -5,6 +5,7 @@ import java.util.List; import forge.GameCommand; import forge.game.Game; import forge.game.ability.AbilityFactory; +import forge.game.ability.AbilityKey; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.player.Player; @@ -115,7 +116,7 @@ public class SkipPhaseEffect extends SpellAbilityEffect { eff.addReplacementEffect(re); game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getAction().moveTo(ZoneType.Command, eff, sa); + game.getAction().moveTo(ZoneType.Command, eff, sa, AbilityKey.newMap()); eff.updateStateForView(); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/SkipTurnEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SkipTurnEffect.java index 4f64e1d89d2..0d81ed142ca 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SkipTurnEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SkipTurnEffect.java @@ -4,6 +4,7 @@ import java.util.List; import forge.game.Game; import forge.game.ability.AbilityFactory; +import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -61,7 +62,7 @@ public class SkipTurnEffect extends SpellAbilityEffect { eff.addReplacementEffect(re); game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getAction().moveTo(ZoneType.Command, eff, sa); + game.getAction().moveTo(ZoneType.Command, eff, sa, AbilityKey.newMap()); eff.updateStateForView(); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/SubgameEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SubgameEffect.java index 7ec64259baf..51bcf22dba2 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SubgameEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SubgameEffect.java @@ -9,6 +9,7 @@ import com.google.common.collect.Lists; import forge.card.MagicColor; import forge.game.Game; import forge.game.GameOutcome; +import forge.game.ability.AbilityKey; import forge.game.ability.ApiType; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -127,7 +128,7 @@ public class SubgameEffect extends SpellAbilityEffect { // Create an effect that lets you cast your companion from your sideboard if (companion != null) { PlayerZone commandZone = player.getZone(ZoneType.Command); - companion = subgame.getAction().moveTo(ZoneType.Command, companion, null); + companion = subgame.getAction().moveTo(ZoneType.Command, companion, null, AbilityKey.newMap()); commandZone.add(Player.createCompanionEffect(subgame, companion)); player.updateZoneForView(commandZone); @@ -242,7 +243,7 @@ public class SubgameEffect extends SpellAbilityEffect { } } for (final Card card : movedCommanders) { - maingame.getAction().moveTo(ZoneType.Library, card, null); + maingame.getAction().moveTo(ZoneType.Library, card, null, AbilityKey.newMap()); } player.shuffle(sa); diff --git a/forge-game/src/main/java/forge/game/ability/effects/SurveilEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SurveilEffect.java index 1ddc7776ea3..b148a44af13 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SurveilEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SurveilEffect.java @@ -1,5 +1,8 @@ package forge.game.ability.effects; +import java.util.Map; + +import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.CardZoneTable; @@ -34,6 +37,9 @@ public class SurveilEffect extends SpellAbilityEffect { boolean isOptional = sa.hasParam("Optional"); CardZoneTable table = new CardZoneTable(); + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); for (final Player p : getTargetPlayers(sa)) { if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { @@ -41,7 +47,7 @@ public class SurveilEffect extends SpellAbilityEffect { continue; } - p.surveil(num, sa, table); + p.surveil(num, sa, table, moveParams); } } table.triggerChangesZoneAll(sa.getHostCard().getGame(), sa); diff --git a/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java b/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java index c13a27706ff..01009a820de 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java @@ -27,7 +27,7 @@ import forge.util.Localizer; public class VentureEffect extends SpellAbilityEffect { - private Card getDungeonCard(SpellAbility sa, Player player) { + private Card getDungeonCard(SpellAbility sa, Player player, Map moveParams) { final Game game = player.getGame(); CardCollectionView commandCards = player.getCardsIn(ZoneType.Command); @@ -50,7 +50,7 @@ public class VentureEffect extends SpellAbilityEffect { Card dungeon = player.getController().chooseDungeon(player, dungeonCards, message); game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); - game.getAction().moveTo(ZoneType.Command, dungeon, sa); + game.getAction().moveTo(ZoneType.Command, dungeon, sa, moveParams); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); return dungeon; @@ -85,13 +85,13 @@ public class VentureEffect extends SpellAbilityEffect { } } - private void ventureIntoDungeon(SpellAbility sa, Player player) { + private void ventureIntoDungeon(SpellAbility sa, Player player, Map moveParams) { if (StaticAbilityCantVenture.cantVenture(player)) { return; } final Game game = player.getGame(); - Card dungeon = getDungeonCard(sa, player); + Card dungeon = getDungeonCard(sa, player, moveParams); String room = dungeon.getCurrentRoom(); String nextRoom = null; @@ -117,9 +117,13 @@ public class VentureEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); + for (final Player p : getTargetPlayers(sa)) { if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { - ventureIntoDungeon(sa, p); + ventureIntoDungeon(sa, p, moveParams); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ZoneExchangeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ZoneExchangeEffect.java index 694fa05e8aa..280d5360963 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ZoneExchangeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ZoneExchangeEffect.java @@ -1,6 +1,9 @@ package forge.game.ability.effects; +import java.util.Map; + import forge.game.Game; +import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; @@ -87,8 +90,11 @@ public class ZoneExchangeEffect extends SpellAbilityEffect { object1.unattachFromEntity(c); object2.attachToEntity(c); } + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); // Exchange Zone - game.getAction().moveTo(zone2, object1, sa); - game.getAction().moveTo(zone1, object2, sa); + game.getAction().moveTo(zone2, object1, sa, moveParams); + game.getAction().moveTo(zone1, object2, sa, moveParams); } } diff --git a/forge-game/src/main/java/forge/game/cost/CostDraw.java b/forge-game/src/main/java/forge/game/cost/CostDraw.java index 6885be83ebe..8a5c77f0287 100644 --- a/forge-game/src/main/java/forge/game/cost/CostDraw.java +++ b/forge-game/src/main/java/forge/game/cost/CostDraw.java @@ -17,6 +17,9 @@ */ package forge.game.cost; +import java.util.Map; + +import forge.game.ability.AbilityKey; import forge.game.card.Card; import forge.game.player.Player; import forge.game.player.PlayerCollection; @@ -94,8 +97,11 @@ public class CostDraw extends CostPart { */ @Override public final boolean payAsDecided(final Player ai, final PaymentDecision decision, SpellAbility ability, final boolean effect) { + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, ability.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, ability.getLastStateGraveyard()); for (final Player p : decision.players) { - p.drawCards(decision.c, ability); + p.drawCards(decision.c, ability, moveParams); } return true; } diff --git a/forge-game/src/main/java/forge/game/cost/CostMill.java b/forge-game/src/main/java/forge/game/cost/CostMill.java index eb184a618d1..69091a6f337 100644 --- a/forge-game/src/main/java/forge/game/cost/CostMill.java +++ b/forge-game/src/main/java/forge/game/cost/CostMill.java @@ -17,6 +17,9 @@ */ package forge.game.cost; +import java.util.Map; + +import forge.game.ability.AbilityKey; import forge.game.card.CardCollection; import forge.game.card.CardZoneTable; import forge.game.player.Player; @@ -88,7 +91,10 @@ public class CostMill extends CostPart { @Override public final boolean payAsDecided(final Player ai, final PaymentDecision decision, SpellAbility ability, final boolean effect) { CardZoneTable table = new CardZoneTable(); - ability.getPaidHash().put("Milled", (CardCollection) ai.mill(decision.c, ZoneType.Graveyard, false, ability, table)); + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, ai.getGame().getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, ai.getGame().getLastStateGraveyard()); + ability.getPaidHash().put("Milled", (CardCollection) ai.mill(decision.c, ZoneType.Graveyard, false, ability, table, moveParams)); table.triggerChangesZoneAll(ai.getGame(), ability); return true; } diff --git a/forge-game/src/main/java/forge/game/cost/CostSacrifice.java b/forge-game/src/main/java/forge/game/cost/CostSacrifice.java index 2577ecb0957..ec48bb0d976 100644 --- a/forge-game/src/main/java/forge/game/cost/CostSacrifice.java +++ b/forge-game/src/main/java/forge/game/cost/CostSacrifice.java @@ -19,8 +19,11 @@ package forge.game.cost; import forge.card.CardType; +import java.util.Map; + import com.google.common.collect.Iterables; +import forge.game.ability.AbilityKey; import forge.game.card.Card; import forge.game.card.CardCollectionView; import forge.game.card.CardLists; @@ -133,7 +136,10 @@ public class CostSacrifice extends CostPartWithList { @Override protected Card doPayment(SpellAbility ability, Card targetCard, final boolean effect) { // no table there, it is already handled by CostPartWithList - return targetCard.getGame().getAction().sacrifice(targetCard, ability, effect, null, null); + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, ability.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, ability.getLastStateGraveyard()); + return targetCard.getGame().getAction().sacrifice(targetCard, ability, effect, null, moveParams); } /* (non-Javadoc) diff --git a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java index 17cfd4e139e..3f4022e6590 100644 --- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java +++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java @@ -393,11 +393,15 @@ public class PhaseHandler implements java.io.Serializable { int numDiscard = playerTurn.isUnlimitedHandSize() || handSize <= max || handSize == 0 ? 0 : handSize - max; if (numDiscard > 0) { + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, game.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, game.getLastStateGraveyard()); + final CardZoneTable table = new CardZoneTable(); final CardCollection discarded = new CardCollection(); boolean firstDiscarded = playerTurn.getNumDiscardedThisTurn() == 0; for (Card c : playerTurn.getController().chooseCardsToDiscardToMaximumHandSize(numDiscard)) { - if (playerTurn.discard(c, null, false, table) != null) { + if (playerTurn.discard(c, null, false, table, moveParams) != null) { discarded.add(c); } } 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 2c63761879c..49dcbf8959d 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -326,10 +326,14 @@ public class Player extends GameEntity implements Comparable { return; } + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, game.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, game.getLastStateGraveyard()); + game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); activeScheme = getZone(ZoneType.SchemeDeck).get(0); // gameAction moveTo ? - game.getAction().moveTo(ZoneType.Command, activeScheme, null); + game.getAction().moveTo(ZoneType.Command, activeScheme, null, moveParams); game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); // Run triggers @@ -1101,10 +1105,13 @@ public class Player extends GameEntity implements Comparable { return true; } - public void surveil(int num, SpellAbility cause, CardZoneTable table) { + public void surveil(int num, SpellAbility cause, CardZoneTable table, Map params) { final Map repParams = AbilityKey.mapFromAffected(this); repParams.put(AbilityKey.Source, cause); repParams.put(AbilityKey.SurveilNum, num); + if (params != null) { + repParams.putAll(params); + } switch (getGame().getReplacementHandler().run(ReplacementType.Surveil, repParams)) { case NotReplaced: @@ -1133,7 +1140,7 @@ public class Player extends GameEntity implements Comparable { if (toGrave != null) { for (Card c : toGrave) { ZoneType oZone = c.getZone().getZoneType(); - Card moved = getGame().getAction().moveToGraveyard(c, cause); + Card moved = getGame().getAction().moveToGraveyard(c, cause, params); table.put(oZone, moved.getZone().getZoneType(), moved); numToGrave++; } @@ -1142,7 +1149,7 @@ public class Player extends GameEntity implements Comparable { if (toTop != null) { Collections.reverse(toTop); // the last card in list will become topmost in library, have to revert thus. for (Card c : toTop) { - getGame().getAction().moveToLibrary(c, cause); + getGame().getAction().moveToLibrary(c, cause, params); numToTop++; } } @@ -1152,6 +1159,9 @@ public class Player extends GameEntity implements Comparable { surveilThisTurn++; final Map runParams = AbilityKey.mapFromPlayer(this); runParams.put(AbilityKey.NumThisTurn, surveilThisTurn); + if (params != null) { + runParams.putAll(params); + } getGame().getTriggerHandler().runTrigger(TriggerType.Surveil, runParams, false); } @@ -1176,13 +1186,13 @@ public class Player extends GameEntity implements Comparable { } public final CardCollectionView drawCard() { - return drawCards(1, null); + return drawCards(1, null, AbilityKey.newMap()); } public final CardCollectionView drawCards(final int n) { - return drawCards(n, null); + return drawCards(n, null, AbilityKey.newMap()); } - public final CardCollectionView drawCards(final int n, SpellAbility cause) { + public final CardCollectionView drawCards(final int n, SpellAbility cause, Map params) { final CardCollection drawn = new CardCollection(); if (n <= 0) { return drawn; @@ -1191,6 +1201,9 @@ public class Player extends GameEntity implements Comparable { // Replacement effects final Map repRunParams = AbilityKey.mapFromAffected(this); repRunParams.put(AbilityKey.Number, n); + if (params != null) { + repRunParams.putAll(params); + } if (game.getReplacementHandler().run(ReplacementType.DrawCards, repRunParams) != ReplacementResult.NotReplaced) { return drawn; @@ -1204,7 +1217,7 @@ public class Player extends GameEntity implements Comparable { if (gameStarted && !canDraw()) { return drawn; } - drawn.addAll(doDraw(toReveal, cause)); + drawn.addAll(doDraw(toReveal, cause, params)); } // reveal multiple drawn cards when playing with the top of the library revealed @@ -1219,13 +1232,16 @@ public class Player extends GameEntity implements Comparable { /** * @return a CardCollectionView of cards actually drawn */ - private CardCollectionView doDraw(Map revealed, SpellAbility cause) { + private CardCollectionView doDraw(Map revealed, SpellAbility cause, Map params) { final CardCollection drawn = new CardCollection(); final PlayerZone library = getZone(ZoneType.Library); // Replacement effects Map repParams = AbilityKey.mapFromAffected(this); repParams.put(AbilityKey.Cause, cause); + if (params != null) { + repParams.putAll(params); + } if (game.getReplacementHandler().run(ReplacementType.Draw, repParams) != ReplacementResult.NotReplaced) { return drawn; } @@ -1246,7 +1262,7 @@ public class Player extends GameEntity implements Comparable { } } - c = game.getAction().moveToHand(c, cause); + c = game.getAction().moveToHand(c, cause, params); drawn.add(c); for (Player p : pList) { @@ -1267,6 +1283,9 @@ public class Player extends GameEntity implements Comparable { view.updateNumDrawnThisTurn(this); final Map runParams = AbilityKey.mapFromPlayer(this); + if (params != null) { + runParams.putAll(params); + } // CR 121.8 card was drawn as part of another sa (e.g. paying with Chromantic Sphere), hide it temporarily if (game.getTopLibForPlayer(this) != null && getPaidForSA() != null && cause != null && getPaidForSA() != cause.getRootAbility()) { @@ -1414,9 +1433,6 @@ public class Player extends GameEntity implements Comparable { numRollsThisTurn++; } - public final Card discard(final Card c, final SpellAbility sa, final boolean effect, CardZoneTable table) { - return discard(c, sa, effect, table, null); - } public final Card discard(final Card c, final SpellAbility sa, final boolean effect, CardZoneTable table, Map params) { if (!c.canBeDiscardedBy(sa, effect)) { return null; @@ -1575,13 +1591,16 @@ public class Player extends GameEntity implements Comparable { } public final CardCollectionView mill(int n, final ZoneType destination, - final boolean bottom, SpellAbility sa, CardZoneTable table) { + final boolean bottom, SpellAbility sa, CardZoneTable table, Map params) { final CardCollectionView lib = getCardsIn(ZoneType.Library); final CardCollection milled = new CardCollection(); // Replacement effects final Map repRunParams = AbilityKey.mapFromAffected(this); repRunParams.put(AbilityKey.Number, n); + if (params != null) { + repRunParams.putAll(params); + } if (destination == ZoneType.Graveyard && !bottom) { switch (getGame().getReplacementHandler().run(ReplacementType.Mill, repRunParams)) { @@ -1618,7 +1637,7 @@ public class Player extends GameEntity implements Comparable { for (Card m : milledView) { final ZoneType origin = m.getZone().getZoneType(); - final Card d = game.getAction().moveTo(destination, m, sa); + final Card d = game.getAction().moveTo(destination, m, sa, params); if (d.getZone().is(destination)) { table.put(origin, d.getZone().getZoneType(), d); } @@ -2580,21 +2599,25 @@ public class Player extends GameEntity implements Comparable { * Takes the top plane of the planar deck and put it face up in the command zone. * Then runs triggers. */ - public void planeswalk() { - planeswalkTo(new CardCollection(getZone(ZoneType.PlanarDeck).get(0))); + public void planeswalk(SpellAbility sa) { + planeswalkTo(sa, new CardCollection(getZone(ZoneType.PlanarDeck).get(0))); } /** * Puts the planes in the argument and puts them face up in the command zone. * Then runs triggers. */ - public void planeswalkTo(final CardCollectionView destinations) { + public void planeswalkTo(SpellAbility sa, final CardCollectionView destinations) { System.out.println(getName() + ": planeswalk to " + destinations.toString()); currentPlanes.addAll(destinations); game.getView().updatePlanarPlayer(getView()); + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); + for (Card c : currentPlanes) { - game.getAction().moveTo(ZoneType.Command,c, null); + game.getAction().moveTo(ZoneType.Command, c, sa, moveParams); //getZone(ZoneType.PlanarDeck).remove(c); //getZone(ZoneType.Command).add(c); } @@ -3414,10 +3437,13 @@ public class Player extends GameEntity implements Comparable { } } - public void learnLesson(SpellAbility sa, CardZoneTable table) { + public void learnLesson(SpellAbility sa, CardZoneTable table, Map params) { // Replacement effects Map repParams = AbilityKey.mapFromAffected(this); repParams.put(AbilityKey.Cause, sa); + if (params != null) { + repParams.putAll(params); + } if (game.getReplacementHandler().run(ReplacementType.Learn, repParams) != ReplacementResult.NotReplaced) { return; } @@ -3438,11 +3464,11 @@ public class Player extends GameEntity implements Comparable { } if (c.isInZone(ZoneType.Sideboard)) { // Sideboard Lesson to Hand game.getAction().reveal(new CardCollection(c), c.getOwner(), true); - Card moved = game.getAction().moveTo(ZoneType.Hand, c, sa); + Card moved = game.getAction().moveTo(ZoneType.Hand, c, sa, params); table.put(ZoneType.Sideboard, ZoneType.Hand, moved); } else if (c.isInZone(ZoneType.Hand)) { // Discard and Draw boolean firstDiscard = getNumDiscardedThisTurn() == 0; - if (discard(c, sa, true, table) != null) { + if (discard(c, sa, true, table, params) != null) { // Change this if something would make multiple player learn at the same time // Discard Trigger outside Effect @@ -3450,9 +3476,12 @@ public class Player extends GameEntity implements Comparable { runParams.put(AbilityKey.Cards, new CardCollection(c)); runParams.put(AbilityKey.Cause, sa); runParams.put(AbilityKey.FirstTime, firstDiscard); + if (params != null) { + runParams.putAll(params); + } getGame().getTriggerHandler().runTrigger(TriggerType.DiscardedAll, runParams, false); - for (Card d : drawCards(1, sa)) { + for (Card d : drawCards(1, sa, params)) { table.put(ZoneType.Library, ZoneType.Hand, d); // does a ChangesZoneAll care about moving from Library to Hand } } diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java index 69b1be0d2b7..3187ab21a96 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java @@ -44,7 +44,8 @@ import io.sentry.Sentry; import io.sentry.event.BreadcrumbBuilder; public class TriggerHandler { - private final List suppressedModes = Collections.synchronizedList(new ArrayList<>()); + private final Set suppressedModes = Collections.synchronizedSet(EnumSet.noneOf(TriggerType.class)); + private boolean allSuppressed = false; private final List activeTriggers = Collections.synchronizedList(new ArrayList<>()); private final List delayedTriggers = Collections.synchronizedList(new ArrayList<>()); @@ -106,18 +107,15 @@ public class TriggerHandler { } public final void setSuppressAllTriggers(final boolean suppress) { - for (TriggerType t : TriggerType.values()) { - if (suppress) { - suppressMode(t); - } else { - clearSuppression(t); - } - } + allSuppressed = suppress; } public final void clearSuppression(final TriggerType mode) { suppressedModes.remove(mode); } + public boolean isTriggerSuppressed(final TriggerType mode) { + return allSuppressed || suppressedModes.contains(mode); + } public static Trigger parseTrigger(final String trigParse, final Card host, final boolean intrinsic) { return parseTrigger(trigParse, host, intrinsic, host.getCurrentState()); @@ -252,7 +250,7 @@ public class TriggerHandler { } public final void runTrigger(final TriggerType mode, final Map runParams, boolean holdTrigger) { - if (suppressedModes.contains(mode)) { + if (isTriggerSuppressed(mode)) { return; } diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 1b652bb756c..979949f9546 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -2803,7 +2803,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } } } - getGame().getAction().moveTo(targetZone, forgeCard, null); + getGame().getAction().moveTo(targetZone, forgeCard, null, AbilityKey.newMap()); if (forgeCard.isCreature()) { forgeCard.setSickness(lastSummoningSickness); } @@ -2858,7 +2858,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont getGame().getAction().moveToBottomOfLibrary(forgeCard, null); } } else { - getGame().getAction().moveTo(targetZone, forgeCard, null); + getGame().getAction().moveTo(targetZone, forgeCard, null, AbilityKey.newMap()); } lastAdded = f; @@ -2898,7 +2898,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont if (c == null) { continue; } - if (getGame().getAction().moveTo(ZoneType.Exile, c, null) != null) { + if (getGame().getAction().moveTo(ZoneType.Exile, c, null, AbilityKey.newMap()) != null) { StringBuilder sb = new StringBuilder(); sb.append(p).append(" exiles ").append(c).append(" due to Dev Cheats."); getGame().getGameLog().add(GameLogEntryType.DISCARD, sb.toString()); @@ -2937,7 +2937,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont if (c == null) { continue; } - if (getGame().getAction().moveTo(ZoneType.Exile, c, null) != null) { + if (getGame().getAction().moveTo(ZoneType.Exile, c, null, AbilityKey.newMap()) != null) { StringBuilder sb = new StringBuilder(); sb.append(p).append(" exiles ").append(c).append(" due to Dev Cheats."); getGame().getGameLog().add(GameLogEntryType.ZONE_CHANGE, sb.toString());