From 419a513d8f9f59df577e5eaa1d8e141efa83b215 Mon Sep 17 00:00:00 2001 From: Alumi Date: Sun, 16 May 2021 06:51:38 +0000 Subject: [PATCH] Deal damage use damage map --- .../src/main/java/forge/game/GameAction.java | 49 ++++++++++ .../src/main/java/forge/game/GameEntity.java | 58 ++---------- .../forge/game/GameEntityCounterTable.java | 7 ++ .../game/ability/effects/DamageAllEffect.java | 21 ++--- .../ability/effects/DamageDealEffect.java | 39 +++----- .../ability/effects/DamageEachEffect.java | 21 ++--- .../ability/effects/DamageResolveEffect.java | 14 +-- .../game/ability/effects/FightEffect.java | 16 ++-- .../ability/effects/RepeatEachEffect.java | 8 +- .../ability/effects/ReplaceDamageEffect.java | 9 +- .../effects/ReplaceSplitDamageEffect.java | 12 +-- .../src/main/java/forge/game/card/Card.java | 29 +----- .../java/forge/game/card/CardDamageMap.java | 8 +- .../main/java/forge/game/combat/Combat.java | 93 +++---------------- .../main/java/forge/game/cost/CostDamage.java | 10 +- .../main/java/forge/game/player/Player.java | 6 +- .../game/replacement/ReplacementHandler.java | 11 ++- .../forge/game/spellability/SpellAbility.java | 17 ++++ .../res/cardsfolder/g/goblin_psychopath.txt | 2 +- .../res/cardsfolder/h/harsh_judgment.txt | 2 +- forge-gui/res/cardsfolder/s/shield_dancer.txt | 2 +- 21 files changed, 161 insertions(+), 273 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 1173045d018..a8cd31f13da 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -47,6 +47,7 @@ import forge.game.ability.effects.AttachEffect; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; +import forge.game.card.CardDamageMap; import forge.game.card.CardFactory; import forge.game.card.CardLists; import forge.game.card.CardPredicates; @@ -61,6 +62,7 @@ import forge.game.event.GameEventCardTapped; import forge.game.event.GameEventFlipCoin; import forge.game.event.GameEventGameStarted; import forge.game.event.GameEventScry; +import forge.game.keyword.Keyword; import forge.game.keyword.KeywordInterface; import forge.game.mulligan.MulliganService; import forge.game.player.GameLossReason; @@ -2125,4 +2127,51 @@ public class GameAction { } } } + + public void dealDamage(final boolean isCombat, final CardDamageMap damageMap, final CardDamageMap preventMap, + final GameEntityCounterTable counterTable, final SpellAbility cause) { + CardDamageMap replaceDamageMap = new CardDamageMap(damageMap); + // Run replacement effect for each entity dealt damage + // TODO: List all possible replacement effects and run them in APNAP order. + // TODO: To handle "Prevented this way" and abilities like "Phantom Nomad", should buffer the replaced SA + // and only run them after all prevention and redirection effects are processed. + for (Map.Entry> et : damageMap.columnMap().entrySet()) { + final GameEntity ge = et.getKey(); + if (isCombat && ge instanceof Card) { + ((Card) ge).clearAssignedDamage(); + } + for (Map.Entry e : et.getValue().entrySet()) { + if (e.getValue() > 0) { + ge.replaceDamage(e.getValue(), e.getKey(), isCombat, replaceDamageMap, preventMap, counterTable, cause); + } + } + } + + // Actually deal damage according to replaced damage map + for (Map.Entry> et : replaceDamageMap.rowMap().entrySet()) { + final Card sourceLKI = et.getKey(); + int sum = 0; + for (Map.Entry e : et.getValue().entrySet()) { + if (e.getValue() <= 0) { + continue; + } + sum += e.getValue(); + e.getKey().addDamageAfterPrevention(e.getValue(), sourceLKI, isCombat, counterTable); + } + + if (sourceLKI.hasKeyword(Keyword.LIFELINK)) { + sourceLKI.getController().gainLife(sum, sourceLKI, cause); + } + } + + preventMap.triggerPreventDamage(isCombat); + preventMap.clear(); + + replaceDamageMap.triggerDamageDoneOnce(isCombat, game); + damageMap.clear(); + replaceDamageMap.clear(); + + counterTable.triggerCountersPutAll(game); + counterTable.clear(); + } } diff --git a/forge-game/src/main/java/forge/game/GameEntity.java b/forge-game/src/main/java/forge/game/GameEntity.java index a8d00b7c52b..19e88d6d1f9 100644 --- a/forge-game/src/main/java/forge/game/GameEntity.java +++ b/forge-game/src/main/java/forge/game/GameEntity.java @@ -67,49 +67,10 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { getView().updateName(this); } - public final int addDamage(final int damage, final Card source, boolean isCombat, boolean noPrevention, + // final Iterable source + public void replaceDamage(final int damage, final Card source, final boolean isCombat, final CardDamageMap damageMap, final CardDamageMap preventMap, GameEntityCounterTable counterTable, final SpellAbility cause) { - int damageToDo = replaceDamage(damage, source, isCombat, !noPrevention, damageMap, preventMap, counterTable, cause); - if (damageToDo <= 0) { - return 0; - } - if (isCombat) { - source.getDamageHistory().registerCombatDamage(this); - return addCombatDamageBase(damageToDo, source, damageMap, counterTable); - } else { - return addDamageAfterPrevention(damageToDo, source, false, damageMap, counterTable); - } - } - - public int addDamage(final int damage, final Card source, final CardDamageMap damageMap, - final CardDamageMap preventMap, GameEntityCounterTable counterTable, final SpellAbility cause) { - int damageToDo = replaceDamage(damage, source, false, true, damageMap, preventMap, counterTable, cause); - - return addDamageAfterPrevention(damageToDo, source, false, damageMap, counterTable); - } - - public final int addCombatDamage(final int damage, final Card source, final CardDamageMap damageMap, - final CardDamageMap preventMap, GameEntityCounterTable counterTable) { - int damageToDo = replaceDamage(damage, source, true, true, damageMap, preventMap, counterTable, null); - - if (damageToDo > 0) { - source.getDamageHistory().registerCombatDamage(this); - } - // damage prevention is already checked - return addCombatDamageBase(damageToDo, source, damageMap, counterTable); - } - - protected int addCombatDamageBase(final int damage, final Card source, CardDamageMap damageMap, GameEntityCounterTable counterTable) { - return addDamageAfterPrevention(damage, source, true, damageMap, counterTable); - } - - public int replaceDamage(final int damage, final Card source, final boolean isCombat, boolean prevention, - final CardDamageMap damageMap, final CardDamageMap preventMap, GameEntityCounterTable counterTable, final SpellAbility cause) { - if (!source.canDamagePrevented(isCombat)) { - prevention = false; - } - - int restDamage = damage; + boolean prevention = source.canDamagePrevented(isCombat) && (cause == null || !cause.hasParam("NoPrevention")); // Replacement effects final Map repParams = AbilityKey.mapFromAffected(this); @@ -132,22 +93,19 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { GameEntity newTarget = (GameEntity) repParams.get(AbilityKey.Affected); // check if this is still the affected card or player if (this.equals(newTarget)) { - restDamage = newDamage; + damageMap.put(source, this, newDamage - damage); } else { - newDamage = newTarget.replaceDamage(newDamage, source, isCombat, prevention, damageMap, preventMap, counterTable, cause); - newTarget.addDamageAfterPrevention(newDamage, source, isCombat, damageMap, counterTable); - restDamage = 0; + damageMap.remove(source, this); + damageMap.put(source, newTarget, newDamage); } break; default: - restDamage = 0; + damageMap.remove(source, this); } - - return restDamage; } // This function handles damage after replacement and prevention effects are applied - public abstract int addDamageAfterPrevention(final int damage, final Card source, final boolean isCombat, CardDamageMap damageMap, GameEntityCounterTable counterTable); + public abstract int addDamageAfterPrevention(final int damage, final Card source, final boolean isCombat, GameEntityCounterTable counterTable); // This should be also usable by the AI to forecast an effect (so it must // not change the game state) diff --git a/forge-game/src/main/java/forge/game/GameEntityCounterTable.java b/forge-game/src/main/java/forge/game/GameEntityCounterTable.java index 2b881bd9437..222e1a16a58 100644 --- a/forge-game/src/main/java/forge/game/GameEntityCounterTable.java +++ b/forge-game/src/main/java/forge/game/GameEntityCounterTable.java @@ -20,6 +20,13 @@ public class GameEntityCounterTable extends ForwardingTable, Ga private Table, GameEntity, Map> dataMap = HashBasedTable.create(); + public GameEntityCounterTable() { + } + + public GameEntityCounterTable(Table, GameEntity, Map> counterTable) { + putAll(counterTable); + } + /* * (non-Javadoc) * @see com.google.common.collect.ForwardingTable#delegate() 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 feab590d460..6174de9e2db 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 @@ -89,26 +89,31 @@ public class DamageAllEffect extends DamageBaseEffect { boolean usedDamageMap = true; CardDamageMap damageMap = sa.getDamageMap(); CardDamageMap preventMap = sa.getPreventMap(); - GameEntityCounterTable counterTable = new GameEntityCounterTable(); + GameEntityCounterTable counterTable = sa.getCounterTable(); if (damageMap == null) { // make a new damage map damageMap = new CardDamageMap(); preventMap = new CardDamageMap(); + counterTable = new GameEntityCounterTable(); usedDamageMap = false; } for (final Card c : list) { - c.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa); + damageMap.put(sourceLKI, c, dmg); } if (!players.equals("")) { final List playerList = AbilityUtils.getDefinedPlayers(card, players, sa); for (final Player p : playerList) { - p.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa); + damageMap.put(sourceLKI, p, dmg); } } + if (!usedDamageMap) { + game.getAction().dealDamage(false, damageMap, preventMap, counterTable, sa); + } + // do Remember there if (rememberCard || rememberPlayer) { for (GameEntity e : damageMap.row(sourceLKI).keySet()) { @@ -120,16 +125,6 @@ public class DamageAllEffect extends DamageBaseEffect { } } - if (!usedDamageMap) { - preventMap.triggerPreventDamage(false); - damageMap.triggerDamageDoneOnce(false, game, sa); - - preventMap.clear(); - damageMap.clear(); - } - - counterTable.triggerCountersPutAll(game); - replaceDying(sa); } } 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 e7b832009ff..07909cc8bb5 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 @@ -145,7 +145,6 @@ public class DamageDealEffect extends DamageBaseEffect { final String damage = sa.getParam("NumDmg"); int dmg = AbilityUtils.calculateAmount(hostCard, damage, sa); - final boolean noPrevention = sa.hasParam("NoPrevention"); final boolean removeDamage = sa.hasParam("Remove"); final boolean divideOnResolution = sa.hasParam("DividerOnResolution"); @@ -172,17 +171,19 @@ public class DamageDealEffect extends DamageBaseEffect { boolean usedDamageMap = true; CardDamageMap damageMap = sa.getDamageMap(); CardDamageMap preventMap = sa.getPreventMap(); - GameEntityCounterTable counterTable = new GameEntityCounterTable(); + GameEntityCounterTable counterTable = sa.getCounterTable(); if (damageMap == null) { // make a new damage map damageMap = new CardDamageMap(); preventMap = new CardDamageMap(); + counterTable = new GameEntityCounterTable(); usedDamageMap = false; } if (sa.hasParam("DamageMap")) { sa.setDamageMap(damageMap); sa.setPreventMap(preventMap); + sa.setCounterTable(counterTable); usedDamageMap = true; } @@ -208,19 +209,12 @@ public class DamageDealEffect extends DamageBaseEffect { Player assigningPlayer = players.get(0); Map map = assigningPlayer.getController().assignCombatDamage(sourceLKI, assigneeCards, dmg, null, true); for (Entry dt : map.entrySet()) { - dt.getKey().addDamage(dt.getValue(), sourceLKI, damageMap, preventMap, counterTable, sa); + damageMap.put(sourceLKI, dt.getKey(), dt.getValue()); } if (!usedDamageMap) { - preventMap.triggerPreventDamage(false); - // non combat damage cause lifegain there - damageMap.triggerDamageDoneOnce(false, game, sa); - - preventMap.clear(); - damageMap.clear(); + game.getAction().dealDamage(false, damageMap, preventMap, counterTable, sa); } - - counterTable.triggerCountersPutAll(game); replaceDying(sa); return; } @@ -244,18 +238,18 @@ public class DamageDealEffect extends DamageBaseEffect { continue; } if (!sa.usesTargeting() || gc.canBeTargetedBy(sa)) { - internalDamageDeal(sa, sourceLKI, gc, dmg, damageMap, preventMap, counterTable); + internalDamageDeal(sa, sourceLKI, gc, dmg, damageMap); } } else if (o instanceof Player) { final Player p = (Player) o; if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) { - p.addDamage(dmg, sourceLKI, false, noPrevention, damageMap, preventMap, counterTable, sa); + damageMap.put(sourceLKI, p, dmg); } } } for (final Card unTgtC : untargetedCards) { if (unTgtC.isInPlay()) { - internalDamageDeal(sa, sourceLKI, unTgtC, dmg, damageMap, preventMap, counterTable); + internalDamageDeal(sa, sourceLKI, unTgtC, dmg, damageMap); } } @@ -264,21 +258,14 @@ public class DamageDealEffect extends DamageBaseEffect { } } if (!usedDamageMap) { - preventMap.triggerPreventDamage(false); - // non combat damage cause lifegain there - damageMap.triggerDamageDoneOnce(false, game, sa); - - preventMap.clear(); - damageMap.clear(); + game.getAction().dealDamage(false, damageMap, preventMap, counterTable, sa); } - counterTable.triggerCountersPutAll(game); replaceDying(sa); } - protected void internalDamageDeal(SpellAbility sa, Card sourceLKI, Card c, int dmg, CardDamageMap damageMap, CardDamageMap preventMap, GameEntityCounterTable counterTable) { + protected void internalDamageDeal(SpellAbility sa, Card sourceLKI, Card c, int dmg, CardDamageMap damageMap) { final Card hostCard = sa.getHostCard(); final Player activationPlayer = sa.getActivatingPlayer(); - final boolean noPrevention = sa.hasParam("NoPrevention"); if (sa.hasParam("Remove")) { c.setDamage(0); @@ -293,17 +280,17 @@ public class DamageDealEffect extends DamageBaseEffect { } int dmgToTarget = Math.min(lethal, dmg); - c.addDamage(dmgToTarget, sourceLKI, false, noPrevention, damageMap, preventMap, counterTable, sa); + damageMap.put(sourceLKI, c, dmgToTarget); List list = Lists.newArrayList(); list.addAll(AbilityUtils.getDefinedCards(hostCard, sa.getParam("ExcessDamage"), sa)); list.addAll(AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("ExcessDamage"), sa)); if (!list.isEmpty()) { - list.get(0).addDamage(dmg - dmgToTarget, sourceLKI, false, noPrevention, damageMap, preventMap, counterTable, sa); + damageMap.put(sourceLKI, list.get(0), dmg - dmgToTarget); } } else { - c.addDamage(dmg, sourceLKI, false, noPrevention, damageMap, preventMap, counterTable, sa); + damageMap.put(sourceLKI, c, dmg); } } } 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 975f7532695..1871bea5b8e 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 @@ -75,12 +75,13 @@ public class DamageEachEffect extends DamageBaseEffect { boolean usedDamageMap = true; CardDamageMap damageMap = sa.getDamageMap(); CardDamageMap preventMap = sa.getPreventMap(); - GameEntityCounterTable counterTable = new GameEntityCounterTable(); + GameEntityCounterTable counterTable = sa.getCounterTable(); if (damageMap == null) { // make a new damage map damageMap = new CardDamageMap(); preventMap = new CardDamageMap(); + counterTable = new GameEntityCounterTable(); usedDamageMap = false; } @@ -95,13 +96,13 @@ public class DamageEachEffect extends DamageBaseEffect { if (o instanceof Card) { final Card c = (Card) o; if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) { - c.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa); + damageMap.put(sourceLKI, c, dmg); } } else if (o instanceof Player) { final Player p = (Player) o; if (!targeted || p.canBeTargetedBy(sa)) { - p.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa); + damageMap.put(sourceLKI, p, dmg); } } } @@ -113,8 +114,7 @@ public class DamageEachEffect extends DamageBaseEffect { final Card sourceLKI = game.getChangeZoneLKIInfo(source); final int dmg = AbilityUtils.calculateAmount(source, "X", sa); - // System.out.println(source+" deals "+dmg+" damage to "+source); - source.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa); + damageMap.put(sourceLKI, source, dmg); } } if (sa.getParam("DefinedCards").equals("Remembered")) { @@ -125,8 +125,7 @@ public class DamageEachEffect extends DamageBaseEffect { for (final Object o : sa.getHostCard().getRemembered()) { if (o instanceof Card) { Card rememberedcard = (Card) o; - // System.out.println(source + " deals " + dmg + " damage to " + rememberedcard); - rememberedcard.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa); + damageMap.put(sourceLKI, rememberedcard, dmg); } } } @@ -134,15 +133,9 @@ public class DamageEachEffect extends DamageBaseEffect { } if (!usedDamageMap) { - preventMap.triggerPreventDamage(false); - damageMap.triggerDamageDoneOnce(false, game, sa); - - preventMap.clear(); - damageMap.clear(); + game.getAction().dealDamage(false, damageMap, preventMap, counterTable, sa); } - counterTable.triggerCountersPutAll(game); - replaceDying(sa); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageResolveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageResolveEffect.java index a9d7c35a677..4ecb1cbd2a4 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageResolveEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageResolveEffect.java @@ -1,5 +1,6 @@ package forge.game.ability.effects; +import forge.game.GameEntityCounterTable; import forge.game.ability.SpellAbilityEffect; import forge.game.card.CardDamageMap; import forge.game.spellability.SpellAbility; @@ -18,16 +19,9 @@ public class DamageResolveEffect extends SpellAbilityEffect { public void resolve(SpellAbility sa) { CardDamageMap damageMap = sa.getDamageMap(); CardDamageMap preventMap = sa.getPreventMap(); - - if (preventMap != null) { - preventMap.triggerPreventDamage(false); - preventMap.clear(); - } - // non combat damage cause lifegain there - if (damageMap != null) { - damageMap.triggerDamageDoneOnce(false, sa.getHostCard().getGame(), sa); - damageMap.clear(); - } + GameEntityCounterTable counterTable = sa.getCounterTable(); + + sa.getHostCard().getGame().getAction().dealDamage(false, damageMap, preventMap, counterTable, sa); } /* (non-Javadoc) diff --git a/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java b/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java index 77ebb1f39e6..f8b66c66051 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/FightEffect.java @@ -146,12 +146,13 @@ public class FightEffect extends DamageBaseEffect { boolean usedDamageMap = true; CardDamageMap damageMap = sa.getDamageMap(); CardDamageMap preventMap = sa.getPreventMap(); - GameEntityCounterTable counterTable = new GameEntityCounterTable(); + GameEntityCounterTable counterTable = sa.getCounterTable(); if (damageMap == null) { // make a new damage map damageMap = new CardDamageMap(); preventMap = new CardDamageMap(); + counterTable = new GameEntityCounterTable(); usedDamageMap = false; } @@ -163,22 +164,17 @@ public class FightEffect extends DamageBaseEffect { final int dmg1 = fightToughness ? fighterA.getNetToughness() : fighterA.getNetPower(); if (fighterA.equals(fighterB)) { - fighterA.addDamage(dmg1 * 2, fighterA, damageMap, preventMap, counterTable, sa); + damageMap.put(fighterA, fighterA, dmg1 * 2); } else { final int dmg2 = fightToughness ? fighterB.getNetToughness() : fighterB.getNetPower(); - fighterB.addDamage(dmg1, fighterA, damageMap, preventMap, counterTable, sa); - fighterA.addDamage(dmg2, fighterB, damageMap, preventMap, counterTable, sa); + damageMap.put(fighterA, fighterB, dmg1); + damageMap.put(fighterB, fighterA, dmg2); } if (!usedDamageMap) { - preventMap.triggerPreventDamage(false); - damageMap.triggerDamageDoneOnce(false, fighterA.getGame(), sa); - - preventMap.clear(); - damageMap.clear(); + sa.getHostCard().getGame().getAction().dealDamage(false, damageMap, preventMap, counterTable, sa); } - counterTable.triggerCountersPutAll(sa.getHostCard().getGame()); replaceDying(sa); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java index c82d1ddc8c9..b9e7da63a3d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java @@ -7,6 +7,7 @@ import com.google.common.collect.Lists; import forge.GameCommand; import forge.game.Game; +import forge.game.GameEntityCounterTable; import forge.game.GameObject; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; @@ -86,6 +87,7 @@ public class RepeatEachEffect extends SpellAbilityEffect { if (sa.hasParam("DamageMap")) { sa.setDamageMap(new CardDamageMap()); sa.setPreventMap(new CardDamageMap()); + sa.setCounterTable(new GameEntityCounterTable()); } if (sa.hasParam("ChangeZoneTable")) { sa.setChangeZoneTable(new CardZoneTable()); @@ -166,11 +168,7 @@ public class RepeatEachEffect extends SpellAbilityEffect { } if(sa.hasParam("DamageMap")) { - sa.getPreventMap().triggerPreventDamage(false); - sa.setPreventMap(null); - // non combat damage cause lifegain there - sa.getDamageMap().triggerDamageDoneOnce(false, game, sa); - sa.setDamageMap(null); + game.getAction().dealDamage(false, sa.getDamageMap(), sa.getPreventMap(), sa.getCounterTable(), sa); } if (sa.hasParam("ChangeZoneTable")) { sa.getChangeZoneTable().triggerChangesZoneAll(game, sa); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ReplaceDamageEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ReplaceDamageEffect.java index f75b7c31b0a..e3353860d3b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ReplaceDamageEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ReplaceDamageEffect.java @@ -60,17 +60,18 @@ public class ReplaceDamageEffect extends SpellAbilityEffect { // Set PreventedDamage SVar card.setSVar("PreventedDamage", "Number$" + n); + Card sourceLKI = (Card) sa.getReplacingObject(AbilityKey.Source); + GameEntity target = (GameEntity) sa.getReplacingObject(AbilityKey.Target); + // Set prevent map entry CardDamageMap preventMap = (CardDamageMap) originalParams.get(AbilityKey.PreventMap); - Card source = (Card) sa.getReplacingObject(AbilityKey.Source); - GameEntity target = (GameEntity) sa.getReplacingObject(AbilityKey.Target); - preventMap.put(source, target, n); + preventMap.put(sourceLKI, target, n); // Following codes are commented out since DamagePrevented trigger is currently not used by any Card. // final Map runParams = AbilityKey.newMap(); // runParams.put(AbilityKey.DamageTarget, target); // runParams.put(AbilityKey.DamageAmount, dmg); - // runParams.put(AbilityKey.DamageSource, source); + // runParams.put(AbilityKey.DamageSource, sourceLKI); // runParams.put(AbilityKey.IsCombatDamage, originalParams.get(AbilityKey.IsCombat)); // game.getTriggerHandler().runTrigger(TriggerType.DamagePrevented, runParams, false); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ReplaceSplitDamageEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ReplaceSplitDamageEffect.java index a8ec7574af5..0c620531592 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ReplaceSplitDamageEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ReplaceSplitDamageEffect.java @@ -58,18 +58,16 @@ public class ReplaceSplitDamageEffect extends SpellAbilityEffect { } Card sourceLKI = (Card) sa.getReplacingObject(AbilityKey.Source); + GameEntity target = (GameEntity) sa.getReplacingObject(AbilityKey.Target); + GameEntity obj = (GameEntity) list.get(0); + boolean isCombat = (Boolean) originalParams.get(AbilityKey.IsCombat); CardDamageMap damageMap = (CardDamageMap) originalParams.get(AbilityKey.DamageMap); CardDamageMap preventMap = (CardDamageMap) originalParams.get(AbilityKey.PreventMap); GameEntityCounterTable counterTable = (GameEntityCounterTable) originalParams.get(AbilityKey.CounterTable); SpellAbility cause = (SpellAbility) originalParams.get(AbilityKey.Cause); - - boolean isCombat = (Boolean) originalParams.get(AbilityKey.IsCombat); - boolean noPrevention = (Boolean) originalParams.get(AbilityKey.NoPreventDamage); - - GameEntity obj = (GameEntity) list.get(0); - - obj.addDamage(n, sourceLKI, isCombat, noPrevention, damageMap, preventMap, counterTable, cause); + damageMap.put(sourceLKI, obj, n); + obj.replaceDamage(n, sourceLKI, isCombat, damageMap, preventMap, counterTable, cause); } // no damage for original target anymore diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 5c2dfeb9764..6176cebf5b1 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -5136,24 +5136,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return total; } - public final void addCombatDamage(final Map map, final CardDamageMap damageMap, final CardDamageMap preventMap, GameEntityCounterTable counterTable) { - for (final Entry entry : map.entrySet()) { - addCombatDamage(entry.getValue(), entry.getKey(), damageMap, preventMap, counterTable); - } - } - - /* - * (non-Javadoc) - * @see forge.game.GameEntity#addCombatDamageBase(int, forge.game.card.Card, forge.game.card.CardDamageMap, forge.game.GameEntityCounterTable) - */ - @Override - protected int addCombatDamageBase(final int damage, final Card source, CardDamageMap damageMap, GameEntityCounterTable counterTable) { - if (isInPlay()) { - return super.addCombatDamageBase(damage, source, damageMap, counterTable); - } - return 0; - } - public final boolean canDamagePrevented(final boolean isCombat) { CardCollection list = new CardCollection(getGame().getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)); list.add(this); @@ -5246,19 +5228,12 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return restDamage; } - public final void addDamage(final Map sourcesMap, CardDamageMap damageMap, GameEntityCounterTable counterTable) { - for (final Entry entry : sourcesMap.entrySet()) { - // damage prevention is already checked! - addDamageAfterPrevention(entry.getValue(), entry.getKey(), true, damageMap, counterTable); - } - } - /** * This function handles damage after replacement and prevention effects are * applied. */ @Override - public final int addDamageAfterPrevention(final int damageIn, final Card source, final boolean isCombat, CardDamageMap damageMap, GameEntityCounterTable counterTable) { + public final int addDamageAfterPrevention(final int damageIn, final Card source, final boolean isCombat, GameEntityCounterTable counterTable) { if (damageIn <= 0) { return 0; // Rule 119.8 @@ -5330,8 +5305,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { game.fireEvent(new GameEventCardDamaged(this, source, damageIn, damageType)); } - damageMap.put(source, this, damageIn); - if (excess > 0) { // Run triggers runParams = AbilityKey.newMap(); diff --git a/forge-game/src/main/java/forge/game/card/CardDamageMap.java b/forge-game/src/main/java/forge/game/card/CardDamageMap.java index 09f8a83d014..2783d5793ec 100644 --- a/forge-game/src/main/java/forge/game/card/CardDamageMap.java +++ b/forge-game/src/main/java/forge/game/card/CardDamageMap.java @@ -18,8 +18,6 @@ import forge.game.Game; import forge.game.GameEntity; import forge.game.GameObjectPredicates; import forge.game.ability.AbilityKey; -import forge.game.keyword.Keyword; -import forge.game.spellability.SpellAbility; import forge.game.trigger.TriggerType; public class CardDamageMap extends ForwardingTable { @@ -50,7 +48,7 @@ public class CardDamageMap extends ForwardingTable { } } - public void triggerDamageDoneOnce(boolean isCombat, final Game game, final SpellAbility sa) { + public void triggerDamageDoneOnce(boolean isCombat, final Game game) { // Source -> Targets for (Map.Entry> e : rowMap().entrySet()) { final Card sourceLKI = e.getKey(); @@ -65,10 +63,6 @@ public class CardDamageMap extends ForwardingTable { runParams.put(AbilityKey.IsCombatDamage, isCombat); game.getTriggerHandler().runTrigger(TriggerType.DamageDealtOnce, runParams, false); - - if (sourceLKI.hasKeyword(Keyword.LIFELINK)) { - sourceLKI.getController().gainLife(sum, sourceLKI, sa); - } } } // Targets -> Source diff --git a/forge-game/src/main/java/forge/game/combat/Combat.java b/forge-game/src/main/java/forge/game/combat/Combat.java index 1a9dff83b8b..0b7902c922c 100644 --- a/forge-game/src/main/java/forge/game/combat/Combat.java +++ b/forge-game/src/main/java/forge/game/combat/Combat.java @@ -73,13 +73,10 @@ public class Combat { private final Multimap attackedByBands = Multimaps.synchronizedMultimap(ArrayListMultimap.create()); private final Multimap blockedBands = Multimaps.synchronizedMultimap(ArrayListMultimap.create()); - private final Map defendingDamageMap = Maps.newHashMap(); - private Map attackersOrderedForDamageAssignment = Maps.newHashMap(); private Map blockersOrderedForDamageAssignment = Maps.newHashMap(); private Map lkiCache = Maps.newHashMap(); - private CardDamageMap dealtDamageTo = new CardDamageMap(); - private boolean dividedToPlayer = false; + private CardDamageMap damageMap = new CardDamageMap(); // List holds creatures who have dealt 1st strike damage to disallow them deal damage on regular basis (unless they have double-strike KW) private CardCollection combatantsThatDealtFirstStrikeDamage = new CardCollection(); @@ -113,9 +110,6 @@ public class Combat { for (Entry entry : combat.blockedBands.entries()) { blockedBands.put(bandsMap.get(entry.getKey()), map.map(entry.getValue())); } - for (Entry entry : combat.defendingDamageMap.entrySet()) { - defendingDamageMap.put(map.map(entry.getKey()), entry.getValue()); - } for (Entry entry : combat.attackersOrderedForDamageAssignment.entrySet()) { attackersOrderedForDamageAssignment.put(map.map(entry.getKey()), map.mapCollection(entry.getValue())); @@ -124,8 +118,8 @@ public class Combat { blockersOrderedForDamageAssignment.put(map.map(entry.getKey()), map.mapCollection(entry.getValue())); } // Note: Doesn't currently set up lkiCache, since it's just a cache and not strictly needed... - for (Table.Cell entry : combat.dealtDamageTo.cellSet()) { - dealtDamageTo.put(map.map(entry.getRowKey()), map.map(entry.getColumnKey()), entry.getValue()); + for (Table.Cell entry : combat.damageMap.cellSet()) { + damageMap.put(map.map(entry.getRowKey()), map.map(entry.getColumnKey()), entry.getValue()); } attackConstraints = new AttackConstraints(this); @@ -170,7 +164,6 @@ public class Combat { attackableEntries.clear(); attackedByBands.clear(); blockedBands.clear(); - defendingDamageMap.clear(); attackersOrderedForDamageAssignment.clear(); blockersOrderedForDamageAssignment.clear(); lkiCache.clear(); @@ -728,6 +721,7 @@ public class Combat { Map map = assigningPlayer.getController().assignCombatDamage(blocker, attackers, damage, null, assigningPlayer != blocker.getController()); for (Entry dt : map.entrySet()) { dt.getKey().addAssignedDamage(dt.getValue(), blocker); + damageMap.put(blocker, dt.getKey(), dt.getValue()); } } } @@ -736,7 +730,6 @@ public class Combat { private final boolean assignAttackersDamage(boolean firstStrikeDamage) { // Assign damage by Attackers - defendingDamageMap.clear(); // this should really happen in deal damage CardCollection orderedBlockers = null; final CardCollection attackers = getAttackers(); boolean assignedDamage = false; @@ -784,20 +777,19 @@ public class Combat { } } assignedDamage = true; + GameEntity defender = getDefenderByAttacker(band); // If the Attacker is unblocked, or it's a trampler and has 0 blockers, deal damage to defender if (orderedBlockers == null || orderedBlockers.isEmpty()) { if (trampler || !band.isBlocked()) { // this is called after declare blockers, no worries 'bout nulls in isBlocked - addDefendingDamage(damageDealt, attacker); + damageMap.put(attacker, defender, damageDealt); } // No damage happens if blocked but no blockers left } else { - GameEntity defender = getDefenderByAttacker(band); Player assigningPlayer = getAttackingPlayer(); // Defensive Formation is very similar to Banding with Blockers // It allows the defending player to assign damage instead of the attacking player if (defender instanceof Card && divideCombatDamageAsChoose) { defender = getDefenderPlayerByAttacker(attacker); - dividedToPlayer = true; } if (defender instanceof Player && defender.hasKeyword("You assign combat damage of each creature attacking you.")) { assigningPlayer = (Player)defender; @@ -810,10 +802,15 @@ public class Combat { damageDealt, defender, divideCombatDamageAsChoose || getAttackingPlayer() != assigningPlayer); for (Entry dt : map.entrySet()) { if (dt.getKey() == null) { - if (dt.getValue() > 0) - addDefendingDamage(dt.getValue(), attacker); + if (dt.getValue() > 0) { + if (defender instanceof Card) { + ((Card) defender).addAssignedDamage(dt.getValue(), attacker); + } + damageMap.put(attacker, defender, dt.getValue()); + } } else { dt.getKey().addAssignedDamage(dt.getValue(), attacker); + damageMap.put(attacker, dt.getKey(), dt.getValue()); } } } // if !hasFirstStrike ... @@ -833,24 +830,6 @@ public class Combat { return !firstStrikeDamage && !combatantsThatDealtFirstStrikeDamage.contains(combatant); } - // Damage to whatever was protected there. - private final void addDefendingDamage(final int n, final Card source) { - final GameEntity ge = getDefenderByAttacker(source); - - if (ge instanceof Card && !dividedToPlayer) { - final Card planeswalker = (Card) ge; - planeswalker.addAssignedDamage(n, source); - return; - } - - if (!defendingDamageMap.containsKey(source)) { - defendingDamageMap.put(source, n); - } - else { - defendingDamageMap.put(source, defendingDamageMap.get(source) + n); - } - } - public final boolean assignCombatDamage(boolean firstStrikeDamage) { boolean assignedDamage = assignAttackersDamage(firstStrikeDamage); assignedDamage |= assignBlockersDamage(firstStrikeDamage); @@ -862,7 +841,7 @@ public class Combat { } public final CardDamageMap getDamageMap() { - return dealtDamageTo; + return damageMap; } public void dealAssignedDamage() { @@ -872,49 +851,7 @@ public class Combat { CardDamageMap preventMap = new CardDamageMap(); GameEntityCounterTable counterTable = new GameEntityCounterTable(); - // This function handles both Regular and First Strike combat assignment - for (final Entry entry : defendingDamageMap.entrySet()) { - GameEntity defender = getDefenderByAttacker(entry.getKey()); - if (dividedToPlayer) { - defender = getDefenderPlayerByAttacker(entry.getKey()); - } - if (defender instanceof Player) { // player - defender.addCombatDamage(entry.getValue(), entry.getKey(), dealtDamageTo, preventMap, counterTable); - } - else if (defender instanceof Card) { // planeswalker - ((Card) defender).getController().addCombatDamage(entry.getValue(), entry.getKey(), dealtDamageTo, preventMap, counterTable); - } - } - - // this can be much better below here... - - final CardCollection combatants = new CardCollection(); - combatants.addAll(getAttackers()); - combatants.addAll(getAllBlockers()); - combatants.addAll(getDefendingPlaneswalkers()); - combatants.addAll(getDefendersCreatures()); - - for (final Card c : combatants) { - // if no assigned damage to resolve, move to next - if (c.getTotalAssignedDamage() == 0) { - continue; - } - - c.addCombatDamage(c.getAssignedDamageMap(), dealtDamageTo, preventMap, counterTable); - c.clearAssignedDamage(); - } - - preventMap.triggerPreventDamage(true); - preventMap.clear(); - // This was deeper before, but that resulted in the stack entry acting like before. - - // Run the trigger to deal combat damage once - // LifeLink for Combat Damage at this place - dealtDamageTo.triggerDamageDoneOnce(true, game, null); - dealtDamageTo.clear(); - - counterTable.triggerCountersPutAll(game); - counterTable.clear(); + game.getAction().dealDamage(true, damageMap, preventMap, counterTable, null); // copy last state again for dying replacement effects game.copyLastState(); diff --git a/forge-game/src/main/java/forge/game/cost/CostDamage.java b/forge-game/src/main/java/forge/game/cost/CostDamage.java index 198ac700ea8..fa2e229a9b0 100644 --- a/forge-game/src/main/java/forge/game/cost/CostDamage.java +++ b/forge-game/src/main/java/forge/game/cost/CostDamage.java @@ -71,15 +71,9 @@ public class CostDamage extends CostPart { CardDamageMap preventMap = new CardDamageMap(); GameEntityCounterTable table = new GameEntityCounterTable(); - payer.addDamage(decision.c, source, damageMap, preventMap, table, sa); + damageMap.put(source, payer, decision.c); + source.getGame().getAction().dealDamage(false, damageMap, preventMap, table, sa); - preventMap.triggerPreventDamage(false); - damageMap.triggerDamageDoneOnce(false, source.getGame(), sa); - table.triggerCountersPutAll(payer.getGame()); - - preventMap.clear(); - damageMap.clear(); - table.clear(); return decision.c > 0; } diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 155405b9f92..e7d51df0ee8 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -68,7 +68,6 @@ import forge.game.ability.effects.DetachedCardEffect; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; -import forge.game.card.CardDamageMap; import forge.game.card.CardFactoryUtil; import forge.game.card.CardLists; import forge.game.card.CardPredicates; @@ -636,7 +635,7 @@ public class Player extends GameEntity implements Comparable { // This function handles damage after replacement and prevention effects are applied @Override - public final int addDamageAfterPrevention(final int amount, final Card source, final boolean isCombat, CardDamageMap damageMap, GameEntityCounterTable counterTable) { + public final int addDamageAfterPrevention(final int amount, final Card source, final boolean isCombat, GameEntityCounterTable counterTable) { if (amount <= 0) { return 0; } @@ -703,9 +702,6 @@ public class Player extends GameEntity implements Comparable { game.fireEvent(new GameEventPlayerDamaged(this, source, amount, isCombat, infect)); - if (amount > 0) { - damageMap.put(source, this, amount); - } return amount; } diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java index fa797b4f80c..d4363e496b5 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java @@ -282,18 +282,19 @@ public class ReplacementHandler { } private void putPreventMapEntry(final Map runParams) { - // Set prevent map entry - CardDamageMap preventMap = (CardDamageMap) runParams.get(AbilityKey.PreventMap); - Card source = (Card) runParams.get(AbilityKey.DamageSource); + Card sourceLKI = (Card) runParams.get(AbilityKey.DamageSource); GameEntity target = (GameEntity) runParams.get(AbilityKey.Affected); Integer damage = (Integer) runParams.get(AbilityKey.DamageAmount); - preventMap.put(source, target, damage); + + // Set prevent map entry + CardDamageMap preventMap = (CardDamageMap) runParams.get(AbilityKey.PreventMap); + preventMap.put(sourceLKI, target, damage); // Following codes are commented out since DamagePrevented trigger is currently not used by any Card. // final Map trigParams = AbilityKey.newMap(); // trigParams.put(AbilityKey.DamageTarget, target); // trigParams.put(AbilityKey.DamageAmount, damage); - // trigParams.put(AbilityKey.DamageSource, source); + // trigParams.put(AbilityKey.DamageSource, sourceLKI); // trigParams.put(AbilityKey.IsCombatDamage, runParams.get(AbilityKey.IsCombat)); // game.getTriggerHandler().runTrigger(TriggerType.DamagePrevented, trigParams, false); } diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index 126ee4656d0..a000d651673 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -45,6 +45,7 @@ import forge.game.ForgeScript; import forge.game.Game; import forge.game.GameActionUtil; import forge.game.GameEntity; +import forge.game.GameEntityCounterTable; import forge.game.GameObject; import forge.game.IHasSVars; import forge.game.IIdentifiable; @@ -186,6 +187,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private CardDamageMap damageMap = null; private CardDamageMap preventMap = null; + private GameEntityCounterTable counterTable = null; private CardZoneTable changeZoneTable = null; public CardCollection getLastStateBattlefield() { @@ -1047,6 +1049,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit if (preventMap != null) { clone.preventMap = new CardDamageMap(preventMap); } + if (counterTable != null) { + clone.counterTable = new GameEntityCounterTable(counterTable); + } if (changeZoneTable != null) { clone.changeZoneTable = new CardZoneTable(); clone.changeZoneTable.putAll(changeZoneTable); @@ -2231,6 +2236,15 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit return null; } + public GameEntityCounterTable getCounterTable() { + if (counterTable != null) { + return counterTable; + } else if (getParent() != null) { + return getParent().getCounterTable(); + } + return null; + } + public CardZoneTable getChangeZoneTable() { if (changeZoneTable != null) { return changeZoneTable; @@ -2246,6 +2260,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit public void setPreventMap(final CardDamageMap map) { preventMap = map; } + public void setCounterTable(final GameEntityCounterTable table) { + counterTable = table; + } public void setChangeZoneTable(final CardZoneTable table) { changeZoneTable = table; } diff --git a/forge-gui/res/cardsfolder/g/goblin_psychopath.txt b/forge-gui/res/cardsfolder/g/goblin_psychopath.txt index 1740627829c..3e31608b698 100644 --- a/forge-gui/res/cardsfolder/g/goblin_psychopath.txt +++ b/forge-gui/res/cardsfolder/g/goblin_psychopath.txt @@ -6,7 +6,7 @@ T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ CoinFlip | TriggerDescription$ T:Mode$ Blocks | ValidCard$ Card.Self | Execute$ CoinFlip | Secondary$ True | TriggerDescription$ Whenever CARDNAME attacks or blocks, flip a coin. If you lose the flip, the next time it would deal combat damage this turn, it deals that damage to you instead. SVar:CoinFlip:DB$ FlipACoin | LoseSubAbility$ CreateEffect SVar:CreateEffect:DB$ Effect | Name$ Goblin Psychopath Effect | ReplacementEffects$ EventDamageDone -SVar:EventDamageDone:Event$ DamageDone | ValidSource$ Card.EffectSource | ReplaceWith$ DamageYou | IsCombat$ True | Description$ The next time EFFECTSOURCE would deal combat damage this turn, it deals that damage to you instead. +SVar:EventDamageDone:Event$ DamageDone | ValidSource$ Card.EffectSource | DamageTarget$ You | ReplaceWith$ DamageYou | IsCombat$ True | Description$ The next time EFFECTSOURCE would deal combat damage this turn, it deals that damage to you instead. SVar:DamageYou:DB$ ReplaceEffect | VarName$ Affected | VarValue$ You | VarType$ Player | SubAbility$ ExileEffect SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile SVar:PsychoX:ReplaceCount$DamageAmount diff --git a/forge-gui/res/cardsfolder/h/harsh_judgment.txt b/forge-gui/res/cardsfolder/h/harsh_judgment.txt index 4c95488a3b1..8cf0d8ca660 100644 --- a/forge-gui/res/cardsfolder/h/harsh_judgment.txt +++ b/forge-gui/res/cardsfolder/h/harsh_judgment.txt @@ -3,7 +3,7 @@ ManaCost:2 W W Types:Enchantment K:ETBReplacement:Other:ChooseColor SVar:ChooseColor:DB$ ChooseColor | Defined$ You | AILogic$ MostProminentInHumanDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a color. -R:Event$ DamageDone | ValidTarget$ You | ActiveZones$ Battlefield | ValidSource$ Instant.ChosenColor,Sorcery.ChosenColor | ReplaceWith$ HarshDmg | Description$ If an instant or sorcery spell of the chosen color would deal damage to you, it deals that damage to its controller instead. +R:Event$ DamageDone | ValidTarget$ You | ActiveZones$ Battlefield | ValidSource$ Instant.ChosenColor,Sorcery.ChosenColor | DamageTarget$ ReplacedSourceController | ReplaceWith$ HarshDmg | Description$ If an instant or sorcery spell of the chosen color would deal damage to you, it deals that damage to its controller instead. SVar:HarshDmg:DB$ ReplaceEffect | VarName$ Affected | VarValue$ ReplacedSourceController | VarType$ Player AI:RemoveDeck:Random SVar:Picture:http://www.wizards.com/global/images/magic/general/harsh_judgment.jpg diff --git a/forge-gui/res/cardsfolder/s/shield_dancer.txt b/forge-gui/res/cardsfolder/s/shield_dancer.txt index 14be14392a4..d6bb9d88471 100644 --- a/forge-gui/res/cardsfolder/s/shield_dancer.txt +++ b/forge-gui/res/cardsfolder/s/shield_dancer.txt @@ -3,7 +3,7 @@ ManaCost:2 W Types:Creature Human Rebel PT:1/3 A:AB$ Effect | Cost$ 2 W | ValidTgts$ Creature.attacking | TgtPrompt$ Select target attacking creature | IsCurse$ True | ReplacementEffects$ DamageShielded | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | AILogic$ Fog | SpellDescription$ The next time target attacking creature would deal combat damage to CARDNAME this turn, that creature deals that damage to itself instead. -SVar:DamageShielded:Event$ DamageDone | IsCombat$ True | ValidSource$ Card.IsRemembered | ValidTarget$ Card.EffectSource | ReplaceWith$ DmgSelf | Description$ The next time the targeted attacking creature would deal combat damage to EFFECTSOURCE this turn, that creature deals that damage to itself instead. +SVar:DamageShielded:Event$ DamageDone | IsCombat$ True | ValidSource$ Card.IsRemembered | ValidTarget$ Card.EffectSource | ReplaceWith$ DmgSelf | DamageTarget$ Remembered | Description$ The next time the targeted attacking creature would deal combat damage to EFFECTSOURCE this turn, that creature deals that damage to itself instead. SVar:DmgSelf:DB$ ReplaceEffect | VarName$ Affected | VarValue$ Remembered | VarType$ Card | SubAbility$ ExileEffect SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile AI:RemoveDeck:All