From 8af7a492c801e762023dbeb42f3fbd4f9c00e2f6 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 7 Dec 2024 14:39:52 +0400 Subject: [PATCH] refactor: fixed dies events support in single cards (part 7, related to #13089, continue from #13088); --- .../cards/n/NadierAgentOfTheDuskenel.java | 3 + .../src/mage/cards/t/ThreeTreeScribe.java | 1 + .../src/mage/cards/v/ValorOfTheWorthy.java | 1 + .../cmr/NadierAgentOfTheDuskenelTest.java | 83 +++++++++++++++++++ .../single/khm/ValorOfTheWorthyTest.java | 70 ++++++++++++++++ .../java/mage/verify/VerifyCardDataTest.java | 4 +- .../LeavesBattlefieldAllTriggeredAbility.java | 1 + 7 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/NadierAgentOfTheDuskenelTest.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/khm/ValorOfTheWorthyTest.java diff --git a/Mage.Sets/src/mage/cards/n/NadierAgentOfTheDuskenel.java b/Mage.Sets/src/mage/cards/n/NadierAgentOfTheDuskenel.java index e459b12dffa..c327d112c8c 100644 --- a/Mage.Sets/src/mage/cards/n/NadierAgentOfTheDuskenel.java +++ b/Mage.Sets/src/mage/cards/n/NadierAgentOfTheDuskenel.java @@ -83,6 +83,9 @@ class NadierAgentOfTheDuskenelEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { + // For Nadier's second ability, use its power from when it was last on the battlefield to determine + // how many tokens to create. + // (2020-11-10) Object obj = getValue("permanentLeftBattlefield"); if (!(obj instanceof Permanent)) { return false; diff --git a/Mage.Sets/src/mage/cards/t/ThreeTreeScribe.java b/Mage.Sets/src/mage/cards/t/ThreeTreeScribe.java index 5cc58e66ead..b443b915419 100644 --- a/Mage.Sets/src/mage/cards/t/ThreeTreeScribe.java +++ b/Mage.Sets/src/mage/cards/t/ThreeTreeScribe.java @@ -50,6 +50,7 @@ class ThreeTreeScribeTriggeredAbility extends TriggeredAbilityImpl { super(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.P1P1.createInstance())); this.setTriggerPhrase("Whenever {this} or another creature you control leaves the battlefield without dying, "); this.addTarget(new TargetControlledCreaturePermanent()); + setLeavesTheBattlefieldTrigger(true); } private ThreeTreeScribeTriggeredAbility(final ThreeTreeScribeTriggeredAbility ability) { diff --git a/Mage.Sets/src/mage/cards/v/ValorOfTheWorthy.java b/Mage.Sets/src/mage/cards/v/ValorOfTheWorthy.java index d34eec16a03..fa7d0c49398 100644 --- a/Mage.Sets/src/mage/cards/v/ValorOfTheWorthy.java +++ b/Mage.Sets/src/mage/cards/v/ValorOfTheWorthy.java @@ -61,6 +61,7 @@ class LeavesTheBattlefieldAttachedTriggeredAbility extends ZoneChangeTriggeredAb public LeavesTheBattlefieldAttachedTriggeredAbility() { super(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritWhiteToken()), "When enchanted creature leaves the battlefield, ", Boolean.FALSE); + setLeavesTheBattlefieldTrigger(true); } private LeavesTheBattlefieldAttachedTriggeredAbility(final LeavesTheBattlefieldAttachedTriggeredAbility ability) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/NadierAgentOfTheDuskenelTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/NadierAgentOfTheDuskenelTest.java new file mode 100644 index 00000000000..430ae373409 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/NadierAgentOfTheDuskenelTest.java @@ -0,0 +1,83 @@ +package org.mage.test.cards.single.cmr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommanderDuelBase; + +/** + * @author JayDi85 + */ + +public class NadierAgentOfTheDuskenelTest extends CardTestCommanderDuelBase { + + @Test + public void test_DieAnother() { + addCustomEffect_TargetDestroy(playerA); + + // Whenever a token you control leaves the battlefield, put a +1/+1 counter on Nadier, Agent of the Duskenel. + // When Nadier leaves the battlefield, create a number of 1/1 green Elf Warrior creature tokens equal to its power. + addCard(Zone.BATTLEFIELD, playerA, "Nadier, Agent of the Duskenel", 1); + // + // {5}, {T}: Create a 1/1 colorless Insect artifact creature token with flying named Wasp. + addCard(Zone.BATTLEFIELD, playerA, "The Hive", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + + // prepare token + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{5}, {T}: Create"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wasp", 1); + + // on leaves non-token -- nothing to happen + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "The Hive"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1); + checkGraveyardCount("on non-token", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "The Hive", 1); + checkStackSize("on non-token - no triggers", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 0); + + // on leaves token - must trigger + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "Wasp"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("on token", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wasp", 0); + checkPermanentCounters("on token - must trigger", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nadier, Agent of the Duskenel", CounterType.P1P1, 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + } + + @Test + public void test_DieItself() { + addCustomEffect_TargetDestroy(playerA, 2); + + // Whenever a token you control leaves the battlefield, put a +1/+1 counter on Nadier, Agent of the Duskenel. + // When Nadier leaves the battlefield, create a number of 1/1 green Elf Warrior creature tokens equal to its power. + addCard(Zone.BATTLEFIELD, playerA, "Nadier, Agent of the Duskenel", 1); + // + // {5}, {T}: Create a 1/1 colorless Insect artifact creature token with flying named Wasp. + addCard(Zone.BATTLEFIELD, playerA, "The Hive", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + + // prepare token + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{5}, {T}: Create"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wasp", 1); + + // on leaves token and itself -- must x2 triggers: + // * one with counter to fizzle + // * one with new token + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "Wasp^Nadier, Agent of the Duskenel"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1); + checkStackSize("must triggers x2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); + setChoice(playerA, "Whenever a token you control leaves"); // x2 triggers from Nadier + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "The Hive", 1); + assertPermanentCount(playerA, "Wasp", 0); + assertGraveyardCount(playerA, "Nadier, Agent of the Duskenel", 1); + assertPermanentCount(playerA, "Elf Warrior Token", 3); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/ValorOfTheWorthyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/ValorOfTheWorthyTest.java new file mode 100644 index 00000000000..765efe44c1f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/ValorOfTheWorthyTest.java @@ -0,0 +1,70 @@ +package org.mage.test.cards.single.khm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class ValorOfTheWorthyTest extends CardTestPlayerBase { + + @Test + public void test_DieTarget() { + addCustomEffect_TargetDestroy(playerA); + + // Enchant creature + // Enchanted creature gets +1/+1. + // When enchanted creature leaves the battlefield, create a 1/1 white Spirit creature token with flying. + addCard(Zone.HAND, playerA, "Valor of the Worthy"); // {W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + + // attach + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Valor of the Worthy", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkPT("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 2 + 1, 2 + 1); + + // destroy target - must trigger + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy"); + addTarget(playerA, "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1); // resolve destroy + checkStackObject("must trigger on destroy", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "When enchanted creature leaves the battlefield", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertTokenCount(playerA, "Spirit Token", 1); + } + + @Test + public void test_DieItself() { + addCustomEffect_TargetDestroy(playerA, 2); + + // Enchant creature + // Enchanted creature gets +1/+1. + // When enchanted creature leaves the battlefield, create a 1/1 white Spirit creature token with flying. + addCard(Zone.HAND, playerA, "Valor of the Worthy"); // {W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + + // attach + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Valor of the Worthy", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkPT("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 2 + 1, 2 + 1); + + // destroy all - must trigger anyway + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy"); + addTarget(playerA, "Valor of the Worthy^Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1); // resolve destroy + checkStackObject("must trigger on destroy", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "When enchanted creature leaves the battlefield", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertTokenCount(playerA, "Spirit Token", 1); + } +} diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index e018ec86f55..2f42b8867a0 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -2021,10 +2021,12 @@ public class VerifyCardDataTest { boolean isPutToGraveAbility = rules.contains("put into") && rules.contains("graveyard") && rules.contains("from the battlefield"); + boolean isLeavesBattlefield = rules.contains("leaves the battlefield"); + isLeavesBattlefield = false; // TODO: remove and fix all bad cards if (triggeredAbility.isLeavesTheBattlefieldTrigger()) { // TODO: add check for wrongly enabled settings too? } else { - if (isDiesAbility || isPutToGraveAbility) { + if (isDiesAbility || isPutToGraveAbility || isLeavesBattlefield) { fail(card, "abilities", "dies related trigger must use setLeavesTheBattlefieldTrigger(true) and possibly override isInUseableZone - " + triggeredAbility.getClass().getSimpleName()); } diff --git a/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldAllTriggeredAbility.java index 139059b683b..b67304bd323 100644 --- a/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/LeavesBattlefieldAllTriggeredAbility.java @@ -39,6 +39,7 @@ public class LeavesBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl { this.filter = filter; this.setTargetPointer = setTargetPointer; setTriggerPhrase("Whenever " + filter.getMessage() + " leaves the battlefield, "); + setLeavesTheBattlefieldTrigger(true); } protected LeavesBattlefieldAllTriggeredAbility(final LeavesBattlefieldAllTriggeredAbility ability) {