mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 20:58:03 +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.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
|
import forge.game.card.CardDamageMap;
|
||||||
import forge.game.card.CardFactory;
|
import forge.game.card.CardFactory;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
@@ -61,6 +62,7 @@ import forge.game.event.GameEventCardTapped;
|
|||||||
import forge.game.event.GameEventFlipCoin;
|
import forge.game.event.GameEventFlipCoin;
|
||||||
import forge.game.event.GameEventGameStarted;
|
import forge.game.event.GameEventGameStarted;
|
||||||
import forge.game.event.GameEventScry;
|
import forge.game.event.GameEventScry;
|
||||||
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
import forge.game.mulligan.MulliganService;
|
import forge.game.mulligan.MulliganService;
|
||||||
import forge.game.player.GameLossReason;
|
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);
|
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) {
|
final CardDamageMap damageMap, final CardDamageMap preventMap, GameEntityCounterTable counterTable, final SpellAbility cause) {
|
||||||
int damageToDo = replaceDamage(damage, source, isCombat, !noPrevention, damageMap, preventMap, counterTable, cause);
|
boolean prevention = source.canDamagePrevented(isCombat) && (cause == null || !cause.hasParam("NoPrevention"));
|
||||||
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;
|
|
||||||
|
|
||||||
// Replacement effects
|
// Replacement effects
|
||||||
final Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(this);
|
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);
|
GameEntity newTarget = (GameEntity) repParams.get(AbilityKey.Affected);
|
||||||
// check if this is still the affected card or player
|
// check if this is still the affected card or player
|
||||||
if (this.equals(newTarget)) {
|
if (this.equals(newTarget)) {
|
||||||
restDamage = newDamage;
|
damageMap.put(source, this, newDamage - damage);
|
||||||
} else {
|
} else {
|
||||||
newDamage = newTarget.replaceDamage(newDamage, source, isCombat, prevention, damageMap, preventMap, counterTable, cause);
|
damageMap.remove(source, this);
|
||||||
newTarget.addDamageAfterPrevention(newDamage, source, isCombat, damageMap, counterTable);
|
damageMap.put(source, newTarget, newDamage);
|
||||||
restDamage = 0;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
restDamage = 0;
|
damageMap.remove(source, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
return restDamage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function handles damage after replacement and prevention effects are applied
|
// 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
|
// This should be also usable by the AI to forecast an effect (so it must
|
||||||
// not change the game state)
|
// 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();
|
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)
|
* (non-Javadoc)
|
||||||
* @see com.google.common.collect.ForwardingTable#delegate()
|
* @see com.google.common.collect.ForwardingTable#delegate()
|
||||||
|
|||||||
@@ -89,26 +89,31 @@ public class DamageAllEffect extends DamageBaseEffect {
|
|||||||
boolean usedDamageMap = true;
|
boolean usedDamageMap = true;
|
||||||
CardDamageMap damageMap = sa.getDamageMap();
|
CardDamageMap damageMap = sa.getDamageMap();
|
||||||
CardDamageMap preventMap = sa.getPreventMap();
|
CardDamageMap preventMap = sa.getPreventMap();
|
||||||
GameEntityCounterTable counterTable = new GameEntityCounterTable();
|
GameEntityCounterTable counterTable = sa.getCounterTable();
|
||||||
|
|
||||||
if (damageMap == null) {
|
if (damageMap == null) {
|
||||||
// make a new damage map
|
// make a new damage map
|
||||||
damageMap = new CardDamageMap();
|
damageMap = new CardDamageMap();
|
||||||
preventMap = new CardDamageMap();
|
preventMap = new CardDamageMap();
|
||||||
|
counterTable = new GameEntityCounterTable();
|
||||||
usedDamageMap = false;
|
usedDamageMap = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final Card c : list) {
|
for (final Card c : list) {
|
||||||
c.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa);
|
damageMap.put(sourceLKI, c, dmg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!players.equals("")) {
|
if (!players.equals("")) {
|
||||||
final List<Player> playerList = AbilityUtils.getDefinedPlayers(card, players, sa);
|
final List<Player> playerList = AbilityUtils.getDefinedPlayers(card, players, sa);
|
||||||
for (final Player p : playerList) {
|
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
|
// do Remember there
|
||||||
if (rememberCard || rememberPlayer) {
|
if (rememberCard || rememberPlayer) {
|
||||||
for (GameEntity e : damageMap.row(sourceLKI).keySet()) {
|
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);
|
replaceDying(sa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,6 @@ public class DamageDealEffect extends DamageBaseEffect {
|
|||||||
final String damage = sa.getParam("NumDmg");
|
final String damage = sa.getParam("NumDmg");
|
||||||
int dmg = AbilityUtils.calculateAmount(hostCard, damage, sa);
|
int dmg = AbilityUtils.calculateAmount(hostCard, damage, sa);
|
||||||
|
|
||||||
final boolean noPrevention = sa.hasParam("NoPrevention");
|
|
||||||
final boolean removeDamage = sa.hasParam("Remove");
|
final boolean removeDamage = sa.hasParam("Remove");
|
||||||
final boolean divideOnResolution = sa.hasParam("DividerOnResolution");
|
final boolean divideOnResolution = sa.hasParam("DividerOnResolution");
|
||||||
|
|
||||||
@@ -172,17 +171,19 @@ public class DamageDealEffect extends DamageBaseEffect {
|
|||||||
boolean usedDamageMap = true;
|
boolean usedDamageMap = true;
|
||||||
CardDamageMap damageMap = sa.getDamageMap();
|
CardDamageMap damageMap = sa.getDamageMap();
|
||||||
CardDamageMap preventMap = sa.getPreventMap();
|
CardDamageMap preventMap = sa.getPreventMap();
|
||||||
GameEntityCounterTable counterTable = new GameEntityCounterTable();
|
GameEntityCounterTable counterTable = sa.getCounterTable();
|
||||||
|
|
||||||
if (damageMap == null) {
|
if (damageMap == null) {
|
||||||
// make a new damage map
|
// make a new damage map
|
||||||
damageMap = new CardDamageMap();
|
damageMap = new CardDamageMap();
|
||||||
preventMap = new CardDamageMap();
|
preventMap = new CardDamageMap();
|
||||||
|
counterTable = new GameEntityCounterTable();
|
||||||
usedDamageMap = false;
|
usedDamageMap = false;
|
||||||
}
|
}
|
||||||
if (sa.hasParam("DamageMap")) {
|
if (sa.hasParam("DamageMap")) {
|
||||||
sa.setDamageMap(damageMap);
|
sa.setDamageMap(damageMap);
|
||||||
sa.setPreventMap(preventMap);
|
sa.setPreventMap(preventMap);
|
||||||
|
sa.setCounterTable(counterTable);
|
||||||
usedDamageMap = true;
|
usedDamageMap = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,19 +209,12 @@ public class DamageDealEffect extends DamageBaseEffect {
|
|||||||
Player assigningPlayer = players.get(0);
|
Player assigningPlayer = players.get(0);
|
||||||
Map<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(sourceLKI, assigneeCards, dmg, null, true);
|
Map<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(sourceLKI, assigneeCards, dmg, null, true);
|
||||||
for (Entry<Card, Integer> dt : map.entrySet()) {
|
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) {
|
if (!usedDamageMap) {
|
||||||
preventMap.triggerPreventDamage(false);
|
game.getAction().dealDamage(false, damageMap, preventMap, counterTable, sa);
|
||||||
// non combat damage cause lifegain there
|
|
||||||
damageMap.triggerDamageDoneOnce(false, game, sa);
|
|
||||||
|
|
||||||
preventMap.clear();
|
|
||||||
damageMap.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
counterTable.triggerCountersPutAll(game);
|
|
||||||
replaceDying(sa);
|
replaceDying(sa);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -244,18 +238,18 @@ public class DamageDealEffect extends DamageBaseEffect {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!sa.usesTargeting() || gc.canBeTargetedBy(sa)) {
|
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) {
|
} else if (o instanceof Player) {
|
||||||
final Player p = (Player) o;
|
final Player p = (Player) o;
|
||||||
if (!sa.usesTargeting() || p.canBeTargetedBy(sa)) {
|
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) {
|
for (final Card unTgtC : untargetedCards) {
|
||||||
if (unTgtC.isInPlay()) {
|
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) {
|
if (!usedDamageMap) {
|
||||||
preventMap.triggerPreventDamage(false);
|
game.getAction().dealDamage(false, damageMap, preventMap, counterTable, sa);
|
||||||
// non combat damage cause lifegain there
|
|
||||||
damageMap.triggerDamageDoneOnce(false, game, sa);
|
|
||||||
|
|
||||||
preventMap.clear();
|
|
||||||
damageMap.clear();
|
|
||||||
}
|
}
|
||||||
counterTable.triggerCountersPutAll(game);
|
|
||||||
replaceDying(sa);
|
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 Card hostCard = sa.getHostCard();
|
||||||
final Player activationPlayer = sa.getActivatingPlayer();
|
final Player activationPlayer = sa.getActivatingPlayer();
|
||||||
final boolean noPrevention = sa.hasParam("NoPrevention");
|
|
||||||
|
|
||||||
if (sa.hasParam("Remove")) {
|
if (sa.hasParam("Remove")) {
|
||||||
c.setDamage(0);
|
c.setDamage(0);
|
||||||
@@ -293,17 +280,17 @@ public class DamageDealEffect extends DamageBaseEffect {
|
|||||||
}
|
}
|
||||||
int dmgToTarget = Math.min(lethal, dmg);
|
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<GameEntity> list = Lists.newArrayList();
|
||||||
list.addAll(AbilityUtils.getDefinedCards(hostCard, sa.getParam("ExcessDamage"), sa));
|
list.addAll(AbilityUtils.getDefinedCards(hostCard, sa.getParam("ExcessDamage"), sa));
|
||||||
list.addAll(AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("ExcessDamage"), sa));
|
list.addAll(AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("ExcessDamage"), sa));
|
||||||
|
|
||||||
if (!list.isEmpty()) {
|
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 {
|
} 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;
|
boolean usedDamageMap = true;
|
||||||
CardDamageMap damageMap = sa.getDamageMap();
|
CardDamageMap damageMap = sa.getDamageMap();
|
||||||
CardDamageMap preventMap = sa.getPreventMap();
|
CardDamageMap preventMap = sa.getPreventMap();
|
||||||
GameEntityCounterTable counterTable = new GameEntityCounterTable();
|
GameEntityCounterTable counterTable = sa.getCounterTable();
|
||||||
|
|
||||||
if (damageMap == null) {
|
if (damageMap == null) {
|
||||||
// make a new damage map
|
// make a new damage map
|
||||||
damageMap = new CardDamageMap();
|
damageMap = new CardDamageMap();
|
||||||
preventMap = new CardDamageMap();
|
preventMap = new CardDamageMap();
|
||||||
|
counterTable = new GameEntityCounterTable();
|
||||||
usedDamageMap = false;
|
usedDamageMap = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,13 +96,13 @@ public class DamageEachEffect extends DamageBaseEffect {
|
|||||||
if (o instanceof Card) {
|
if (o instanceof Card) {
|
||||||
final Card c = (Card) o;
|
final Card c = (Card) o;
|
||||||
if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) {
|
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) {
|
} else if (o instanceof Player) {
|
||||||
final Player p = (Player) o;
|
final Player p = (Player) o;
|
||||||
if (!targeted || p.canBeTargetedBy(sa)) {
|
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 Card sourceLKI = game.getChangeZoneLKIInfo(source);
|
||||||
|
|
||||||
final int dmg = AbilityUtils.calculateAmount(source, "X", sa);
|
final int dmg = AbilityUtils.calculateAmount(source, "X", sa);
|
||||||
// System.out.println(source+" deals "+dmg+" damage to "+source);
|
damageMap.put(sourceLKI, source, dmg);
|
||||||
source.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sa.getParam("DefinedCards").equals("Remembered")) {
|
if (sa.getParam("DefinedCards").equals("Remembered")) {
|
||||||
@@ -125,8 +125,7 @@ public class DamageEachEffect extends DamageBaseEffect {
|
|||||||
for (final Object o : sa.getHostCard().getRemembered()) {
|
for (final Object o : sa.getHostCard().getRemembered()) {
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card) {
|
||||||
Card rememberedcard = (Card) o;
|
Card rememberedcard = (Card) o;
|
||||||
// System.out.println(source + " deals " + dmg + " damage to " + rememberedcard);
|
damageMap.put(sourceLKI, rememberedcard, dmg);
|
||||||
rememberedcard.addDamage(dmg, sourceLKI, damageMap, preventMap, counterTable, sa);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,15 +133,9 @@ public class DamageEachEffect extends DamageBaseEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!usedDamageMap) {
|
if (!usedDamageMap) {
|
||||||
preventMap.triggerPreventDamage(false);
|
game.getAction().dealDamage(false, damageMap, preventMap, counterTable, sa);
|
||||||
damageMap.triggerDamageDoneOnce(false, game, sa);
|
|
||||||
|
|
||||||
preventMap.clear();
|
|
||||||
damageMap.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
counterTable.triggerCountersPutAll(game);
|
|
||||||
|
|
||||||
replaceDying(sa);
|
replaceDying(sa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
|
import forge.game.GameEntityCounterTable;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
import forge.game.card.CardDamageMap;
|
import forge.game.card.CardDamageMap;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
@@ -18,16 +19,9 @@ public class DamageResolveEffect extends SpellAbilityEffect {
|
|||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
CardDamageMap damageMap = sa.getDamageMap();
|
CardDamageMap damageMap = sa.getDamageMap();
|
||||||
CardDamageMap preventMap = sa.getPreventMap();
|
CardDamageMap preventMap = sa.getPreventMap();
|
||||||
|
GameEntityCounterTable counterTable = sa.getCounterTable();
|
||||||
|
|
||||||
if (preventMap != null) {
|
sa.getHostCard().getGame().getAction().dealDamage(false, damageMap, preventMap, counterTable, sa);
|
||||||
preventMap.triggerPreventDamage(false);
|
|
||||||
preventMap.clear();
|
|
||||||
}
|
|
||||||
// non combat damage cause lifegain there
|
|
||||||
if (damageMap != null) {
|
|
||||||
damageMap.triggerDamageDoneOnce(false, sa.getHostCard().getGame(), sa);
|
|
||||||
damageMap.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
|
|||||||
@@ -146,12 +146,13 @@ public class FightEffect extends DamageBaseEffect {
|
|||||||
boolean usedDamageMap = true;
|
boolean usedDamageMap = true;
|
||||||
CardDamageMap damageMap = sa.getDamageMap();
|
CardDamageMap damageMap = sa.getDamageMap();
|
||||||
CardDamageMap preventMap = sa.getPreventMap();
|
CardDamageMap preventMap = sa.getPreventMap();
|
||||||
GameEntityCounterTable counterTable = new GameEntityCounterTable();
|
GameEntityCounterTable counterTable = sa.getCounterTable();
|
||||||
|
|
||||||
if (damageMap == null) {
|
if (damageMap == null) {
|
||||||
// make a new damage map
|
// make a new damage map
|
||||||
damageMap = new CardDamageMap();
|
damageMap = new CardDamageMap();
|
||||||
preventMap = new CardDamageMap();
|
preventMap = new CardDamageMap();
|
||||||
|
counterTable = new GameEntityCounterTable();
|
||||||
usedDamageMap = false;
|
usedDamageMap = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,22 +164,17 @@ public class FightEffect extends DamageBaseEffect {
|
|||||||
|
|
||||||
final int dmg1 = fightToughness ? fighterA.getNetToughness() : fighterA.getNetPower();
|
final int dmg1 = fightToughness ? fighterA.getNetToughness() : fighterA.getNetPower();
|
||||||
if (fighterA.equals(fighterB)) {
|
if (fighterA.equals(fighterB)) {
|
||||||
fighterA.addDamage(dmg1 * 2, fighterA, damageMap, preventMap, counterTable, sa);
|
damageMap.put(fighterA, fighterA, dmg1 * 2);
|
||||||
} else {
|
} else {
|
||||||
final int dmg2 = fightToughness ? fighterB.getNetToughness() : fighterB.getNetPower();
|
final int dmg2 = fightToughness ? fighterB.getNetToughness() : fighterB.getNetPower();
|
||||||
|
|
||||||
fighterB.addDamage(dmg1, fighterA, damageMap, preventMap, counterTable, sa);
|
damageMap.put(fighterA, fighterB, dmg1);
|
||||||
fighterA.addDamage(dmg2, fighterB, damageMap, preventMap, counterTable, sa);
|
damageMap.put(fighterB, fighterA, dmg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!usedDamageMap) {
|
if (!usedDamageMap) {
|
||||||
preventMap.triggerPreventDamage(false);
|
sa.getHostCard().getGame().getAction().dealDamage(false, damageMap, preventMap, counterTable, sa);
|
||||||
damageMap.triggerDamageDoneOnce(false, fighterA.getGame(), sa);
|
|
||||||
|
|
||||||
preventMap.clear();
|
|
||||||
damageMap.clear();
|
|
||||||
}
|
}
|
||||||
counterTable.triggerCountersPutAll(sa.getHostCard().getGame());
|
|
||||||
|
|
||||||
replaceDying(sa);
|
replaceDying(sa);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.google.common.collect.Lists;
|
|||||||
|
|
||||||
import forge.GameCommand;
|
import forge.GameCommand;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
|
import forge.game.GameEntityCounterTable;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
import forge.game.ability.SpellAbilityEffect;
|
||||||
@@ -86,6 +87,7 @@ public class RepeatEachEffect extends SpellAbilityEffect {
|
|||||||
if (sa.hasParam("DamageMap")) {
|
if (sa.hasParam("DamageMap")) {
|
||||||
sa.setDamageMap(new CardDamageMap());
|
sa.setDamageMap(new CardDamageMap());
|
||||||
sa.setPreventMap(new CardDamageMap());
|
sa.setPreventMap(new CardDamageMap());
|
||||||
|
sa.setCounterTable(new GameEntityCounterTable());
|
||||||
}
|
}
|
||||||
if (sa.hasParam("ChangeZoneTable")) {
|
if (sa.hasParam("ChangeZoneTable")) {
|
||||||
sa.setChangeZoneTable(new CardZoneTable());
|
sa.setChangeZoneTable(new CardZoneTable());
|
||||||
@@ -166,11 +168,7 @@ public class RepeatEachEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(sa.hasParam("DamageMap")) {
|
if(sa.hasParam("DamageMap")) {
|
||||||
sa.getPreventMap().triggerPreventDamage(false);
|
game.getAction().dealDamage(false, sa.getDamageMap(), sa.getPreventMap(), sa.getCounterTable(), sa);
|
||||||
sa.setPreventMap(null);
|
|
||||||
// non combat damage cause lifegain there
|
|
||||||
sa.getDamageMap().triggerDamageDoneOnce(false, game, sa);
|
|
||||||
sa.setDamageMap(null);
|
|
||||||
}
|
}
|
||||||
if (sa.hasParam("ChangeZoneTable")) {
|
if (sa.hasParam("ChangeZoneTable")) {
|
||||||
sa.getChangeZoneTable().triggerChangesZoneAll(game, sa);
|
sa.getChangeZoneTable().triggerChangesZoneAll(game, sa);
|
||||||
|
|||||||
@@ -60,17 +60,18 @@ public class ReplaceDamageEffect extends SpellAbilityEffect {
|
|||||||
// Set PreventedDamage SVar
|
// Set PreventedDamage SVar
|
||||||
card.setSVar("PreventedDamage", "Number$" + n);
|
card.setSVar("PreventedDamage", "Number$" + n);
|
||||||
|
|
||||||
|
Card sourceLKI = (Card) sa.getReplacingObject(AbilityKey.Source);
|
||||||
|
GameEntity target = (GameEntity) sa.getReplacingObject(AbilityKey.Target);
|
||||||
|
|
||||||
// Set prevent map entry
|
// Set prevent map entry
|
||||||
CardDamageMap preventMap = (CardDamageMap) originalParams.get(AbilityKey.PreventMap);
|
CardDamageMap preventMap = (CardDamageMap) originalParams.get(AbilityKey.PreventMap);
|
||||||
Card source = (Card) sa.getReplacingObject(AbilityKey.Source);
|
preventMap.put(sourceLKI, target, n);
|
||||||
GameEntity target = (GameEntity) sa.getReplacingObject(AbilityKey.Target);
|
|
||||||
preventMap.put(source, target, n);
|
|
||||||
|
|
||||||
// Following codes are commented out since DamagePrevented trigger is currently not used by any Card.
|
// Following codes are commented out since DamagePrevented trigger is currently not used by any Card.
|
||||||
// final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
// final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
// runParams.put(AbilityKey.DamageTarget, target);
|
// runParams.put(AbilityKey.DamageTarget, target);
|
||||||
// runParams.put(AbilityKey.DamageAmount, dmg);
|
// runParams.put(AbilityKey.DamageAmount, dmg);
|
||||||
// runParams.put(AbilityKey.DamageSource, source);
|
// runParams.put(AbilityKey.DamageSource, sourceLKI);
|
||||||
// runParams.put(AbilityKey.IsCombatDamage, originalParams.get(AbilityKey.IsCombat));
|
// runParams.put(AbilityKey.IsCombatDamage, originalParams.get(AbilityKey.IsCombat));
|
||||||
// game.getTriggerHandler().runTrigger(TriggerType.DamagePrevented, runParams, false);
|
// game.getTriggerHandler().runTrigger(TriggerType.DamagePrevented, runParams, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,18 +58,16 @@ public class ReplaceSplitDamageEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Card sourceLKI = (Card) sa.getReplacingObject(AbilityKey.Source);
|
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 damageMap = (CardDamageMap) originalParams.get(AbilityKey.DamageMap);
|
||||||
CardDamageMap preventMap = (CardDamageMap) originalParams.get(AbilityKey.PreventMap);
|
CardDamageMap preventMap = (CardDamageMap) originalParams.get(AbilityKey.PreventMap);
|
||||||
GameEntityCounterTable counterTable = (GameEntityCounterTable) originalParams.get(AbilityKey.CounterTable);
|
GameEntityCounterTable counterTable = (GameEntityCounterTable) originalParams.get(AbilityKey.CounterTable);
|
||||||
SpellAbility cause = (SpellAbility) originalParams.get(AbilityKey.Cause);
|
SpellAbility cause = (SpellAbility) originalParams.get(AbilityKey.Cause);
|
||||||
|
damageMap.put(sourceLKI, obj, n);
|
||||||
boolean isCombat = (Boolean) originalParams.get(AbilityKey.IsCombat);
|
obj.replaceDamage(n, sourceLKI, isCombat, damageMap, preventMap, counterTable, cause);
|
||||||
boolean noPrevention = (Boolean) originalParams.get(AbilityKey.NoPreventDamage);
|
|
||||||
|
|
||||||
GameEntity obj = (GameEntity) list.get(0);
|
|
||||||
|
|
||||||
obj.addDamage(n, sourceLKI, isCombat, noPrevention, damageMap, preventMap, counterTable, cause);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// no damage for original target anymore
|
// no damage for original target anymore
|
||||||
|
|||||||
@@ -5136,24 +5136,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
return total;
|
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) {
|
public final boolean canDamagePrevented(final boolean isCombat) {
|
||||||
CardCollection list = new CardCollection(getGame().getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES));
|
CardCollection list = new CardCollection(getGame().getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES));
|
||||||
list.add(this);
|
list.add(this);
|
||||||
@@ -5246,19 +5228,12 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
return restDamage;
|
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
|
* This function handles damage after replacement and prevention effects are
|
||||||
* applied.
|
* applied.
|
||||||
*/
|
*/
|
||||||
@Override
|
@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) {
|
if (damageIn <= 0) {
|
||||||
return 0; // Rule 119.8
|
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));
|
game.fireEvent(new GameEventCardDamaged(this, source, damageIn, damageType));
|
||||||
}
|
}
|
||||||
|
|
||||||
damageMap.put(source, this, damageIn);
|
|
||||||
|
|
||||||
if (excess > 0) {
|
if (excess > 0) {
|
||||||
// Run triggers
|
// Run triggers
|
||||||
runParams = AbilityKey.newMap();
|
runParams = AbilityKey.newMap();
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ import forge.game.Game;
|
|||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.GameObjectPredicates;
|
import forge.game.GameObjectPredicates;
|
||||||
import forge.game.ability.AbilityKey;
|
import forge.game.ability.AbilityKey;
|
||||||
import forge.game.keyword.Keyword;
|
|
||||||
import forge.game.spellability.SpellAbility;
|
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
|
|
||||||
public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
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
|
// Source -> Targets
|
||||||
for (Map.Entry<Card, Map<GameEntity, Integer>> e : rowMap().entrySet()) {
|
for (Map.Entry<Card, Map<GameEntity, Integer>> e : rowMap().entrySet()) {
|
||||||
final Card sourceLKI = e.getKey();
|
final Card sourceLKI = e.getKey();
|
||||||
@@ -65,10 +63,6 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
|
|||||||
runParams.put(AbilityKey.IsCombatDamage, isCombat);
|
runParams.put(AbilityKey.IsCombatDamage, isCombat);
|
||||||
|
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.DamageDealtOnce, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.DamageDealtOnce, runParams, false);
|
||||||
|
|
||||||
if (sourceLKI.hasKeyword(Keyword.LIFELINK)) {
|
|
||||||
sourceLKI.getController().gainLife(sum, sourceLKI, sa);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Targets -> Source
|
// Targets -> Source
|
||||||
|
|||||||
@@ -73,13 +73,10 @@ public class Combat {
|
|||||||
private final Multimap<GameEntity, AttackingBand> attackedByBands = Multimaps.synchronizedMultimap(ArrayListMultimap.create());
|
private final Multimap<GameEntity, AttackingBand> attackedByBands = Multimaps.synchronizedMultimap(ArrayListMultimap.create());
|
||||||
private final Multimap<AttackingBand, Card> blockedBands = 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> attackersOrderedForDamageAssignment = Maps.newHashMap();
|
||||||
private Map<Card, CardCollection> blockersOrderedForDamageAssignment = Maps.newHashMap();
|
private Map<Card, CardCollection> blockersOrderedForDamageAssignment = Maps.newHashMap();
|
||||||
private Map<GameEntity, CombatLki> lkiCache = Maps.newHashMap();
|
private Map<GameEntity, CombatLki> lkiCache = Maps.newHashMap();
|
||||||
private CardDamageMap dealtDamageTo = new CardDamageMap();
|
private CardDamageMap damageMap = new CardDamageMap();
|
||||||
private boolean dividedToPlayer = false;
|
|
||||||
|
|
||||||
// List holds creatures who have dealt 1st strike damage to disallow them deal damage on regular basis (unless they have double-strike KW)
|
// 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();
|
private CardCollection combatantsThatDealtFirstStrikeDamage = new CardCollection();
|
||||||
@@ -113,9 +110,6 @@ public class Combat {
|
|||||||
for (Entry<AttackingBand, Card> entry : combat.blockedBands.entries()) {
|
for (Entry<AttackingBand, Card> entry : combat.blockedBands.entries()) {
|
||||||
blockedBands.put(bandsMap.get(entry.getKey()), map.map(entry.getValue()));
|
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()) {
|
for (Entry<Card, CardCollection> entry : combat.attackersOrderedForDamageAssignment.entrySet()) {
|
||||||
attackersOrderedForDamageAssignment.put(map.map(entry.getKey()), map.mapCollection(entry.getValue()));
|
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()));
|
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...
|
// 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()) {
|
for (Table.Cell<Card, GameEntity, Integer> entry : combat.damageMap.cellSet()) {
|
||||||
dealtDamageTo.put(map.map(entry.getRowKey()), map.map(entry.getColumnKey()), entry.getValue());
|
damageMap.put(map.map(entry.getRowKey()), map.map(entry.getColumnKey()), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
attackConstraints = new AttackConstraints(this);
|
attackConstraints = new AttackConstraints(this);
|
||||||
@@ -170,7 +164,6 @@ public class Combat {
|
|||||||
attackableEntries.clear();
|
attackableEntries.clear();
|
||||||
attackedByBands.clear();
|
attackedByBands.clear();
|
||||||
blockedBands.clear();
|
blockedBands.clear();
|
||||||
defendingDamageMap.clear();
|
|
||||||
attackersOrderedForDamageAssignment.clear();
|
attackersOrderedForDamageAssignment.clear();
|
||||||
blockersOrderedForDamageAssignment.clear();
|
blockersOrderedForDamageAssignment.clear();
|
||||||
lkiCache.clear();
|
lkiCache.clear();
|
||||||
@@ -728,6 +721,7 @@ public class Combat {
|
|||||||
Map<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(blocker, attackers, damage, null, assigningPlayer != blocker.getController());
|
Map<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(blocker, attackers, damage, null, assigningPlayer != blocker.getController());
|
||||||
for (Entry<Card, Integer> dt : map.entrySet()) {
|
for (Entry<Card, Integer> dt : map.entrySet()) {
|
||||||
dt.getKey().addAssignedDamage(dt.getValue(), blocker);
|
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) {
|
private final boolean assignAttackersDamage(boolean firstStrikeDamage) {
|
||||||
// Assign damage by Attackers
|
// Assign damage by Attackers
|
||||||
defendingDamageMap.clear(); // this should really happen in deal damage
|
|
||||||
CardCollection orderedBlockers = null;
|
CardCollection orderedBlockers = null;
|
||||||
final CardCollection attackers = getAttackers();
|
final CardCollection attackers = getAttackers();
|
||||||
boolean assignedDamage = false;
|
boolean assignedDamage = false;
|
||||||
@@ -784,20 +777,19 @@ public class Combat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
assignedDamage = true;
|
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 the Attacker is unblocked, or it's a trampler and has 0 blockers, deal damage to defender
|
||||||
if (orderedBlockers == null || orderedBlockers.isEmpty()) {
|
if (orderedBlockers == null || orderedBlockers.isEmpty()) {
|
||||||
if (trampler || !band.isBlocked()) { // this is called after declare blockers, no worries 'bout nulls in isBlocked
|
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
|
} // No damage happens if blocked but no blockers left
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GameEntity defender = getDefenderByAttacker(band);
|
|
||||||
Player assigningPlayer = getAttackingPlayer();
|
Player assigningPlayer = getAttackingPlayer();
|
||||||
// Defensive Formation is very similar to Banding with Blockers
|
// Defensive Formation is very similar to Banding with Blockers
|
||||||
// It allows the defending player to assign damage instead of the attacking player
|
// It allows the defending player to assign damage instead of the attacking player
|
||||||
if (defender instanceof Card && divideCombatDamageAsChoose) {
|
if (defender instanceof Card && divideCombatDamageAsChoose) {
|
||||||
defender = getDefenderPlayerByAttacker(attacker);
|
defender = getDefenderPlayerByAttacker(attacker);
|
||||||
dividedToPlayer = true;
|
|
||||||
}
|
}
|
||||||
if (defender instanceof Player && defender.hasKeyword("You assign combat damage of each creature attacking you.")) {
|
if (defender instanceof Player && defender.hasKeyword("You assign combat damage of each creature attacking you.")) {
|
||||||
assigningPlayer = (Player)defender;
|
assigningPlayer = (Player)defender;
|
||||||
@@ -810,10 +802,15 @@ public class Combat {
|
|||||||
damageDealt, defender, divideCombatDamageAsChoose || getAttackingPlayer() != assigningPlayer);
|
damageDealt, defender, divideCombatDamageAsChoose || getAttackingPlayer() != assigningPlayer);
|
||||||
for (Entry<Card, Integer> dt : map.entrySet()) {
|
for (Entry<Card, Integer> dt : map.entrySet()) {
|
||||||
if (dt.getKey() == null) {
|
if (dt.getKey() == null) {
|
||||||
if (dt.getValue() > 0)
|
if (dt.getValue() > 0) {
|
||||||
addDefendingDamage(dt.getValue(), attacker);
|
if (defender instanceof Card) {
|
||||||
|
((Card) defender).addAssignedDamage(dt.getValue(), attacker);
|
||||||
|
}
|
||||||
|
damageMap.put(attacker, defender, dt.getValue());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
dt.getKey().addAssignedDamage(dt.getValue(), attacker);
|
dt.getKey().addAssignedDamage(dt.getValue(), attacker);
|
||||||
|
damageMap.put(attacker, dt.getKey(), dt.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // if !hasFirstStrike ...
|
} // if !hasFirstStrike ...
|
||||||
@@ -833,24 +830,6 @@ public class Combat {
|
|||||||
return !firstStrikeDamage && !combatantsThatDealtFirstStrikeDamage.contains(combatant);
|
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) {
|
public final boolean assignCombatDamage(boolean firstStrikeDamage) {
|
||||||
boolean assignedDamage = assignAttackersDamage(firstStrikeDamage);
|
boolean assignedDamage = assignAttackersDamage(firstStrikeDamage);
|
||||||
assignedDamage |= assignBlockersDamage(firstStrikeDamage);
|
assignedDamage |= assignBlockersDamage(firstStrikeDamage);
|
||||||
@@ -862,7 +841,7 @@ public class Combat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final CardDamageMap getDamageMap() {
|
public final CardDamageMap getDamageMap() {
|
||||||
return dealtDamageTo;
|
return damageMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dealAssignedDamage() {
|
public void dealAssignedDamage() {
|
||||||
@@ -872,49 +851,7 @@ public class Combat {
|
|||||||
CardDamageMap preventMap = new CardDamageMap();
|
CardDamageMap preventMap = new CardDamageMap();
|
||||||
GameEntityCounterTable counterTable = new GameEntityCounterTable();
|
GameEntityCounterTable counterTable = new GameEntityCounterTable();
|
||||||
|
|
||||||
// This function handles both Regular and First Strike combat assignment
|
game.getAction().dealDamage(true, damageMap, preventMap, counterTable, null);
|
||||||
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();
|
|
||||||
|
|
||||||
// copy last state again for dying replacement effects
|
// copy last state again for dying replacement effects
|
||||||
game.copyLastState();
|
game.copyLastState();
|
||||||
|
|||||||
@@ -71,15 +71,9 @@ public class CostDamage extends CostPart {
|
|||||||
CardDamageMap preventMap = new CardDamageMap();
|
CardDamageMap preventMap = new CardDamageMap();
|
||||||
GameEntityCounterTable table = new GameEntityCounterTable();
|
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;
|
return decision.c > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ import forge.game.ability.effects.DetachedCardEffect;
|
|||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardDamageMap;
|
|
||||||
import forge.game.card.CardFactoryUtil;
|
import forge.game.card.CardFactoryUtil;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
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
|
// This function handles damage after replacement and prevention effects are applied
|
||||||
@Override
|
@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) {
|
if (amount <= 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -703,9 +702,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
|
|
||||||
game.fireEvent(new GameEventPlayerDamaged(this, source, amount, isCombat, infect));
|
game.fireEvent(new GameEventPlayerDamaged(this, source, amount, isCombat, infect));
|
||||||
|
|
||||||
if (amount > 0) {
|
|
||||||
damageMap.put(source, this, amount);
|
|
||||||
}
|
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -282,18 +282,19 @@ public class ReplacementHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void putPreventMapEntry(final Map<AbilityKey, Object> runParams) {
|
private void putPreventMapEntry(final Map<AbilityKey, Object> runParams) {
|
||||||
// Set prevent map entry
|
Card sourceLKI = (Card) runParams.get(AbilityKey.DamageSource);
|
||||||
CardDamageMap preventMap = (CardDamageMap) runParams.get(AbilityKey.PreventMap);
|
|
||||||
Card source = (Card) runParams.get(AbilityKey.DamageSource);
|
|
||||||
GameEntity target = (GameEntity) runParams.get(AbilityKey.Affected);
|
GameEntity target = (GameEntity) runParams.get(AbilityKey.Affected);
|
||||||
Integer damage = (Integer) runParams.get(AbilityKey.DamageAmount);
|
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.
|
// Following codes are commented out since DamagePrevented trigger is currently not used by any Card.
|
||||||
// final Map<AbilityKey, Object> trigParams = AbilityKey.newMap();
|
// final Map<AbilityKey, Object> trigParams = AbilityKey.newMap();
|
||||||
// trigParams.put(AbilityKey.DamageTarget, target);
|
// trigParams.put(AbilityKey.DamageTarget, target);
|
||||||
// trigParams.put(AbilityKey.DamageAmount, damage);
|
// trigParams.put(AbilityKey.DamageAmount, damage);
|
||||||
// trigParams.put(AbilityKey.DamageSource, source);
|
// trigParams.put(AbilityKey.DamageSource, sourceLKI);
|
||||||
// trigParams.put(AbilityKey.IsCombatDamage, runParams.get(AbilityKey.IsCombat));
|
// trigParams.put(AbilityKey.IsCombatDamage, runParams.get(AbilityKey.IsCombat));
|
||||||
// game.getTriggerHandler().runTrigger(TriggerType.DamagePrevented, trigParams, false);
|
// game.getTriggerHandler().runTrigger(TriggerType.DamagePrevented, trigParams, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import forge.game.ForgeScript;
|
|||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.GameActionUtil;
|
import forge.game.GameActionUtil;
|
||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
|
import forge.game.GameEntityCounterTable;
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
import forge.game.IHasSVars;
|
import forge.game.IHasSVars;
|
||||||
import forge.game.IIdentifiable;
|
import forge.game.IIdentifiable;
|
||||||
@@ -186,6 +187,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
|
|
||||||
private CardDamageMap damageMap = null;
|
private CardDamageMap damageMap = null;
|
||||||
private CardDamageMap preventMap = null;
|
private CardDamageMap preventMap = null;
|
||||||
|
private GameEntityCounterTable counterTable = null;
|
||||||
private CardZoneTable changeZoneTable = null;
|
private CardZoneTable changeZoneTable = null;
|
||||||
|
|
||||||
public CardCollection getLastStateBattlefield() {
|
public CardCollection getLastStateBattlefield() {
|
||||||
@@ -1047,6 +1049,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
if (preventMap != null) {
|
if (preventMap != null) {
|
||||||
clone.preventMap = new CardDamageMap(preventMap);
|
clone.preventMap = new CardDamageMap(preventMap);
|
||||||
}
|
}
|
||||||
|
if (counterTable != null) {
|
||||||
|
clone.counterTable = new GameEntityCounterTable(counterTable);
|
||||||
|
}
|
||||||
if (changeZoneTable != null) {
|
if (changeZoneTable != null) {
|
||||||
clone.changeZoneTable = new CardZoneTable();
|
clone.changeZoneTable = new CardZoneTable();
|
||||||
clone.changeZoneTable.putAll(changeZoneTable);
|
clone.changeZoneTable.putAll(changeZoneTable);
|
||||||
@@ -2231,6 +2236,15 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GameEntityCounterTable getCounterTable() {
|
||||||
|
if (counterTable != null) {
|
||||||
|
return counterTable;
|
||||||
|
} else if (getParent() != null) {
|
||||||
|
return getParent().getCounterTable();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public CardZoneTable getChangeZoneTable() {
|
public CardZoneTable getChangeZoneTable() {
|
||||||
if (changeZoneTable != null) {
|
if (changeZoneTable != null) {
|
||||||
return changeZoneTable;
|
return changeZoneTable;
|
||||||
@@ -2246,6 +2260,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
public void setPreventMap(final CardDamageMap map) {
|
public void setPreventMap(final CardDamageMap map) {
|
||||||
preventMap = map;
|
preventMap = map;
|
||||||
}
|
}
|
||||||
|
public void setCounterTable(final GameEntityCounterTable table) {
|
||||||
|
counterTable = table;
|
||||||
|
}
|
||||||
public void setChangeZoneTable(final CardZoneTable table) {
|
public void setChangeZoneTable(final CardZoneTable table) {
|
||||||
changeZoneTable = table;
|
changeZoneTable = table;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
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:CoinFlip:DB$ FlipACoin | LoseSubAbility$ CreateEffect
|
||||||
SVar:CreateEffect:DB$ Effect | Name$ Goblin Psychopath Effect | ReplacementEffects$ EventDamageDone
|
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:DamageYou:DB$ ReplaceEffect | VarName$ Affected | VarValue$ You | VarType$ Player | SubAbility$ ExileEffect
|
||||||
SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
||||||
SVar:PsychoX:ReplaceCount$DamageAmount
|
SVar:PsychoX:ReplaceCount$DamageAmount
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:2 W W
|
|||||||
Types:Enchantment
|
Types:Enchantment
|
||||||
K:ETBReplacement:Other:ChooseColor
|
K:ETBReplacement:Other:ChooseColor
|
||||||
SVar:ChooseColor:DB$ ChooseColor | Defined$ You | AILogic$ MostProminentInHumanDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a color.
|
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
|
SVar:HarshDmg:DB$ ReplaceEffect | VarName$ Affected | VarValue$ ReplacedSourceController | VarType$ Player
|
||||||
AI:RemoveDeck:Random
|
AI:RemoveDeck:Random
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/harsh_judgment.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/harsh_judgment.jpg
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ ManaCost:2 W
|
|||||||
Types:Creature Human Rebel
|
Types:Creature Human Rebel
|
||||||
PT:1/3
|
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.
|
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:DmgSelf:DB$ ReplaceEffect | VarName$ Affected | VarValue$ Remembered | VarType$ Card | SubAbility$ ExileEffect
|
||||||
SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
SVar:ExileEffect:DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile
|
||||||
AI:RemoveDeck:All
|
AI:RemoveDeck:All
|
||||||
|
|||||||
Reference in New Issue
Block a user