From 525e013ebd071d7d0d11828d6a0ceb53e7812358 Mon Sep 17 00:00:00 2001 From: jimga150 Date: Thu, 4 Apr 2024 22:16:44 -0400 Subject: [PATCH] Fix damage triggers (#12033) * Fix DealtDamageAttachedTriggeredAbility and update test * Fix AegarTheFreezingFlameTriggeredAbility and add test * fix enum reference for DealtDamageAttachedTriggeredAbility * fix Pious Warrior * Fix souls of the faultless * fix Stuffy Doll * Fix Vengeful Pharoah and add test * fix Wall of Essence * fix Wall of Souls * fix Rite of Passage and add test * incorporate simple fixes - these cards use no methods that aren't re-implemented in DamagedBatchForOnePermanentEvent * fix Chandra's Spitfire * fix Darien, King of Kjeldor * fix Jace, Cunning Castaway * fix Mindblade Render * fix Popular Entertainer * fix Swarmborn Giant * fix The Raven's Warning * fix War Elemental * fix Wildfire Elemental * make simple player damage trigger fixes * Add isCombatDamage to DamagedBatchForOnePermanentEvent and utilize in trigger fixes * fix Fall of Cair Andros * fix Toralf, God of Fury * optimize some imports --- .../mage/cards/a/AegarTheFreezingFlame.java | 17 ++++- .../src/mage/cards/a/AngelheartVial.java | 2 +- Mage.Sets/src/mage/cards/a/Arcbond.java | 2 +- Mage.Sets/src/mage/cards/b/BloodHound.java | 2 +- .../src/mage/cards/c/ChandrasSpitfire.java | 9 +-- .../src/mage/cards/d/DarienKingOfKjeldor.java | 3 +- .../mage/cards/e/ExpeditedInheritance.java | 2 +- .../src/mage/cards/f/FallOfCairAndros.java | 15 +++- Mage.Sets/src/mage/cards/f/FilthyCur.java | 2 +- .../src/mage/cards/i/InnocentBystander.java | 2 +- .../src/mage/cards/j/JaceCunningCastaway.java | 29 +++----- .../mage/cards/k/KazarovSengirPureblood.java | 2 +- Mage.Sets/src/mage/cards/l/Lich.java | 2 +- .../src/mage/cards/l/LivingArtifact.java | 3 +- .../src/mage/cards/m/MindbladeRender.java | 48 ++++++------ .../src/mage/cards/p/PhyrexianNegator.java | 3 +- .../src/mage/cards/p/PhyrexianTotem.java | 2 +- Mage.Sets/src/mage/cards/p/PiousWarrior.java | 14 ++-- .../src/mage/cards/p/PopularEntertainer.java | 36 ++++----- Mage.Sets/src/mage/cards/r/Repercussion.java | 2 +- Mage.Sets/src/mage/cards/r/RiteOfPassage.java | 4 +- .../src/mage/cards/s/SoulsOfTheFaultless.java | 20 +++-- .../src/mage/cards/s/SowerOfDiscord.java | 2 +- Mage.Sets/src/mage/cards/s/StuffyDoll.java | 46 ++---------- Mage.Sets/src/mage/cards/s/SunDroplet.java | 2 +- .../src/mage/cards/s/SwarmbornGiant.java | 16 ++-- .../src/mage/cards/t/TheRavensWarning.java | 40 ++++------ .../src/mage/cards/t/ToralfGodOfFury.java | 15 ++-- .../src/mage/cards/v/VengefulPharaoh.java | 73 ++++++++----------- Mage.Sets/src/mage/cards/w/WallOfEssence.java | 21 +++--- Mage.Sets/src/mage/cards/w/WallOfSouls.java | 15 ++-- Mage.Sets/src/mage/cards/w/WarElemental.java | 4 +- .../src/mage/cards/w/WildfireElemental.java | 9 +-- .../src/mage/cards/w/WrathfulRedDragon.java | 2 +- .../oneshot/damage/SpitefulShadowsTest.java | 45 ++++++++++-- .../cards/control/VengefulPharaohTest.java | 55 ++++++++++++++ .../test/cards/damage/ExcessDamageTest.java | 46 ++++++++++++ .../cards/single/fdn/RiteOfPassageTest.java | 27 +++++++ .../DealtDamageAttachedTriggeredAbility.java | 2 +- .../DamagedBatchForOnePermanentEvent.java | 6 ++ 40 files changed, 386 insertions(+), 261 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/AegarTheFreezingFlame.java b/Mage.Sets/src/mage/cards/a/AegarTheFreezingFlame.java index 1a80e15bd5b..ab3c5d2ed06 100644 --- a/Mage.Sets/src/mage/cards/a/AegarTheFreezingFlame.java +++ b/Mage.Sets/src/mage/cards/a/AegarTheFreezingFlame.java @@ -9,6 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; +import mage.game.events.DamagedBatchForOnePermanentEvent; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.watchers.Watcher; @@ -56,14 +57,22 @@ class AegarTheFreezingFlameTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - DamagedEvent dEvent = (DamagedEvent) event; - if (dEvent.getExcess() < 1 - || !game.getOpponents(getControllerId()).contains(game.getControllerId(event.getTargetId()))) { + DamagedBatchForOnePermanentEvent dEvent = (DamagedBatchForOnePermanentEvent) event; + + int excess = dEvent.getEvents() + .stream() + .mapToInt(DamagedEvent::getExcess) + .sum(); + + boolean controlledByOpponent = + game.getOpponents(getControllerId()).contains(game.getControllerId(event.getTargetId())); + + if (excess < 1 || !controlledByOpponent) { return false; } AegarTheFreezingFlameWatcher watcher = game.getState().getWatcher(AegarTheFreezingFlameWatcher.class); diff --git a/Mage.Sets/src/mage/cards/a/AngelheartVial.java b/Mage.Sets/src/mage/cards/a/AngelheartVial.java index 9e5a8c4388c..9c8968ee7ff 100644 --- a/Mage.Sets/src/mage/cards/a/AngelheartVial.java +++ b/Mage.Sets/src/mage/cards/a/AngelheartVial.java @@ -68,7 +68,7 @@ class AngelheartVialTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/a/Arcbond.java b/Mage.Sets/src/mage/cards/a/Arcbond.java index 84f53d47eb3..a7e99622ba0 100644 --- a/Mage.Sets/src/mage/cards/a/Arcbond.java +++ b/Mage.Sets/src/mage/cards/a/Arcbond.java @@ -84,7 +84,7 @@ class ArcbondDelayedTriggeredAbility extends DelayedTriggeredAbility { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/b/BloodHound.java b/Mage.Sets/src/mage/cards/b/BloodHound.java index c7cfffaea0a..942a67c4953 100644 --- a/Mage.Sets/src/mage/cards/b/BloodHound.java +++ b/Mage.Sets/src/mage/cards/b/BloodHound.java @@ -67,7 +67,7 @@ class BloodHoundTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/c/ChandrasSpitfire.java b/Mage.Sets/src/mage/cards/c/ChandrasSpitfire.java index aa70ccd5112..2d3800e8eb7 100644 --- a/Mage.Sets/src/mage/cards/c/ChandrasSpitfire.java +++ b/Mage.Sets/src/mage/cards/c/ChandrasSpitfire.java @@ -14,9 +14,8 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedPlayerEvent; +import mage.game.events.DamagedBatchForOnePlayerEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; /** * @@ -58,13 +57,13 @@ class ChandrasSpitfireAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - DamagedPlayerEvent damageEvent = (DamagedPlayerEvent)event; - return !damageEvent.isCombatDamage() && game.getOpponents(controllerId).contains(event.getTargetId()); + DamagedBatchForOnePlayerEvent dEvent = (DamagedBatchForOnePlayerEvent) event; + return !dEvent.isCombatDamage() && dEvent.getAmount() > 0 && game.getOpponents(controllerId).contains(dEvent.getTargetId()); } @Override diff --git a/Mage.Sets/src/mage/cards/d/DarienKingOfKjeldor.java b/Mage.Sets/src/mage/cards/d/DarienKingOfKjeldor.java index 6c5f1778268..ac94d207915 100644 --- a/Mage.Sets/src/mage/cards/d/DarienKingOfKjeldor.java +++ b/Mage.Sets/src/mage/cards/d/DarienKingOfKjeldor.java @@ -16,7 +16,6 @@ import mage.constants.SuperType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.token.SoldierToken; import mage.players.Player; @@ -66,7 +65,7 @@ class DarienKingOfKjeldorTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/e/ExpeditedInheritance.java b/Mage.Sets/src/mage/cards/e/ExpeditedInheritance.java index 38a2f905363..9a3a5351604 100644 --- a/Mage.Sets/src/mage/cards/e/ExpeditedInheritance.java +++ b/Mage.Sets/src/mage/cards/e/ExpeditedInheritance.java @@ -58,7 +58,7 @@ class ExpeditedInheritanceTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/f/FallOfCairAndros.java b/Mage.Sets/src/mage/cards/f/FallOfCairAndros.java index 83646f7a6ed..26d3c005deb 100644 --- a/Mage.Sets/src/mage/cards/f/FallOfCairAndros.java +++ b/Mage.Sets/src/mage/cards/f/FallOfCairAndros.java @@ -12,6 +12,8 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; +import mage.game.events.DamagedBatchForOnePermanentEvent; +import mage.game.events.DamagedEvent; import mage.game.events.DamagedPermanentEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -63,7 +65,7 @@ class FallOfCairAndrosTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override @@ -73,12 +75,17 @@ class FallOfCairAndrosTriggeredAbility extends TriggeredAbilityImpl { || !game.getOpponents(getControllerId()).contains(permanent.getControllerId())) { return false; } - DamagedPermanentEvent dEvent = (DamagedPermanentEvent) event; - if (dEvent.isCombatDamage() || dEvent.getExcess() < 1) { + DamagedBatchForOnePermanentEvent dEvent = (DamagedBatchForOnePermanentEvent) event; + int excessDamage = dEvent.getEvents() + .stream() + .mapToInt(DamagedEvent::getExcess) + .sum(); + + if (dEvent.isCombatDamage() || excessDamage < 1) { return false; } this.getEffects().clear(); - this.addEffect(new AmassEffect(dEvent.getExcess(), SubType.ORC)); + this.addEffect(new AmassEffect(excessDamage, SubType.ORC)); return true; } diff --git a/Mage.Sets/src/mage/cards/f/FilthyCur.java b/Mage.Sets/src/mage/cards/f/FilthyCur.java index 4b836797234..d983778da5d 100644 --- a/Mage.Sets/src/mage/cards/f/FilthyCur.java +++ b/Mage.Sets/src/mage/cards/f/FilthyCur.java @@ -60,7 +60,7 @@ class DealtDamageLoseLifeTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/i/InnocentBystander.java b/Mage.Sets/src/mage/cards/i/InnocentBystander.java index f88a5cd37ac..6efdbc1f578 100644 --- a/Mage.Sets/src/mage/cards/i/InnocentBystander.java +++ b/Mage.Sets/src/mage/cards/i/InnocentBystander.java @@ -59,7 +59,7 @@ class InnocentBystanderTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/j/JaceCunningCastaway.java b/Mage.Sets/src/mage/cards/j/JaceCunningCastaway.java index 24ece90b229..fbcadcb0dab 100644 --- a/Mage.Sets/src/mage/cards/j/JaceCunningCastaway.java +++ b/Mage.Sets/src/mage/cards/j/JaceCunningCastaway.java @@ -12,7 +12,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.events.DamagedPlayerEvent; +import mage.game.events.DamagedBatchForOnePlayerEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.token.JaceCunningCastawayIllusionToken; @@ -98,26 +98,21 @@ class JaceCunningCastawayDamageTriggeredAbility extends DelayedTriggeredAbility @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) { - if (((DamagedPlayerEvent) event).isCombatDamage()) { - Permanent creature = game.getPermanent(event.getSourceId()); - if (creature != null && creature.isControlledBy(controllerId) - && !damagedPlayerIds.contains(event.getTargetId())) { - damagedPlayerIds.add(event.getTargetId()); - return true; - } - } - } - if (event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST) { - damagedPlayerIds.clear(); - } - return false; + + DamagedBatchForOnePlayerEvent dEvent = (DamagedBatchForOnePlayerEvent) event; + + int damageFromYours = dEvent.getEvents() + .stream() + .filter(ev -> ev.getSourceId().equals(controllerId)) + .mapToInt(GameEvent::getAmount) + .sum(); + + return dEvent.isCombatDamage() && damageFromYours > 0; } @Override diff --git a/Mage.Sets/src/mage/cards/k/KazarovSengirPureblood.java b/Mage.Sets/src/mage/cards/k/KazarovSengirPureblood.java index 46ce5ed3973..d09a18e848d 100644 --- a/Mage.Sets/src/mage/cards/k/KazarovSengirPureblood.java +++ b/Mage.Sets/src/mage/cards/k/KazarovSengirPureblood.java @@ -75,7 +75,7 @@ class KazarovSengirPurebloodTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/l/Lich.java b/Mage.Sets/src/mage/cards/l/Lich.java index 8c0068ca351..4be19bb51ee 100644 --- a/Mage.Sets/src/mage/cards/l/Lich.java +++ b/Mage.Sets/src/mage/cards/l/Lich.java @@ -118,7 +118,7 @@ class LichDamageTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/l/LivingArtifact.java b/Mage.Sets/src/mage/cards/l/LivingArtifact.java index 2af86b186b4..c4b3c6520d8 100644 --- a/Mage.Sets/src/mage/cards/l/LivingArtifact.java +++ b/Mage.Sets/src/mage/cards/l/LivingArtifact.java @@ -24,7 +24,6 @@ import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.TargetPermanent; import mage.target.common.TargetArtifactPermanent; @@ -80,7 +79,7 @@ class LivingArtifactTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/m/MindbladeRender.java b/Mage.Sets/src/mage/cards/m/MindbladeRender.java index 37fcea8888b..d0ef08950bc 100644 --- a/Mage.Sets/src/mage/cards/m/MindbladeRender.java +++ b/Mage.Sets/src/mage/cards/m/MindbladeRender.java @@ -1,21 +1,22 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedPlayerEvent; +import mage.game.events.DamagedBatchForOnePlayerEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** * * @author TheElk801 @@ -46,17 +47,13 @@ public final class MindbladeRender extends CardImpl { class MindbladeRenderTriggeredAbility extends TriggeredAbilityImpl { - private boolean usedForCombatDamageStep; - public MindbladeRenderTriggeredAbility() { super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1)); this.addEffect(new LoseLifeSourceControllerEffect(1)); - this.usedForCombatDamageStep = false; } private MindbladeRenderTriggeredAbility(final MindbladeRenderTriggeredAbility effect) { super(effect); - this.usedForCombatDamageStep = effect.usedForCombatDamageStep; } @Override @@ -66,34 +63,37 @@ class MindbladeRenderTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST) { - usedForCombatDamageStep = false; - return false; - } - if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER) { - return false; - } + Player controller = game.getPlayer(getControllerId()); if (controller == null) { return false; } - Permanent attacker = game.getPermanentOrLKIBattlefield(event.getSourceId()); - if (attacker == null) { + + DamagedBatchForOnePlayerEvent dEvent = (DamagedBatchForOnePlayerEvent) event; + + if (!controller.hasOpponent(dEvent.getTargetId(), game)){ return false; } - if (((DamagedPlayerEvent) event).isCombatDamage() - && controller.hasOpponent(event.getTargetId(), game) - && attacker.hasSubtype(SubType.WARRIOR, game) - && !usedForCombatDamageStep) { - usedForCombatDamageStep = true; - return true; + + if (!dEvent.isCombatDamage()){ + return false; } - return false; + + int warriorDamage = dEvent.getEvents() + .stream() + .filter(ev -> { + Permanent attacker = game.getPermanentOrLKIBattlefield(ev.getSourceId()); + return attacker.hasSubtype(SubType.WARRIOR, game); + }) + .mapToInt(GameEvent::getAmount) + .sum(); + + return warriorDamage > 0; } @Override diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianNegator.java b/Mage.Sets/src/mage/cards/p/PhyrexianNegator.java index ef76d274ea0..f5f462bbf71 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianNegator.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianNegator.java @@ -15,7 +15,6 @@ import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.players.Player; import mage.target.targetpointer.FixedTarget; /** @@ -64,7 +63,7 @@ class PhyrexianNegatorTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianTotem.java b/Mage.Sets/src/mage/cards/p/PhyrexianTotem.java index 82d7c5e07c8..af89b977e0d 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianTotem.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianTotem.java @@ -99,7 +99,7 @@ class PhyrexianTotemTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/p/PiousWarrior.java b/Mage.Sets/src/mage/cards/p/PiousWarrior.java index e5afe8e378a..892927fc46e 100644 --- a/Mage.Sets/src/mage/cards/p/PiousWarrior.java +++ b/Mage.Sets/src/mage/cards/p/PiousWarrior.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedEvent; +import mage.game.events.DamagedBatchForOnePermanentEvent; import mage.game.events.GameEvent; import mage.players.Player; @@ -64,14 +64,18 @@ class PiousWarriorTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.sourceId) && ((DamagedEvent)event).isCombatDamage() ) { - this.getEffects().get(0).setValue("damageAmount", event.getAmount()); - return true; + + DamagedBatchForOnePermanentEvent dEvent = (DamagedBatchForOnePermanentEvent) event; + int damage = dEvent.getAmount(); + + if (event.getTargetId().equals(this.sourceId) && dEvent.isCombatDamage() && damage > 0) { + this.getEffects().setValue("damageAmount", damage); + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/p/PopularEntertainer.java b/Mage.Sets/src/mage/cards/p/PopularEntertainer.java index 7259b021591..3ff04e51477 100644 --- a/Mage.Sets/src/mage/cards/p/PopularEntertainer.java +++ b/Mage.Sets/src/mage/cards/p/PopularEntertainer.java @@ -12,13 +12,10 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; -import mage.game.events.DamagedPlayerEvent; +import mage.game.events.DamagedBatchForOnePlayerEvent; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; /** @@ -51,8 +48,6 @@ public final class PopularEntertainer extends CardImpl { class PopularEntertainerAbility extends TriggeredAbilityImpl { - private final List damagedPlayerIds = new ArrayList<>(); - PopularEntertainerAbility() { super(Zone.BATTLEFIELD, new GoadTargetEffect(), false); } @@ -68,31 +63,26 @@ class PopularEntertainerAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_POST) { - damagedPlayerIds.clear(); + DamagedBatchForOnePlayerEvent dEvent = (DamagedBatchForOnePlayerEvent) event; + + int damage = dEvent.getEvents() + .stream() + .filter(ev -> ev.getSourceId().equals(controllerId)) + .mapToInt(GameEvent::getAmount) + .sum(); + + if (!dEvent.isCombatDamage() || damage < 1){ return false; } - if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER - || !((DamagedPlayerEvent) event).isCombatDamage()) { - return false; - } - Permanent creature = game.getPermanent(event.getSourceId()); - if (creature == null - || !creature.isControlledBy(getControllerId()) - || damagedPlayerIds.contains(event.getTargetId())) { - return false; - } - damagedPlayerIds.add(event.getTargetId()); FilterPermanent filter = new FilterCreaturePermanent( - "creature controlled by " + game.getPlayer(event.getTargetId()).getName() + "creature controlled by " + game.getPlayer(dEvent.getTargetId()).getName() ); - filter.add(new ControllerIdPredicate(event.getTargetId())); + filter.add(new ControllerIdPredicate(dEvent.getTargetId())); this.getTargets().clear(); this.addTarget(new TargetPermanent(filter)); return true; diff --git a/Mage.Sets/src/mage/cards/r/Repercussion.java b/Mage.Sets/src/mage/cards/r/Repercussion.java index 16d073cc375..2f7c4e51c30 100644 --- a/Mage.Sets/src/mage/cards/r/Repercussion.java +++ b/Mage.Sets/src/mage/cards/r/Repercussion.java @@ -54,7 +54,7 @@ class RepercussionTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override diff --git a/Mage.Sets/src/mage/cards/r/RiteOfPassage.java b/Mage.Sets/src/mage/cards/r/RiteOfPassage.java index 2ac19b2afce..2242d1c3d19 100644 --- a/Mage.Sets/src/mage/cards/r/RiteOfPassage.java +++ b/Mage.Sets/src/mage/cards/r/RiteOfPassage.java @@ -61,7 +61,7 @@ class RiteOfPassageTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override @@ -69,7 +69,7 @@ class RiteOfPassageTriggeredAbility extends TriggeredAbilityImpl { UUID targetId = event.getTargetId(); Permanent permanent = game.getPermanent(targetId); if (permanent != null && StaticFilters.FILTER_CONTROLLED_CREATURE.match(permanent, getControllerId(), this, game)) { - getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); + getEffects().setTargetPointer(new FixedTarget(targetId, game)); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/s/SoulsOfTheFaultless.java b/Mage.Sets/src/mage/cards/s/SoulsOfTheFaultless.java index cbb3cc36d90..dd1528d9297 100644 --- a/Mage.Sets/src/mage/cards/s/SoulsOfTheFaultless.java +++ b/Mage.Sets/src/mage/cards/s/SoulsOfTheFaultless.java @@ -15,9 +15,8 @@ import mage.constants.SubType; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedEvent; +import mage.game.events.DamagedBatchForOnePermanentEvent; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.players.Player; /** @@ -67,20 +66,19 @@ class SoulsOfTheFaultlessTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.sourceId) - && ((DamagedEvent) event).isCombatDamage()) { - Permanent source = game.getPermanent(event.getSourceId()); - if (source == null) { - source = (Permanent) game.getLastKnownInformation(event.getSourceId(), Zone.BATTLEFIELD); - } - UUID attackerId = source != null ? source.getControllerId() : null; + DamagedBatchForOnePermanentEvent dEvent = (DamagedBatchForOnePermanentEvent) event; + + int damage = dEvent.getAmount(); + + if (dEvent.getTargetId().equals(this.sourceId) && dEvent.isCombatDamage() && damage > 0) { + UUID attackerId = game.getActivePlayerId(); for (Effect effect : this.getEffects()) { - effect.setValue("damageAmount", event.getAmount()); + effect.setValue("damageAmount", damage); effect.setValue("attackerId", attackerId); } return true; diff --git a/Mage.Sets/src/mage/cards/s/SowerOfDiscord.java b/Mage.Sets/src/mage/cards/s/SowerOfDiscord.java index 2fed274fae1..308c0337f12 100644 --- a/Mage.Sets/src/mage/cards/s/SowerOfDiscord.java +++ b/Mage.Sets/src/mage/cards/s/SowerOfDiscord.java @@ -124,7 +124,7 @@ class SowerOfDiscordTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/s/StuffyDoll.java b/Mage.Sets/src/mage/cards/s/StuffyDoll.java index 4d8c45cf61a..28f4560693f 100644 --- a/Mage.Sets/src/mage/cards/s/StuffyDoll.java +++ b/Mage.Sets/src/mage/cards/s/StuffyDoll.java @@ -4,6 +4,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.DealtDamageToSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; @@ -39,7 +40,7 @@ public final class StuffyDoll extends CardImpl { // Stuffy Doll is indestructible. this.addAbility(IndestructibleAbility.getInstance()); // Whenever Stuffy Doll is dealt damage, it deals that much damage to the chosen player. - this.addAbility(new StuffyDollTriggeredAbility()); + this.addAbility(new DealtDamageToSourceTriggeredAbility(new StuffyDollEffect(), false)); // {T}: Stuffy Doll deals 1 damage to itself. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageSelfEffect(1), new TapSourceCost())); } @@ -54,51 +55,20 @@ public final class StuffyDoll extends CardImpl { } } -class StuffyDollTriggeredAbility extends TriggeredAbilityImpl { +class StuffyDollEffect extends OneShotEffect { - public StuffyDollTriggeredAbility() { - super(Zone.BATTLEFIELD, new StuffyDollGainLifeEffect()); - setTriggerPhrase("Whenever {this} is dealt damage, "); - } - - private StuffyDollTriggeredAbility(final StuffyDollTriggeredAbility effect) { - super(effect); - } - - @Override - public StuffyDollTriggeredAbility copy() { - return new StuffyDollTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.sourceId)) { - this.getEffects().get(0).setValue("damageAmount", event.getAmount()); - return true; - } - return false; - } -} - -class StuffyDollGainLifeEffect extends OneShotEffect { - - StuffyDollGainLifeEffect() { + StuffyDollEffect() { super(Outcome.GainLife); staticText = "it deals that much damage to the chosen player"; } - private StuffyDollGainLifeEffect(final StuffyDollGainLifeEffect effect) { + private StuffyDollEffect(final StuffyDollEffect effect) { super(effect); } @Override - public StuffyDollGainLifeEffect copy() { - return new StuffyDollGainLifeEffect(this); + public StuffyDollEffect copy() { + return new StuffyDollEffect(this); } @Override @@ -106,7 +76,7 @@ class StuffyDollGainLifeEffect extends OneShotEffect { UUID playerId = (UUID) game.getState().getValue(source.getSourceId() + "_player"); Player player = game.getPlayer(playerId); if (player != null && player.canRespond()) { - player.damage((Integer) this.getValue("damageAmount"), source.getSourceId(), source, game); + player.damage((Integer) this.getValue("damage"), source.getSourceId(), source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/s/SunDroplet.java b/Mage.Sets/src/mage/cards/s/SunDroplet.java index 2d53bef3c4e..3830f941642 100644 --- a/Mage.Sets/src/mage/cards/s/SunDroplet.java +++ b/Mage.Sets/src/mage/cards/s/SunDroplet.java @@ -65,7 +65,7 @@ class SunDropletTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SwarmbornGiant.java b/Mage.Sets/src/mage/cards/s/SwarmbornGiant.java index 11ab1b5e5ba..3cd0f13715c 100644 --- a/Mage.Sets/src/mage/cards/s/SwarmbornGiant.java +++ b/Mage.Sets/src/mage/cards/s/SwarmbornGiant.java @@ -1,7 +1,6 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; @@ -15,13 +14,14 @@ import mage.abilities.keyword.ReachAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedEvent; +import mage.game.events.DamagedBatchForOnePlayerEvent; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; + +import java.util.UUID; /** * @@ -79,14 +79,14 @@ class SwarmbornGiantTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.getControllerId())) { - DamagedEvent damagedEvent = (DamagedEvent) event; - return damagedEvent.isCombatDamage(); + DamagedBatchForOnePlayerEvent dEvent = (DamagedBatchForOnePlayerEvent) event; + if (dEvent.getTargetId().equals(this.getControllerId())) { + return dEvent.isCombatDamage() && dEvent.getAmount() > 0; } return false; } diff --git a/Mage.Sets/src/mage/cards/t/TheRavensWarning.java b/Mage.Sets/src/mage/cards/t/TheRavensWarning.java index 2b4736baa1f..3c051472725 100644 --- a/Mage.Sets/src/mage/cards/t/TheRavensWarning.java +++ b/Mage.Sets/src/mage/cards/t/TheRavensWarning.java @@ -1,7 +1,5 @@ package mage.cards.t; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; import mage.abilities.DelayedTriggeredAbility; @@ -16,11 +14,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.game.Game; -import mage.game.events.DamagedPlayerEvent; +import mage.game.events.DamagedBatchForOnePlayerEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.token.BlueBirdToken; -import mage.target.targetpointer.FixedTarget; /** * @@ -67,8 +64,6 @@ public final class TheRavensWarning extends CardImpl { class TheRavensWarningTriggeredAbility extends DelayedTriggeredAbility { - private final Set damagedPlayerIds = new HashSet<>(); - public TheRavensWarningTriggeredAbility() { super(new LookAtTargetPlayerHandEffect(), Duration.EndOfTurn, false); this.addEffect(new DrawCardSourceControllerEffect(1)); @@ -86,28 +81,25 @@ class TheRavensWarningTriggeredAbility extends DelayedTriggeredAbility { // Code based on ControlledCreaturesDealCombatDamagePlayerTriggeredAbility @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_PRIORITY - || event.getType() == GameEvent.EventType.ZONE_CHANGE; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) { - DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event; - Permanent p = game.getPermanent(event.getSourceId()); - if (damageEvent.isCombatDamage() && p != null && p.isControlledBy(this.getControllerId()) - && !damagedPlayerIds.contains(event.getPlayerId()) && p.hasAbility(FlyingAbility.getInstance(), game)) { - damagedPlayerIds.add(event.getPlayerId()); - this.getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); - return true; - } - } - if (event.getType() == GameEvent.EventType.COMBAT_DAMAGE_STEP_PRIORITY || - (event.getType() == GameEvent.EventType.ZONE_CHANGE && event.getTargetId().equals(getSourceId()))) { - damagedPlayerIds.clear(); - } - return false; + + DamagedBatchForOnePlayerEvent dEvent = (DamagedBatchForOnePlayerEvent) event; + + int flyingDamage = dEvent.getEvents() + .stream() + .filter(ev -> ev.getSourceId().equals(controllerId)) + .filter(ev -> { + Permanent permanent = game.getPermanentOrLKIBattlefield(ev.getSourceId()); + return permanent.isCreature() && permanent.hasAbility(FlyingAbility.getInstance(), game); + }) + .mapToInt(GameEvent::getAmount) + .sum(); + + return flyingDamage > 0 && dEvent.isCombatDamage(); } @Override diff --git a/Mage.Sets/src/mage/cards/t/ToralfGodOfFury.java b/Mage.Sets/src/mage/cards/t/ToralfGodOfFury.java index 8ffc1f9bb88..2af8737fb86 100644 --- a/Mage.Sets/src/mage/cards/t/ToralfGodOfFury.java +++ b/Mage.Sets/src/mage/cards/t/ToralfGodOfFury.java @@ -26,6 +26,7 @@ import mage.filter.common.FilterPermanentOrPlayer; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.MageObjectReferencePredicate; import mage.game.Game; +import mage.game.events.DamagedBatchForOnePermanentEvent; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -103,24 +104,28 @@ class ToralfGodOfFuryTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - DamagedEvent dEvent = (DamagedEvent) event; - if (dEvent.getExcess() < 1 + DamagedBatchForOnePermanentEvent dEvent = (DamagedBatchForOnePermanentEvent) event; + int excessDamage = dEvent.getEvents() + .stream() + .mapToInt(DamagedEvent::getExcess) + .sum(); + + if (excessDamage < 1 || dEvent.isCombatDamage() || !game.getOpponents(getControllerId()).contains(game.getControllerId(event.getTargetId()))) { return false; } this.getEffects().clear(); this.getTargets().clear(); - int excessDamage = dEvent.getExcess(); this.addEffect(new DamageTargetEffect(excessDamage)); FilterPermanentOrPlayer filter = new FilterAnyTarget(); filter.getPermanentFilter().add(Predicates.not(new MageObjectReferencePredicate(event.getTargetId(), game))); - this.addTarget(new TargetPermanentOrPlayer(filter).withChooseHint(Integer.toString(excessDamage) + " damage")); + this.addTarget(new TargetPermanentOrPlayer(filter).withChooseHint(excessDamage + " damage")); return true; } diff --git a/Mage.Sets/src/mage/cards/v/VengefulPharaoh.java b/Mage.Sets/src/mage/cards/v/VengefulPharaoh.java index 0e575424ecc..98f75e574e1 100644 --- a/Mage.Sets/src/mage/cards/v/VengefulPharaoh.java +++ b/Mage.Sets/src/mage/cards/v/VengefulPharaoh.java @@ -2,7 +2,6 @@ package mage.cards.v; import mage.MageInt; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; @@ -15,10 +14,8 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedEvent; -import mage.game.events.GameEvent; +import mage.game.events.*; import mage.game.permanent.Permanent; -import mage.game.turn.Step; import mage.players.Player; import mage.target.common.TargetAttackingCreature; @@ -55,12 +52,6 @@ public final class VengefulPharaoh extends CardImpl { class VengefulPharaohTriggeredAbility extends TriggeredAbilityImpl { - Step stepTriggeredPlayer; - int turnTriggeredPlayer; - - Step stepTriggeredPlansewalker; - int turnTriggeredPlaneswalker; - public VengefulPharaohTriggeredAbility() { super(Zone.GRAVEYARD, new VengefulPharaohEffect(), false); this.addTarget(new TargetAttackingCreature()); @@ -68,10 +59,6 @@ class VengefulPharaohTriggeredAbility extends TriggeredAbilityImpl { private VengefulPharaohTriggeredAbility(final VengefulPharaohTriggeredAbility ability) { super(ability); - this.stepTriggeredPlansewalker = ability.stepTriggeredPlansewalker; - this.turnTriggeredPlaneswalker = ability.turnTriggeredPlaneswalker; - this.stepTriggeredPlayer = ability.stepTriggeredPlayer; - this.turnTriggeredPlayer = ability.turnTriggeredPlayer; } @Override @@ -81,48 +68,46 @@ class VengefulPharaohTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkInterveningIfClause(Game game) { - // 9/22/2011 - If multiple creatures deal combat damage to you and to a planeswalker you control - // simultaneously, Vengeful Pharaoh will trigger twice. The first trigger will cause Vengeful Pharaoh - // to be put on top of your library. The second trigger will then do nothing, as Vengeful Pharaoh is - // no longer in your graveyard when it tries to resolve. Note that the second trigger will do nothing - // even if Vengeful Pharaoh is put back into your graveyard before it tries to resolve, as it's a - // different Vengeful Pharaoh than the one that was there before. - MageObjectReference mor = new MageObjectReference(getSourceId(), game); - return mor.refersTo(this.getSourceObject(game), game); + // Vengeful Pharaoh must be in your graveyard when combat damage is dealt to you or a planeswalker you control + // in order for its ability to trigger. That is, it can’t die and trigger from your graveyard during the same + // combat damage step. (2011-09-22) + + // If Vengeful Pharaoh is no longer in your graveyard when the triggered ability would resolve, the triggered + // ability won’t do anything. (2011-09-22) + return game.getState().getZone(getSourceId()) == Zone.GRAVEYARD; } @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER - || event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + // If multiple creatures deal combat damage to you simultaneously, Vengeful Pharaoh will only trigger once. + // (2011-09-22) + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER + || event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if ((event.getType() == GameEvent.EventType.DAMAGED_PLAYER && event.getTargetId().equals(this.getControllerId())) - && ((DamagedEvent) event).isCombatDamage()) { - if (!game.getPhase().getStep().equals(stepTriggeredPlayer) || game.getTurnNum() != turnTriggeredPlayer) { - stepTriggeredPlayer = game.getPhase().getStep(); - turnTriggeredPlayer = game.getTurnNum(); - return true; - } + + if ((event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER + && event.getTargetId().equals(this.getControllerId()))) { + DamagedBatchForOnePlayerEvent dEvent = (DamagedBatchForOnePlayerEvent) event; + return dEvent.isCombatDamage() && dEvent.getAmount() > 0; } - if (event.getType() == GameEvent.EventType.DAMAGED_PERMANENT && ((DamagedEvent) event).isCombatDamage()) { + if (event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.isPlaneswalker(game) && permanent.isControlledBy(this.getControllerId())) { - if (!game.getPhase().getStep().equals(stepTriggeredPlansewalker) || game.getTurnNum() != turnTriggeredPlaneswalker) { - stepTriggeredPlansewalker = game.getPhase().getStep(); - turnTriggeredPlaneswalker = game.getTurnNum(); - return true; - } - } + DamagedBatchForOnePermanentEvent dEvent = (DamagedBatchForOnePermanentEvent) event; + return permanent != null + && permanent.isPlaneswalker(game) + && permanent.isControlledBy(this.getControllerId()) + && dEvent.isCombatDamage() && dEvent.getAmount() > 0; } return false; } @Override public String getRule() { - return "Whenever combat damage is dealt to you or a planeswalker you control, if {this} is in your graveyard, destroy target attacking creature, then put {this} on top of your library."; + return "Whenever combat damage is dealt to you or a planeswalker you control, if {this} is in your " + + "graveyard, destroy target attacking creature, then put {this} on top of your library."; } } @@ -148,9 +133,13 @@ class VengefulPharaohEffect extends OneShotEffect { Card card = game.getCard(source.getSourceId()); if (card != null && controller != null) { Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - permanent.destroy(source, game, false); + if (permanent == null) { + // If the attacking creature is an illegal target when the triggered ability tries to resolve, + // it won’t resolve and none of its effects will happen. Vengeful Pharaoh will remain in your graveyard. + // (2011-09-22) + return false; } + permanent.destroy(source, game, false); controller.moveCardToLibraryWithInfo(card, source, game, Zone.GRAVEYARD, true, true); return true; } diff --git a/Mage.Sets/src/mage/cards/w/WallOfEssence.java b/Mage.Sets/src/mage/cards/w/WallOfEssence.java index 25c52da13c5..e1d99e68312 100644 --- a/Mage.Sets/src/mage/cards/w/WallOfEssence.java +++ b/Mage.Sets/src/mage/cards/w/WallOfEssence.java @@ -13,9 +13,8 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedEvent; +import mage.game.events.DamagedBatchForOnePermanentEvent; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.players.Player; import java.util.UUID; @@ -66,20 +65,20 @@ class WallOfEssenceTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent == null - || !permanent.isCreature(game) - || !event.getTargetId().equals(this.sourceId) - || !((DamagedEvent) event).isCombatDamage()) { - return false; + + DamagedBatchForOnePermanentEvent dEvent = (DamagedBatchForOnePermanentEvent) event; + int damage = dEvent.getAmount(); + + if (event.getTargetId().equals(this.sourceId) && dEvent.isCombatDamage() && damage > 0) { + this.getEffects().setValue("damageAmount", damage); + return true; } - this.getEffects().setValue("damageAmount", event.getAmount()); - return true; + return false; } } diff --git a/Mage.Sets/src/mage/cards/w/WallOfSouls.java b/Mage.Sets/src/mage/cards/w/WallOfSouls.java index 6a063a058a3..1168be41b3f 100644 --- a/Mage.Sets/src/mage/cards/w/WallOfSouls.java +++ b/Mage.Sets/src/mage/cards/w/WallOfSouls.java @@ -1,6 +1,5 @@ package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; @@ -13,10 +12,12 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedEvent; +import mage.game.events.DamagedBatchForOnePermanentEvent; import mage.game.events.GameEvent; import mage.target.common.TargetOpponentOrPlaneswalker; +import java.util.UUID; + /** * * @author fireshoes @@ -66,13 +67,17 @@ class WallOfSoulsTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getTargetId().equals(this.sourceId) && ((DamagedEvent) event).isCombatDamage()) { - this.getEffects().setValue("damage", event.getAmount()); + + DamagedBatchForOnePermanentEvent dEvent = (DamagedBatchForOnePermanentEvent) event; + int damage = dEvent.getAmount(); + + if (event.getTargetId().equals(this.sourceId) && dEvent.isCombatDamage() && damage > 0) { + this.getEffects().setValue("damage", damage); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/w/WarElemental.java b/Mage.Sets/src/mage/cards/w/WarElemental.java index bf78701cce7..29b0f742e79 100644 --- a/Mage.Sets/src/mage/cards/w/WarElemental.java +++ b/Mage.Sets/src/mage/cards/w/WarElemental.java @@ -70,12 +70,12 @@ class WarElementalTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) { + if (game.getOpponents(this.controllerId).contains(event.getTargetId())) { this.getEffects().get(0).setValue("damageAmount", event.getAmount()); return true; } diff --git a/Mage.Sets/src/mage/cards/w/WildfireElemental.java b/Mage.Sets/src/mage/cards/w/WildfireElemental.java index c6080478dde..26d8c315f6d 100644 --- a/Mage.Sets/src/mage/cards/w/WildfireElemental.java +++ b/Mage.Sets/src/mage/cards/w/WildfireElemental.java @@ -10,7 +10,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.DamagedPlayerEvent; +import mage.game.events.DamagedBatchForOnePlayerEvent; import mage.game.events.GameEvent; import java.util.UUID; @@ -58,14 +58,13 @@ class WildfireElementalTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; } @Override public boolean checkTrigger(GameEvent event, Game game) { - DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event; - return !damageEvent.isCombatDamage() - && game.getOpponents(controllerId).contains(event.getTargetId()); + DamagedBatchForOnePlayerEvent dEvent = (DamagedBatchForOnePlayerEvent) event; + return !dEvent.isCombatDamage() && dEvent.getAmount() > 0 && game.getOpponents(controllerId).contains(dEvent.getTargetId()); } @Override diff --git a/Mage.Sets/src/mage/cards/w/WrathfulRedDragon.java b/Mage.Sets/src/mage/cards/w/WrathfulRedDragon.java index 3e2b9314554..ac1d42c5868 100644 --- a/Mage.Sets/src/mage/cards/w/WrathfulRedDragon.java +++ b/Mage.Sets/src/mage/cards/w/WrathfulRedDragon.java @@ -76,7 +76,7 @@ class WrathfulRedDragonTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SpitefulShadowsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SpitefulShadowsTest.java index 4eaffcbd2b7..35473d2a8a6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SpitefulShadowsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SpitefulShadowsTest.java @@ -16,7 +16,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase; public class SpitefulShadowsTest extends CardTestPlayerBase { @Test - public void testCard() { + public void SpitefulShadowsPoisonTest() { // Infect (This creature deals damage to creatures in the form of -1/-1 counters and to players in the form of poison counters.) addCard(Zone.BATTLEFIELD, playerA, "Glistener Elf"); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); @@ -32,13 +32,13 @@ public class SpitefulShadowsTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.END_TURN); execute(); - assertLife(playerA, 20); - assertLife(playerB, 20); + assertLife(playerA, currentGame.getStartingLife()); + assertLife(playerB, currentGame.getStartingLife()); assertCounterCount(playerA, CounterType.POISON, 3); } @Test - public void testCard1() { + public void SpitefulShadowsRegularTest() { addCard(Zone.BATTLEFIELD, playerA, "Craw Wurm"); // Creature 6/4 addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); @@ -53,9 +53,42 @@ public class SpitefulShadowsTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.END_TURN); execute(); - assertLife(playerA, 17); - assertLife(playerB, 20); + assertLife(playerA, currentGame.getStartingLife() - 3); + assertLife(playerB, currentGame.getStartingLife()); assertCounterCount(playerA, CounterType.POISON, 0); } + @Test + public void SpitefulShadowsMultiDamageTest() { + addCard(Zone.BATTLEFIELD, playerA, "Craw Wurm"); // Creature 6/4 + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerB, "Agent of Stromgald", 1); + + // Whenever an opponent is dealt noncombat damage, Chandra’s Spitfire gets +3/+0 until end of turn. + addCard(Zone.BATTLEFIELD, playerB, "Chandra's Spitfire", 1); + + // Enchant creature + // Whenever enchanted creature is dealt damage, it deals that much damage to its controller. + addCard(Zone.HAND, playerA, "Spiteful Shadows"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spiteful Shadows", "Craw Wurm"); + + attack(1, playerA, "Craw Wurm", playerB); + block(1, playerB, "Memnite", "Craw Wurm"); + block(1, playerB, "Agent of Stromgald", "Craw Wurm"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, currentGame.getStartingLife() - 2); + assertLife(playerB, currentGame.getStartingLife()); + assertCounterCount(playerA, CounterType.POISON, 0); + + // Since Spiteful Shadows should have only triggered once, so should have chandra's spitfire. + assertPowerToughness(playerB, "Chandra's Spitfire", 4, 3); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/VengefulPharaohTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/VengefulPharaohTest.java index 0e0ae36cb51..dd123fc39b7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/control/VengefulPharaohTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/VengefulPharaohTest.java @@ -22,6 +22,8 @@ public class VengefulPharaohTest extends CardTestPlayerBase { // Destroy target permanent. addCard(Zone.HAND, playerA, "Vindicate", 1); // Sorcery {1}{W}{B} + // Enchant creature + // You control enchanted creature. addCard(Zone.HAND, playerB, "Control Magic", 1); // Enchantment addCard(Zone.BATTLEFIELD, playerB, "Island", 4); addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); @@ -45,4 +47,57 @@ public class VengefulPharaohTest extends CardTestPlayerBase { assertLife(playerA, 18); } + + @Test + public void doubleTriggerTest() { + // If multiple creatures deal combat damage to you and to a planeswalker you control simultaneously, + // Vengeful Pharaoh will trigger twice. The first trigger will cause Vengeful Pharaoh to be put on top of your + // library. The second trigger will then do nothing, as Vengeful Pharaoh is no longer in your graveyard when it + // tries to resolve. Note that the second trigger will do nothing even if Vengeful Pharaoh is put back into + // your graveyard before it tries to resolve, as it’s a different Vengeful Pharaoh than the one that was there + // before. (2011-09-22) + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + // Deathtouch (Any amount of damage this deals to a creature is enough to destroy it.) + // Whenever combat damage is dealt to you or a planeswalker you control, if Vengeful Pharaoh is in your + // graveyard, destroy target attacking creature, then put Vengeful Pharaoh on top of your library. + addCard(Zone.BATTLEFIELD, playerA, "Vengeful Pharaoh", 1); // Creature 5/4 {2}{B}{B}{B} + + // Legendary Planeswalker — Tibalt {R}{R} + // +1: Draw a card, then discard a card at random. + // −4: Tibalt, the Fiend-Blooded deals damage equal to the number of cards in target player’s hand to that player. + // −6: Gain control of all creatures until end of turn. Untap them. They gain haste until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Tibalt, the Fiend-Blooded", 1); + + // Destroy target permanent. + addCard(Zone.HAND, playerA, "Vindicate", 1); // Sorcery {1}{W}{B} + + // 2/1 + addCard(Zone.BATTLEFIELD, playerB, "Expedition Envoy", 1); + + // 2/2 + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vindicate", "Vengeful Pharaoh"); + + attack(2, playerB, "Expedition Envoy", playerA); + attack(2, playerB, "Grizzly Bears", "Tibalt, the Fiend-Blooded"); + + // choose trigger to go on the stack first + setChoice(playerA, "Whenever combat damage"); + + addTarget(playerA, "Expedition Envoy"); + addTarget(playerA, "Grizzly Bears"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, currentGame.getStartingLife() - 2); + assertLibraryCount(playerA, "Vengeful Pharaoh", 1); + assertGraveyardCount(playerB, "Grizzly Bears", 1); + assertPermanentCount(playerB, "Expedition Envoy", 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java index e9a069e270a..831b6f77dd8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/damage/ExcessDamageTest.java @@ -12,6 +12,14 @@ public class ExcessDamageTest extends CardTestPlayerBase { private static final String spill = "Flame Spill"; private static final String bear = "Grizzly Bears"; + private static final String envoy = "Expedition Envoy"; + + //Bonded Construct can’t attack alone. + private static final String bondedConstruct = "Bonded Construct"; + + // Spend only mana produced by creatures to cast this spell. + private static final String myrSuperion = "Myr Superion"; + private static final String jab = "Flame Jab"; private static final String spirit = "Pestilent Spirit"; private static final String myr = "Darksteel Myr"; @@ -131,6 +139,44 @@ public class ExcessDamageTest extends CardTestPlayerBase { assertGraveyardCount(playerB, bear, 1); } + @Test + public void testAegarTheFreezingFlameMultiDamage() { + + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + addCard(Zone.BATTLEFIELD, playerA, aegar); + addCard(Zone.BATTLEFIELD, playerA, bear); + addCard(Zone.BATTLEFIELD, playerA, envoy); + addCard(Zone.BATTLEFIELD, playerA, bondedConstruct); + addCard(Zone.HAND, playerA, bolt); + + addCard(Zone.BATTLEFIELD, playerB, myrSuperion); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, myrSuperion, true); + + attack(2, playerB, myrSuperion, playerA); + block(2, playerA, bear, myrSuperion); + block(2, playerA, envoy, myrSuperion); + block(2, playerA, bondedConstruct, myrSuperion); + + //Assign this much damage to the first blocking creature + setChoice(playerB, "X=2"); + + //Assign this much damage to the second blocking creature + setChoice(playerB, "X=1"); + + //Assign this much damage to the third blocking creature + setChoice(playerB, "X=1"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + + // Excess damage was dealt by the total of 3 blocking creatures, 2 of which each dealt more than lethal damage, + // but Aegar's ability should only trigger once + assertHandCount(playerA, 1); + } + @Test public void testMagmaticGalleon() { String mg = "Magmatic Galleon"; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java index 2ae8ccec098..6f1035cc025 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fdn/RiteOfPassageTest.java @@ -25,6 +25,33 @@ public class RiteOfPassageTest extends CardTestPlayerBase { assertCounterCount("Watchwolf", CounterType.P1P1, 1); } + @Test + public void addCounterMultiDamage(){ + // Watchwolf 3/3 + addCard(Zone.BATTLEFIELD, playerA, "Watchwolf", 1); + // Whenever a creature you control is dealt damage, put a +1/+1 counter on it. + addCard(Zone.BATTLEFIELD, playerA, "Rite of Passage"); + + addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerB, "Agent of Stromgald", 1); + + attack(1, playerA, "Watchwolf", playerB); + block(1, playerB, "Memnite", "Watchwolf"); + block(1, playerB, "Agent of Stromgald", "Watchwolf"); + + // Assign this much damage to Memnite + setChoice(playerA, "X=1"); + + // Assign this much damage to Agent of Stromgald + setChoice(playerA, "X=1"); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertCounterCount("Watchwolf", CounterType.P1P1, 1); + } + @Test public void addCounterNonLethalOppControlsVorinclex(){ // Watchwolf 3/3 diff --git a/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedTriggeredAbility.java index 7e00d119919..48227613ddd 100644 --- a/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedTriggeredAbility.java @@ -42,7 +42,7 @@ public class DealtDamageAttachedTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PERMANENT; + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PERMANENT; } @Override diff --git a/Mage/src/main/java/mage/game/events/DamagedBatchForOnePermanentEvent.java b/Mage/src/main/java/mage/game/events/DamagedBatchForOnePermanentEvent.java index 007afd6fcf1..fbf5d254095 100644 --- a/Mage/src/main/java/mage/game/events/DamagedBatchForOnePermanentEvent.java +++ b/Mage/src/main/java/mage/game/events/DamagedBatchForOnePermanentEvent.java @@ -5,4 +5,10 @@ public class DamagedBatchForOnePermanentEvent extends BatchEvent