mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 20:28:00 +00:00
Skip ChangesZoneAll inside Moved replacement (#4493)
* Support passing CardZoneTable
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user