diff --git a/.gitattributes b/.gitattributes index fba13bfc69d..850b250a616 100644 --- a/.gitattributes +++ b/.gitattributes @@ -720,6 +720,7 @@ forge-game/src/main/java/forge/game/trigger/TriggerCountered.java -text forge-game/src/main/java/forge/game/trigger/TriggerCrewed.java -text forge-game/src/main/java/forge/game/trigger/TriggerCycled.java svneol=native#text/plain forge-game/src/main/java/forge/game/trigger/TriggerDamageDone.java svneol=native#text/plain +forge-game/src/main/java/forge/game/trigger/TriggerDamageDoneOnce.java -text svneol=unset#text/plain forge-game/src/main/java/forge/game/trigger/TriggerDamagePrevented.java -text svneol=unset#text/plain forge-game/src/main/java/forge/game/trigger/TriggerDamagePreventedOnce.java -text svneol=unset#text/plain forge-game/src/main/java/forge/game/trigger/TriggerDealtCombatDamageOnce.java -text diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java index 2f651a80fdf..304eb84c143 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamageDealEffect.java @@ -117,77 +117,80 @@ public class DamageDealEffect extends DamageBaseEffect { final boolean remember = sa.hasParam("RememberDamaged"); - final List definedSources = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DamageSource"), sa); - if (definedSources == null || definedSources.isEmpty()) { - return; - } - final Card source = definedSources.get(0); - final Card sourceLKI = sa.getHostCard().getGame().getChangeZoneLKIInfo(definedSources.get(0)); // make a new damage map, combat damage will be applied later into combat map CardDamageMap damageMap = new CardDamageMap(); CardDamageMap preventMap = new CardDamageMap(); + - if (divideOnResolution) { - // Dividing Damage up to multiple targets using combat damage box - // Currently only used for Master of the Wild Hunt - List players = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("DividerOnResolution"), sa); - if (players.isEmpty()) { - return; - } - - CardCollection assigneeCards = new CardCollection(); - // Do we have a way of doing this in a better fashion? - for (GameObject obj : tgts) { - if (obj instanceof Card) { - assigneeCards.add((Card)obj); - } - } - - Player assigningPlayer = players.get(0); - Map map = assigningPlayer.getController().assignCombatDamage(sourceLKI, assigneeCards, dmg, null, true); - for (Entry dt : map.entrySet()) { - dt.getKey().addDamage(dt.getValue(), sourceLKI, damageMap, preventMap); - } - - // transport combat damage back into combat damage map - if (combatDmg) { - game.getCombat().getDamageMap().putAll(damageMap); - } else { - preventMap.triggerPreventDamage(false); - // non combat damage cause lifegain there - damageMap.dealLifelinkDamage(); - replaceDying(sa); - } + final List definedSources = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DamageSource"), sa); + if (definedSources == null || definedSources.isEmpty()) { return; } - for (final Object o : tgts) { - dmg = (sa.usesTargeting() && sa.hasParam("DividedAsYouChoose")) ? sa.getTargetRestrictions().getDividedValue(o) : dmg; - if (o instanceof Card) { - final Card c = (Card) o; - if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) { - if (removeDamage) { - c.setDamage(0); - c.setHasBeenDealtDeathtouchDamage(false); - c.clearAssignedDamage(); - } - else { - c.addDamage(dmg, sourceLKI, combatDmg, noPrevention, damageMap, preventMap); + for (Card source : definedSources) { + final Card sourceLKI = sa.getHostCard().getGame().getChangeZoneLKIInfo(source); + + if (divideOnResolution) { + // Dividing Damage up to multiple targets using combat damage box + // Currently only used for Master of the Wild Hunt + List players = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("DividerOnResolution"), sa); + if (players.isEmpty()) { + return; + } + + CardCollection assigneeCards = new CardCollection(); + // Do we have a way of doing this in a better fashion? + for (GameObject obj : tgts) { + if (obj instanceof Card) { + assigneeCards.add((Card)obj); } } - } else if (o instanceof Player) { - final Player p = (Player) o; - if (!targeted || p.canBeTargetedBy(sa)) { - p.addDamage(dmg, sourceLKI, combatDmg, noPrevention, damageMap, preventMap); + + Player assigningPlayer = players.get(0); + Map map = assigningPlayer.getController().assignCombatDamage(sourceLKI, assigneeCards, dmg, null, true); + for (Entry dt : map.entrySet()) { + dt.getKey().addDamage(dt.getValue(), sourceLKI, damageMap, preventMap); + } + + // transport combat damage back into combat damage map + if (combatDmg) { + game.getCombat().getDamageMap().putAll(damageMap); + } else { + preventMap.triggerPreventDamage(false); + // non combat damage cause lifegain there + damageMap.dealLifelinkDamage(); + replaceDying(sa); + } + return; + } + + for (final Object o : tgts) { + dmg = (sa.usesTargeting() && sa.hasParam("DividedAsYouChoose")) ? sa.getTargetRestrictions().getDividedValue(o) : dmg; + if (o instanceof Card) { + final Card c = (Card) o; + if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) { + if (removeDamage) { + c.setDamage(0); + c.setHasBeenDealtDeathtouchDamage(false); + c.clearAssignedDamage(); + } + else { + c.addDamage(dmg, sourceLKI, combatDmg, noPrevention, damageMap, preventMap); + } + } + } else if (o instanceof Player) { + final Player p = (Player) o; + if (!targeted || p.canBeTargetedBy(sa)) { + p.addDamage(dmg, sourceLKI, combatDmg, noPrevention, damageMap, preventMap); + } } } + + if (remember) { + source.addRemembered(damageMap.row(sourceLKI).keySet()); + } } - - if (remember) { - source.addRemembered(damageMap.row(sourceLKI).keySet()); - } - // transport combat damage back into combat damage map if (combatDmg) { game.getCombat().getDamageMap().putAll(damageMap); @@ -195,6 +198,7 @@ public class DamageDealEffect extends DamageBaseEffect { preventMap.triggerPreventDamage(false); // non combat damage cause lifegain there damageMap.dealLifelinkDamage(); + damageMap.triggerDamageDoneOnce(false); } replaceDying(sa); diff --git a/forge-game/src/main/java/forge/game/card/CardDamageMap.java b/forge-game/src/main/java/forge/game/card/CardDamageMap.java index 1efb403c9b6..9d24e8c0d7f 100644 --- a/forge-game/src/main/java/forge/game/card/CardDamageMap.java +++ b/forge-game/src/main/java/forge/game/card/CardDamageMap.java @@ -8,6 +8,7 @@ import java.util.Map; import com.google.common.collect.ForwardingTable; import com.google.common.collect.HashBasedTable; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.google.common.collect.Table; import forge.game.GameEntity; @@ -48,6 +49,24 @@ public class CardDamageMap extends ForwardingTable { } } + public void triggerDamageDoneOnce(boolean isCombat) { + for (Map.Entry> e : this.columnMap().entrySet()) { + int sum = 0; + for (final int i : e.getValue().values()) { + sum += i; + } + if (sum > 0) { + final GameEntity ge = e.getKey(); + final Map runParams = Maps.newHashMap(); + runParams.put("DamageTarget", ge); + runParams.put("DamageSources", Sets.newHashSet(e.getValue().keySet())); + runParams.put("DamageAmount", sum); + runParams.put("IsCombatDamage", isCombat); + + ge.getGame().getTriggerHandler().runTrigger(TriggerType.DamageDoneOnce, runParams, false); + } + } + } /** * special put logic, sum the values */ diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerDamageDoneOnce.java b/forge-game/src/main/java/forge/game/trigger/TriggerDamageDoneOnce.java new file mode 100644 index 00000000000..ca4ad0e1e8b --- /dev/null +++ b/forge-game/src/main/java/forge/game/trigger/TriggerDamageDoneOnce.java @@ -0,0 +1,61 @@ +package forge.game.trigger; + +import java.util.Map; + +import forge.game.card.Card; +import forge.game.spellability.SpellAbility; + +public class TriggerDamageDoneOnce extends Trigger { + + public TriggerDamageDoneOnce(Map params, Card host, boolean intrinsic) { + super(params, host, intrinsic); + + } + + @Override + public boolean performTest(Map runParams2) { + final Object tgt = runParams2.get("DamageTarget"); + if (this.mapParams.containsKey("ValidTarget")) { + if (!matchesValid(tgt, this.mapParams.get("ValidTarget").split(","), this.getHostCard())) { + return false; + } + } + + + if (this.mapParams.containsKey("CombatDamage")) { + if (this.mapParams.get("CombatDamage").equals("True")) { + if (!((Boolean) runParams2.get("IsCombatDamage"))) { + return false; + } + } else if (this.mapParams.get("CombatDamage").equals("False")) { + if (((Boolean) runParams2.get("IsCombatDamage"))) { + return false; + } + } + } + + return true; + } + + @Override + public void setTriggeringObjects(SpellAbility sa) { + + if (this.getRunParams().containsKey("DamageTarget")) { + sa.setTriggeringObject("Target", this.getRunParams().get("DamageTarget")); + } + sa.setTriggeringObject("DamageAmount", this.getRunParams().get("DamageAmount")); + + + } + + @Override + public String getImportantStackObjects(SpellAbility sa) { + StringBuilder sb = new StringBuilder(); + if (sa.getTriggeringObject("Target") != null) { + sb.append("Damaged: ").append(sa.getTriggeringObject("Target")).append(", "); + } + sb.append("Amount: ").append(sa.getTriggeringObject("DamageAmount")); + return sb.toString(); + } + +} diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerType.java b/forge-game/src/main/java/forge/game/trigger/TriggerType.java index 8ef4d17a89c..a85993ad9e9 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerType.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerType.java @@ -40,6 +40,7 @@ public enum TriggerType { Crewed(TriggerCrewed.class), Cycled(TriggerCycled.class), DamageDone(TriggerDamageDone.class), + DamageDoneOnce(TriggerDamageDoneOnce.class), DamagePrevented(TriggerDamagePrevented.class), DamagePreventedOnce(TriggerDamagePreventedOnce.class), DealtCombatDamageOnce(TriggerDealtCombatDamageOnce.class),