Skip to content

Commit

Permalink
refactor: fixed dies events support in single cards (part 8, related to
Browse files Browse the repository at this point in the history
#13089, continue from #13088);
  • Loading branch information
JayDi85 committed Dec 14, 2024
1 parent a970dc4 commit b855434
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 5 deletions.
1 change: 1 addition & 0 deletions Mage.Sets/src/mage/cards/c/CuratorsWard.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class CuratorsWardTriggeredAbility extends TriggeredAbilityImpl {

public CuratorsWardTriggeredAbility() {
super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(2), false);
setLeavesTheBattlefieldTrigger(true);
}

private CuratorsWardTriggeredAbility(final CuratorsWardTriggeredAbility ability) {
Expand Down
1 change: 1 addition & 0 deletions Mage.Sets/src/mage/cards/g/GracefulAntelope.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public GracefulAntelope(UUID ownerId, CardSetInfo setInfo) {

// Plainswalk
this.addAbility(new PlainswalkAbility());

// Whenever Graceful Antelope deals combat damage to a player, you may have target land become a Plains until Graceful Antelope leaves the battlefield.
Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new BecomesBasicLandTargetEffect(Duration.UntilSourceLeavesBattlefield, SubType.PLAINS), true);
ability.addTarget(new TargetLandPermanent());
Expand Down
3 changes: 1 addition & 2 deletions Mage.Sets/src/mage/cards/p/Portcullis.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ public final class Portcullis extends CardImpl {
public Portcullis(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");

// Whenever a creature enters the battlefield, if there are two or more other creatures on the battlefield, exile that creature.
// Return that card to the battlefield under its owner's control when Portcullis leaves the battlefield.
// Whenever a creature enters the battlefield, if there are two or more other creatures on the battlefield, exile that creature. Return that card to the battlefield under its owner's control when Portcullis leaves the battlefield.
String rule = "Whenever a creature enters the battlefield, if there are two or more other creatures on the battlefield, exile that creature.";
String rule2 = " Return that card to the battlefield under its owner's control when {this} leaves the battlefield.";
TriggeredAbility ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new PortcullisExileEffect(),
Expand Down
4 changes: 3 additions & 1 deletion Mage.Sets/src/mage/cards/s/SludgeStrider.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class SludgeStriderTriggeredAbility extends TriggeredAbilityImpl {
public SludgeStriderTriggeredAbility() {
// setting optional = false because DoIfCostPaid already asks the player
super(Zone.BATTLEFIELD, new DoIfCostPaid(new SludgeStriderEffect(), new GenericManaCost(1)), false);
setLeavesTheBattlefieldTrigger(true);
}

private SludgeStriderTriggeredAbility(final SludgeStriderTriggeredAbility ability) {
Expand All @@ -69,7 +70,8 @@ private SludgeStriderTriggeredAbility(final SludgeStriderTriggeredAbility abilit

@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD || event.getType() == GameEvent.EventType.ZONE_CHANGE;
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD
|| event.getType() == GameEvent.EventType.ZONE_CHANGE;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.mage.test.cards.single.who;

import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;

/**
* @author JayDi85
*/
public class CrackInTimeTest extends CardTestPlayerBase {

@Test
public void test_ExileUntilLeaves() {
addCustomEffect_TargetDestroy(playerA);

// When Crack in Time enters the battlefield and at the beginning of your precombat main phase,
// exile target creature an opponent controls until Crack in Time leaves the battlefield.
addCard(Zone.HAND, playerA, "Crack in Time"); // {3}{W}
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1);

// exile
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crack in Time");
addTarget(playerA, "Balduvian Bears"); // exile on etb
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkExileCount("on exile", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Balduvian Bears", 1);

// destroy and return
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "target destroy", "Crack in Time");
waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN);
checkExileCount("on return", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Balduvian Bears", 0);
checkPermanentCount("on return", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Balduvian Bears", 1);

setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
}
}
23 changes: 21 additions & 2 deletions Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import mage.abilities.costs.Cost;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.ExileUntilSourceLeavesEffect;
import mage.abilities.effects.common.FightTargetsEffect;
import mage.abilities.effects.common.counter.ProliferateEffect;
import mage.abilities.effects.keyword.ScryEffect;
Expand Down Expand Up @@ -74,6 +75,7 @@ public class VerifyCardDataTest {

private static final String FULL_ABILITIES_CHECK_SET_CODES = "MH3;M3C"; // check ability text due mtgjson, can use multiple sets like MAT;CMD or * for all
private static final boolean CHECK_ONLY_ABILITIES_TEXT = false; // use when checking text locally, suppresses unnecessary checks and output messages
private static final boolean CHECK_COPYABLE_FIELDS = true; // disable for better verify test performance

private static final boolean AUTO_FIX_SAMPLE_DECKS = false; // debug only: auto-fix sample decks by test_checkSampleDecks test run

Expand Down Expand Up @@ -1632,7 +1634,9 @@ private void checkAll(Card card, MtgJsonCard ref, int cardIndex) {
checkRarityAndBasicLands(card, ref);
checkMissingAbilities(card, ref);
checkWrongSymbolsInRules(card);
checkCardCanBeCopied(card);
if (CHECK_COPYABLE_FIELDS) {
checkCardCanBeCopied(card);
}
}
checkWrongAbilitiesText(card, ref, cardIndex);
}
Expand Down Expand Up @@ -1994,6 +1998,10 @@ private void checkMissingAbilities(Card card, MtgJsonCard ref) {
ignoredCards.add("Infested Thrinax");
ignoredCards.add("Xira, the Golden Sting");
ignoredCards.add("Mawloc");
ignoredCards.add("Crack in Time");
ignoredCards.add("Mysterious Limousine");
ignoredCards.add("Graceful Antelope");
ignoredCards.add("Portcullis");
List<String> ignoredAbilities = new ArrayList<>();
ignoredAbilities.add("roll"); // roll die effects
ignoredAbilities.add("with \"When"); // token creating effects
Expand All @@ -2009,6 +2017,18 @@ private void checkMissingAbilities(Card card, MtgJsonCard ref) {
if (triggeredAbility == null) {
continue;
}

// ignore exile effects
// example 1: exile up to one other target nonland permanent until Constable of the Realm leaves the battlefield.
if (ability.getAllEffects().stream().anyMatch(e -> e instanceof ExileUntilSourceLeavesEffect)) {
continue;
}
// example 2: When Hostage Taker enters the battlefield, exile another target artifact or creature until Hostage Taker leaves the battlefield
if (ability instanceof EntersBattlefieldTriggeredAbility) {
continue;
}


// search and check dies related abilities
String rules = triggeredAbility.getRule();
if (ignoredAbilities.stream().anyMatch(rules::contains)) {
Expand All @@ -2022,7 +2042,6 @@ private void checkMissingAbilities(Card card, MtgJsonCard ref) {
&& 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public OnLeaveReturnExiledAbility(Zone zone) {
super(new ReturnExiledPermanentsEffect(zone), Duration.OneUse);
this.usesStack = false;
this.setRuleVisible(false);
setLeavesTheBattlefieldTrigger(true);
}

protected OnLeaveReturnExiledAbility(final OnLeaveReturnExiledAbility ability) {
Expand Down

0 comments on commit b855434

Please sign in to comment.