mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 10:40:06 -08:00
refactor: improved ETB rules generations, fixed some cards/abilities (related to #12791)
This commit is contained in:
parent
0d0661cc92
commit
19269b22b0
24 changed files with 129 additions and 41 deletions
|
|
@ -31,10 +31,10 @@ public final class BloodtitheCollector extends CardImpl {
|
||||||
// Flying
|
// Flying
|
||||||
this.addAbility(FlyingAbility.getInstance());
|
this.addAbility(FlyingAbility.getInstance());
|
||||||
|
|
||||||
// When Bloodtithe Collector enters the battlefield, if an opponent lost life this turn, each opponent discards a card.
|
// When this creature enters, if an opponent lost life this turn, each opponent discards a card.
|
||||||
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
|
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
|
||||||
new EntersBattlefieldTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT)),
|
new EntersBattlefieldTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT)),
|
||||||
OpponentsLostLifeCondition.instance, "When {this} enters, " +
|
OpponentsLostLifeCondition.instance, "When this creature enters, " +
|
||||||
"if an opponent lost life this turn, each opponent discards a card."
|
"if an opponent lost life this turn, each opponent discards a card."
|
||||||
).addHint(OpponentsLostLifeHint.instance));
|
).addHint(OpponentsLostLifeHint.instance));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ public final class CemeteryTampering extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}");
|
||||||
|
|
||||||
// Hideaway 5
|
// Hideaway 5
|
||||||
this.addAbility(new HideawayAbility(5));
|
this.addAbility(new HideawayAbility(this, 5));
|
||||||
|
|
||||||
// At the beginning of your upkeep, you may mill three cards. Then if there are twenty or more cards in your graveyard, you may play the exiled card without paying its mana cost.
|
// At the beginning of your upkeep, you may mill three cards. Then if there are twenty or more cards in your graveyard, you may play the exiled card without paying its mana cost.
|
||||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(
|
this.addAbility(new BeginningOfUpkeepTriggeredAbility(
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ public final class CollectorsCage extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{W}");
|
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{W}");
|
||||||
|
|
||||||
// Hideaway 5
|
// Hideaway 5
|
||||||
this.addAbility(new HideawayAbility(5));
|
this.addAbility(new HideawayAbility(this, 5));
|
||||||
|
|
||||||
// {1}, {T}: Put a +1/+1 counter on target creature you control. Then if you control three or more creatures with different powers, you may play the exiled card without paying its mana cost.
|
// {1}, {T}: Put a +1/+1 counter on target creature you control. Then if you control three or more creatures with different powers, you may play the exiled card without paying its mana cost.
|
||||||
Ability ability = new SimpleActivatedAbility(
|
Ability ability = new SimpleActivatedAbility(
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,12 @@ public final class DwynensElite extends CardImpl {
|
||||||
this.power = new MageInt(2);
|
this.power = new MageInt(2);
|
||||||
this.toughness = new MageInt(2);
|
this.toughness = new MageInt(2);
|
||||||
|
|
||||||
// When Dwynen's Elite enters the battlefield, if you control another Elf, create a 1/1 green Elf Warrior creature token.
|
// When this creature enters, if you control another Elf, create a 1/1 green Elf Warrior creature token.
|
||||||
TriggeredAbility triggeredAbility = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ElfWarriorToken()));
|
TriggeredAbility triggeredAbility = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ElfWarriorToken()));
|
||||||
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
|
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
|
||||||
triggeredAbility,
|
triggeredAbility,
|
||||||
new PermanentsOnTheBattlefieldCondition(filter),
|
new PermanentsOnTheBattlefieldCondition(filter),
|
||||||
"When {this} enters, if you control another Elf, create a 1/1 green Elf Warrior creature token."));
|
"When this creature enters, if you control another Elf, create a 1/1 green Elf Warrior creature token."));
|
||||||
}
|
}
|
||||||
|
|
||||||
private DwynensElite(final DwynensElite card) {
|
private DwynensElite(final DwynensElite card) {
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ public final class FightRigging extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
|
||||||
|
|
||||||
// Hideaway 5
|
// Hideaway 5
|
||||||
this.addAbility(new HideawayAbility(5));
|
this.addAbility(new HideawayAbility(this, 5));
|
||||||
|
|
||||||
// At the beginning of combat on your turn, put a +1/+1 counter on target creature you control. Then if you control a creature with power 7 or greater, you may play the exiled card without paying its mana cost.
|
// At the beginning of combat on your turn, put a +1/+1 counter on target creature you control. Then if you control a creature with power 7 or greater, you may play the exiled card without paying its mana cost.
|
||||||
Ability ability = new BeginningOfCombatTriggeredAbility(
|
Ability ability = new BeginningOfCombatTriggeredAbility(
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,11 @@ public final class GatekeeperOfMalakir extends CardImpl {
|
||||||
// Kicker {B} (You may pay an additional {B} as you cast this spell.)
|
// Kicker {B} (You may pay an additional {B} as you cast this spell.)
|
||||||
this.addAbility(new KickerAbility("{B}"));
|
this.addAbility(new KickerAbility("{B}"));
|
||||||
|
|
||||||
// When Gatekeeper of Malakir enters the battlefield, if it was kicked, target player sacrifices a creature.
|
// When this creature enters, if it was kicked, target player sacrifices a creature.
|
||||||
EntersBattlefieldTriggeredAbility ability =
|
EntersBattlefieldTriggeredAbility ability =
|
||||||
new EntersBattlefieldTriggeredAbility(new SacrificeEffect(filter, 1, "target player"));
|
new EntersBattlefieldTriggeredAbility(new SacrificeEffect(filter, 1, "target player"))
|
||||||
Ability conditionalAbility = new ConditionalInterveningIfTriggeredAbility(ability, KickedCondition.ONCE, "When {this} enters, if it was kicked, target player sacrifices a creature.");
|
.setTriggerPhrase("When this creature enters, ");
|
||||||
|
Ability conditionalAbility = new ConditionalInterveningIfTriggeredAbility(ability, KickedCondition.ONCE, "When this creature enters, if it was kicked, target player sacrifices a creature.");
|
||||||
conditionalAbility.addTarget(new TargetPlayer());
|
conditionalAbility.addTarget(new TargetPlayer());
|
||||||
this.addAbility(conditionalAbility);
|
this.addAbility(conditionalAbility);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ public final class GoblinBushwhacker extends CardImpl {
|
||||||
// Kicker {R} (You may pay an additional {R} as you cast this spell.)
|
// Kicker {R} (You may pay an additional {R} as you cast this spell.)
|
||||||
this.addAbility(new KickerAbility("{R}"));
|
this.addAbility(new KickerAbility("{R}"));
|
||||||
|
|
||||||
// When Goblin Bushwhacker enters the battlefield, if it was kicked, creatures you control get +1/+0 and gain haste until end of turn.
|
// When this creature enters, if it was kicked, creatures you control get +1/+0 and gain haste until end of turn.
|
||||||
EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new BoostControlledEffect(1, 0, Duration.EndOfTurn), false);
|
EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new BoostControlledEffect(1, 0, Duration.EndOfTurn), false);
|
||||||
ability.addEffect(new GainAbilityControlledEffect(
|
ability.addEffect(new GainAbilityControlledEffect(
|
||||||
HasteAbility.getInstance(),
|
HasteAbility.getInstance(),
|
||||||
|
|
@ -44,7 +44,7 @@ public final class GoblinBushwhacker extends CardImpl {
|
||||||
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
|
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
|
||||||
ability,
|
ability,
|
||||||
KickedCondition.ONCE,
|
KickedCondition.ONCE,
|
||||||
"When {this} enters, "
|
"When this creature enters, "
|
||||||
+ "if it was kicked, "
|
+ "if it was kicked, "
|
||||||
+ "creatures you control get +1/+0 and gain haste until end of turn."
|
+ "creatures you control get +1/+0 and gain haste until end of turn."
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ public final class HowltoothHollow extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
||||||
|
|
||||||
// Hideaway
|
// Hideaway
|
||||||
this.addAbility(new HideawayAbility(4));
|
this.addAbility(new HideawayAbility(this, 4));
|
||||||
this.addAbility(new EntersBattlefieldTappedAbility());
|
this.addAbility(new EntersBattlefieldTappedAbility());
|
||||||
|
|
||||||
// {tap}: Add {B}.
|
// {tap}: Add {B}.
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ public final class MosswortBridge extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
||||||
|
|
||||||
// Hideaway (This land enters the battlefield tapped. When it does, look at the top four cards of your library, exile one face down, then put the rest on the bottom of your library.)
|
// Hideaway (This land enters the battlefield tapped. When it does, look at the top four cards of your library, exile one face down, then put the rest on the bottom of your library.)
|
||||||
this.addAbility(new HideawayAbility(4));
|
this.addAbility(new HideawayAbility(this, 4));
|
||||||
this.addAbility(new EntersBattlefieldTappedAbility());
|
this.addAbility(new EntersBattlefieldTappedAbility());
|
||||||
|
|
||||||
// {T}: Add {G}.
|
// {T}: Add {G}.
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,10 @@ public final class NullpriestOfOblivion extends CardImpl {
|
||||||
// Lifelink
|
// Lifelink
|
||||||
this.addAbility(LifelinkAbility.getInstance());
|
this.addAbility(LifelinkAbility.getInstance());
|
||||||
|
|
||||||
// When Nullpriest of Oblivion enters the battlefield, if it was kicked, return target creature card from your graveyard to the battlefield.
|
// When this creature enters the battlefield, if it was kicked, return target creature card from your graveyard to the battlefield.
|
||||||
Ability ability = new ConditionalInterveningIfTriggeredAbility(
|
Ability ability = new ConditionalInterveningIfTriggeredAbility(
|
||||||
new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()),
|
new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()),
|
||||||
KickedCondition.ONCE, "When {this} enters, if it was kicked, " +
|
KickedCondition.ONCE, "When this creature enters, if it was kicked, " +
|
||||||
"return target creature card from your graveyard to the battlefield."
|
"return target creature card from your graveyard to the battlefield."
|
||||||
);
|
);
|
||||||
ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD));
|
ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD));
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ public final class RabbleRousing extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}");
|
||||||
|
|
||||||
// Hideaway 5
|
// Hideaway 5
|
||||||
this.addAbility(new HideawayAbility(5));
|
this.addAbility(new HideawayAbility(this, 5));
|
||||||
|
|
||||||
// Whenever you attack with one or more creatures, create that many 1/1 green and white Citizen creature tokens. Then if you control ten or more creatures, you may play the exiled card without paying its mana cost.
|
// Whenever you attack with one or more creatures, create that many 1/1 green and white Citizen creature tokens. Then if you control ten or more creatures, you may play the exiled card without paying its mana cost.
|
||||||
Ability ability = new AttacksWithCreaturesTriggeredAbility(
|
Ability ability = new AttacksWithCreaturesTriggeredAbility(
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ public final class ShelldockIsle extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
||||||
|
|
||||||
// Hideaway
|
// Hideaway
|
||||||
this.addAbility(new HideawayAbility(4));
|
this.addAbility(new HideawayAbility(this, 4));
|
||||||
this.addAbility(new EntersBattlefieldTappedAbility());
|
this.addAbility(new EntersBattlefieldTappedAbility());
|
||||||
|
|
||||||
// {tap}: Add {U}.
|
// {tap}: Add {U}.
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ public class SmugglersBuggy extends CardImpl {
|
||||||
// Hideaway 4
|
// Hideaway 4
|
||||||
// (When this artifact enters the battlefield, look at the top four cards of your library,
|
// (When this artifact enters the battlefield, look at the top four cards of your library,
|
||||||
// exile one face down, then put the rest on the bottom in a random order.)
|
// exile one face down, then put the rest on the bottom in a random order.)
|
||||||
this.addAbility(new HideawayAbility(4));
|
this.addAbility(new HideawayAbility(this, 4));
|
||||||
|
|
||||||
// Whenever Smuggler’s Buggy deals combat damage to a player, you may cast the exiled card without paying its mana cost.
|
// Whenever Smuggler’s Buggy deals combat damage to a player, you may cast the exiled card without paying its mana cost.
|
||||||
// If you do, return Smuggler’s Buggy to its owner’s hand.
|
// If you do, return Smuggler’s Buggy to its owner’s hand.
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ public final class SpinerockKnoll extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
||||||
|
|
||||||
// Hideaway
|
// Hideaway
|
||||||
this.addAbility(new HideawayAbility(4));
|
this.addAbility(new HideawayAbility(this, 4));
|
||||||
this.addAbility(new EntersBattlefieldTappedAbility());
|
this.addAbility(new EntersBattlefieldTappedAbility());
|
||||||
|
|
||||||
// {tap}: Add {R}.
|
// {tap}: Add {R}.
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,11 @@ public final class StormFleetSpy extends CardImpl {
|
||||||
this.power = new MageInt(2);
|
this.power = new MageInt(2);
|
||||||
this.toughness = new MageInt(2);
|
this.toughness = new MageInt(2);
|
||||||
|
|
||||||
// Raid — When Storm Fleet Spy enters the battlefield, if you attacked this turn, draw a card.
|
// Raid — When this creature enters, if you attacked this turn, draw a card.
|
||||||
Ability ability = new ConditionalInterveningIfTriggeredAbility(
|
Ability ability = new ConditionalInterveningIfTriggeredAbility(
|
||||||
new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)),
|
new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)),
|
||||||
RaidCondition.instance,
|
RaidCondition.instance,
|
||||||
"When {this} enters, if you attacked this turn, draw a card.");
|
"When this creature enters, if you attacked this turn, draw a card.");
|
||||||
ability.setAbilityWord(AbilityWord.RAID);
|
ability.setAbilityWord(AbilityWord.RAID);
|
||||||
ability.addHint(RaidHint.instance);
|
ability.addHint(RaidHint.instance);
|
||||||
this.addAbility(ability, new PlayerAttackedWatcher());
|
this.addAbility(ability, new PlayerAttackedWatcher());
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ public final class WatcherForTomorrow extends CardImpl {
|
||||||
this.toughness = new MageInt(1);
|
this.toughness = new MageInt(1);
|
||||||
|
|
||||||
// Hideaway
|
// Hideaway
|
||||||
this.addAbility(new HideawayAbility(4));
|
this.addAbility(new HideawayAbility(this, 4));
|
||||||
this.addAbility(new EntersBattlefieldTappedAbility());
|
this.addAbility(new EntersBattlefieldTappedAbility());
|
||||||
|
|
||||||
// When Watcher for Tomorrow leaves the battlefield, put the exiled card into its owner's hand.
|
// When Watcher for Tomorrow leaves the battlefield, put the exiled card into its owner's hand.
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ public final class WidespreadThieving extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}");
|
||||||
|
|
||||||
// Hideaway 5
|
// Hideaway 5
|
||||||
this.addAbility(new HideawayAbility(5));
|
this.addAbility(new HideawayAbility(this, 5));
|
||||||
|
|
||||||
// Whenever you cast a multicolored spell, create a Treasure token. Then you may pay {W}{U}{B}{R}{G}. If you do, you may play the exiled card without paying its mana cost.
|
// Whenever you cast a multicolored spell, create a Treasure token. Then you may pay {W}{U}{B}{R}{G}. If you do, you may play the exiled card without paying its mana cost.
|
||||||
Ability ability = new SpellCastControllerTriggeredAbility(
|
Ability ability = new SpellCastControllerTriggeredAbility(
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ public final class WindbriskHeights extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
||||||
|
|
||||||
// Hideaway (This land enters the battlefield tapped. When it does, look at the top four cards of your library, exile one face down, then put the rest on the bottom of your library.)
|
// Hideaway (This land enters the battlefield tapped. When it does, look at the top four cards of your library, exile one face down, then put the rest on the bottom of your library.)
|
||||||
this.addAbility(new HideawayAbility(4));
|
this.addAbility(new HideawayAbility(this, 4));
|
||||||
this.addAbility(new EntersBattlefieldTappedAbility());
|
this.addAbility(new EntersBattlefieldTappedAbility());
|
||||||
|
|
||||||
// {tap}: Add {W}.
|
// {tap}: Add {W}.
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ public final class Wiretapping extends CardImpl {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}");
|
||||||
|
|
||||||
// Hideaway 5
|
// Hideaway 5
|
||||||
this.addAbility(new HideawayAbility(5));
|
this.addAbility(new HideawayAbility(this, 5));
|
||||||
|
|
||||||
// Whenever you draw your first card during each of your draw steps, draw a card. Then if you have nine or more cards in hand, you may play the exiled card without paying its mana cost.
|
// Whenever you draw your first card during each of your draw steps, draw a card. Then if you have nine or more cards in hand, you may play the exiled card without paying its mana cost.
|
||||||
this.addAbility(new WiretappingTriggeredAbility());
|
this.addAbility(new WiretappingTriggeredAbility());
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,8 @@ public class VerifyCardDataTest {
|
||||||
"shroud", "banding", "flanking", "horsemanship", "legendary landwalk"
|
"shroud", "banding", "flanking", "horsemanship", "legendary landwalk"
|
||||||
);
|
);
|
||||||
|
|
||||||
private static final List<String> doubleWords = new ArrayList<>();
|
private static final List<String> doubleWords = new ArrayList<>(); // for inner calc
|
||||||
|
private static final List<String> etbTriggerPhrases = new ArrayList<>(); // for inner calc
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// numbers
|
// numbers
|
||||||
|
|
@ -1989,6 +1990,7 @@ public class VerifyCardDataTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
String refLowerText = ref.text.toLowerCase(Locale.ENGLISH);
|
String refLowerText = ref.text.toLowerCase(Locale.ENGLISH);
|
||||||
|
String cardLowerText = String.join("\n", card.getRules()).toLowerCase(Locale.ENGLISH);
|
||||||
|
|
||||||
// special check: kicker ability must be in rules
|
// special check: kicker ability must be in rules
|
||||||
if (card.getAbilities().containsClass(MultikickerAbility.class) && card.getRules().stream().noneMatch(rule -> rule.contains("Multikicker"))) {
|
if (card.getAbilities().containsClass(MultikickerAbility.class) && card.getRules().stream().noneMatch(rule -> rule.contains("Multikicker"))) {
|
||||||
|
|
@ -2048,6 +2050,21 @@ public class VerifyCardDataTest {
|
||||||
fail(card, "abilities", "mutate cards aren't implemented and shouldn't be available");
|
fail(card, "abilities", "mutate cards aren't implemented and shouldn't be available");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// special check: some new creature's ETB must use When this creature enters instead When {this} enters
|
||||||
|
if (EntersBattlefieldTriggeredAbility.ENABLE_TRIGGER_PHRASE_AUTO_FIX) {
|
||||||
|
if (etbTriggerPhrases.isEmpty()) {
|
||||||
|
etbTriggerPhrases.addAll(EntersBattlefieldTriggeredAbility.getPossibleTriggerPhrases());
|
||||||
|
Assert.assertTrue(etbTriggerPhrases.get(0).startsWith("when"));
|
||||||
|
}
|
||||||
|
if (refLowerText.contains("when")) {
|
||||||
|
for (String needTriggerPhrase : etbTriggerPhrases) {
|
||||||
|
if (refLowerText.contains(needTriggerPhrase) && !cardLowerText.contains(needTriggerPhrase)) {
|
||||||
|
fail(card, "abilities", "wrong creature's ETB trigger phrase, must use: " + needTriggerPhrase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// special check: wrong dies triggers (there are also a runtime check on wrong usage, see isInUseableZoneDiesTrigger)
|
// special check: wrong dies triggers (there are also a runtime check on wrong usage, see isInUseableZoneDiesTrigger)
|
||||||
Set<String> ignoredCards = new HashSet<>();
|
Set<String> ignoredCards = new HashSet<>();
|
||||||
ignoredCards.add("Caller of the Claw");
|
ignoredCards.add("Caller of the Claw");
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,22 @@ package mage.abilities.common;
|
||||||
|
|
||||||
import mage.abilities.TriggeredAbilityImpl;
|
import mage.abilities.TriggeredAbilityImpl;
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
|
import mage.cards.Card;
|
||||||
|
import mage.constants.SubType;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
public class EntersBattlefieldTriggeredAbility extends TriggeredAbilityImpl {
|
public class EntersBattlefieldTriggeredAbility extends TriggeredAbilityImpl {
|
||||||
|
|
||||||
|
static public boolean ENABLE_TRIGGER_PHRASE_AUTO_FIX = false;
|
||||||
|
|
||||||
public EntersBattlefieldTriggeredAbility(Effect effect) {
|
public EntersBattlefieldTriggeredAbility(Effect effect) {
|
||||||
this(effect, false);
|
this(effect, false);
|
||||||
}
|
}
|
||||||
|
|
@ -47,4 +54,59 @@ public class EntersBattlefieldTriggeredAbility extends TriggeredAbilityImpl {
|
||||||
public EntersBattlefieldTriggeredAbility copy() {
|
public EntersBattlefieldTriggeredAbility copy() {
|
||||||
return new EntersBattlefieldTriggeredAbility(this);
|
return new EntersBattlefieldTriggeredAbility(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntersBattlefieldTriggeredAbility setTriggerPhrase(String triggerPhrase) {
|
||||||
|
super.setTriggerPhrase(triggerPhrase);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find description of "{this}" like "this creature"
|
||||||
|
*/
|
||||||
|
static public String getThisObjectDescription(Card card) {
|
||||||
|
// prepare {this} description
|
||||||
|
|
||||||
|
// short names like Aatchik for Aatchik, Emerald Radian
|
||||||
|
// except: Mu Yanling, Wind Rider (maybe related to spaces in name)
|
||||||
|
List<String> parts = Arrays.asList(card.getName().split(","));
|
||||||
|
if (parts.size() > 1 && !parts.get(0).contains(" ")) {
|
||||||
|
return parts.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// some types have priority, e.g. Vehicle instead artifact, example: Boommobile
|
||||||
|
if (card.getSubtype().contains(SubType.VEHICLE)) {
|
||||||
|
return "this Vehicle";
|
||||||
|
}
|
||||||
|
if (card.getSubtype().contains(SubType.AURA)) {
|
||||||
|
return "this Aura";
|
||||||
|
}
|
||||||
|
|
||||||
|
// by priority
|
||||||
|
if (card.isCreature()) {
|
||||||
|
return "this creature";
|
||||||
|
} else if (card.isPlaneswalker()) {
|
||||||
|
return "this planeswalker";
|
||||||
|
} else if (card.isLand()) {
|
||||||
|
return "this land";
|
||||||
|
} else if (card.isEnchantment()) {
|
||||||
|
return "this enchantment";
|
||||||
|
} else if (card.isArtifact()) {
|
||||||
|
return "this artifact";
|
||||||
|
} else {
|
||||||
|
return "this permanent";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getPossibleTriggerPhrases() {
|
||||||
|
// for verify tests - must be same list as above (only {this} relates phrases)
|
||||||
|
return Arrays.asList(
|
||||||
|
"when this creature enters",
|
||||||
|
"when this planeswalker enters",
|
||||||
|
"when this land enters",
|
||||||
|
"when this enchantment enters",
|
||||||
|
"when this artifact enters",
|
||||||
|
"when this permanent enters"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ import java.util.UUID;
|
||||||
public class ChampionAbility extends StaticAbility {
|
public class ChampionAbility extends StaticAbility {
|
||||||
|
|
||||||
protected final String objectDescription;
|
protected final String objectDescription;
|
||||||
|
protected final String etbObjectDescription;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Champion one or more creature types or if the subtype array is empty
|
* Champion one or more creature types or if the subtype array is empty
|
||||||
|
|
@ -59,6 +60,8 @@ public class ChampionAbility extends StaticAbility {
|
||||||
public ChampionAbility(Card card, SubType... subtypes) {
|
public ChampionAbility(Card card, SubType... subtypes) {
|
||||||
super(Zone.BATTLEFIELD, null);
|
super(Zone.BATTLEFIELD, null);
|
||||||
|
|
||||||
|
this.etbObjectDescription = EntersBattlefieldTriggeredAbility.getThisObjectDescription(card);
|
||||||
|
|
||||||
List<SubType> subTypes = Arrays.asList(subtypes);
|
List<SubType> subTypes = Arrays.asList(subtypes);
|
||||||
FilterControlledPermanent filter;
|
FilterControlledPermanent filter;
|
||||||
switch (subTypes.size()) {
|
switch (subTypes.size()) {
|
||||||
|
|
@ -105,6 +108,7 @@ public class ChampionAbility extends StaticAbility {
|
||||||
protected ChampionAbility(final ChampionAbility ability) {
|
protected ChampionAbility(final ChampionAbility ability) {
|
||||||
super(ability);
|
super(ability);
|
||||||
this.objectDescription = ability.objectDescription;
|
this.objectDescription = ability.objectDescription;
|
||||||
|
this.etbObjectDescription = ability.etbObjectDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -115,7 +119,7 @@ public class ChampionAbility extends StaticAbility {
|
||||||
@Override
|
@Override
|
||||||
public String getRule() {
|
public String getRule() {
|
||||||
return "Champion " + CardUtil.addArticle(objectDescription)
|
return "Champion " + CardUtil.addArticle(objectDescription)
|
||||||
+ " <i>(When this enters the battlefield, sacrifice it unless you exile another " + objectDescription
|
+ " <i>(When " + etbObjectDescription + " enters, sacrifice it unless you exile another " + objectDescription
|
||||||
+ " you control. When this leaves the battlefield, that card returns to the battlefield.)</i>";
|
+ " you control. When this leaves the battlefield, that card returns to the battlefield.)</i>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,21 +36,25 @@ import java.util.*;
|
||||||
public class HideawayAbility extends EntersBattlefieldTriggeredAbility {
|
public class HideawayAbility extends EntersBattlefieldTriggeredAbility {
|
||||||
|
|
||||||
private final int amount;
|
private final int amount;
|
||||||
|
private final String etbObjectDescription;
|
||||||
|
|
||||||
public HideawayAbility(int amount) {
|
public HideawayAbility(Card card, int amount) {
|
||||||
super(new HideawayExileEffect(amount));
|
super(new HideawayExileEffect(amount));
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
this.addWatcher(new HideawayWatcher());
|
this.addWatcher(new HideawayWatcher());
|
||||||
|
|
||||||
|
this.etbObjectDescription = EntersBattlefieldTriggeredAbility.getThisObjectDescription(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HideawayAbility(final HideawayAbility ability) {
|
private HideawayAbility(final HideawayAbility ability) {
|
||||||
super(ability);
|
super(ability);
|
||||||
this.amount = ability.amount;
|
this.amount = ability.amount;
|
||||||
|
this.etbObjectDescription = ability.etbObjectDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRule() {
|
public String getRule() {
|
||||||
return "Hideaway " + this.amount + " <i>(When this permanent enters the battlefield, look at the top "
|
return "Hideaway " + this.amount + " <i>(When " + this.etbObjectDescription + " enters, look at the top "
|
||||||
+ CardUtil.numberToText(this.amount) + " cards of your library, exile one face down, " +
|
+ CardUtil.numberToText(this.amount) + " cards of your library, exile one face down, " +
|
||||||
"then put the rest on the bottom of your library in a random order.)</i>";
|
"then put the rest on the bottom of your library in a random order.)</i>";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import mage.MageObject;
|
||||||
import mage.MageObjectImpl;
|
import mage.MageObjectImpl;
|
||||||
import mage.Mana;
|
import mage.Mana;
|
||||||
import mage.abilities.*;
|
import mage.abilities.*;
|
||||||
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.effects.common.continuous.HasSubtypesSourceEffect;
|
import mage.abilities.effects.common.continuous.HasSubtypesSourceEffect;
|
||||||
import mage.abilities.keyword.ChangelingAbility;
|
import mage.abilities.keyword.ChangelingAbility;
|
||||||
|
|
@ -344,19 +345,18 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rules fix: workaround to add text auto-replacement for creature's ETB
|
// rules fix: workaround to fix "When {this} enters" into "When this xxx enters"
|
||||||
if (this.isCreature() && ability.getRule().startsWith("When {this} enters") && ability instanceof TriggeredAbility) {
|
if (EntersBattlefieldTriggeredAbility.ENABLE_TRIGGER_PHRASE_AUTO_FIX) {
|
||||||
TriggeredAbility triggeredAbility = ((TriggeredAbility) ability);
|
if (ability instanceof TriggeredAbility) {
|
||||||
if (triggeredAbility.getTriggerPhrase() != null) {
|
TriggeredAbility triggeredAbility = ((TriggeredAbility) ability);
|
||||||
// TODO: delete or enable after wizards update all cards, not last sets only, see https://github.com/magefree/mage/issues/12791
|
if (triggeredAbility.getTriggerPhrase() != null && triggeredAbility.getTriggerPhrase().startsWith("When {this} enters")) {
|
||||||
//triggeredAbility.setTriggerPhrase(triggeredAbility.getTriggerPhrase().replace("{this}", "this creature"));
|
// there are old sets with old oracle, but it's ok for newer sets, so keep that rules fix
|
||||||
|
// see https://github.com/magefree/mage/issues/12791
|
||||||
|
String etbDescription = EntersBattlefieldTriggeredAbility.getThisObjectDescription(this);
|
||||||
|
triggeredAbility.setTriggerPhrase(triggeredAbility.getTriggerPhrase().replace("{this}", etbDescription));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// verify check: all creatures with ETB must use "When this creature enters" instead "When {this} enters"
|
|
||||||
if (this.isCreature() && ability.getRule().startsWith("When {this} enters")) {
|
|
||||||
// see above
|
|
||||||
//throw new IllegalArgumentException("Wrong code usage: creature's ETB ability must use text like \"When this creature enters\"");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addAbility(Ability ability, Watcher watcher) {
|
protected void addAbility(Ability ability, Watcher watcher) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue