mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
Merge branch 'refactor_damage_replacement' into 'master'
Deal damage use damage map See merge request core-developers/forge!4696
This commit is contained in:
@@ -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<GameEntity, Map<Card, Integer>> et : damageMap.columnMap().entrySet()) {
|
||||
final GameEntity ge = et.getKey();
|
||||
if (isCombat && ge instanceof Card) {
|
||||
((Card) ge).clearAssignedDamage();
|
||||
}
|
||||
for (Map.Entry<Card, Integer> 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<Card, Map<GameEntity, Integer>> et : replaceDamageMap.rowMap().entrySet()) {
|
||||
final Card sourceLKI = et.getKey();
|
||||
int sum = 0;
|
||||
for (Map.Entry<GameEntity, Integer> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Card> 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<AbilityKey, Object> 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)
|
||||
|
||||
@@ -20,6 +20,13 @@ public class GameEntityCounterTable extends ForwardingTable<Optional<Player>, Ga
|
||||
|
||||
private Table<Optional<Player>, GameEntity, Map<CounterType, Integer>> dataMap = HashBasedTable.create();
|
||||
|
||||
public GameEntityCounterTable() {
|
||||
}
|
||||
|
||||
public GameEntityCounterTable(Table<Optional<Player>, GameEntity, Map<CounterType, Integer>> counterTable) {
|
||||
putAll(counterTable);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.google.common.collect.ForwardingTable#delegate()
|
||||
|
||||
@@ -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<Player> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(sourceLKI, assigneeCards, dmg, null, true);
|
||||
for (Entry<Card, Integer> 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<GameEntity> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<AbilityKey, Object> 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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5136,24 +5136,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return total;
|
||||
}
|
||||
|
||||
public final void addCombatDamage(final Map<Card, Integer> map, final CardDamageMap damageMap, final CardDamageMap preventMap, GameEntityCounterTable counterTable) {
|
||||
for (final Entry<Card, Integer> 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<Card>, IHasSVars {
|
||||
return restDamage;
|
||||
}
|
||||
|
||||
public final void addDamage(final Map<Card, Integer> sourcesMap, CardDamageMap damageMap, GameEntityCounterTable counterTable) {
|
||||
for (final Entry<Card, Integer> 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<Card>, IHasSVars {
|
||||
game.fireEvent(new GameEventCardDamaged(this, source, damageIn, damageType));
|
||||
}
|
||||
|
||||
damageMap.put(source, this, damageIn);
|
||||
|
||||
if (excess > 0) {
|
||||
// Run triggers
|
||||
runParams = AbilityKey.newMap();
|
||||
|
||||
@@ -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<Card, GameEntity, Integer> {
|
||||
@@ -50,7 +48,7 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
||||
}
|
||||
}
|
||||
|
||||
public void triggerDamageDoneOnce(boolean isCombat, final Game game, final SpellAbility sa) {
|
||||
public void triggerDamageDoneOnce(boolean isCombat, final Game game) {
|
||||
// Source -> Targets
|
||||
for (Map.Entry<Card, Map<GameEntity, Integer>> e : rowMap().entrySet()) {
|
||||
final Card sourceLKI = e.getKey();
|
||||
@@ -65,10 +63,6 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
||||
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
|
||||
|
||||
@@ -73,13 +73,10 @@ public class Combat {
|
||||
private final Multimap<GameEntity, AttackingBand> attackedByBands = Multimaps.synchronizedMultimap(ArrayListMultimap.create());
|
||||
private final Multimap<AttackingBand, Card> blockedBands = Multimaps.synchronizedMultimap(ArrayListMultimap.create());
|
||||
|
||||
private final Map<Card, Integer> defendingDamageMap = Maps.newHashMap();
|
||||
|
||||
private Map<Card, CardCollection> attackersOrderedForDamageAssignment = Maps.newHashMap();
|
||||
private Map<Card, CardCollection> blockersOrderedForDamageAssignment = Maps.newHashMap();
|
||||
private Map<GameEntity, CombatLki> 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<AttackingBand, Card> entry : combat.blockedBands.entries()) {
|
||||
blockedBands.put(bandsMap.get(entry.getKey()), map.map(entry.getValue()));
|
||||
}
|
||||
for (Entry<Card, Integer> entry : combat.defendingDamageMap.entrySet()) {
|
||||
defendingDamageMap.put(map.map(entry.getKey()), entry.getValue());
|
||||
}
|
||||
|
||||
for (Entry<Card, CardCollection> 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<Card, GameEntity, Integer> entry : combat.dealtDamageTo.cellSet()) {
|
||||
dealtDamageTo.put(map.map(entry.getRowKey()), map.map(entry.getColumnKey()), entry.getValue());
|
||||
for (Table.Cell<Card, GameEntity, Integer> 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<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(blocker, attackers, damage, null, assigningPlayer != blocker.getController());
|
||||
for (Entry<Card, Integer> 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<Card, Integer> 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<Card, Integer> 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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Player> {
|
||||
|
||||
// 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<Player> {
|
||||
|
||||
game.fireEvent(new GameEventPlayerDamaged(this, source, amount, isCombat, infect));
|
||||
|
||||
if (amount > 0) {
|
||||
damageMap.put(source, this, amount);
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
|
||||
|
||||
@@ -282,18 +282,19 @@ public class ReplacementHandler {
|
||||
}
|
||||
|
||||
private void putPreventMapEntry(final Map<AbilityKey, Object> 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<AbilityKey, Object> 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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user