Ketramose, the New Dawn - fixed that it doesn't trigger on itself (miss setLeavesTheBattlefieldTrigger, related to eafbd5a95c)

This commit is contained in:
Oleg Agafonov 2025-04-23 05:38:14 +04:00
parent 5b68b20a92
commit 0b4dbaabba
3 changed files with 52 additions and 28 deletions

View file

@ -13,21 +13,14 @@ import mage.abilities.effects.common.combat.CantAttackBlockUnlessConditionSource
import mage.abilities.keyword.IndestructibleAbility; import mage.abilities.keyword.IndestructibleAbility;
import mage.abilities.keyword.LifelinkAbility; import mage.abilities.keyword.LifelinkAbility;
import mage.abilities.keyword.MenaceAbility; import mage.abilities.keyword.MenaceAbility;
import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.*;
import mage.constants.ComparisonType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeBatchEvent; import mage.game.events.ZoneChangeBatchEvent;
import mage.game.events.ZoneChangeEvent; import mage.game.events.ZoneChangeEvent;
import mage.game.events.ZoneChangeGroupEvent;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
/** /**
@ -72,10 +65,13 @@ public final class KetramoseTheNewDawn extends CardImpl {
} }
} }
class KetramoseTriggeredAbility extends TriggeredAbilityImpl {
class KetramoseTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility<ZoneChangeEvent> {
KetramoseTriggeredAbility() { KetramoseTriggeredAbility() {
super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false); super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false);
this.addEffect(new LoseLifeSourceControllerEffect(1)); this.addEffect(new LoseLifeSourceControllerEffect(1));
setLeavesTheBattlefieldTrigger(true);
} }
private KetramoseTriggeredAbility(final KetramoseTriggeredAbility ability) { private KetramoseTriggeredAbility(final KetramoseTriggeredAbility ability) {
@ -84,19 +80,25 @@ class KetramoseTriggeredAbility extends TriggeredAbilityImpl {
@Override @Override
public boolean checkEventType(GameEvent event, Game game) { public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ZONE_CHANGE_GROUP; return event.getType() == GameEvent.EventType.ZONE_CHANGE_BATCH;
}
@Override
public boolean checkEvent(ZoneChangeEvent event, Game game) {
if (event.getToZone() != Zone.EXILED) {
return false;
}
return event.getFromZone() == Zone.GRAVEYARD || event.getFromZone() == Zone.BATTLEFIELD;
} }
@Override @Override
public boolean checkTrigger(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
return game.getActivePlayerId().equals(getControllerId()) return game.getActivePlayerId().equals(getControllerId())
&& zEvent.getFromZone() == Zone.GRAVEYARD || zEvent.getFromZone() == Zone.BATTLEFIELD; && !getFilteredEvents((ZoneChangeBatchEvent) event, game).isEmpty();
} }
@Override @Override
public TriggeredAbility copy() public TriggeredAbility copy() {
{
return new KetramoseTriggeredAbility(this); return new KetramoseTriggeredAbility(this);
} }

View file

@ -1,4 +1,3 @@
package mage.cards.r; package mage.cards.r;
import java.util.UUID; import java.util.UUID;
@ -32,10 +31,11 @@ public final class RelicOfProgenitus extends CardImpl {
public RelicOfProgenitus(UUID ownerId, CardSetInfo setInfo) { public RelicOfProgenitus(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}");
// {tap}: Target player exiles a card from their graveyard. // {T}: Target player exiles a card from their graveyard.
Ability firstAbility = new SimpleActivatedAbility(new RelicOfProgenitusEffect(), new TapSourceCost()); Ability firstAbility = new SimpleActivatedAbility(new RelicOfProgenitusEffect(), new TapSourceCost());
firstAbility.addTarget(new TargetPlayer()); firstAbility.addTarget(new TargetPlayer());
this.addAbility(firstAbility); this.addAbility(firstAbility);
// {1}, Exile Relic of Progenitus: Exile all cards from all graveyards. Draw a card. // {1}, Exile Relic of Progenitus: Exile all cards from all graveyards. Draw a card.
Ability secondAbility = new SimpleActivatedAbility(new ExileGraveyardAllPlayersEffect(), new GenericManaCost(1)); Ability secondAbility = new SimpleActivatedAbility(new ExileGraveyardAllPlayersEffect(), new GenericManaCost(1));
secondAbility.addCost(new ExileSourceCost()); secondAbility.addCost(new ExileSourceCost());

View file

@ -5,17 +5,27 @@ import mage.constants.Zone;
import org.junit.Test; import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase; import org.mage.test.serverside.base.CardTestPlayerBase;
import static org.junit.jupiter.api.Assertions.*;
public class KetramoseTheNewDawnTest extends CardTestPlayerBase { public class KetramoseTheNewDawnTest extends CardTestPlayerBase {
/**
* Whenever one or more cards are put into exile from graveyards and/or the battlefield
* during your turn, you draw a card and lose 1 life.
*/
private final String ketramose = "Ketramose, the New Dawn"; private final String ketramose = "Ketramose, the New Dawn";
/**
* {T}: Target player exiles a card from their graveyard.
* {1}, Exile Relic of Progenitus: Exile all cards from all graveyards. Draw a card.
*/
private final String relic = "Relic of Progenitus"; private final String relic = "Relic of Progenitus";
/**
* Exile target creature you control, then return it to the battlefield under its owner's control.
*/
private final String ephemerate = "Ephemerate"; private final String ephemerate = "Ephemerate";
@Test @Test
public void testExile() { public void testExile() {
setStrictChooseMode(true); setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, ketramose); addCard(Zone.BATTLEFIELD, playerA, ketramose);
addCard(Zone.BATTLEFIELD, playerA, relic); addCard(Zone.BATTLEFIELD, playerA, relic);
addCard(Zone.HAND, playerA, ephemerate); addCard(Zone.HAND, playerA, ephemerate);
@ -23,17 +33,29 @@ public class KetramoseTheNewDawnTest extends CardTestPlayerBase {
addCard(Zone.GRAVEYARD, playerA, "Forest", 10); addCard(Zone.GRAVEYARD, playerA, "Forest", 10);
addCard(Zone.GRAVEYARD, playerB, "Forest", 10); addCard(Zone.GRAVEYARD, playerB, "Forest", 10);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}:"); // exile single
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target player");
addTarget(playerA, playerB); addTarget(playerA, playerB);
addTarget(playerB, "Forest"); addTarget(playerB, "Forest");
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{1}, Exile {this}"); waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN); checkLife("exile single - after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 20 - 1);
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, ephemerate, ketramose, true);
// must have two triggers: exile on cost and exile on resolve
// exile on cost
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, Exile {this}");
checkStackSize("exile on cost - ability + trigger on stack", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2);
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1);
checkLife("exile on cost - after trigger", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 20 - 1 - 1);
// exile on resolve
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkLife("exile on resolve - after trigger", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 20 - 1 - 1 - 1);
// exile ketramose itself and return
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ephemerate, ketramose);
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkLife("exile itself - must trigger", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 20 - 1 - 1 - 1 - 1);
setStopAt(1, PhaseStep.END_TURN); setStopAt(1, PhaseStep.END_TURN);
execute(); execute();
assertHandCount(playerA, 4);
assertLife(playerA, 20 - 3);
} }
} }