mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 10:40:06 -08:00
refactor: fixed dies events support in single cards (part 6);
This commit is contained in:
parent
d49ff89a81
commit
b1024d23fc
10 changed files with 80 additions and 29 deletions
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.cards.c;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -39,6 +38,7 @@ public final class CallerOfTheClaw extends CardImpl {
|
|||
|
||||
// Flash
|
||||
this.addAbility(FlashAbility.getInstance());
|
||||
|
||||
// When Caller of the Claw enters the battlefield, create a 2/2 green Bear creature token for each nontoken creature put into your graveyard from the battlefield this turn.
|
||||
this.getSpellAbility().addWatcher(new CallerOfTheClawWatcher());
|
||||
Effect effect = new CreateTokenEffect(new BearToken(), new CallerOfTheClawDynamicValue());
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
|
|
@ -91,6 +93,7 @@ class DiabolicServitudeCreatureDiesTriggeredAbility extends TriggeredAbilityImpl
|
|||
|
||||
public DiabolicServitudeCreatureDiesTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new DiabolicServitudeExileCreatureEffect(), false);
|
||||
setLeavesTheBattlefieldTrigger(true);
|
||||
}
|
||||
|
||||
private DiabolicServitudeCreatureDiesTriggeredAbility(final DiabolicServitudeCreatureDiesTriggeredAbility ability) {
|
||||
|
|
@ -123,6 +126,11 @@ class DiabolicServitudeCreatureDiesTriggeredAbility extends TriggeredAbilityImpl
|
|||
public String getRule() {
|
||||
return "When the creature put onto the battlefield with {this} dies, exile it and return {this} to its owner's hand.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject sourceObject, GameEvent event) {
|
||||
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, sourceObject, event, game);
|
||||
}
|
||||
}
|
||||
|
||||
class DiabolicServitudeExileCreatureEffect extends OneShotEffect {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.cards.e;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
|
|
@ -97,6 +98,7 @@ class EndlessEvilBounceAbility extends TriggeredAbilityImpl {
|
|||
|
||||
public EndlessEvilBounceAbility() {
|
||||
super(Zone.BATTLEFIELD, new ReturnToHandSourceEffect(false, true));
|
||||
setLeavesTheBattlefieldTrigger(true);
|
||||
}
|
||||
|
||||
private EndlessEvilBounceAbility(final EndlessEvilBounceAbility effect) {
|
||||
|
|
@ -126,4 +128,9 @@ class EndlessEvilBounceAbility extends TriggeredAbilityImpl {
|
|||
public String getRule() {
|
||||
return "When enchanted creature dies, if that creature was a Horror, return {this} to its owner's hand.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject sourceObject, GameEvent event) {
|
||||
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, sourceObject, event, game);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package mage.cards.e;
|
|||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
|
|
@ -65,6 +66,7 @@ class EnigmaSphinxTriggeredAbility extends TriggeredAbilityImpl {
|
|||
public EnigmaSphinxTriggeredAbility(Effect effect, boolean optional) {
|
||||
super(Zone.ALL, effect, optional);
|
||||
setTriggerPhrase("When {this} is put into your graveyard from the battlefield, ");
|
||||
setLeavesTheBattlefieldTrigger(true);
|
||||
}
|
||||
|
||||
private EnigmaSphinxTriggeredAbility(final EnigmaSphinxTriggeredAbility ability) {
|
||||
|
|
@ -94,6 +96,11 @@ class EnigmaSphinxTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject sourceObject, GameEvent event) {
|
||||
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, sourceObject, event, game);
|
||||
}
|
||||
}
|
||||
|
||||
class EnigmaSphinxEffect extends OneShotEffect {
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ class KayasGhostformTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
KayasGhostformTriggeredAbility() {
|
||||
super(Zone.ALL, new ReturnToBattlefieldUnderYourControlAttachedEffect(), false);
|
||||
setLeavesTheBattlefieldTrigger(true);
|
||||
}
|
||||
|
||||
private KayasGhostformTriggeredAbility(final KayasGhostformTriggeredAbility ability) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package mage.cards.m;
|
|||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
|
|
@ -59,6 +60,7 @@ class MagusOfTheBridgeTriggeredAbility extends TriggeredAbilityImpl {
|
|||
public MagusOfTheBridgeTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new ExileSourceEffect());
|
||||
setTriggerPhrase("When a creature is put into an opponent's graveyard from the battlefield, ");
|
||||
setLeavesTheBattlefieldTrigger(true);
|
||||
}
|
||||
|
||||
private MagusOfTheBridgeTriggeredAbility(final MagusOfTheBridgeTriggeredAbility ability) {
|
||||
|
|
@ -86,4 +88,9 @@ class MagusOfTheBridgeTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInUseableZone(Game game, MageObject sourceObject, GameEvent event) {
|
||||
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, sourceObject, event, game);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ class TaekoThePatientAvalancheTriggeredAbility extends TriggeredAbilityImpl {
|
|||
super(Zone.BATTLEFIELD, new ScryEffect(1, false));
|
||||
this.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance()).concatBy("and"));
|
||||
this.setTriggerPhrase("Whenever another creature you control leaves the battlefield, if it didn't die, ");
|
||||
setLeavesTheBattlefieldTrigger(true);
|
||||
}
|
||||
|
||||
private TaekoThePatientAvalancheTriggeredAbility(final TaekoThePatientAvalancheTriggeredAbility ability) {
|
||||
|
|
|
|||
|
|
@ -1985,33 +1985,52 @@ public class VerifyCardDataTest {
|
|||
fail(card, "abilities", "mutate cards aren't implemented and shouldn't be available");
|
||||
}
|
||||
|
||||
// special check: wrong dies triggers
|
||||
card.getAbilities().stream()
|
||||
.filter(a -> a instanceof TriggeredAbility)
|
||||
.map(a -> (TriggeredAbility) a)
|
||||
.filter(a -> !a.isLeavesTheBattlefieldTrigger())
|
||||
//.filter(a -> a.getRule().contains("whenever") || a.getRule().contains("Whenever")) // TODO: research failed cards
|
||||
.filter(a -> a.getRule().contains("die ")
|
||||
|| a.getRule().contains("dies ")
|
||||
|| a.getRule().contains("die,")
|
||||
|| a.getRule().contains("dies,")
|
||||
|| (a.getRule().contains("put into")
|
||||
&& a.getRule().contains("graveyard")
|
||||
&& a.getRule().contains("from the battlefield"))
|
||||
)
|
||||
.filter(a -> !a.getRule().contains("roll")) // ignore roll die effects
|
||||
.filter(a -> !a.getRule().contains("with \"When")) // ignore token creating effects
|
||||
.filter(a -> !a.getRule().contains("gains \"When")) // ignore token creating effects
|
||||
.filter(a -> !a.getRule().contains("and \"When")) // ignore token creating effects
|
||||
.filter(a -> !a.getRule().contains("dies while {this} is in your graveyard")) // ignore Boneyard Scourge
|
||||
.filter(a -> !a.getRule().contains("all creature cards that were put into your")) // ignore Fell Shepherd
|
||||
.filter(a -> !card.getName().equals("Massacre Girl") // delayed trigger fixed, but verify check can't find it
|
||||
&& !card.getName().equals("Infested Thrinax")
|
||||
&& !card.getName().equals("Xira, the Golden Sting")
|
||||
)
|
||||
.forEach(a -> {
|
||||
fail(card, "abilities", "dies trigger must use setLeavesTheBattlefieldTrigger(true) and override isInUseableZone - " + a.getClass().getSimpleName());
|
||||
});
|
||||
// special check: wrong dies triggers (there are also a runtime check on wrong usage, see isInUseableZoneDiesTrigger)
|
||||
Set<String> ignoredCards = new HashSet<>();
|
||||
ignoredCards.add("Caller of the Claw");
|
||||
ignoredCards.add("Boneyard Scourge");
|
||||
ignoredCards.add("Fell Shepherd");
|
||||
ignoredCards.add("Massacre Girl");
|
||||
ignoredCards.add("Infested Thrinax");
|
||||
ignoredCards.add("Xira, the Golden Sting");
|
||||
ignoredCards.add("Mawloc");
|
||||
List<String> ignoredAbilities = new ArrayList<>();
|
||||
ignoredAbilities.add("roll"); // roll die effects
|
||||
ignoredAbilities.add("with \"When"); // token creating effects
|
||||
ignoredAbilities.add("gains \"When"); // token creating effects
|
||||
ignoredAbilities.add("and \"When"); // token creating effects
|
||||
ignoredAbilities.add("it has \"When"); // token creating effects
|
||||
ignoredAbilities.add("beginning of your end step"); // step triggers
|
||||
ignoredAbilities.add("beginning of each end step"); // step triggers
|
||||
ignoredAbilities.add("beginning of combat"); // step triggers
|
||||
if (!ignoredCards.contains(card.getName())) {
|
||||
for (Ability ability : card.getAbilities()) {
|
||||
TriggeredAbility triggeredAbility = ability instanceof TriggeredAbility ? (TriggeredAbility) ability : null;
|
||||
if (triggeredAbility == null) {
|
||||
continue;
|
||||
}
|
||||
// search and check dies related abilities
|
||||
String rules = triggeredAbility.getRule();
|
||||
if (ignoredAbilities.stream().anyMatch(rules::contains)) {
|
||||
continue;
|
||||
}
|
||||
boolean isDiesAbility = rules.contains("die ")
|
||||
|| rules.contains("dies ")
|
||||
|| rules.contains("die,")
|
||||
|| rules.contains("dies,");
|
||||
boolean isPutToGraveAbility = rules.contains("put into")
|
||||
&& rules.contains("graveyard")
|
||||
&& rules.contains("from the battlefield");
|
||||
if (triggeredAbility.isLeavesTheBattlefieldTrigger()) {
|
||||
// TODO: add check for wrongly enabled settings too?
|
||||
} else {
|
||||
if (isDiesAbility || isPutToGraveAbility) {
|
||||
fail(card, "abilities", "dies related trigger must use setLeavesTheBattlefieldTrigger(true) and possibly override isInUseableZone - "
|
||||
+ triggeredAbility.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// special check: duplicated words in ability text (wrong target/filter usage)
|
||||
// example: You may exile __two two__ blue cards
|
||||
|
|
|
|||
|
|
@ -1290,7 +1290,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSourceObjectAbility(Game game, MageObject sourceObject, GameEvent event) {
|
||||
public final boolean hasSourceObjectAbility(Game game, MageObject sourceObject, GameEvent event) {
|
||||
MageObject object = sourceObject;
|
||||
if (object == null) {
|
||||
object = game.getPermanentEntering(getSourceId());
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ public class HauntAbility extends TriggeredAbilityImpl {
|
|||
setTriggerPhrase((creatureHaunt ? "When {this} enters or the creature it haunts dies, "
|
||||
: "When the creature {this} haunts dies, ")
|
||||
);
|
||||
setLeavesTheBattlefieldTrigger(true);
|
||||
}
|
||||
|
||||
private HauntAbility(final HauntAbility ability) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue