refactor: fixed dies events support in single cards (part 3);

This commit is contained in:
Oleg Agafonov 2024-11-09 17:55:07 +04:00
parent c3343110f3
commit dc9f349828
14 changed files with 84 additions and 2 deletions

View file

@ -54,6 +54,7 @@ class DaxosBlessedByTheSunAbility extends TriggeredAbilityImpl {
DaxosBlessedByTheSunAbility() {
super(Zone.BATTLEFIELD, new GainLifeEffect(1));
setLeavesTheBattlefieldTrigger(true);
}
private DaxosBlessedByTheSunAbility(DaxosBlessedByTheSunAbility ability) {

View file

@ -62,6 +62,7 @@ class DeathTyrantTriggeredAbility extends TriggeredAbilityImpl {
DeathTyrantTriggeredAbility() {
super(Zone.BATTLEFIELD, new CreateTokenEffect(new ZombieToken()));
setTriggerPhrase("Whenever an attacking creature you control or a blocking creature an opponent controls dies, ");
setLeavesTheBattlefieldTrigger(true);
}
private DeathTyrantTriggeredAbility(final DeathTyrantTriggeredAbility ability) {

View file

@ -53,6 +53,7 @@ class DreadhoundTriggeredAbility extends TriggeredAbilityImpl {
public DreadhoundTriggeredAbility() {
super(Zone.BATTLEFIELD, new LoseLifeOpponentsEffect(1));
setTriggerPhrase("Whenever a creature dies or a creature card is put into a graveyard from a library, ");
setLeavesTheBattlefieldTrigger(true);
}
private DreadhoundTriggeredAbility(final DreadhoundTriggeredAbility ability) {

View file

@ -48,6 +48,7 @@ class GutterGrimeTriggeredAbility extends TriggeredAbilityImpl {
public GutterGrimeTriggeredAbility() {
super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.SLIME.createInstance()), false);
this.addEffect(new GutterGrimeEffect());
setLeavesTheBattlefieldTrigger(true);
}
private GutterGrimeTriggeredAbility(final GutterGrimeTriggeredAbility ability) {
@ -83,6 +84,11 @@ class GutterGrimeTriggeredAbility extends TriggeredAbilityImpl {
public String getRule() {
return "Whenever a nontoken creature you control dies, put a slime counter on {this}, then create a green Ooze creature token with \"This creature's power and toughness are each equal to the number of slime counters on {this}.\"";
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
}
class GutterGrimeEffect extends OneShotEffect {

View file

@ -1,6 +1,7 @@
package mage.cards.h;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.keyword.LifelinkAbility;
import mage.cards.CardImpl;
@ -51,6 +52,7 @@ class HatefulEidolonTriggeredAbility extends TriggeredAbilityImpl {
HatefulEidolonTriggeredAbility() {
super(Zone.BATTLEFIELD, null, false);
setLeavesTheBattlefieldTrigger(true);
}
private HatefulEidolonTriggeredAbility(final HatefulEidolonTriggeredAbility ability) {
@ -105,4 +107,9 @@ class HatefulEidolonTriggeredAbility extends TriggeredAbilityImpl {
return "Whenever an enchanted creature dies, draw a card for each "
+ "Aura you controlled that was attached to it.";
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
}

View file

@ -1,7 +1,9 @@
package mage.cards.i;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.dynamicvalue.common.SavedDamageValue;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
@ -55,6 +57,7 @@ class InfestedThrinaxTriggeredAbility extends DelayedTriggeredAbility {
InfestedThrinaxTriggeredAbility() {
super(new CreateTokenEffect(new SaprolingToken(), SavedDamageValue.MUCH), Duration.EndOfTurn, false, false);
setLeavesTheBattlefieldTrigger(true);
}
private InfestedThrinaxTriggeredAbility(final InfestedThrinaxTriggeredAbility ability) {
@ -89,4 +92,9 @@ class InfestedThrinaxTriggeredAbility extends DelayedTriggeredAbility {
return "Whenever a nontoken creature you control dies, " +
"create a number of 1/1 green Saproling creature tokens equal to that creature's power.";
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
}

View file

@ -94,6 +94,7 @@ class JerrenCorruptedBishopTriggeredAbility extends TriggeredAbilityImpl {
JerrenCorruptedBishopTriggeredAbility() {
super(Zone.BATTLEFIELD, new LoseLifeSourceControllerEffect(1));
this.addEffect(new CreateTokenEffect(new HumanToken()));
setLeavesTheBattlefieldTrigger(true);
}
private JerrenCorruptedBishopTriggeredAbility(final JerrenCorruptedBishopTriggeredAbility ability) {

View file

@ -1,8 +1,10 @@
package mage.cards.m;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.BoostAllEffect;
@ -77,6 +79,7 @@ class MassacreGirlDelayedTriggeredAbility extends DelayedTriggeredAbility {
MassacreGirlDelayedTriggeredAbility() {
super(new BoostAllEffect(-1, -1, Duration.EndOfTurn, true), Duration.EndOfTurn, false);
setLeavesTheBattlefieldTrigger(true);
}
private MassacreGirlDelayedTriggeredAbility(final MassacreGirlDelayedTriggeredAbility ability) {
@ -103,4 +106,9 @@ class MassacreGirlDelayedTriggeredAbility extends DelayedTriggeredAbility {
public String getRule() {
return "Whenever a creature dies this turn, each creature other than {this} gets -1/-1 until end of turn";
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
}

View file

@ -1994,6 +1994,9 @@ public class VerifyCardDataTest {
.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 -> !card.getName().equals("Massacre Girl") // delayed trigger fixed, but verify check can't find it
&& !card.getName().equals("Infested Thrinax")
)
.filter(a -> !a.isLeavesTheBattlefieldTrigger())
.forEach(a -> {
fail(card, "abilities", "dies trigger must use setLeavesTheBattlefieldTrigger(true) and override isInUseableZone - " + a.getClass().getSimpleName());
@ -2319,6 +2322,9 @@ public class VerifyCardDataTest {
}
private void checkWrongAbilitiesTextStart() {
if (FULL_ABILITIES_CHECK_SET_CODES.isEmpty()) {
return;
}
System.out.println("Ability text checks started for " + FULL_ABILITIES_CHECK_SET_CODES);
wrongAbilityStatsTotal = 0;
wrongAbilityStatsGood = 0;
@ -2326,6 +2332,10 @@ public class VerifyCardDataTest {
}
private void checkWrongAbilitiesTextEnd() {
if (FULL_ABILITIES_CHECK_SET_CODES.isEmpty()) {
return;
}
// TODO: implement tests result/stats by github actions to show in check message compared to prev version
System.out.println();
System.out.printf("Stats for %d cards checked for abilities text:%n", wrongAbilityStatsTotal);

View file

@ -1255,13 +1255,13 @@ public abstract class AbilityImpl implements Ability {
}
return allEvents.stream().anyMatch(e -> {
// TODO: add more events with zone change logic (or make it even't param)?
// TODO: add more events with zone change logic (or make it event's param)?
switch (e.getType()) {
case DESTROYED_PERMANENT:
case EXPLOITED_CREATURE:
return true;
case ZONE_CHANGE:
return ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD;
return ((ZoneChangeEvent) e).getFromZone() == Zone.BATTLEFIELD;
default:
return false;
}

View file

@ -1,5 +1,6 @@
package mage.abilities.common;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
@ -32,6 +33,7 @@ public class DealtDamageAttachedAndDiedTriggeredAbility extends TriggeredAbility
setTriggerPhrase(getWhen() + CardUtil.addArticle(filter.getMessage()) + " dealt damage by "
+ CardUtil.getTextWithFirstCharLowerCase(attachmentType.verb()) +
" creature this turn dies, ");
setLeavesTheBattlefieldTrigger(true);
}
protected DealtDamageAttachedAndDiedTriggeredAbility(final DealtDamageAttachedAndDiedTriggeredAbility ability) {
@ -75,4 +77,9 @@ public class DealtDamageAttachedAndDiedTriggeredAbility extends TriggeredAbility
}
return true;
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
}

View file

@ -1,5 +1,6 @@
package mage.abilities.common;
import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.cards.Card;
@ -48,6 +49,7 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl {
this.setTargetPointer = setTargetPointer;
this.rememberSource = rememberSource;
setTriggerPhrase(generateTriggerPhrase());
setLeavesTheBattlefieldTrigger(true);
}
protected DiesAttachedTriggeredAbility(final DiesAttachedTriggeredAbility ability) {
@ -157,4 +159,9 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl {
}
return sb.toString();
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
}

View file

@ -1,9 +1,11 @@
package mage.abilities.common.delayed;
import mage.MageObject;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.Modes;
import mage.abilities.TriggeredAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.Effects;
import mage.constants.Duration;
@ -103,4 +105,14 @@ public class UntilYourNextTurnDelayedTriggeredAbility extends DelayedTriggeredAb
public int getSourceObjectZoneChangeCounter() {
return ability.getSourceObjectZoneChangeCounter();
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
if (isLeavesTheBattlefieldTrigger()) {
// TODO: leaves battlefield and die are not same! Is it possible make a diff logic?
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
} else {
return super.isInUseableZone(game, source, event);
}
}
}

View file

@ -1,5 +1,6 @@
package mage.abilities.decorator;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Modes;
import mage.abilities.TriggeredAbility;
@ -41,6 +42,9 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl {
this.ability = ability;
this.condition = condition;
this.abilityText = text;
if (ability.isLeavesTheBattlefieldTrigger()) {
this.setLeavesTheBattlefieldTrigger(true);
}
}
protected ConditionalTriggeredAbility(final ConditionalTriggeredAbility triggered) {
@ -118,4 +122,13 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl {
return this;
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
if (isLeavesTheBattlefieldTrigger()) {
// TODO: leaves battlefield and die are not same! Is it possible make a diff logic?
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
} else {
return super.isInUseableZone(game, source, event);
}
}
}