mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Big Damage Rewrite Part 2: now use CombatDamageMap everywhere
This commit is contained in:
@@ -20,6 +20,7 @@ package forge.game;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardDamageMap;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.event.GameEventCardAttachment;
|
||||
import forge.game.event.GameEventCardAttachment.AttachMethod;
|
||||
@@ -55,22 +56,51 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||
getView().updateName(this);
|
||||
}
|
||||
|
||||
public int addDamage(final int damage, final Card source) {
|
||||
public int addDamage(final int damage, final Card source, final CardDamageMap damageMap) {
|
||||
int damageToDo = damage;
|
||||
|
||||
damageToDo = replaceDamage(damageToDo, source, false);
|
||||
damageToDo = replaceDamage(damageToDo, source, false, true, damageMap);
|
||||
damageToDo = preventDamage(damageToDo, source, false);
|
||||
|
||||
return addDamageAfterPrevention(damageToDo, source, false);
|
||||
return addDamageAfterPrevention(damageToDo, source, false, damageMap);
|
||||
}
|
||||
|
||||
public int addDamageWithoutPrevention(final int damage, final Card source) {
|
||||
int damageToDo = replaceDamage(damage, source, false);
|
||||
return addDamageAfterPrevention(damageToDo, source, false);
|
||||
public int addDamageWithoutPrevention(final int damage, final Card source, final CardDamageMap damageMap) {
|
||||
int damageToDo = replaceDamage(damage, source, false, false, damageMap);
|
||||
return addDamageAfterPrevention(damageToDo, source, false, damageMap);
|
||||
}
|
||||
|
||||
public int replaceDamage(final int damage, final Card source, final boolean isCombat, final boolean prevention, final CardDamageMap damageMap) {
|
||||
// Replacement effects
|
||||
final Map<String, Object> repParams = Maps.newHashMap();
|
||||
repParams.put("Event", "DamageDone");
|
||||
repParams.put("Affected", this);
|
||||
repParams.put("DamageSource", source);
|
||||
repParams.put("DamageAmount", damage);
|
||||
repParams.put("IsCombat", isCombat);
|
||||
|
||||
switch (getGame().getReplacementHandler().run(repParams)) {
|
||||
case NotReplaced:
|
||||
return damage;
|
||||
case Updated:
|
||||
int newDamage = (int) repParams.get("DamageAmount");
|
||||
GameEntity newTarget = (GameEntity)repParams.get("Affected");
|
||||
// check if this is still the affected card or player
|
||||
if (this.equals(newTarget)) {
|
||||
return newDamage;
|
||||
} else {
|
||||
if (prevention) {
|
||||
newDamage = newTarget.preventDamage(newDamage, source, isCombat);
|
||||
}
|
||||
newTarget.addDamageAfterPrevention(newDamage, source, isCombat, damageMap);
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// This function handles damage after replacement and prevention effects are applied
|
||||
public abstract int addDamageAfterPrevention(final int damage, final Card source, final boolean isCombat);
|
||||
public abstract int addDamageAfterPrevention(final int damage, final Card source, final boolean isCombat, CardDamageMap damageMap);
|
||||
|
||||
// This should be also usable by the AI to forecast an effect (so it must
|
||||
// not change the game state)
|
||||
@@ -80,8 +110,6 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||
// not change the game state)
|
||||
public abstract int staticReplaceDamage(final int damage, final Card source, final boolean isCombat);
|
||||
|
||||
public abstract int replaceDamage(final int damage, final Card source, final boolean isCombat);
|
||||
|
||||
public abstract int preventDamage(final int damage, final Card source, final boolean isCombat);
|
||||
|
||||
public int getPreventNextDamage() {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardDamageMap;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -26,19 +28,20 @@ public class DamageAllEffect extends SpellAbilityEffect {
|
||||
final String damage = sa.getParam("NumDmg");
|
||||
final int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
||||
|
||||
final String definedStr = sa.getParam("DamageSource");
|
||||
final List<Card> definedSources = AbilityUtils.getDefinedCards(sa.getHostCard(), definedStr, sa);
|
||||
|
||||
final List<Card> definedSources = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DamageSource"), sa);
|
||||
final Card source = definedSources.get(0);
|
||||
|
||||
if (source != sa.getHostCard()) {
|
||||
sb.append(source.toString()).append(" deals");
|
||||
if (!definedSources.isEmpty() && definedSources.get(0) != sa.getHostCard()) {
|
||||
sb.append(definedSources.get(0).toString()).append(" deals");
|
||||
} else if ("ParentTarget".equals(definedStr)){
|
||||
sb.append("Target creature deals");
|
||||
} else {
|
||||
sb.append("Deals");
|
||||
}
|
||||
|
||||
sb.append(" ").append(dmg).append(" damage to ").append(desc);
|
||||
|
||||
return sb.toString();
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,28 +80,30 @@ public class DamageAllEffect extends SpellAbilityEffect {
|
||||
|
||||
list = AbilityUtils.filterListByType(list, sa.getParam("ValidCards"), sa);
|
||||
|
||||
int damageSum = 0;
|
||||
CardDamageMap damageMap = new CardDamageMap();
|
||||
|
||||
for (final Card c : list) {
|
||||
int cardDamage = c.addDamage(dmg, sourceLKI);
|
||||
damageSum += cardDamage;
|
||||
if (cardDamage > 0 && rememberCard) {
|
||||
source.addRemembered(c);
|
||||
}
|
||||
c.addDamage(dmg, sourceLKI, damageMap);
|
||||
}
|
||||
|
||||
if (!players.equals("")) {
|
||||
final List<Player> playerList = AbilityUtils.getDefinedPlayers(card, players, sa);
|
||||
for (final Player p : playerList) {
|
||||
int playerDamage = p.addDamage(dmg, sourceLKI);
|
||||
damageSum += playerDamage;
|
||||
if (playerDamage > 0 && rememberPlayer) {
|
||||
source.addRemembered(p);
|
||||
p.addDamage(dmg, sourceLKI, damageMap);
|
||||
}
|
||||
}
|
||||
|
||||
// do Remember there
|
||||
if (rememberCard || rememberPlayer) {
|
||||
for (GameEntity e : damageMap.row(sourceLKI).keySet()) {
|
||||
if (e instanceof Card && rememberCard) {
|
||||
source.addRemembered(e);
|
||||
} else if (e instanceof Player && rememberPlayer) {
|
||||
source.addRemembered(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (damageSum > 0 && sourceLKI.hasKeyword("Lifelink")) {
|
||||
source.getController().gainLife(damageSum, sourceLKI);
|
||||
}
|
||||
damageMap.dealLifelinkDamage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardDamageMap;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.util.Lang;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -35,10 +36,9 @@ public class DamageDealEffect extends SpellAbilityEffect {
|
||||
return "";
|
||||
|
||||
final List<Card> definedSources = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DamageSource"), sa);
|
||||
Card source = definedSources.isEmpty() ? new Card(-1, sa.getHostCard().getGame()) : definedSources.get(0);
|
||||
|
||||
if (source != sa.getHostCard()) {
|
||||
sb.append(source.toString()).append(" deals");
|
||||
if (!definedSources.isEmpty() && definedSources.get(0) != sa.getHostCard()) {
|
||||
sb.append(definedSources.get(0).toString()).append(" deals");
|
||||
} else {
|
||||
sb.append("Deals");
|
||||
}
|
||||
@@ -68,6 +68,9 @@ public class DamageDealEffect extends SpellAbilityEffect {
|
||||
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
final Card hostCard = sa.getHostCard();
|
||||
final Game game = hostCard.getGame();
|
||||
|
||||
final String damage = sa.getParam("NumDmg");
|
||||
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
|
||||
|
||||
@@ -120,7 +123,8 @@ public class DamageDealEffect extends SpellAbilityEffect {
|
||||
final Card source = definedSources.get(0);
|
||||
final Card sourceLKI = sa.getHostCard().getGame().getChangeZoneLKIInfo(definedSources.get(0));
|
||||
|
||||
int damageSum = 0;
|
||||
// make a new damage map, combat damage will be applied later into combat map
|
||||
CardDamageMap damageMap = new CardDamageMap();
|
||||
|
||||
if (divideOnResolution) {
|
||||
// Dividing Damage up to multiple targets using combat damage box
|
||||
@@ -141,12 +145,15 @@ public class DamageDealEffect extends SpellAbilityEffect {
|
||||
Player assigningPlayer = players.get(0);
|
||||
Map<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(sourceLKI, assigneeCards, dmg, null, true);
|
||||
for (Entry<Card, Integer> dt : map.entrySet()) {
|
||||
damageSum += dt.getKey().addDamage(dt.getValue(), sourceLKI);
|
||||
dt.getKey().addDamage(dt.getValue(), sourceLKI, damageMap);
|
||||
}
|
||||
|
||||
// non combat damage cause lifegain there
|
||||
if (!combatDmg && damageSum > 0 && sourceLKI.hasKeyword("Lifelink")) {
|
||||
sourceLKI.getController().gainLife(damageSum, sourceLKI);
|
||||
// transport combat damage back into combat damage map
|
||||
if (combatDmg) {
|
||||
game.getCombat().getDamageMap().putAll(damageMap);
|
||||
} else {
|
||||
// non combat damage cause lifegain there
|
||||
damageMap.dealLifelinkDamage();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -162,55 +169,39 @@ public class DamageDealEffect extends SpellAbilityEffect {
|
||||
c.clearAssignedDamage();
|
||||
}
|
||||
else if (noPrevention) {
|
||||
int damagePrev = c.addDamageWithoutPrevention(dmg, sourceLKI);
|
||||
damageSum += damagePrev;
|
||||
if (damagePrev > 0 && remember) {
|
||||
source.addRemembered(c);
|
||||
}
|
||||
c.addDamageWithoutPrevention(dmg, sourceLKI, damageMap);
|
||||
} else if (combatDmg) {
|
||||
HashMap<Card, Integer> combatmap = Maps.newHashMap();
|
||||
Map<Card, Integer> combatmap = Maps.newHashMap();
|
||||
combatmap.put(sourceLKI, dmg);
|
||||
c.addCombatDamage(combatmap);
|
||||
if (remember) {
|
||||
source.addRemembered(c);
|
||||
}
|
||||
c.addCombatDamage(combatmap, damageMap);
|
||||
} else {
|
||||
int damageDealt = c.addDamage(dmg, sourceLKI);
|
||||
damageSum += damageDealt;
|
||||
if (damageDealt > 0 && remember) {
|
||||
source.addRemembered(c);
|
||||
}
|
||||
c.addDamage(dmg, sourceLKI, damageMap);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (o instanceof Player) {
|
||||
final Player p = (Player) o;
|
||||
if (!targeted || p.canBeTargetedBy(sa)) {
|
||||
if (noPrevention) {
|
||||
int damagePrev = p.addDamageWithoutPrevention(dmg, sourceLKI);
|
||||
damageSum += damagePrev;
|
||||
if (damagePrev > 0 && remember) {
|
||||
source.addRemembered(p);
|
||||
}
|
||||
p.addDamageWithoutPrevention(dmg, sourceLKI, damageMap);
|
||||
} else if (combatDmg) {
|
||||
p.addCombatDamage(dmg, sourceLKI);
|
||||
if (remember) {
|
||||
source.addRemembered(p);
|
||||
}
|
||||
p.addCombatDamage(dmg, sourceLKI, damageMap);
|
||||
} else {
|
||||
int damageDealt = p.addDamage(dmg, sourceLKI);
|
||||
damageSum += damageDealt;
|
||||
if (damageDealt > 0 && remember) {
|
||||
source.addRemembered(p);
|
||||
}
|
||||
p.addDamage(dmg, sourceLKI, damageMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// non combat damage cause lifegain there
|
||||
if (!combatDmg && damageSum > 0 && sourceLKI.hasKeyword("Lifelink")) {
|
||||
sourceLKI.getController().gainLife(damageSum, sourceLKI);
|
||||
if (remember) {
|
||||
source.addRemembered(damageMap.row(sourceLKI).keySet());
|
||||
}
|
||||
|
||||
// transport combat damage back into combat damage map
|
||||
if (combatDmg) {
|
||||
game.getCombat().getDamageMap().putAll(damageMap);
|
||||
} else {
|
||||
// non combat damage cause lifegain there
|
||||
damageMap.dealLifelinkDamage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import forge.game.GameObject;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardDamageMap;
|
||||
import forge.game.card.CardFactoryUtil;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.player.Player;
|
||||
@@ -69,29 +70,27 @@ public class DamageEachEffect extends SpellAbilityEffect {
|
||||
final List<GameObject> tgts = getTargets(sa, "DefinedPlayers");
|
||||
|
||||
final boolean targeted = (sa.usesTargeting());
|
||||
CardDamageMap damageMap = new CardDamageMap();
|
||||
|
||||
for (final Object o : tgts) {
|
||||
for (final Card source : sources) {
|
||||
final Card sourceLKI = source.getGame().getChangeZoneLKIInfo(source);
|
||||
|
||||
final int dmg = CardFactoryUtil.xCount(source, sa.getSVar("X"));
|
||||
int damageSum = 0;
|
||||
|
||||
// System.out.println(source+" deals "+dmg+" damage to "+o.toString());
|
||||
if (o instanceof Card) {
|
||||
final Card c = (Card) o;
|
||||
if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) {
|
||||
damageSum += c.addDamage(dmg, sourceLKI);
|
||||
c.addDamage(dmg, sourceLKI, damageMap);
|
||||
}
|
||||
|
||||
} else if (o instanceof Player) {
|
||||
final Player p = (Player) o;
|
||||
if (!targeted || p.canBeTargetedBy(sa)) {
|
||||
damageSum += p.addDamage(dmg, sourceLKI);
|
||||
p.addDamage(dmg, sourceLKI, damageMap);
|
||||
}
|
||||
}
|
||||
if (damageSum > 0 && sourceLKI.hasKeyword("Lifelink")) {
|
||||
sourceLKI.getController().gainLife(damageSum, sourceLKI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,10 +101,7 @@ public class DamageEachEffect extends SpellAbilityEffect {
|
||||
|
||||
final int dmg = CardFactoryUtil.xCount(source, card.getSVar("X"));
|
||||
// System.out.println(source+" deals "+dmg+" damage to "+source);
|
||||
int damage = source.addDamage(dmg, sourceLKI);
|
||||
if (damage > 0 && sourceLKI.hasKeyword("Lifelink")) {
|
||||
sourceLKI.getController().gainLife(damage, sourceLKI);
|
||||
}
|
||||
source.addDamage(dmg, sourceLKI, damageMap);
|
||||
}
|
||||
}
|
||||
if (sa.getParam("DefinedCards").equals("Remembered")) {
|
||||
@@ -113,20 +109,17 @@ public class DamageEachEffect extends SpellAbilityEffect {
|
||||
final int dmg = CardFactoryUtil.xCount(source, card.getSVar("X"));
|
||||
final Card sourceLKI = source.getGame().getChangeZoneLKIInfo(source);
|
||||
|
||||
Card rememberedcard;
|
||||
int damageSum = 0;
|
||||
for (final Object o : sa.getHostCard().getRemembered()) {
|
||||
if (o instanceof Card) {
|
||||
rememberedcard = (Card) o;
|
||||
Card rememberedcard = (Card) o;
|
||||
// System.out.println(source + " deals " + dmg + " damage to " + rememberedcard);
|
||||
damageSum += rememberedcard.addDamage(dmg, sourceLKI);
|
||||
rememberedcard.addDamage(dmg, sourceLKI, damageMap);
|
||||
}
|
||||
}
|
||||
if (damageSum > 0 && sourceLKI.hasKeyword("Lifelink")) {
|
||||
sourceLKI.getController().gainLife(damageSum, sourceLKI);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
damageMap.dealLifelinkDamage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,12 @@ import forge.game.Game;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.SpellAbilityEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardDamageMap;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.trigger.TriggerType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FightEffect extends SpellAbilityEffect {
|
||||
|
||||
@@ -52,14 +53,17 @@ public class FightEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
boolean fightToughness = sa.hasParam("FightWithToughness");
|
||||
CardDamageMap damageMap = new CardDamageMap();
|
||||
|
||||
dealDamage(fighters.get(0), fighters.get(1), fightToughness);
|
||||
dealDamage(fighters.get(1), fighters.get(0), fightToughness);
|
||||
dealDamage(fighters.get(0), fighters.get(1), fightToughness, damageMap);
|
||||
dealDamage(fighters.get(1), fighters.get(0), fightToughness, damageMap);
|
||||
|
||||
damageMap.dealLifelinkDamage();
|
||||
|
||||
for (Card c : fighters) {
|
||||
final HashMap<String, Object> runParams = Maps.newHashMap();
|
||||
runParams.put("Fighter", c);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.Fight, runParams, false);
|
||||
final Map<String, Object> runParams = Maps.newHashMap();
|
||||
runParams.put("Fighter", c);
|
||||
game.getTriggerHandler().runTrigger(TriggerType.Fight, runParams, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,14 +110,10 @@ public class FightEffect extends SpellAbilityEffect {
|
||||
return fighterList;
|
||||
}
|
||||
|
||||
private void dealDamage(Card source, Card target, boolean fightToughness) {
|
||||
private void dealDamage(Card source, Card target, boolean fightToughness, CardDamageMap damageMap) {
|
||||
final int dmg = fightToughness ? source.getNetToughness() : source.getNetPower();
|
||||
|
||||
int damageDealt = target.addDamage(dmg, source);
|
||||
|
||||
if (damageDealt > 0 && source.hasKeyword("Lifelink")) {
|
||||
source.getController().gainLife(damageDealt, source);
|
||||
}
|
||||
target.addDamage(dmg, source, damageMap);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5844,12 +5844,12 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return total;
|
||||
}
|
||||
|
||||
public final void addCombatDamage(final Map<Card, Integer> map) {
|
||||
public final void addCombatDamage(final Map<Card, Integer> map, CardDamageMap damageMap) {
|
||||
for (final Entry<Card, Integer> entry : map.entrySet()) {
|
||||
final Card source = entry.getKey();
|
||||
int damageToAdd = entry.getValue();
|
||||
|
||||
damageToAdd = replaceDamage(damageToAdd, source, true);
|
||||
damageToAdd = replaceDamage(damageToAdd, source, true, true, damageMap);
|
||||
damageToAdd = preventDamage(damageToAdd, source, true);
|
||||
|
||||
if (damageToAdd > 0) {
|
||||
@@ -5859,7 +5859,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
}
|
||||
|
||||
if (isInPlay()) {
|
||||
addDamage(map);
|
||||
addDamage(map, damageMap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6142,33 +6142,10 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
return restDamage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int replaceDamage(final int damageIn, final Card source, final boolean isCombat) {
|
||||
// Replacement effects
|
||||
final Map<String, Object> repParams = Maps.newHashMap();
|
||||
repParams.put("Event", "DamageDone");
|
||||
repParams.put("Affected", this);
|
||||
repParams.put("DamageSource", source);
|
||||
repParams.put("DamageAmount", damageIn);
|
||||
repParams.put("IsCombat", isCombat);
|
||||
|
||||
switch (getGame().getReplacementHandler().run(repParams)) {
|
||||
case NotReplaced:
|
||||
return damageIn;
|
||||
case Updated:
|
||||
// check if this is still the affected card
|
||||
if (this.equals(repParams.get("Affected"))) {
|
||||
return (int) repParams.get("DamageAmount");
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public final void addDamage(final Map<Card, Integer> sourcesMap) {
|
||||
public final void addDamage(final Map<Card, Integer> sourcesMap, CardDamageMap damageMap) {
|
||||
for (final Entry<Card, Integer> entry : sourcesMap.entrySet()) {
|
||||
// damage prevention is already checked!
|
||||
addDamageAfterPrevention(entry.getValue(), entry.getKey(), true);
|
||||
addDamageAfterPrevention(entry.getValue(), entry.getKey(), true, damageMap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6177,7 +6154,7 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
* applied.
|
||||
*/
|
||||
@Override
|
||||
public final int addDamageAfterPrevention(final int damageIn, final Card source, final boolean isCombat) {
|
||||
public final int addDamageAfterPrevention(final int damageIn, final Card source, final boolean isCombat, CardDamageMap damageMap) {
|
||||
|
||||
if (damageIn == 0) {
|
||||
return 0; // Rule 119.8
|
||||
@@ -6185,9 +6162,6 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
|
||||
addReceivedDamageFromThisTurn(source, damageIn);
|
||||
source.addDealtDamageToThisTurn(this, damageIn);
|
||||
if (isCombat) {
|
||||
game.getCombat().addDealtDamageTo(source, this, damageIn);
|
||||
}
|
||||
|
||||
// Run triggers
|
||||
final Map<String, Object> runParams = Maps.newTreeMap();
|
||||
@@ -6231,6 +6205,11 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
// Play the Damage sound
|
||||
game.fireEvent(new GameEventCardDamaged(this, source, damageIn, damageType));
|
||||
}
|
||||
|
||||
if (damageIn > 0) {
|
||||
damageMap.put(source, this, damageIn);
|
||||
}
|
||||
|
||||
return damageIn;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
@@ -40,6 +39,7 @@ import forge.game.GameObjectMap;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardDamageMap;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.util.collect.FCollection;
|
||||
@@ -68,7 +68,7 @@ public class Combat {
|
||||
private Map<Card, CardCollection> attackersOrderedForDamageAssignment = Maps.newHashMap();
|
||||
private Map<Card, CardCollection> blockersOrderedForDamageAssignment = Maps.newHashMap();
|
||||
private Map<GameEntity, CombatLki> lkiCache = Maps.newHashMap();
|
||||
private Table<Card, GameEntity, Integer> dealtDamageTo = HashBasedTable.create();
|
||||
private CardDamageMap dealtDamageTo = 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();
|
||||
@@ -716,45 +716,21 @@ public class Combat {
|
||||
return assignedDamage;
|
||||
}
|
||||
|
||||
public final void addDealtDamageTo(final Card source, final GameEntity ge, final int dmg) {
|
||||
int old = dealtDamageTo.contains(source, ge) ? dealtDamageTo.get(source, ge) : 0;
|
||||
dealtDamageTo.put(source, ge, dmg + old);
|
||||
public final CardDamageMap getDamageMap() {
|
||||
return dealtDamageTo;
|
||||
}
|
||||
|
||||
public void dealAssignedDamage() {
|
||||
playerWhoAttacks.getGame().copyLastState();
|
||||
|
||||
// This function handles both Regular and First Strike combat assignment
|
||||
final Map<Card, Integer> defMap = defendingDamageMap;
|
||||
final Map<GameEntity, CardCollection> wasDamaged = Maps.newHashMap();
|
||||
|
||||
for (final Entry<Card, Integer> entry : defMap.entrySet()) {
|
||||
for (final Entry<Card, Integer> entry : defendingDamageMap.entrySet()) {
|
||||
GameEntity defender = getDefenderByAttacker(entry.getKey());
|
||||
if (defender instanceof Player) { // player
|
||||
int dmg = ((Player) defender).addCombatDamage(entry.getValue(), entry.getKey());
|
||||
if (dmg > 0) {
|
||||
if (wasDamaged.containsKey(defender)) {
|
||||
wasDamaged.get(defender).add(entry.getKey());
|
||||
} else {
|
||||
CardCollection l = new CardCollection();
|
||||
l.add(entry.getKey());
|
||||
wasDamaged.put(defender, l);
|
||||
}
|
||||
this.addDealtDamageTo(entry.getKey(), defender, entry.getValue());
|
||||
}
|
||||
((Player) defender).addCombatDamage(entry.getValue(), entry.getKey(), dealtDamageTo);
|
||||
}
|
||||
else if (defender instanceof Card) { // planeswalker
|
||||
int dmg = ((Card) defender).getController().addCombatDamage(entry.getValue(), entry.getKey());
|
||||
if (dmg > 0) {
|
||||
if (wasDamaged.containsKey(defender)) {
|
||||
wasDamaged.get(defender).add(entry.getKey());
|
||||
}
|
||||
else {
|
||||
CardCollection l = new CardCollection();
|
||||
l.add(entry.getKey());
|
||||
wasDamaged.put(defender, l);
|
||||
}
|
||||
}
|
||||
((Card) defender).getController().addCombatDamage(entry.getValue(), entry.getKey(), dealtDamageTo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -765,41 +741,39 @@ public class Combat {
|
||||
combatants.addAll(getAllBlockers());
|
||||
combatants.addAll(getDefendingPlaneswalkers());
|
||||
|
||||
Card c;
|
||||
for (int i = 0; i < combatants.size(); i++) {
|
||||
c = combatants.get(i);
|
||||
|
||||
for (final Card c : combatants) {
|
||||
// if no assigned damage to resolve, move to next
|
||||
if (c.getTotalAssignedDamage() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
c.addCombatDamage(c.getAssignedDamageMap());
|
||||
c.addCombatDamage(c.getAssignedDamageMap(), dealtDamageTo);
|
||||
c.clearAssignedDamage();
|
||||
}
|
||||
|
||||
// Run triggers
|
||||
for (final GameEntity ge : wasDamaged.keySet()) {
|
||||
for (final GameEntity ge : dealtDamageTo.columnKeySet()) {
|
||||
final Map<String, Object> runParams = Maps.newHashMap();
|
||||
runParams.put("DamageSources", wasDamaged.get(ge));
|
||||
runParams.put("DamageSources", dealtDamageTo.column(ge).keySet());
|
||||
runParams.put("DamageTarget", ge);
|
||||
ge.getGame().getTriggerHandler().runTrigger(TriggerType.CombatDamageDoneOnce, runParams, false);
|
||||
}
|
||||
// This was deeper before, but that resulted in the stack entry acting like before.
|
||||
|
||||
// LifeLink for Combat Damage at this place
|
||||
dealtDamageTo.dealLifelinkDamage();
|
||||
|
||||
// when ... deals combat damage to one or more
|
||||
for (final Card damageSource : dealtDamageTo.rowKeySet()) {
|
||||
final Map<String, Object> runParams = Maps.newHashMap();
|
||||
Map<GameEntity, Integer> row = dealtDamageTo.row(damageSource);
|
||||
|
||||
// TODO find better way to get the sum
|
||||
int dealtDamage = 0;
|
||||
for (Map.Entry<GameEntity, Integer> e : row.entrySet()) {
|
||||
dealtDamage += e.getValue();
|
||||
}
|
||||
// LifeLink for Combat Damage at this place
|
||||
if (dealtDamage > 0 && damageSource.hasKeyword("Lifelink")) {
|
||||
damageSource.getController().gainLife(dealtDamage, damageSource);
|
||||
for (Integer i : row.values()) {
|
||||
dealtDamage += i;
|
||||
}
|
||||
|
||||
runParams.put("DamageSource", damageSource);
|
||||
runParams.put("DamageTargets", row.keySet());
|
||||
runParams.put("DamageAmount", dealtDamage);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package forge.game.cost;
|
||||
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardDamageMap;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
@@ -60,10 +61,12 @@ public class CostDamage extends CostPart {
|
||||
@Override
|
||||
public boolean payAsDecided(Player payer, PaymentDecision decision, SpellAbility sa) {
|
||||
final Card source = sa.getHostCard();
|
||||
int dmg = payer.addDamage(decision.c, source);
|
||||
if (dmg > 0 && source.hasKeyword("Lifelink")) {
|
||||
source.getController().gainLife(dmg, source);
|
||||
}
|
||||
CardDamageMap damageMap = new CardDamageMap();
|
||||
|
||||
payer.addDamage(decision.c, source, damageMap);
|
||||
|
||||
damageMap.dealLifelinkDamage();
|
||||
|
||||
return decision.c > 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -534,15 +534,12 @@ 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) {
|
||||
public final int addDamageAfterPrevention(final int amount, final Card source, final boolean isCombat, CardDamageMap damageMap) {
|
||||
if (amount <= 0) {
|
||||
return 0;
|
||||
}
|
||||
//String additionalLog = "";
|
||||
source.addDealtDamageToPlayerThisTurn(getName(), amount);
|
||||
if (isCombat) {
|
||||
game.getCombat().addDealtDamageTo(source, this, amount);
|
||||
}
|
||||
|
||||
boolean infect = source.hasKeyword("Infect")
|
||||
|| hasKeyword("All damage is dealt to you as though its source had infect.");
|
||||
@@ -553,8 +550,10 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
else {
|
||||
// Worship does not reduce the damage dealt but changes the effect
|
||||
// of the damage
|
||||
if (hasKeyword("Damage that would reduce your life total to less than 1 reduces it to 1 instead.")
|
||||
&& life <= amount) {
|
||||
if (hasKeyword("DamageLifeThreshold:7") && life - 7 <= amount) {
|
||||
// only active if life is over 7, so no bad thing
|
||||
loseLife(Math.min(amount, life - 7));
|
||||
} else if (hasKeyword("DamageLifeThreshold:1") && life <= amount) {
|
||||
loseLife(Math.min(amount, life - 1));
|
||||
}
|
||||
else {
|
||||
@@ -589,6 +588,10 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
game.getTriggerHandler().runTrigger(TriggerType.DamageDone, runParams, false);
|
||||
|
||||
game.fireEvent(new GameEventPlayerDamaged(this, source, amount, isCombat, infect));
|
||||
|
||||
if (amount > 0) {
|
||||
damageMap.put(source, this, amount);
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
|
||||
@@ -648,7 +651,9 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
public final int staticReplaceDamage(final int damage, final Card source, final boolean isCombat) {
|
||||
int restDamage = damage;
|
||||
|
||||
if (hasKeyword("Damage that would reduce your life total to less than 1 reduces it to 1 instead.")) {
|
||||
if (hasKeyword("DamageLifeThreshold:7")) {
|
||||
restDamage = Math.min(restDamage, life - 7);
|
||||
} else if (hasKeyword("DamageLifeThreshold:1")) {
|
||||
restDamage = Math.min(restDamage, life - 1);
|
||||
}
|
||||
|
||||
@@ -716,29 +721,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
return restDamage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int replaceDamage(final int damage, final Card source, final boolean isCombat) {
|
||||
// Replacement effects
|
||||
final Map<String, Object> repParams = Maps.newHashMap();
|
||||
repParams.put("Event", "DamageDone");
|
||||
repParams.put("Affected", this);
|
||||
repParams.put("DamageSource", source);
|
||||
repParams.put("DamageAmount", damage);
|
||||
repParams.put("IsCombat", isCombat);
|
||||
|
||||
switch (getGame().getReplacementHandler().run(repParams)) {
|
||||
case NotReplaced:
|
||||
return damage;
|
||||
case Updated:
|
||||
// check if this is still the affected player
|
||||
if (this.equals(repParams.get("Affected"))) {
|
||||
return (int) repParams.get("DamageAmount");
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int preventDamage(final int damage, final Card source, final boolean isCombat) {
|
||||
if (game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noPrevention)
|
||||
@@ -885,13 +867,13 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
return Aggregates.max(getOpponents(), Accessors.FN_GET_ASSIGNED_DAMAGE);
|
||||
}
|
||||
|
||||
public final int addCombatDamage(final int damage, final Card source) {
|
||||
public final int addCombatDamage(final int damage, final Card source, CardDamageMap damageMap) {
|
||||
int damageToDo = damage;
|
||||
|
||||
damageToDo = replaceDamage(damageToDo, source, true);
|
||||
damageToDo = replaceDamage(damageToDo, source, true, true, damageMap);
|
||||
damageToDo = preventDamage(damageToDo, source, true);
|
||||
|
||||
addDamageAfterPrevention(damageToDo, source, true); // damage prevention is already checked
|
||||
damageToDo = addDamageAfterPrevention(damageToDo, source, true, damageMap); // damage prevention is already checked
|
||||
|
||||
if (damageToDo > 0) {
|
||||
source.getDamageHistory().registerCombatDamage(this);
|
||||
|
||||
@@ -21,7 +21,7 @@ import forge.game.GameEntity;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -53,7 +53,7 @@ public class TriggerCombatDamageDoneOnce extends Trigger {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public final boolean performTest(final java.util.Map<String, Object> runParams2) {
|
||||
final List<Card> srcs = (List<Card>) runParams2.get("DamageSources");
|
||||
final Set<Card> srcs = (Set<Card>) runParams2.get("DamageSources");
|
||||
final GameEntity tgt = (GameEntity) runParams2.get("DamageTarget");
|
||||
|
||||
if (this.mapParams.containsKey("ValidSource")) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import forge.game.ability.effects.FlipCoinEffect;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardDamageMap;
|
||||
import forge.game.card.CardLists;
|
||||
import forge.game.card.CardPredicates;
|
||||
import forge.game.card.CardPredicates.Presets;
|
||||
@@ -385,8 +386,11 @@ public class HumanPlay {
|
||||
if (!p.getController().confirmPayment(part, "Do you want " + source + " to deal " + amount + " damage to you?")) {
|
||||
return false;
|
||||
}
|
||||
CardDamageMap damageMap = new CardDamageMap();
|
||||
|
||||
p.addDamage(amount, source);
|
||||
p.addDamage(amount, source, damageMap);
|
||||
|
||||
damageMap.dealLifelinkDamage();
|
||||
}
|
||||
else if (part instanceof CostPutCounter) {
|
||||
CounterType counterType = ((CostPutCounter) part).getCounter();
|
||||
|
||||
Reference in New Issue
Block a user