refactor: improved ETB rules generations, fixed some cards/abilities (related to #12791)

This commit is contained in:
Oleg Agafonov 2025-02-09 17:25:48 +04:00
parent 0d0661cc92
commit 19269b22b0
24 changed files with 129 additions and 41 deletions

View file

@ -2,15 +2,22 @@ package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.cards.Card;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import java.util.Arrays;
import java.util.List;
/**
* @author BetaSteward_at_googlemail.com
*/
public class EntersBattlefieldTriggeredAbility extends TriggeredAbilityImpl {
static public boolean ENABLE_TRIGGER_PHRASE_AUTO_FIX = false;
public EntersBattlefieldTriggeredAbility(Effect effect) {
this(effect, false);
}
@ -47,4 +54,59 @@ public class EntersBattlefieldTriggeredAbility extends TriggeredAbilityImpl {
public EntersBattlefieldTriggeredAbility copy() {
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"
);
}
}

View file

@ -47,6 +47,7 @@ import java.util.UUID;
public class ChampionAbility extends StaticAbility {
protected final String objectDescription;
protected final String etbObjectDescription;
/**
* 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) {
super(Zone.BATTLEFIELD, null);
this.etbObjectDescription = EntersBattlefieldTriggeredAbility.getThisObjectDescription(card);
List<SubType> subTypes = Arrays.asList(subtypes);
FilterControlledPermanent filter;
switch (subTypes.size()) {
@ -105,6 +108,7 @@ public class ChampionAbility extends StaticAbility {
protected ChampionAbility(final ChampionAbility ability) {
super(ability);
this.objectDescription = ability.objectDescription;
this.etbObjectDescription = ability.etbObjectDescription;
}
@Override
@ -115,7 +119,7 @@ public class ChampionAbility extends StaticAbility {
@Override
public String getRule() {
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>";
}
}

View file

@ -36,21 +36,25 @@ import java.util.*;
public class HideawayAbility extends EntersBattlefieldTriggeredAbility {
private final int amount;
private final String etbObjectDescription;
public HideawayAbility(int amount) {
public HideawayAbility(Card card, int amount) {
super(new HideawayExileEffect(amount));
this.amount = amount;
this.addWatcher(new HideawayWatcher());
this.etbObjectDescription = EntersBattlefieldTriggeredAbility.getThisObjectDescription(card);
}
private HideawayAbility(final HideawayAbility ability) {
super(ability);
this.amount = ability.amount;
this.etbObjectDescription = ability.etbObjectDescription;
}
@Override
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, " +
"then put the rest on the bottom of your library in a random order.)</i>";
}

View file

@ -4,6 +4,7 @@ import mage.MageObject;
import mage.MageObjectImpl;
import mage.Mana;
import mage.abilities.*;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.continuous.HasSubtypesSourceEffect;
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
if (this.isCreature() && ability.getRule().startsWith("When {this} enters") && ability instanceof TriggeredAbility) {
TriggeredAbility triggeredAbility = ((TriggeredAbility) ability);
if (triggeredAbility.getTriggerPhrase() != null) {
// TODO: delete or enable after wizards update all cards, not last sets only, see https://github.com/magefree/mage/issues/12791
//triggeredAbility.setTriggerPhrase(triggeredAbility.getTriggerPhrase().replace("{this}", "this creature"));
// rules fix: workaround to fix "When {this} enters" into "When this xxx enters"
if (EntersBattlefieldTriggeredAbility.ENABLE_TRIGGER_PHRASE_AUTO_FIX) {
if (ability instanceof TriggeredAbility) {
TriggeredAbility triggeredAbility = ((TriggeredAbility) ability);
if (triggeredAbility.getTriggerPhrase() != null && triggeredAbility.getTriggerPhrase().startsWith("When {this} enters")) {
// 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) {