Skip ChangesZoneAll inside Moved replacement (#4493)

* Support passing CardZoneTable
This commit is contained in:
tool4ever
2024-01-10 10:41:28 +01:00
committed by GitHub
parent 1c518aec1a
commit 144ceee32e
32 changed files with 166 additions and 186 deletions

View File

@@ -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<AbilityKey, Object> 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<AbilityKey, Object> params) {
public final Card sacrifice(final Card c, final SpellAbility source, final boolean effect, Map<AbilityKey, Object> 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<AbilityKey, Object> params) {
public final boolean destroy(final Card c, final SpellAbility sa, final boolean regenerate, Map<AbilityKey, Object> 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<AbilityKey, Object> params) {
protected final Card sacrificeDestroy(final Card c, SpellAbility cause, Map<AbilityKey, Object> 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;
}

View File

@@ -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;

View File

@@ -761,13 +761,13 @@ public abstract class SpellAbilityEffect {
};
}
protected static void discard(SpellAbility sa, CardZoneTable table, final boolean effect, Map<Player, CardCollectionView> discardedMap, Map<AbilityKey, Object> params) {
protected static void discard(SpellAbility sa, final boolean effect, Map<Player, CardCollectionView> discardedMap, Map<AbilityKey, Object> params) {
Set<Player> 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);
}
}

View File

@@ -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<AbilityKey, Object> 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);

View File

@@ -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);
}
}
}
}

View File

@@ -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<AbilityKey, Object> 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<AbilityKey, Object> 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<AbilityKey, Object> 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<AbilityKey,Object> 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);
}
}
}

View File

@@ -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<AbilityKey, Object> 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);
}

View File

@@ -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<AbilityKey, Object> 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<AbilityKey, Object> 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<AbilityKey, Object> 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<AbilityKey, Object> 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<AbilityKey, Object> 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;
}

View File

@@ -91,12 +91,12 @@ public class DestroyAllEffect extends SpellAbilityEffect {
Map<AbilityKey, Object> params = AbilityKey.newMap();
params.put(AbilityKey.LastStateBattlefield, game.copyLastStateBattlefield());
CardZoneTable table = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard());
params.put(AbilityKey.InternalTriggerTable, table);
Map<Integer, Card> 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));
}
}

View File

@@ -61,8 +61,9 @@ public class DestroyEffect extends SpellAbilityEffect {
Map<AbilityKey, Object> 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<Integer, Card> 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<Integer, Card> cachedMap, Map<AbilityKey, Object> params) {
protected void internalDestroy(Card gameCard, SpellAbility sa, Map<Integer, Card> cachedMap, Map<AbilityKey, Object> 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);

View File

@@ -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<AbilityKey, Object> 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<AbilityKey, Object> 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);

View File

@@ -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<AbilityKey, Object> 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<AbilityKey, Object> moveParams = AbilityKey.newMap();
moveParams.put(AbilityKey.InternalTriggerTable, table);
final Iterator<Card> 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);
}
}

View File

@@ -279,10 +279,10 @@ public class DiscardEffect extends SpellAbilityEffect {
Map<AbilityKey, Object> 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);
}
}

View File

@@ -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<String, Object> 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) {

View File

@@ -24,8 +24,9 @@ public class LearnEffect extends SpellAbilityEffect {
Map<AbilityKey, Object> 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);
}

View File

@@ -41,6 +41,7 @@ public class MillEffect extends SpellAbilityEffect {
Map<AbilityKey, Object> 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

View File

@@ -86,10 +86,11 @@ public class SacrificeAllEffect extends SpellAbilityEffect {
Map<AbilityKey, Object> 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);
}

View File

@@ -103,12 +103,13 @@ public class SacrificeEffect extends SpellAbilityEffect {
Map<AbilityKey, Object> 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);

View File

@@ -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<ZoneType, ZoneType, CardColle
}
public CardZoneTable() {
this(CardCollection.EMPTY, CardCollection.EMPTY);
this(null, null);
}
public CardZoneTable(CardCollectionView lastStateBattlefield, CardCollectionView lastStateGraveyard) {
this.lastStateBattlefield = lastStateBattlefield;
this.lastStateGraveyard = lastStateGraveyard;
this.lastStateBattlefield = ObjectUtils.firstNonNull(lastStateBattlefield, CardCollection.EMPTY);
this.lastStateGraveyard = ObjectUtils.firstNonNull(lastStateGraveyard, CardCollection.EMPTY);
}
public CardCollectionView getLastStateBattlefield() {
@@ -81,6 +84,10 @@ public class CardZoneTable extends ForwardingTable<ZoneType, ZoneType, CardColle
public void triggerChangesZoneAll(final Game game, final SpellAbility cause) {
triggerTokenCreatedOnce(game);
if (cause != null && cause.isReplacementAbility() && cause.getReplacementEffect().getMode() == ReplacementType.Moved) {
// will be handled by original "cause" instead
return;
}
if (!isEmpty()) {
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Cards, new CardZoneTable(this));

View File

@@ -197,13 +197,14 @@ public class CostDiscard extends CostPartWithList {
@Override
protected Card doPayment(Player payer, SpellAbility ability, Card targetCard, final boolean effect) {
final Map<AbilityKey, Object> 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)

View File

@@ -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<AbilityKey, Object> 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;
}

View File

@@ -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<AbilityKey, Object> moveParams = AbilityKey.newMap();
moveParams.put(AbilityKey.InternalTriggerTable, table);
return targetCard.getGame().getAction().moveToGraveyard(targetCard, null, moveParams);
}
public <T> T accept(ICostVisitor<T> visitor) {

View File

@@ -94,7 +94,8 @@ public class CostMill extends CostPart {
Map<AbilityKey, Object> 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;
}

View File

@@ -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;
}

View File

@@ -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<AbilityKey, Object> 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));
}
}

View File

@@ -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<AbilityKey, Object> moveParams = AbilityKey.newMap();
moveParams.put(AbilityKey.InternalTriggerTable, table);
return targetCard.getGame().getAction().moveToLibrary(targetCard, Integer.parseInt(getLibPos()), null, moveParams);
}
public <T> T accept(ICostVisitor<T> visitor) {

View File

@@ -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<AbilityKey, Object> moveParams = AbilityKey.newMap();
moveParams.put(AbilityKey.InternalTriggerTable, table);
return targetCard.getGame().getAction().moveToHand(targetCard, null, moveParams);
}
/* (non-Javadoc)

View File

@@ -148,7 +148,8 @@ public class CostSacrifice extends CostPartWithList {
Map<AbilityKey, Object> 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)

View File

@@ -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<AbilityKey, Object> 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<AbilityKey, Object> 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<Card> blocked = Lists.newArrayList();
Map<Integer, Card> 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<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Attacker, a);

View File

@@ -1430,15 +1430,11 @@ public class Player extends GameEntity implements Comparable<Player> {
numRollsThisTurn++;
}
public final Card discard(final Card c, final SpellAbility sa, final boolean effect, CardZoneTable table, Map<AbilityKey, Object> params) {
public final Card discard(final Card c, final SpellAbility sa, final boolean effect, Map<AbilityKey, Object> 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<Player> {
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<Player> {
return notedNum.get(notedFor);
}
public final CardCollectionView mill(int n, final ZoneType destination, SpellAbility sa, CardZoneTable table, Map<AbilityKey, Object> params) {
public final CardCollectionView mill(int n, final ZoneType destination, SpellAbility sa, Map<AbilityKey, Object> params) {
// Replacement effects
final Map<AbilityKey, Object> repRunParams = AbilityKey.mapFromAffected(this);
repRunParams.put(AbilityKey.Number, n);
@@ -1637,11 +1630,7 @@ public class Player extends GameEntity implements Comparable<Player> {
}
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<Player> {
}
}
public void learnLesson(SpellAbility sa, CardZoneTable table, Map<AbilityKey, Object> params) {
public void learnLesson(SpellAbility sa, Map<AbilityKey, Object> params) {
if (hasLost()) {
return;
}
@@ -3815,10 +3804,9 @@ public class Player extends GameEntity implements Comparable<Player> {
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<Player> {
}
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);
}
}
}

View File

@@ -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);

View File

@@ -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<AbilityKey, Object> 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();