From 4c069754a635bad0f2e13f601e080826f9849f6d Mon Sep 17 00:00:00 2001 From: xenohedron Date: Fri, 16 Aug 2024 23:12:15 -0400 Subject: [PATCH] fix #12657 with common class, add test --- .../src/mage/cards/s/ScytheOfTheWretched.java | 73 ++--------------- .../mage/cards/u/UnscytheKillerOfKings.java | 73 +++-------------- .../src/mage/cards/v/VampiricEmbrace.java | 20 ++--- .../watchers/UnscytheKillerOfKingsTest.java | 24 ++++++ ...DamageAttachedAndDiedTriggeredAbility.java | 78 +++++++++++++++++++ 5 files changed, 126 insertions(+), 142 deletions(-) create mode 100644 Mage/src/main/java/mage/abilities/common/DealtDamageAttachedAndDiedTriggeredAbility.java diff --git a/Mage.Sets/src/mage/cards/s/ScytheOfTheWretched.java b/Mage.Sets/src/mage/cards/s/ScytheOfTheWretched.java index 59c59f13a95..b6bab55ab17 100644 --- a/Mage.Sets/src/mage/cards/s/ScytheOfTheWretched.java +++ b/Mage.Sets/src/mage/cards/s/ScytheOfTheWretched.java @@ -1,10 +1,7 @@ - package mage.cards.s; -import java.util.UUID; -import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DealtDamageAttachedAndDiedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.Effect; @@ -15,18 +12,12 @@ import mage.abilities.keyword.EquipAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; -import mage.target.targetpointer.TargetPointer; + +import java.util.UUID; /** * @@ -43,7 +34,7 @@ public final class ScytheOfTheWretched extends CardImpl { this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(2, 2, Duration.WhileOnBattlefield))); // Whenever a creature dealt damage by equipped creature this turn dies, return that card to the battlefield under your control. Attach Scythe of the Wretched to that creature. - this.addAbility(new ScytheOfTheWretchedTriggeredAbility()); + this.addAbility(new DealtDamageAttachedAndDiedTriggeredAbility(new ScytheOfTheWretchedReanimateEffect(), false)); // Equip {4} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(4), false)); @@ -59,59 +50,6 @@ public final class ScytheOfTheWretched extends CardImpl { } } -class ScytheOfTheWretchedTriggeredAbility extends TriggeredAbilityImpl { - - public ScytheOfTheWretchedTriggeredAbility() { - super(Zone.ALL, new ScytheOfTheWretchedReanimateEffect(), false); - setTriggerPhrase("Whenever a creature dealt damage by equipped creature this turn dies, "); - } - - private ScytheOfTheWretchedTriggeredAbility(final ScytheOfTheWretchedTriggeredAbility ability) { - super(ability); - } - - @Override - public ScytheOfTheWretchedTriggeredAbility copy() { - return new ScytheOfTheWretchedTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - ZoneChangeEvent zoneChange = (ZoneChangeEvent) event; - if (zoneChange.isDiesEvent() && zoneChange.getTarget().isCreature(game)) { - Permanent equippedCreature = getEquippedCreature(game); - for (MageObjectReference mor : zoneChange.getTarget().getDealtDamageByThisTurn()) { - Permanent permanent = (Permanent) game.getLastKnownInformation(mor.getSourceId(), Zone.BATTLEFIELD); - if ((equippedCreature != null && mor.refersTo(equippedCreature, game)) - || (permanent != null && permanent.getAttachments().contains(getSourceId()))) { - setTarget(new FixedTarget(event.getTargetId())); - return true; - } - } - } - return false; - } - - private void setTarget(TargetPointer target) { - for (Effect effect : getEffects()) { - effect.setTargetPointer(target); - } - } - - private Permanent getEquippedCreature(Game game) { - Permanent equipment = game.getPermanent(getSourceId()); - if (equipment != null && equipment.getAttachedTo() != null) { - return game.getPermanent(equipment.getAttachedTo()); - } - return null; - } -} - class ScytheOfTheWretchedReanimateEffect extends OneShotEffect { ScytheOfTheWretchedReanimateEffect() { @@ -134,7 +72,6 @@ class ScytheOfTheWretchedReanimateEffect extends OneShotEffect { effect.apply(game, source); return true; } - return false; } diff --git a/Mage.Sets/src/mage/cards/u/UnscytheKillerOfKings.java b/Mage.Sets/src/mage/cards/u/UnscytheKillerOfKings.java index 3656efb6987..52cad886320 100644 --- a/Mage.Sets/src/mage/cards/u/UnscytheKillerOfKings.java +++ b/Mage.Sets/src/mage/cards/u/UnscytheKillerOfKings.java @@ -1,10 +1,9 @@ package mage.cards.u; -import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DealtDamageAttachedAndDiedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.Effect; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; @@ -15,16 +14,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; import mage.game.permanent.token.ZombieToken; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; +import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; -import mage.abilities.costs.mana.GenericManaCost; -import mage.target.common.TargetControlledCreaturePermanent; /** * @author jeffwadsworth @@ -44,7 +38,7 @@ public final class UnscytheKillerOfKings extends CardImpl { this.addAbility(ability); // Whenever a creature dealt damage by equipped creature this turn dies, you may exile that card. If you do, create a 2/2 black Zombie creature token. - this.addAbility(new UnscytheKillerOfKingsTriggeredAbility(new UnscytheEffect())); + this.addAbility(new DealtDamageAttachedAndDiedTriggeredAbility(new UnscytheEffect(), true)); // Equip {2} this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(2), new TargetControlledCreaturePermanent(), false)); @@ -60,57 +54,11 @@ public final class UnscytheKillerOfKings extends CardImpl { } } -class UnscytheKillerOfKingsTriggeredAbility extends TriggeredAbilityImpl { - - public UnscytheKillerOfKingsTriggeredAbility(Effect effect) { - super(Zone.ALL, effect, true); - setTriggerPhrase("Whenever a creature dealt damage by equipped creature this turn dies, "); - } - - private UnscytheKillerOfKingsTriggeredAbility(final UnscytheKillerOfKingsTriggeredAbility ability) { - super(ability); - } - - @Override - public UnscytheKillerOfKingsTriggeredAbility copy() { - return new UnscytheKillerOfKingsTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!((ZoneChangeEvent) event).isDiesEvent()) { - return false; - } - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (!zEvent.getTarget().isCreature(game)) { - return false; - } // target token can't create Zombie - Permanent equipment = game.getPermanent(getSourceId()); - // the currently equiped creature must have done damage to the dying creature - if (equipment == null || equipment.getAttachedTo() == null) { - return false; - } - boolean damageDealt = false; - for (MageObjectReference mor : zEvent.getTarget().getDealtDamageByThisTurn()) { - if (mor.refersTo(equipment.getAttachedTo(), game)) { - getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); - return true; - } - } - return false; - } -} - class UnscytheEffect extends OneShotEffect { UnscytheEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "you may exile that card. If you do, create a 2/2 black Zombie creature token"; + this.staticText = "exile that card. If you do, create a 2/2 black Zombie creature token"; } private UnscytheEffect(final UnscytheEffect effect) { @@ -125,16 +73,13 @@ class UnscytheEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } Card card = game.getCard(getTargetPointer().getFirst(game, source)); - if (card == null) { + if (controller == null || card == null) { return false; } - if (game.getState().getZone(card.getId()) == Zone.GRAVEYARD && controller.moveCardToExileWithInfo(card, null, "", source, game, Zone.GRAVEYARD, true)) { - ZombieToken zombie = new ZombieToken(); - return zombie.putOntoBattlefield(1, game, source, source.getControllerId()); + if (game.getState().getZone(card.getId()) == Zone.GRAVEYARD + && controller.moveCardToExileWithInfo(card, null, "", source, game, Zone.GRAVEYARD, true)) { + return new ZombieToken().putOntoBattlefield(1, game, source, source.getControllerId()); } return false; } diff --git a/Mage.Sets/src/mage/cards/v/VampiricEmbrace.java b/Mage.Sets/src/mage/cards/v/VampiricEmbrace.java index 58ea09e5157..3a53922d7bc 100644 --- a/Mage.Sets/src/mage/cards/v/VampiricEmbrace.java +++ b/Mage.Sets/src/mage/cards/v/VampiricEmbrace.java @@ -1,28 +1,25 @@ - package mage.cards.v; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.DealtDamageAndDiedTriggeredAbility; +import mage.abilities.common.DealtDamageAttachedAndDiedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; -import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * * @author Backfir3 @@ -39,14 +36,17 @@ public final class VampiricEmbrace extends CardImpl { this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); Ability ability = new EnchantAbility(auraTarget); this.addAbility(ability); + // Enchanted creature gets +2/+2 and has flying. ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(2, 2)); Effect effect = new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA); effect.setText("and has flying"); ability.addEffect(effect); this.addAbility(ability); + // Whenever a creature dealt damage by enchanted creature this turn dies, put a +1/+1 counter on that creature. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(new DealtDamageAndDiedTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false), AttachmentType.AURA))); + this.addAbility(new DealtDamageAttachedAndDiedTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), + false, StaticFilters.FILTER_PERMANENT_CREATURE, SetTargetPointer.PERMANENT, AttachmentType.AURA)); } private VampiricEmbrace(final VampiricEmbrace card) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/UnscytheKillerOfKingsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/UnscytheKillerOfKingsTest.java index f13f44b118f..6f0862487d8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/watchers/UnscytheKillerOfKingsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/watchers/UnscytheKillerOfKingsTest.java @@ -65,4 +65,28 @@ public class UnscytheKillerOfKingsTest extends CardTestPlayerBase { } + @Test + public void testTradeAndTrigger() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Unscythe, Killer of Kings"); + addCard(Zone.BATTLEFIELD, playerA, "Fugitive Wizard"); // 1/1 + addCard(Zone.BATTLEFIELD, playerB, "Minotaur Aggressor"); // 6/2 first strike + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", "Fugitive Wizard"); + + attack(1, playerA, "Fugitive Wizard", playerB); + block(1, playerB, "Minotaur Aggressor", "Fugitive Wizard"); + + setChoice(playerA, true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Fugitive Wizard", 1); + assertPermanentCount(playerA, "Zombie Token", 1); + assertExileCount("Minotaur Aggressor", 1); + + } + } diff --git a/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedAndDiedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedAndDiedTriggeredAbility.java new file mode 100644 index 00000000000..1ad19fa0246 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/DealtDamageAttachedAndDiedTriggeredAbility.java @@ -0,0 +1,78 @@ +package mage.abilities.common; + +import mage.MageObjectReference; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.AttachmentType; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +public class DealtDamageAttachedAndDiedTriggeredAbility extends TriggeredAbilityImpl { + + private final FilterCreaturePermanent filter; + private final SetTargetPointer setTargetPointer; + + public DealtDamageAttachedAndDiedTriggeredAbility(Effect effect, boolean optional) { + this(effect, optional, StaticFilters.FILTER_PERMANENT_CREATURE, SetTargetPointer.PERMANENT, AttachmentType.EQUIPMENT); + } + + public DealtDamageAttachedAndDiedTriggeredAbility(Effect effect, boolean optional, FilterCreaturePermanent filter, + SetTargetPointer setTargetPointer, AttachmentType attachmentType) { + super(Zone.ALL, effect, optional); + this.filter = filter; + this.setTargetPointer = setTargetPointer; + setTriggerPhrase(getWhen() + CardUtil.addArticle(filter.getMessage()) + " dealt damage by " + + CardUtil.getTextWithFirstCharLowerCase(attachmentType.verb()) + + " creature this turn dies, "); + } + + protected DealtDamageAttachedAndDiedTriggeredAbility(final DealtDamageAttachedAndDiedTriggeredAbility ability) { + super(ability); + this.filter = ability.filter; + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public DealtDamageAttachedAndDiedTriggeredAbility copy() { + return new DealtDamageAttachedAndDiedTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent attachment = getSourcePermanentOrLKI(game); + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (attachment == null || !zEvent.isDiesEvent() || !filter.match(zEvent.getTarget(), getControllerId(), this, game)) { + return false; + } + MageObjectReference creatureMOR = new MageObjectReference(attachment.getAttachedTo(), attachment.getAttachedToZoneChangeCounter(), game); + if (zEvent.getTarget().getDealtDamageByThisTurn() + .stream() + .noneMatch(mor -> { + if (mor.equals(creatureMOR)) { + return true; + } + Permanent permanent = (Permanent) game.getLastKnownInformation(mor.getSourceId(), + Zone.BATTLEFIELD, mor.getZoneChangeCounter()); + return permanent != null && permanent.getAttachments().contains(getSourceId()); + })) { + return false; + } + if (this.setTargetPointer == SetTargetPointer.PERMANENT) { + getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); + } + return true; + } +}