diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 0304efda411..66a5a6881ff 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -538,8 +538,10 @@ public class GameAction { if (repres != ReplacementResult.NotReplaced) continue; } if (card == c) { + storeChangesZoneAll(copied, zoneFrom, zoneTo, params); zoneTo.add(copied, position, toBattlefield ? null : lastKnownInfo); // the modified state of the card is also reported here (e.g. for Morbid + Awaken) } else { + storeChangesZoneAll(card, zoneFrom, zoneTo, params); zoneTo.add(card, position, CardUtil.getLKICopy(card)); card.setState(CardStateName.Original, false); card.setBackSide(false); @@ -548,6 +550,7 @@ public class GameAction { card.setZone(zoneTo); } } else { + storeChangesZoneAll(copied, zoneFrom, zoneTo, params); // "enter the battlefield as a copy" - apply code here // but how to query for input here and continue later while the callers assume synchronous result? zoneTo.add(copied, position, toBattlefield ? null : lastKnownInfo); // the modified state of the card is also reported here (e.g. for Morbid + Awaken) @@ -723,6 +726,12 @@ public class GameAction { return copied; } + private void storeChangesZoneAll(Card c, Zone zoneFrom, Zone zoneTo, Map params) { + if (params != null && params.containsKey(AbilityKey.InternalTriggerTable)) { + ((CardZoneTable) params.get(AbilityKey.InternalTriggerTable)).put(zoneFrom != null ? zoneFrom.getZoneType() : null, zoneTo.getZoneType(), c); + } + } + private static void unattachCardLeavingBattlefield(final Card copied) { // remove attachments from creatures copied.unAttachAllCards(); @@ -1251,6 +1260,7 @@ public class GameAction { boolean checkAgain = false; CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard()); + mapParams.put(AbilityKey.InternalTriggerTable, table); for (final Player p : game.getPlayers()) { for (final ZoneType zt : ZoneType.values()) { @@ -1396,7 +1406,7 @@ public class GameAction { } for (Card c : noRegCreats) { c.updateWasDestroyed(true); - sacrificeDestroy(c, null, table, mapParams); + sacrificeDestroy(c, null, mapParams); } if (desCreats != null) { @@ -1408,7 +1418,7 @@ public class GameAction { orderedDesCreats = true; } for (Card c : desCreats) { - destroy(c, null, true, table, mapParams); + destroy(c, null, true, mapParams); } } @@ -1418,7 +1428,7 @@ public class GameAction { } for (Card c : sacrificeList) { c.updateWasDestroyed(true); - sacrifice(c, null, true, table, mapParams); + sacrifice(c, null, true, mapParams); } setHoldCheckingStaticAbilities(false); @@ -1856,7 +1866,7 @@ public class GameAction { return true; } - public final Card sacrifice(final Card c, final SpellAbility source, final boolean effect, CardZoneTable table, Map params) { + public final Card sacrifice(final Card c, final SpellAbility source, final boolean effect, Map params) { if (!c.canBeSacrificedBy(source, effect)) { return null; } @@ -1864,10 +1874,10 @@ public class GameAction { c.getController().addSacrificedThisTurn(c, source); c.updateWasDestroyed(true); - return sacrificeDestroy(c, source, table, params); + return sacrificeDestroy(c, source, params); } - public final boolean destroy(final Card c, final SpellAbility sa, final boolean regenerate, CardZoneTable table, Map params) { + public final boolean destroy(final Card c, final SpellAbility sa, final boolean regenerate, Map params) { if (!c.canBeDestroyed()) { return false; } @@ -1904,7 +1914,7 @@ public class GameAction { // in case the destroyed card has such a trigger game.getTriggerHandler().registerActiveLTBTrigger(c); - final Card sacrificed = sacrificeDestroy(c, sa, table, params); + final Card sacrificed = sacrificeDestroy(c, sa, params); return sacrificed != null; } @@ -1912,15 +1922,12 @@ public class GameAction { * @return the sacrificed Card in its new location, or {@code null} if the * sacrifice wasn't successful. */ - protected final Card sacrificeDestroy(final Card c, SpellAbility cause, CardZoneTable table, Map params) { + protected final Card sacrificeDestroy(final Card c, SpellAbility cause, Map params) { if (!c.isInPlay()) { return null; } final Card newCard = moveToGraveyard(c, cause, params); - if (table != null && newCard != null && newCard.getZone() != null) { - table.put(ZoneType.Battlefield, newCard.getZone().getZoneType(), newCard); - } return newCard; } diff --git a/forge-game/src/main/java/forge/game/ability/AbilityKey.java b/forge-game/src/main/java/forge/game/ability/AbilityKey.java index 98c68f02969..86f984dbe61 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityKey.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityKey.java @@ -119,7 +119,6 @@ public enum AbilityKey { ScryBottom("ScryBottom"), ScryNum("ScryNum"), Sides("Sides"), - SimultaneousETB("SimultaneousETB"), Source("Source"), Sources("Sources"), SourceSA("SourceSA"), @@ -135,8 +134,11 @@ public enum AbilityKey { Token("Token"), TokenNum("TokenNum"), Vehicle("Vehicle"), - Won("Won"); + Won("Won"), + // below used across different Replacements, don't reuse + InternalTriggerTable("InternalTriggerTable"), + SimultaneousETB("SimultaneousETB"); // for CR 614.13c private String key; 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 0f23570d41c..c3e4c26ba42 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -761,13 +761,13 @@ public abstract class SpellAbilityEffect { }; } - protected static void discard(SpellAbility sa, CardZoneTable table, final boolean effect, Map discardedMap, Map params) { + protected static void discard(SpellAbility sa, 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, params) != null) { + if (p.discard(card, sa, effect, params) != null) { discardedByPlayer.add(card); if (sa.hasParam("RememberDiscarded")) { @@ -915,4 +915,11 @@ public abstract class SpellAbilityEffect { movedCard.setExiledWith(exilingSource); movedCard.setExiledBy(cause.getActivatingPlayer()); } + + public CardZoneTable getChangeZoneTable(SpellAbility sa, CardCollectionView lastStateBattlefield, CardCollectionView lastStateGraveyard) { + if (sa.isReplacementAbility() && sa.getReplacingObject(AbilityKey.InternalTriggerTable) != null) { + return (CardZoneTable) sa.getReplacingObject(AbilityKey.InternalTriggerTable); + } + return new CardZoneTable(lastStateBattlefield, lastStateGraveyard); + } } 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 61b7dcd0826..245eaa51cdf 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 @@ -48,10 +48,11 @@ public class BalanceEffect extends SpellAbilityEffect { min = Math.min(min, validCards.get(i).size()); } + CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateBattlefield()); Map params = AbilityKey.newMap(); params.put(AbilityKey.LastStateBattlefield, game.getLastStateBattlefield()); params.put(AbilityKey.LastStateGraveyard, game.getLastStateBattlefield()); - CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateBattlefield()); + params.put(AbilityKey.InternalTriggerTable, table); for (int i = 0; i < players.size(); i++) { Player p = players.get(i); @@ -64,13 +65,13 @@ public class BalanceEffect extends SpellAbilityEffect { } else { // Battlefield for (Card card : p.getController().choosePermanentsToSacrifice(sa, numToBalance, numToBalance, validCards.get(i), valid)) { if (null == card) continue; - game.getAction().sacrifice(card, sa, true, table, params); + game.getAction().sacrifice(card, sa, true, params); } } } if (zone.equals(ZoneType.Hand)) { - discard(sa, table, true, discardedMap, params); + discard(sa, true, discardedMap, params); } table.triggerChangesZoneAll(game, sa); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java index 1fbd452520c..e0420915eb3 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneAllEffect.java @@ -155,7 +155,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect { CardLists.shuffle(cards); } - final CardZoneTable triggerList = new CardZoneTable(lastStateBattlefield, lastStateGraveyard); + final CardZoneTable triggerList = getChangeZoneTable(sa, lastStateBattlefield, lastStateGraveyard); for (final Card c : cards) { final Zone originZone = game.getZoneOf(c); @@ -240,21 +240,6 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect { if (imprint != null) { game.getCardState(source).addImprintedCard(movedCard); } - - triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), movedCard); - - if (c.getMeldedWith() != null) { - Card meld = game.getCardState(c.getMeldedWith(), null); - if (meld != null) { - triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), meld); - } - } - if (c.hasMergedCard()) { - for (final Card cm : c.getMergedCards()) { - if (cm == c) continue; - triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), cm); - } - } } } 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 1e50f59344f..9542fb932e9 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 @@ -471,7 +471,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield(); CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard(); - final CardZoneTable triggerList = new CardZoneTable(lastStateBattlefield, lastStateGraveyard); + final CardZoneTable triggerList = getChangeZoneTable(sa, lastStateBattlefield, lastStateGraveyard); GameEntityCounterTable counterTable = new GameEntityCounterTable(); // changing zones for spells on the stack for (final SpellAbility tgtSA : getTargetSpells(sa)) { @@ -546,6 +546,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect { } Card movedCard = null; + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); + moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); + moveParams.put(AbilityKey.InternalTriggerTable, triggerList); if (destination.equals(ZoneType.Library)) { // If a card is moved to library from the stack, remove its spells from the stack @@ -559,9 +563,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect { movedCard = game.getAction().moveToLibrary(gameCard, libraryPosition, sa); } else if (destination.equals(ZoneType.Battlefield)) { - Map moveParams = AbilityKey.newMap(); - moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); - moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); moveParams.put(AbilityKey.SimultaneousETB, tgtCards); if (sa.isReplacementAbility()) { ReplacementEffect re = sa.getReplacementEffect(); @@ -704,9 +705,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect { handleExiledWith(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())) { @@ -750,13 +748,8 @@ public class ChangeZoneEffect extends SpellAbilityEffect { } if (!movedCard.getZone().equals(originZone)) { Card meld = null; - triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), movedCard); - if (gameCard.getMeldedWith() != null) { meld = game.getCardState(gameCard.getMeldedWith(), null); - if (meld != null) { - triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), meld); - } if (sa.hasParam("WithCountersType")) { CounterType cType = CounterType.getType(sa.getParam("WithCountersType")); int cAmount = AbilityUtils.calculateAmount(hostCard, sa.getParamOrDefault("WithCountersAmount", "1"), sa); @@ -766,7 +759,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect { if (gameCard.hasMergedCard()) { for (final Card c : gameCard.getMergedCards()) { if (c == gameCard) continue; - triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), c); if (sa.hasParam("WithCountersType")) { CounterType cType = CounterType.getType(sa.getParam("WithCountersType")); int cAmount = AbilityUtils.calculateAmount(hostCard, sa.getParamOrDefault("WithCountersAmount", "1"), sa); @@ -1265,7 +1257,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield(); CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard(); - final CardZoneTable triggerList = new CardZoneTable(lastStateBattlefield, lastStateGraveyard); + final CardZoneTable triggerList = getChangeZoneTable(sa, lastStateBattlefield, lastStateGraveyard); for (Player player : HiddenOriginChoicesMap.keySet()) { boolean searchedLibrary = HiddenOriginChoicesMap.get(player).searchedLibrary; @@ -1284,6 +1276,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect { moveParams.put(AbilityKey.FoundSearchingLibrary, searchedLibrary); moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); + moveParams.put(AbilityKey.InternalTriggerTable, triggerList); if (destination.equals(ZoneType.Library)) { movedCard = game.getAction().moveToLibrary(c, libraryPos, sa, moveParams); } @@ -1420,12 +1413,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect { movedCards.add(movedCard); if (originZone != null) { - triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), movedCard); - if (c.getMeldedWith() != null) { Card meld = game.getCardState(c.getMeldedWith(), null); if (meld != null) { - triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), meld); if (destination.equals(ZoneType.Exile)) { handleExiledWith(meld, sa); } @@ -1434,7 +1424,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect { if (c.hasMergedCard()) { for (final Card card : c.getMergedCards()) { if (card == c) continue; - triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), card); if (destination.equals(ZoneType.Exile)) { handleExiledWith(c, sa); } @@ -1553,12 +1542,12 @@ public class ChangeZoneEffect extends SpellAbilityEffect { */ private void removeFromStack(final SpellAbility tgtSA, final SpellAbility srcSA, final SpellAbilityStackInstance si, final Game game, CardZoneTable triggerList, GameEntityCounterTable counterTable) { final Card tgtHost = tgtSA.getHostCard(); - final Zone originZone = tgtHost.getZone(); game.getStack().remove(si); Map params = AbilityKey.newMap(); params.put(AbilityKey.StackSa, tgtSA); params.put(AbilityKey.StackSi, si); + params.put(AbilityKey.InternalTriggerTable, triggerList); Card movedCard = null; if (srcSA.hasParam("Destination")) { @@ -1609,9 +1598,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect { if (!tgtSA.isAbility()) { System.out.println("Moving spell to " + srcSA.getParam("Destination")); } - if (originZone != null && movedCard != null) { - triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), movedCard); - } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ConniveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ConniveEffect.java index e2e1877ed4c..d79ecf61cae 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ConniveEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ConniveEffect.java @@ -31,9 +31,9 @@ public class ConniveEffect extends SpellAbilityEffect { if (tgt.size() <= 0) { return ""; } else { - final StringBuilder sb = new StringBuilder(); - sb.append(Lang.joinHomogenous(tgt)).append(tgt.size() > 1 ? " connive." : " connives."); - return sb.toString(); + final StringBuilder sb = new StringBuilder(); + sb.append(Lang.joinHomogenous(tgt)).append(tgt.size() > 1 ? " connive." : " connives."); + return sb.toString(); } } @@ -73,6 +73,7 @@ public class ConniveEffect extends SpellAbilityEffect { Map moveParams = AbilityKey.newMap(); moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); + moveParams.put(AbilityKey.InternalTriggerTable, triggerList); Card conniver = connivers.size() > 1 ? p.getController().chooseSingleEntityForEffect(connivers, sa, Localizer.getInstance().getMessage("lblChooseConniver"), null) : connivers.get(0); @@ -100,7 +101,7 @@ public class ConniveEffect extends SpellAbilityEffect { conniver.addCounter(CounterEnumType.P1P1, numCntrs, p, table); } discardedMap.put(p, CardCollection.getView(toBeDiscarded)); - discard(sa, triggerList, true, discardedMap, moveParams); + discard(sa, true, discardedMap, moveParams); table.replaceCounterEffect(game, sa, true); triggerList.triggerChangesZoneAll(game, sa); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CounterEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CounterEffect.java index ec9cca4e8ca..92cc55cbea7 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CounterEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CounterEffect.java @@ -13,7 +13,6 @@ import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityStackInstance; import forge.game.spellability.SpellPermanent; import forge.game.trigger.TriggerType; -import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.util.Localizer; @@ -53,9 +52,10 @@ public class CounterEffect extends SpellAbilityEffect { @Override public void resolve(SpellAbility sa) { final Game game = sa.getActivatingPlayer().getGame(); - Map params = AbilityKey.newMap(); CardZoneTable table = new CardZoneTable(); + params.put(AbilityKey.InternalTriggerTable, table); + for (final SpellAbility tgtSA : getTargetSpells(sa)) { final Card tgtSACard = tgtSA.getHostCard(); // should remember even that spell cannot be countered @@ -91,13 +91,13 @@ public class CounterEffect extends SpellAbilityEffect { } } - if (!removeFromStack(tgtSA, sa, si, table)) { + if (!removeFromStack(tgtSA, sa, si, params)) { continue; } // Destroy Permanent may be able to be turned into a SubAbility if (tgtSA.isAbility() && sa.hasParam("DestroyPermanent")) { - game.getAction().destroy(tgtSACard, sa, true, table, params); + game.getAction().destroy(tgtSACard, sa, true, params); } if (sa.hasParam("RememberCountered")) { @@ -193,7 +193,6 @@ public class CounterEffect extends SpellAbilityEffect { // Dry run Destroy on each validAffected to see if it can be destroyed at this moment boolean willDestroyCondition = false; final boolean noRegen = tgtSA.hasParam("NoRegen"); - CardZoneTable testTable = new CardZoneTable(); Map testParams = AbilityKey.newMap(); testParams.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield()); @@ -207,7 +206,7 @@ public class CounterEffect extends SpellAbilityEffect { Card toBeDestroyed = CardFactory.copyCard(aff, true); game.getTriggerHandler().setSuppressAllTriggers(true); - boolean destroyed = game.getAction().destroy(toBeDestroyed, tgtSA, !noRegen, testTable, testParams); + boolean destroyed = game.getAction().destroy(toBeDestroyed, tgtSA, !noRegen, testParams); game.getTriggerHandler().setSuppressAllTriggers(false); if (destroyed) { @@ -236,11 +235,10 @@ public class CounterEffect extends SpellAbilityEffect { * a {@link forge.game.spellability.SpellAbilityStackInstance} * object. */ - private static boolean removeFromStack(final SpellAbility tgtSA, final SpellAbility srcSA, final SpellAbilityStackInstance si, CardZoneTable triggerList) { + private static boolean removeFromStack(final SpellAbility tgtSA, final SpellAbility srcSA, final SpellAbilityStackInstance si, Map params) { final Game game = tgtSA.getActivatingPlayer().getGame(); Card movedCard = null; final Card c = tgtSA.getHostCard(); - final Zone originZone = c.getZone(); // Run any applicable replacement effects. final Map repParams = AbilityKey.mapFromAffected(tgtSA.getHostCard()); @@ -254,7 +252,6 @@ public class CounterEffect extends SpellAbilityEffect { // if the target card on stack was a spell with Bestow, then unbestow it c.unanimateBestow(); - Map params = AbilityKey.newMap(); params.put(AbilityKey.StackSa, tgtSA); params.put(AbilityKey.StackSi, si); @@ -305,9 +302,6 @@ public class CounterEffect extends SpellAbilityEffect { game.getGameLog().add(GameLogEntryType.ZONE_CHANGE, "Send countered spell to " + destination); } - if (originZone != null && movedCard != null) { - triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), movedCard); - } return true; } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DestroyAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DestroyAllEffect.java index e9c51966707..44d101cfcc6 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DestroyAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DestroyAllEffect.java @@ -91,12 +91,12 @@ public class DestroyAllEffect extends SpellAbilityEffect { Map params = AbilityKey.newMap(); params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield()); - CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard()); + params.put(AbilityKey.InternalTriggerTable, table); Map cachedMap = Maps.newHashMap(); for (Card c : list) { - if (game.getAction().destroy(c, sa, !noRegen, table, params) && remDestroyed) { + if (game.getAction().destroy(c, sa, !noRegen, params) && remDestroyed) { card.addRemembered(CardUtil.getLKICopy(c, cachedMap)); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java index 454bcd69719..fb571a3ec51 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DestroyEffect.java @@ -61,8 +61,9 @@ public class DestroyEffect extends SpellAbilityEffect { Map params = AbilityKey.newMap(); params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield()); + CardZoneTable table = getChangeZoneTable(sa, game.getLastStateBattlefield(), CardCollection.EMPTY); + params.put(AbilityKey.InternalTriggerTable, table); - CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), CardCollection.EMPTY); Map cachedMap = Maps.newHashMap(); for (final Card tgtC : tgtCards) { if (!tgtC.isInPlay()) { @@ -75,21 +76,21 @@ public class DestroyEffect extends SpellAbilityEffect { if (gameCard == null || !tgtC.equalsWithTimestamp(gameCard)) { continue; } - internalDestroy(gameCard, sa, table, cachedMap, params); + internalDestroy(gameCard, sa, cachedMap, params); } untargetedCards = GameActionUtil.orderCardsByTheirOwners(game, untargetedCards, ZoneType.Graveyard, sa); for (final Card unTgtC : untargetedCards) { if (unTgtC.isInPlay()) { - internalDestroy(unTgtC, sa, table, cachedMap, params); + internalDestroy(unTgtC, sa, cachedMap, params); } } table.triggerChangesZoneAll(game, sa); } - protected void internalDestroy(Card gameCard, SpellAbility sa, CardZoneTable table, Map cachedMap, Map params) { + protected void internalDestroy(Card gameCard, SpellAbility sa, Map cachedMap, Map params) { final Card card = sa.getHostCard(); final Game game = card.getGame(); final boolean remDestroyed = sa.hasParam("RememberDestroyed"); @@ -100,9 +101,9 @@ public class DestroyEffect extends SpellAbilityEffect { final Card lki = sa.hasParam("RememberLKI") ? CardUtil.getLKICopy(gameCard, cachedMap) : null; if (sac) { - destroyed = game.getAction().sacrifice(gameCard, sa, true, table, params) != null; + destroyed = game.getAction().sacrifice(gameCard, sa, true, params) != null; } else { - destroyed = game.getAction().destroy(gameCard, sa, !noRegen, table, params); + destroyed = game.getAction().destroy(gameCard, sa, !noRegen, params); } if (destroyed && remDestroyed) { card.addRemembered(gameCard); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java index de69738efac..399cb2dc495 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java @@ -15,7 +15,6 @@ import forge.game.player.DelayedReveal; import forge.game.player.Player; import forge.game.player.PlayerView; import forge.game.spellability.SpellAbility; -import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; import forge.util.CardTranslation; import forge.util.Lang; @@ -372,13 +371,13 @@ public class DigEffect extends SpellAbilityEffect { movedCards = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, movedCards, destZone1, sa); } - for (Card c : movedCards) { - final ZoneType origin = c.getZone().getZoneType(); + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.InternalTriggerTable, table); + for (Card c : movedCards) { if (destZone1.equals(ZoneType.Library) || destZone1.equals(ZoneType.PlanarDeck) || destZone1.equals(ZoneType.SchemeDeck)) { c = game.getAction().moveTo(destZone1, c, libraryPosition, sa); } else { - Map moveParams = AbilityKey.newMap(); moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); if (sa.hasParam("Tapped")) { @@ -421,9 +420,6 @@ public class DigEffect extends SpellAbilityEffect { handleExiledWith(c, sa); } } - if (!origin.equals(c.getZone().getZoneType())) { - table.put(origin, c.getZone().getZoneType(), c); - } if (sa.hasParam("ExileFaceDown")) { c.turnFaceDown(true); @@ -465,11 +461,7 @@ public class DigEffect extends SpellAbilityEffect { } for (final Card c : afterOrder) { - final ZoneType origin = c.getZone().getZoneType(); - Card m = game.getAction().moveTo(destZone2, c, libraryPosition2, sa); - if (m != null && !origin.equals(m.getZone().getZoneType())) { - table.put(origin, m.getZone().getZoneType(), m); - } + Card m = game.getAction().moveTo(destZone2, c, libraryPosition2, sa, moveParams); if (remZone2) { host.addRemembered(m); } @@ -477,12 +469,7 @@ public class DigEffect extends SpellAbilityEffect { } else { // just move them randomly for (Card c : rest) { - final ZoneType origin = c.getZone().getZoneType(); - final PlayerZone toZone = c.getOwner().getZone(destZone2); - c = game.getAction().moveTo(toZone, c, sa); - if (!origin.equals(c.getZone().getZoneType())) { - table.put(origin, c.getZone().getZoneType(), c); - } + c = game.getAction().moveTo(destZone2, c, sa, moveParams); if (destZone2 == ZoneType.Exile) { if (sa.hasParam("ExileWithCounter")) { c.addCounter(CounterType.getType(sa.getParam("ExileWithCounter")), 1, player, counterTable); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java index 2c95137cf1b..6567468f11b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java @@ -196,7 +196,6 @@ public class DigUntilEffect extends SpellAbilityEffect { while (itr.hasNext()) { final Card c = itr.next(); - final ZoneType origin = c.getZone().getZoneType(); if (optionalFound) { boolean result = p.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantPutCardToZone", foundDest.getTranslatedName()), null); if (!result) { @@ -209,10 +208,12 @@ public class DigUntilEffect extends SpellAbilityEffect { } } + CardZoneTable tableSeq = new CardZoneTable(); Map moveParams = AbilityKey.newMap(); moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield); moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard); - Card m = null; + moveParams.put(AbilityKey.InternalTriggerTable, tableSeq); + if (foundDest.equals(ZoneType.Battlefield)) { moveParams.put(AbilityKey.SimultaneousETB, new CardCollection(c)); if (sa.hasParam("GainControl")) { @@ -238,21 +239,17 @@ public class DigUntilEffect extends SpellAbilityEffect { if (sa.hasParam("Tapped")) { c.setTapped(true); } - m = game.getAction().moveTo(c.getController().getZone(foundDest), c, sa, moveParams); + game.getAction().moveTo(foundDest, c, sa, moveParams); if (addToCombat(c, sa, "Attacking", "Blocking")) { combatChanged = true; } } else if (sa.hasParam("NoMoveFound")) { //Don't do anything } else { - m = game.getAction().moveTo(foundDest, c, foundLibPos, sa, moveParams); + game.getAction().moveTo(foundDest, c, foundLibPos, sa, moveParams); } - if (m != null && !origin.equals(m.getZone().getZoneType())) { - CardZoneTable trigList = new CardZoneTable(); - trigList.put(origin, m.getZone().getZoneType(), m); - trigList.triggerChangesZoneAll(game, sa); - } + tableSeq.triggerChangesZoneAll(game, sa); } revealed.removeAll(found); } @@ -287,14 +284,13 @@ public class DigUntilEffect extends SpellAbilityEffect { revealed = (CardCollection)p.getController().orderMoveToZoneList(revealed, finalDest, sa); } + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.InternalTriggerTable, table); + final Iterator itr = revealed.iterator(); while (itr.hasNext()) { final Card c = itr.next(); - final ZoneType origin = c.getZone().getZoneType(); - final Card m = game.getAction().moveTo(finalDest, c, finalPos, sa); - if (m != null && !origin.equals(m.getZone().getZoneType())) { - table.put(origin, m.getZone().getZoneType(), m); - } + game.getAction().moveTo(finalDest, c, finalPos, sa, moveParams); } } 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 16d66432c89..df15a016f4a 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 @@ -279,10 +279,10 @@ public class DiscardEffect extends SpellAbilityEffect { Map params = AbilityKey.newMap(); params.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); params.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); + params.put(AbilityKey.InternalTriggerTable, table); - discard(sa, table, true, discardedMap, params); + discard(sa, 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/ExploreEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java index bba615bdcb3..c8e202fffd6 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 @@ -17,7 +17,6 @@ import forge.game.replacement.ReplacementResult; import forge.game.replacement.ReplacementType; import forge.game.spellability.SpellAbility; import forge.game.trigger.TriggerType; -import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.util.CardTranslation; import forge.util.Lang; @@ -63,25 +62,24 @@ public class ExploreEffect extends SpellAbilityEffect { for (final Card c : tgts) { final Player pl = c.getController(); for (int i = 0; i < amount; i++) { - GameEntityCounterTable table = new GameEntityCounterTable(); - final CardZoneTable triggerList = new CardZoneTable(); - if (game.getReplacementHandler().run(ReplacementType.Explore, AbilityKey.mapFromAffected(c)) != ReplacementResult.NotReplaced) { continue; } + GameEntityCounterTable table = new GameEntityCounterTable(); + final CardZoneTable triggerList = new CardZoneTable(); + moveParams.put(AbilityKey.InternalTriggerTable, triggerList); + // revealed land card boolean revealedLand = false; CardCollection top = pl.getTopXCardsFromLibrary(1); if (!top.isEmpty()) { - Card movedCard = null; game.getAction().reveal(top, pl, false, Localizer.getInstance().getMessage("lblRevealedForExplore") + " - "); final Card r = top.getFirst(); - final Zone originZone = game.getZoneOf(r); if (r.isLand()) { - movedCard = game.getAction().moveTo(ZoneType.Hand, r, sa, moveParams); + game.getAction().moveTo(ZoneType.Hand, r, sa, moveParams); revealedLand = true; } else { Map params = Maps.newHashMap(); @@ -89,11 +87,7 @@ public class ExploreEffect extends SpellAbilityEffect { if (pl.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblPutThisCardToYourGraveyard", CardTranslation.getTranslatedName(r.getName())), r, params)) - movedCard = game.getAction().moveTo(ZoneType.Graveyard, r, sa, moveParams); - } - - if (originZone != null && movedCard != null) { - triggerList.put(originZone.getZoneType(), movedCard.getZone().getZoneType(), movedCard); + game.getAction().moveTo(ZoneType.Graveyard, r, sa, moveParams); } } if (!revealedLand) { 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 68d89b805d2..6fef2dade36 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 @@ -24,8 +24,9 @@ public class LearnEffect extends SpellAbilityEffect { Map moveParams = AbilityKey.newMap(); moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); + moveParams.put(AbilityKey.InternalTriggerTable, table); for (Player p : getTargetPlayers(sa)) { - p.learnLesson(sa, table, moveParams); + p.learnLesson(sa, moveParams); } table.triggerChangesZoneAll(game, sa); } 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 e7283215c98..6177be0330a 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 @@ -41,6 +41,7 @@ public class MillEffect extends SpellAbilityEffect { Map moveParams = AbilityKey.newMap(); moveParams.put(AbilityKey.LastStateBattlefield, sa.getLastStateBattlefield()); moveParams.put(AbilityKey.LastStateGraveyard, sa.getLastStateGraveyard()); + moveParams.put(AbilityKey.InternalTriggerTable, table); for (final Player p : getTargetPlayers(sa)) { if (!p.isInGame()) { @@ -58,7 +59,7 @@ public class MillEffect extends SpellAbilityEffect { continue; } } - final CardCollectionView milled = p.mill(numCards, destination, sa, table, moveParams); + final CardCollectionView milled = p.mill(numCards, destination, sa, 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/SacrificeAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SacrificeAllEffect.java index 24fb3de2be1..44ed4ded06b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SacrificeAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SacrificeAllEffect.java @@ -86,10 +86,11 @@ public class SacrificeAllEffect extends SpellAbilityEffect { Map params = AbilityKey.newMap(); params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield()); CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), CardCollection.EMPTY); + params.put(AbilityKey.InternalTriggerTable, table); for (Card sac : list) { final Card lKICopy = CardUtil.getLKICopy(sac, cachedMap); - if (game.getAction().sacrifice(sac, sa, true, table, params) != null) { + if (game.getAction().sacrifice(sac, sa, true, params) != null) { if (remSacrificed) { card.addRemembered(lKICopy); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java index 04d3ff299a1..da14f41f5a5 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java @@ -103,12 +103,13 @@ public class SacrificeEffect extends SpellAbilityEffect { Map params = AbilityKey.newMap(); params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield()); CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), CardCollection.EMPTY); + params.put(AbilityKey.InternalTriggerTable, table); if (valid.equals("Self") && game.getZoneOf(card) != null) { if (game.getZoneOf(card).is(ZoneType.Battlefield)) { if (!optional || activator.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantSacrificeThis", card.getName()), null)) { - if (game.getAction().sacrifice(card, sa, true, table, params) != null) { + if (game.getAction().sacrifice(card, sa, true, params) != null) { if (remSacrificed) { card.addRemembered(card); } @@ -170,8 +171,8 @@ public class SacrificeEffect extends SpellAbilityEffect { if (devour || exploit || remSacrificed) { lKICopy = CardUtil.getLKICopy(sac, cachedMap); } - boolean wasSacrificed = !destroy && game.getAction().sacrifice(sac, sa, true, table, params) != null; - boolean wasDestroyed = destroy && game.getAction().destroy(sac, sa, true, table, params); + boolean wasSacrificed = !destroy && game.getAction().sacrifice(sac, sa, true, params) != null; + boolean wasDestroyed = destroy && game.getAction().destroy(sac, sa, true, params); // Run Devour Trigger if (devour) { card.addDevoured(lKICopy); diff --git a/forge-game/src/main/java/forge/game/card/CardZoneTable.java b/forge-game/src/main/java/forge/game/card/CardZoneTable.java index 2321adcb7f7..663bde08fff 100644 --- a/forge-game/src/main/java/forge/game/card/CardZoneTable.java +++ b/forge-game/src/main/java/forge/game/card/CardZoneTable.java @@ -5,12 +5,15 @@ package forge.game.card; import java.util.Map; +import org.apache.commons.lang3.ObjectUtils; + import com.google.common.collect.*; import forge.game.CardTraitBase; import forge.game.Game; import forge.game.ability.AbilityKey; import forge.game.player.PlayerCollection; +import forge.game.replacement.ReplacementType; import forge.game.spellability.SpellAbility; import forge.game.trigger.TriggerType; import forge.game.zone.ZoneType; @@ -32,12 +35,12 @@ public class CardZoneTable extends ForwardingTable runParams = AbilityKey.newMap(); runParams.put(AbilityKey.Cards, new CardZoneTable(this)); diff --git a/forge-game/src/main/java/forge/game/cost/CostDiscard.java b/forge-game/src/main/java/forge/game/cost/CostDiscard.java index 2066eedf014..6ddb7f5b8db 100644 --- a/forge-game/src/main/java/forge/game/cost/CostDiscard.java +++ b/forge-game/src/main/java/forge/game/cost/CostDiscard.java @@ -197,13 +197,14 @@ public class CostDiscard extends CostPartWithList { @Override protected Card doPayment(Player payer, SpellAbility ability, Card targetCard, final boolean effect) { final Map runParams = AbilityKey.newMap(); + runParams.put(AbilityKey.InternalTriggerTable, table); if (ability.isCycling() && targetCard.equals(ability.getHostCard())) { // discard itself for cycling cost runParams.put(AbilityKey.Cycling, true); } // if this is caused by 118.12 it's also an effect SpellAbility cause = targetCard.getGame().getStack().isResolving(ability.getHostCard()) ? ability : null; - return payer.discard(targetCard, cause, effect, null, runParams); + return payer.discard(targetCard, cause, effect, runParams); } /* (non-Javadoc) diff --git a/forge-game/src/main/java/forge/game/cost/CostExile.java b/forge-game/src/main/java/forge/game/cost/CostExile.java index 80130f12d98..07dd6a397f0 100644 --- a/forge-game/src/main/java/forge/game/cost/CostExile.java +++ b/forge-game/src/main/java/forge/game/cost/CostExile.java @@ -20,6 +20,7 @@ package forge.game.cost; import com.google.common.collect.Lists; import forge.card.CardType; import forge.game.Game; +import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; import forge.game.card.*; @@ -32,6 +33,7 @@ import forge.util.collect.FCollectionView; import org.apache.commons.lang3.StringUtils; import java.util.List; +import java.util.Map; /** * The Class CostExile. @@ -256,7 +258,11 @@ public class CostExile extends CostPartWithList { @Override protected Card doPayment(Player payer, SpellAbility ability, Card targetCard, final boolean effect) { final Game game = targetCard.getGame(); - Card newCard = game.getAction().exile(targetCard, null, null); + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.LastStateBattlefield, game.getLastStateBattlefield()); + moveParams.put(AbilityKey.LastStateGraveyard, game.getLastStateGraveyard()); + moveParams.put(AbilityKey.InternalTriggerTable, table); + Card newCard = game.getAction().exile(targetCard, null, moveParams); SpellAbilityEffect.handleExiledWith(newCard, ability); return newCard; } diff --git a/forge-game/src/main/java/forge/game/cost/CostExiledMoveToGrave.java b/forge-game/src/main/java/forge/game/cost/CostExiledMoveToGrave.java index 9db427a9f5c..18665cc07a8 100644 --- a/forge-game/src/main/java/forge/game/cost/CostExiledMoveToGrave.java +++ b/forge-game/src/main/java/forge/game/cost/CostExiledMoveToGrave.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.card.CardCollectionView; import forge.game.card.CardLists; @@ -76,7 +79,6 @@ public class CostExiledMoveToGrave extends CostPartWithList { @Override public final boolean canPay(final SpellAbility ability, final Player payer, final boolean effect) { - int i = getAbilityAmount(ability); return getMaxAmountX(ability, payer, effect) >= i; @@ -84,7 +86,9 @@ public class CostExiledMoveToGrave extends CostPartWithList { @Override protected Card doPayment(Player payer, SpellAbility ability, Card targetCard, final boolean effect) { - return targetCard.getGame().getAction().moveToGraveyard(targetCard, null); + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.InternalTriggerTable, table); + return targetCard.getGame().getAction().moveToGraveyard(targetCard, null, moveParams); } public T accept(ICostVisitor visitor) { 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 37bd5073cd7..07f22b4785d 100644 --- a/forge-game/src/main/java/forge/game/cost/CostMill.java +++ b/forge-game/src/main/java/forge/game/cost/CostMill.java @@ -94,7 +94,8 @@ public class CostMill extends CostPart { Map moveParams = AbilityKey.newMap(); moveParams.put(AbilityKey.LastStateBattlefield, ai.getGame().getLastStateBattlefield()); moveParams.put(AbilityKey.LastStateGraveyard, ai.getGame().getLastStateGraveyard()); - ability.getPaidHash().put("Milled", true, (CardCollection) ai.mill(decision.c, ZoneType.Graveyard, ability, table, moveParams)); + moveParams.put(AbilityKey.InternalTriggerTable, table); + ability.getPaidHash().put("Milled", true, (CardCollection) ai.mill(decision.c, ZoneType.Graveyard, ability, moveParams)); table.triggerChangesZoneAll(ai.getGame(), ability); return true; } diff --git a/forge-game/src/main/java/forge/game/cost/CostPartWithList.java b/forge-game/src/main/java/forge/game/cost/CostPartWithList.java index f74505e6eeb..07745b56478 100644 --- a/forge-game/src/main/java/forge/game/cost/CostPartWithList.java +++ b/forge-game/src/main/java/forge/game/cost/CostPartWithList.java @@ -20,7 +20,6 @@ package forge.game.cost; import forge.game.card.*; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -import forge.game.zone.Zone; /** * The Class CostPartWithList. @@ -105,7 +104,6 @@ public abstract class CostPartWithList extends CostPart { public final boolean executePayment(Player payer, SpellAbility ability, Card targetCard, final boolean effect) { lkiList.add(CardUtil.getLKICopy(targetCard)); - final Zone origin = targetCard.getZone(); final Card newCard = doPayment(payer, ability, targetCard, effect); // need to update the LKI info to ensure correct interaction with cards which may trigger on this @@ -113,12 +111,7 @@ public abstract class CostPartWithList extends CostPart { targetCard.getGame().updateLastStateForCard(targetCard); if (newCard != null) { - final Zone newZone = newCard.getZone(); cardList.add(newCard); - - if (!origin.equals(newZone)) { - table.put(origin.getZoneType(), newZone.getZoneType(), newCard); - } } return true; } diff --git a/forge-game/src/main/java/forge/game/cost/CostPayment.java b/forge-game/src/main/java/forge/game/cost/CostPayment.java index e04b0e7870a..868b1c8e5f4 100644 --- a/forge-game/src/main/java/forge/game/cost/CostPayment.java +++ b/forge-game/src/main/java/forge/game/cost/CostPayment.java @@ -29,6 +29,7 @@ import com.google.common.collect.Maps; import forge.card.MagicColor; import forge.card.mana.ManaCostShard; import forge.game.Game; +import forge.game.ability.AbilityKey; import forge.game.card.Card; import forge.game.card.CardZoneTable; import forge.game.mana.Mana; @@ -332,6 +333,9 @@ public class CostPayment extends ManaConversionMatrix { public static boolean handleOfferings(final SpellAbility sa, boolean test, boolean costIsPaid) { final Game game = sa.getHostCard().getGame(); final CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard()); + Map params = AbilityKey.newMap(); + params.put(AbilityKey.InternalTriggerTable, table); + if (sa.isOffering()) { if (sa.getSacrificedAsOffering() == null) { return false; @@ -341,7 +345,7 @@ public class CostPayment extends ManaConversionMatrix { if (test) { sa.resetSacrificedAsOffering(); } else if (costIsPaid) { - game.getAction().sacrifice(offering, sa, false, table, null); + game.getAction().sacrifice(offering, sa, false, params); } } if (sa.isEmerge()) { @@ -353,7 +357,7 @@ public class CostPayment extends ManaConversionMatrix { if (test) { sa.resetSacrificedAsEmerge(); } else if (costIsPaid) { - game.getAction().sacrifice(emerge, sa, false, table, null); + game.getAction().sacrifice(emerge, sa, false, params); sa.setSacrificedAsEmerge(game.getChangeZoneLKIInfo(emerge)); } } diff --git a/forge-game/src/main/java/forge/game/cost/CostPutCardToLib.java b/forge-game/src/main/java/forge/game/cost/CostPutCardToLib.java index b9cb0202d5a..23cd369ce88 100644 --- a/forge-game/src/main/java/forge/game/cost/CostPutCardToLib.java +++ b/forge-game/src/main/java/forge/game/cost/CostPutCardToLib.java @@ -17,7 +17,10 @@ */ package forge.game.cost; +import java.util.Map; + import forge.game.Game; +import forge.game.ability.AbilityKey; import forge.game.card.Card; import forge.game.card.CardCollectionView; import forge.game.card.CardLists; @@ -157,7 +160,9 @@ public class CostPutCardToLib extends CostPartWithList { @Override protected Card doPayment(Player payer, SpellAbility ability, Card targetCard, final boolean effect) { - return targetCard.getGame().getAction().moveToLibrary(targetCard, Integer.parseInt(getLibPos()), null); + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.InternalTriggerTable, table); + return targetCard.getGame().getAction().moveToLibrary(targetCard, Integer.parseInt(getLibPos()), null, moveParams); } public T accept(ICostVisitor visitor) { diff --git a/forge-game/src/main/java/forge/game/cost/CostReturn.java b/forge-game/src/main/java/forge/game/cost/CostReturn.java index 50135c71bcc..1d52e89103a 100644 --- a/forge-game/src/main/java/forge/game/cost/CostReturn.java +++ b/forge-game/src/main/java/forge/game/cost/CostReturn.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.card.CardCollectionView; import forge.game.card.CardLists; @@ -116,7 +119,9 @@ public class CostReturn extends CostPartWithList { */ @Override protected Card doPayment(Player payer, SpellAbility ability, Card targetCard, final boolean effect) { - return targetCard.getGame().getAction().moveToHand(targetCard, null); + Map moveParams = AbilityKey.newMap(); + moveParams.put(AbilityKey.InternalTriggerTable, table); + return targetCard.getGame().getAction().moveToHand(targetCard, null, moveParams); } /* (non-Javadoc) 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 9248e74bd4a..eecea3a1fc6 100644 --- a/forge-game/src/main/java/forge/game/cost/CostSacrifice.java +++ b/forge-game/src/main/java/forge/game/cost/CostSacrifice.java @@ -148,7 +148,8 @@ public class CostSacrifice extends CostPartWithList { Map moveParams = AbilityKey.newMap(); moveParams.put(AbilityKey.LastStateBattlefield, game.getLastStateBattlefield()); moveParams.put(AbilityKey.LastStateGraveyard, game.getLastStateGraveyard()); - return game.getAction().sacrifice(targetCard, ability, effect, null, moveParams); + moveParams.put(AbilityKey.InternalTriggerTable, table); + return game.getAction().sacrifice(targetCard, ability, effect, 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 fda8e9771b1..a28cc111408 100644 --- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java +++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java @@ -374,15 +374,16 @@ public class PhaseHandler implements java.io.Serializable { int numDiscard = playerTurn.isUnlimitedHandSize() || handSize <= max || handSize == 0 ? 0 : handSize - max; if (numDiscard > 0) { + final CardZoneTable table = new CardZoneTable(); Map moveParams = AbilityKey.newMap(); moveParams.put(AbilityKey.LastStateBattlefield, game.getLastStateBattlefield()); moveParams.put(AbilityKey.LastStateGraveyard, game.getLastStateGraveyard()); + moveParams.put(AbilityKey.InternalTriggerTable, table); - 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, moveParams) != null) { + if (playerTurn.discard(c, null, false, moveParams) != null) { discarded.add(c); } } @@ -536,9 +537,9 @@ public class PhaseHandler implements java.io.Serializable { Map moveParams = AbilityKey.newMap(); moveParams.put(AbilityKey.LastStateBattlefield, game.getLastStateBattlefield()); moveParams.put(AbilityKey.LastStateGraveyard, game.getLastStateGraveyard()); + moveParams.put(AbilityKey.InternalTriggerTable, table); final SpellAbility sa = new SpellAbility.EmptySa(playerTurn.getRadiationEffect(), playerTurn); - final CardCollectionView milled = playerTurn.mill(numRad, ZoneType.Graveyard, sa, - table, moveParams); + final CardCollectionView milled = playerTurn.mill(numRad, ZoneType.Graveyard, sa, moveParams); game.getAction().reveal(milled, playerTurn, false, Localizer.getInstance().getMessage("lblMilledCards", playerTurn), false); game.getGameLog().add(GameLogEntryType.ZONE_CHANGE, playerTurn + " milled " + @@ -810,6 +811,7 @@ public class PhaseHandler implements java.io.Serializable { } List blocked = Lists.newArrayList(); + Map lkiCache = Maps.newHashMap(); for (final Card a : combat.getAttackers()) { if (combat.isBlocked(a)) { @@ -835,8 +837,8 @@ public class PhaseHandler implements java.io.Serializable { // Run this trigger once for each blocker for (final Card b : blockers) { - b.addBlockedThisTurn(CardUtil.getLKICopy(a)); - a.addBlockedByThisTurn(CardUtil.getLKICopy(b)); + b.addBlockedThisTurn(CardUtil.getLKICopy(a, lkiCache)); + a.addBlockedByThisTurn(CardUtil.getLKICopy(b, lkiCache)); final Map runParams = AbilityKey.newMap(); runParams.put(AbilityKey.Attacker, a); 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 230ae9e9e71..fcb9f9815a1 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -1430,15 +1430,11 @@ public class Player extends GameEntity implements Comparable { numRollsThisTurn++; } - public final Card discard(final Card c, final SpellAbility sa, final boolean effect, CardZoneTable table, Map params) { + public final Card discard(final Card c, final SpellAbility sa, final boolean effect, Map params) { if (!c.canBeDiscardedBy(sa, effect)) { return null; } - // TODO: This line should be moved inside CostPayment somehow - /*if (sa != null) { - sa.addCostToHashList(c, "Discarded"); - }*/ final Card source = sa != null ? sa.getHostCard() : null; final ZoneType origin = c.getZone().getZoneType(); @@ -1480,9 +1476,6 @@ public class Player extends GameEntity implements Comparable { newCard.setDiscarded(true); - if (table != null) { - table.put(origin, newCard.getZone().getZoneType(), newCard); - } sb.append("."); numDiscardedThisTurn++; // Run triggers @@ -1600,7 +1593,7 @@ public class Player extends GameEntity implements Comparable { return notedNum.get(notedFor); } - public final CardCollectionView mill(int n, final ZoneType destination, SpellAbility sa, CardZoneTable table, Map params) { + public final CardCollectionView mill(int n, final ZoneType destination, SpellAbility sa, Map params) { // Replacement effects final Map repRunParams = AbilityKey.mapFromAffected(this); repRunParams.put(AbilityKey.Number, n); @@ -1637,11 +1630,7 @@ public class Player extends GameEntity implements Comparable { } for (Card m : milled) { - final ZoneType origin = m.getZone().getZoneType(); - final Card d = game.getAction().moveTo(destination, m, sa, params); - if (d.getZone().is(destination)) { - table.put(origin, d.getZone().getZoneType(), d); - } + game.getAction().moveTo(destination, m, sa, params); } // MilledAll trigger @@ -3784,7 +3773,7 @@ public class Player extends GameEntity implements Comparable { } } - public void learnLesson(SpellAbility sa, CardZoneTable table, Map params) { + public void learnLesson(SpellAbility sa, Map params) { if (hasLost()) { return; } @@ -3815,10 +3804,9 @@ 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, 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, params) != null) { + if (discard(c, sa, true, params) != null) { // Change this if something would make multiple player learn at the same time // Discard Trigger outside Effect @@ -3831,9 +3819,7 @@ public class Player extends GameEntity implements Comparable { } getGame().getTriggerHandler().runTrigger(TriggerType.DiscardedAll, runParams, false); - for (Card d : drawCards(1, sa, params)) { - table.put(ZoneType.Library, ZoneType.Hand, d); // does a ChangesZoneAll care about moving from Library to Hand - } + drawCards(1, sa, params); } } } diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java index 3230fd328b4..82541061534 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java @@ -312,7 +312,7 @@ public class ReplacementHandler { replacementEffect.setReplacingObjects(runParams, tailend); //set original Params to update them later tailend.setReplacingObject(AbilityKey.OriginalParams, runParams); - tailend.setReplacingObjectsFrom(runParams, AbilityKey.SimultaneousETB); + tailend.setReplacingObjectsFrom(runParams, AbilityKey.InternalTriggerTable, AbilityKey.SimultaneousETB); tailend = tailend.getSubAbility(); } while(tailend != null); diff --git a/forge-gui/src/main/java/forge/player/HumanPlay.java b/forge-gui/src/main/java/forge/player/HumanPlay.java index e496d2041b3..f85982455d1 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlay.java +++ b/forge-gui/src/main/java/forge/player/HumanPlay.java @@ -510,26 +510,26 @@ public class HumanPlay { } private static boolean handleOfferingConvokeAndDelve(final SpellAbility ability, CardCollection cardsToDelve, boolean manaInputCancelled) { - Card hostCard = ability.getHostCard(); + final Card hostCard = ability.getHostCard(); final Game game = hostCard.getGame(); - final CardZoneTable table = new CardZoneTable(); + Map params = AbilityKey.newMap(); + params.put(AbilityKey.InternalTriggerTable, table); + if (!manaInputCancelled && !cardsToDelve.isEmpty()) { for (final Card c : cardsToDelve) { hostCard.addDelved(c); - final ZoneType o = c.getZone().getZoneType(); - final Card d = game.getAction().exile(c, null, null); + final Card d = game.getAction().exile(c, null, params); hostCard.addExiledCard(d); d.setExiledWith(hostCard); d.setExiledBy(hostCard.getController()); - table.put(o, d.getZone().getZoneType(), d); } } if (ability.isOffering() && ability.getSacrificedAsOffering() != null) { final Card offering = ability.getSacrificedAsOffering(); offering.setUsedToPay(false); if (!manaInputCancelled) { - game.getAction().sacrifice(offering, ability, false, table, null); + game.getAction().sacrifice(offering, ability, false, params); } ability.resetSacrificedAsOffering(); } @@ -537,7 +537,7 @@ public class HumanPlay { final Card emerge = ability.getSacrificedAsEmerge(); emerge.setUsedToPay(false); if (!manaInputCancelled) { - game.getAction().sacrifice(emerge, ability, false, table, null); + game.getAction().sacrifice(emerge, ability, false, params); ability.setSacrificedAsEmerge(game.getChangeZoneLKIInfo(emerge)); } else { ability.resetSacrificedAsEmerge();