From 589cc37e114ff859bde2aaca4cc025307f01e75e Mon Sep 17 00:00:00 2001 From: Agetian Date: Sat, 10 Oct 2015 18:15:59 +0000 Subject: [PATCH] - Highly experimental: implement an in-between-zone-change LKI information saving mechanism that allows the game to store and retrieve information about the card prior to its latest zone change (currently this is preserved as long as stack is resolving and is cleared whenever the stack is empty after resolution). - Currently this is used by damage-dealing abilities (Damage*Effect). This fixes e.g. Nylea's Bow + sacrificing a creature equipped with Mortarpod to deal 1 damage to opponent's creature (the damage dealt has to be lethal (deathtouch) according to the LKI information about the sacrificed card). - This is a rather intrusive and experimental change that is likely to affect something in an unwanted way, please test and feel free to improve. --- forge-game/src/main/java/forge/game/Game.java | 21 +++++++++++++++++++ .../src/main/java/forge/game/GameAction.java | 2 ++ .../game/ability/effects/DamageAllEffect.java | 5 +++-- .../ability/effects/DamageDealEffect.java | 17 ++++++++------- .../ability/effects/DamageEachEffect.java | 14 +++++++++---- .../main/java/forge/game/card/CardUtil.java | 4 ++++ .../main/java/forge/game/zone/MagicStack.java | 5 +++++ 7 files changed, 54 insertions(+), 14 deletions(-) diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index 9900c757922..2cb5d4c9d66 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -42,6 +42,7 @@ import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; import forge.game.card.CardLists; import forge.game.card.CardPredicates; +import forge.game.card.CardUtil; import forge.game.card.CardView; import forge.game.combat.Combat; import forge.game.cost.Cost; @@ -71,6 +72,7 @@ import forge.util.Aggregates; import forge.util.collect.FCollection; import forge.util.collect.FCollectionView; import forge.util.Visitor; +import java.util.HashMap; /** * Represents the state of a single game, a new instance is created for each game. @@ -142,6 +144,24 @@ public class Game { return list; } + // methods that deal with saving, retrieving and clearing LKI information about cards on zone change + private final HashMap changeZoneLKIInfo = new HashMap<>(); + public final void setChangeZoneLKIInfo(Card c) { + if (c == null) { + return; + } + changeZoneLKIInfo.put(c.getId(), CardUtil.getLKICopy(c)); + } + public final Card getChangeZoneLKIInfo(Card c) { + if (c == null) { + return null; + } + return changeZoneLKIInfo.containsKey(c.getId()) ? changeZoneLKIInfo.get(c.getId()) : c; + } + public final void clearChangeZoneLKIInfo() { + changeZoneLKIInfo.clear(); + } + private final GameEntityCache spabCache = new GameEntityCache<>(); public SpellAbility getSpellAbility(final SpellAbilityView view) { return spabCache.get(view); @@ -729,4 +749,5 @@ public class Game { } return rarities; } + } diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 31da2cde691..5f2e5e40f44 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -105,6 +105,8 @@ public class GameAction { } public Card changeZone(final Zone zoneFrom, Zone zoneTo, final Card c, Integer position) { + game.setChangeZoneLKIInfo(c); + if (c.isCopiedSpell() || (c.isImmutable() && zoneTo.is(ZoneType.Exile))) { // Remove Effect from command immediately, this is essential when some replacement // effects happen during the resolving of a spellability ("the next time ..." effect) diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java index 61816894137..6bc41965a70 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageAllEffect.java @@ -45,6 +45,7 @@ public class DamageAllEffect extends SpellAbilityEffect { public void resolve(SpellAbility sa) { final List definedSources = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DamageSource"), sa); final Card card = definedSources.get(0); + final Card sourceLKI = card.getGame().getChangeZoneLKIInfo(card); final Card source = sa.getHostCard(); final Game game = sa.getActivatingPlayer().getGame(); @@ -74,7 +75,7 @@ public class DamageAllEffect extends SpellAbilityEffect { list = AbilityUtils.filterListByType(list, sa.getParam("ValidCards"), sa); for (final Card c : list) { - if (c.addDamage(dmg, card) && (sa.hasParam("RememberDamaged") || sa.hasParam("RememberDamagedCreature"))) { + if (c.addDamage(dmg, sourceLKI) && (sa.hasParam("RememberDamaged") || sa.hasParam("RememberDamagedCreature"))) { source.addRemembered(c); } } @@ -82,7 +83,7 @@ public class DamageAllEffect extends SpellAbilityEffect { if (!players.equals("")) { final List playerList = AbilityUtils.getDefinedPlayers(card, players, sa); for (final Player p : playerList) { - if (p.addDamage(dmg, card) && (sa.hasParam("RememberDamaged") || sa.hasParam("RememberDamagedPlayer"))) { + if (p.addDamage(dmg, sourceLKI) && (sa.hasParam("RememberDamaged") || sa.hasParam("RememberDamagedPlayer"))) { source.addRemembered(p); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java index 4f5928b1ab4..9fc2a87a63e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java @@ -117,6 +117,7 @@ public class DamageDealEffect extends SpellAbilityEffect { return; } final Card source = definedSources.get(0); + final Card sourceLKI = sa.getHostCard().getGame().getChangeZoneLKIInfo(definedSources.get(0)); if (divideOnResolution) { // Dividing Damage up to multiple targets using combat damage box @@ -135,9 +136,9 @@ public class DamageDealEffect extends SpellAbilityEffect { } Player assigningPlayer = players.get(0); - Map map = assigningPlayer.getController().assignCombatDamage(source, assigneeCards, dmg, null, true); + Map map = assigningPlayer.getController().assignCombatDamage(sourceLKI, assigneeCards, dmg, null, true); for (Entry dt : map.entrySet()) { - dt.getKey().addDamage(dt.getValue(), source); + dt.getKey().addDamage(dt.getValue(), sourceLKI); } return; @@ -154,18 +155,18 @@ public class DamageDealEffect extends SpellAbilityEffect { c.clearAssignedDamage(); } else if (noPrevention) { - if (c.addDamageWithoutPrevention(dmg, source) && remember) { + if (c.addDamageWithoutPrevention(dmg, sourceLKI) && remember) { source.addRemembered(c); } } else if (combatDmg) { HashMap combatmap = new HashMap(); - combatmap.put(source, dmg); + combatmap.put(sourceLKI, dmg); c.addCombatDamage(combatmap); if (remember) { source.addRemembered(c); } } else { - if (c.addDamage(dmg, source) && remember) { + if (c.addDamage(dmg, sourceLKI) && remember) { source.addRemembered(c); } } @@ -175,16 +176,16 @@ public class DamageDealEffect extends SpellAbilityEffect { final Player p = (Player) o; if (!targeted || p.canBeTargetedBy(sa)) { if (noPrevention) { - if (p.addDamageWithoutPrevention(dmg, source) && remember) { + if (p.addDamageWithoutPrevention(dmg, sourceLKI) && remember) { source.addRemembered(p); } } else if (combatDmg) { - p.addCombatDamage(dmg, source); + p.addCombatDamage(dmg, sourceLKI); if (remember) { source.addRemembered(p); } } else { - if (p.addDamage(dmg, source) && remember) { + if (p.addDamage(dmg, sourceLKI) && remember) { source.addRemembered(p); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java index 60680de5b06..7e4a1c16342 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageEachEffect.java @@ -72,18 +72,20 @@ public class DamageEachEffect extends SpellAbilityEffect { for (final Object o : tgts) { for (final Card source : sources) { + final Card sourceLKI = source.getGame().getChangeZoneLKIInfo(source); + final int dmg = CardFactoryUtil.xCount(source, sa.getSVar("X")); // System.out.println(source+" deals "+dmg+" damage to "+o.toString()); if (o instanceof Card) { final Card c = (Card) o; if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) { - c.addDamage(dmg, source); + c.addDamage(dmg, sourceLKI); } } else if (o instanceof Player) { final Player p = (Player) o; if (!targeted || p.canBeTargetedBy(sa)) { - p.addDamage(dmg, source); + p.addDamage(dmg, sourceLKI); } } } @@ -92,20 +94,24 @@ public class DamageEachEffect extends SpellAbilityEffect { if (sa.hasParam("DefinedCards")) { if (sa.getParam("DefinedCards").equals("Self")) { for (final Card source : sources) { + final Card sourceLKI = source.getGame().getChangeZoneLKIInfo(source); + final int dmg = CardFactoryUtil.xCount(source, card.getSVar("X")); // System.out.println(source+" deals "+dmg+" damage to "+source); - source.addDamage(dmg, source); + source.addDamage(dmg, sourceLKI); } } if (sa.getParam("DefinedCards").equals("Remembered")) { for (final Card source : sources) { final int dmg = CardFactoryUtil.xCount(source, card.getSVar("X")); + final Card sourceLKI = source.getGame().getChangeZoneLKIInfo(source); + Card rememberedcard; for (final Object o : sa.getHostCard().getRemembered()) { if (o instanceof Card) { rememberedcard = (Card) o; // System.out.println(source + " deals " + dmg + " damage to " + rememberedcard); - rememberedcard.addDamage(dmg, source); + rememberedcard.addDamage(dmg, sourceLKI); } } } diff --git a/forge-game/src/main/java/forge/game/card/CardUtil.java b/forge-game/src/main/java/forge/game/card/CardUtil.java index 5f47afd131d..77c490d1833 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -290,6 +290,10 @@ public final class CardUtil { newCopy.addImprintedCard(o); } + newCopy.setChangedCardColors(in.getChangedCardColors()); + newCopy.setChangedCardKeywords(in.getChangedCardKeywords()); + newCopy.setChangedCardTypes(in.getChangedCardTypes()); + return newCopy; } diff --git a/forge-game/src/main/java/forge/game/zone/MagicStack.java b/forge-game/src/main/java/forge/game/zone/MagicStack.java index 5cf0db869c3..9e67fd19737 100644 --- a/forge-game/src/main/java/forge/game/zone/MagicStack.java +++ b/forge-game/src/main/java/forge/game/zone/MagicStack.java @@ -512,6 +512,11 @@ public class MagicStack /* extends MyObservable */ implements Iterable