refactor: fixed dies events support in single cards (part 7, related to #13089, continue from #13088);

This commit is contained in:
Oleg Agafonov 2024-12-07 14:39:52 +04:00
parent b571080260
commit 8af7a492c8
7 changed files with 162 additions and 1 deletions

View file

@ -83,6 +83,9 @@ class NadierAgentOfTheDuskenelEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { 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"); Object obj = getValue("permanentLeftBattlefield");
if (!(obj instanceof Permanent)) { if (!(obj instanceof Permanent)) {
return false; return false;

View file

@ -50,6 +50,7 @@ class ThreeTreeScribeTriggeredAbility extends TriggeredAbilityImpl {
super(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.P1P1.createInstance())); super(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.P1P1.createInstance()));
this.setTriggerPhrase("Whenever {this} or another creature you control leaves the battlefield without dying, "); this.setTriggerPhrase("Whenever {this} or another creature you control leaves the battlefield without dying, ");
this.addTarget(new TargetControlledCreaturePermanent()); this.addTarget(new TargetControlledCreaturePermanent());
setLeavesTheBattlefieldTrigger(true);
} }
private ThreeTreeScribeTriggeredAbility(final ThreeTreeScribeTriggeredAbility ability) { private ThreeTreeScribeTriggeredAbility(final ThreeTreeScribeTriggeredAbility ability) {

View file

@ -61,6 +61,7 @@ class LeavesTheBattlefieldAttachedTriggeredAbility extends ZoneChangeTriggeredAb
public LeavesTheBattlefieldAttachedTriggeredAbility() { public LeavesTheBattlefieldAttachedTriggeredAbility() {
super(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritWhiteToken()), "When enchanted creature leaves the battlefield, ", Boolean.FALSE); super(Zone.BATTLEFIELD, new CreateTokenEffect(new SpiritWhiteToken()), "When enchanted creature leaves the battlefield, ", Boolean.FALSE);
setLeavesTheBattlefieldTrigger(true);
} }
private LeavesTheBattlefieldAttachedTriggeredAbility(final LeavesTheBattlefieldAttachedTriggeredAbility ability) { private LeavesTheBattlefieldAttachedTriggeredAbility(final LeavesTheBattlefieldAttachedTriggeredAbility ability) {

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -2021,10 +2021,12 @@ public class VerifyCardDataTest {
boolean isPutToGraveAbility = rules.contains("put into") boolean isPutToGraveAbility = rules.contains("put into")
&& rules.contains("graveyard") && rules.contains("graveyard")
&& rules.contains("from the battlefield"); && rules.contains("from the battlefield");
boolean isLeavesBattlefield = rules.contains("leaves the battlefield");
isLeavesBattlefield = false; // TODO: remove and fix all bad cards
if (triggeredAbility.isLeavesTheBattlefieldTrigger()) { if (triggeredAbility.isLeavesTheBattlefieldTrigger()) {
// TODO: add check for wrongly enabled settings too? // TODO: add check for wrongly enabled settings too?
} else { } else {
if (isDiesAbility || isPutToGraveAbility) { if (isDiesAbility || isPutToGraveAbility || isLeavesBattlefield) {
fail(card, "abilities", "dies related trigger must use setLeavesTheBattlefieldTrigger(true) and possibly override isInUseableZone - " fail(card, "abilities", "dies related trigger must use setLeavesTheBattlefieldTrigger(true) and possibly override isInUseableZone - "
+ triggeredAbility.getClass().getSimpleName()); + triggeredAbility.getClass().getSimpleName());
} }

View file

@ -39,6 +39,7 @@ public class LeavesBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl {
this.filter = filter; this.filter = filter;
this.setTargetPointer = setTargetPointer; this.setTargetPointer = setTargetPointer;
setTriggerPhrase("Whenever " + filter.getMessage() + " leaves the battlefield, "); setTriggerPhrase("Whenever " + filter.getMessage() + " leaves the battlefield, ");
setLeavesTheBattlefieldTrigger(true);
} }
protected LeavesBattlefieldAllTriggeredAbility(final LeavesBattlefieldAllTriggeredAbility ability) { protected LeavesBattlefieldAllTriggeredAbility(final LeavesBattlefieldAllTriggeredAbility ability) {