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

This commit is contained in:
Oleg Agafonov 2024-11-23 09:15:09 +04:00
parent 740a9347ae
commit 6d55e4b9e6
25 changed files with 262 additions and 13 deletions

View file

@ -65,6 +65,7 @@ class BridgeFromBelowAbility extends TriggeredAbilityImpl {
this.filter = filter; this.filter = filter;
this.withInterveningIf(SourceInGraveyardCondition.instance); this.withInterveningIf(SourceInGraveyardCondition.instance);
setTriggerPhrase(filter.getMessage()); setTriggerPhrase(filter.getMessage());
setLeavesTheBattlefieldTrigger(true); // it's not required for Bridge from Below, but better to keep same code style and verify pass
} }
private BridgeFromBelowAbility(final BridgeFromBelowAbility ability) { private BridgeFromBelowAbility(final BridgeFromBelowAbility ability) {

View file

@ -70,6 +70,7 @@ class LyndeCheerfulTormentorCurseDiesTriggeredAbility extends TriggeredAbilityIm
new LyndeCheerfulTormentorReturnCurseEffect() new LyndeCheerfulTormentorReturnCurseEffect()
) )
)); ));
setLeavesTheBattlefieldTrigger(true);
} }
private LyndeCheerfulTormentorCurseDiesTriggeredAbility(final LyndeCheerfulTormentorCurseDiesTriggeredAbility ability) { private LyndeCheerfulTormentorCurseDiesTriggeredAbility(final LyndeCheerfulTormentorCurseDiesTriggeredAbility ability) {

View file

@ -1,5 +1,6 @@
package mage.cards.m; package mage.cards.m;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
@ -50,6 +51,7 @@ class MartyrsBondTriggeredAbility extends TriggeredAbilityImpl {
public MartyrsBondTriggeredAbility() { public MartyrsBondTriggeredAbility() {
super(Zone.BATTLEFIELD, new MartyrsBondEffect()); super(Zone.BATTLEFIELD, new MartyrsBondEffect());
setLeavesTheBattlefieldTrigger(true);
} }
private MartyrsBondTriggeredAbility(final MartyrsBondTriggeredAbility ability) { private MartyrsBondTriggeredAbility(final MartyrsBondTriggeredAbility ability) {
@ -88,6 +90,10 @@ class MartyrsBondTriggeredAbility extends TriggeredAbilityImpl {
return "Whenever {this} or another nonland permanent you control is put into a graveyard from the battlefield, each opponent sacrifices a permanent that shares a card type with it."; return "Whenever {this} or another nonland permanent you control is put into a graveyard from the battlefield, each opponent sacrifices a permanent that shares a card type with it.";
} }
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
} }
class MartyrsBondEffect extends OneShotEffect { class MartyrsBondEffect extends OneShotEffect {

View file

@ -3,6 +3,7 @@ package mage.cards.m;
import java.util.UUID; import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.TrampleAbility;
@ -47,6 +48,7 @@ class MolderBeastTriggeredAbility extends TriggeredAbilityImpl {
public MolderBeastTriggeredAbility() { public MolderBeastTriggeredAbility() {
super(Zone.BATTLEFIELD, new BoostSourceEffect(2, 0, Duration.EndOfTurn), false); super(Zone.BATTLEFIELD, new BoostSourceEffect(2, 0, Duration.EndOfTurn), false);
setLeavesTheBattlefieldTrigger(true);
} }
private MolderBeastTriggeredAbility(final MolderBeastTriggeredAbility ability) { private MolderBeastTriggeredAbility(final MolderBeastTriggeredAbility ability) {
@ -74,4 +76,9 @@ class MolderBeastTriggeredAbility extends TriggeredAbilityImpl {
public MolderBeastTriggeredAbility copy() { public MolderBeastTriggeredAbility copy() {
return new MolderBeastTriggeredAbility(this); return new MolderBeastTriggeredAbility(this);
} }
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
} }

View file

@ -3,6 +3,7 @@ package mage.cards.n;
import java.util.UUID; import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.costs.mana.ColoredManaCost; import mage.abilities.costs.mana.ColoredManaCost;
import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DoIfCostPaid;
@ -55,6 +56,7 @@ class NetherTraitorTriggeredAbility extends TriggeredAbilityImpl {
NetherTraitorTriggeredAbility(){ NetherTraitorTriggeredAbility(){
super(Zone.GRAVEYARD, new DoIfCostPaid(new ReturnSourceFromGraveyardToBattlefieldEffect(), new ColoredManaCost(ColoredManaSymbol.B))); super(Zone.GRAVEYARD, new DoIfCostPaid(new ReturnSourceFromGraveyardToBattlefieldEffect(), new ColoredManaCost(ColoredManaSymbol.B)));
setLeavesTheBattlefieldTrigger(true);
} }
private NetherTraitorTriggeredAbility(final NetherTraitorTriggeredAbility ability) { private NetherTraitorTriggeredAbility(final NetherTraitorTriggeredAbility ability) {
@ -94,4 +96,9 @@ class NetherTraitorTriggeredAbility extends TriggeredAbilityImpl {
public String getRule() { public String getRule() {
return "Whenever another creature is put into your graveyard from the battlefield, you may pay {B}. If you do, return {this} from your graveyard to the battlefield."; return "Whenever another creature is put into your graveyard from the battlefield, you may pay {B}. If you do, return {this} from your graveyard to the battlefield.";
} }
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
} }

View file

@ -1,5 +1,6 @@
package mage.cards.n; package mage.cards.n;
import mage.MageObject;
import mage.ObjectColor; import mage.ObjectColor;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
@ -72,6 +73,7 @@ class NimDeathmantleTriggeredAbility extends TriggeredAbilityImpl {
NimDeathmantleTriggeredAbility() { NimDeathmantleTriggeredAbility() {
super(Zone.BATTLEFIELD, new NimDeathmantleEffect(), false); super(Zone.BATTLEFIELD, new NimDeathmantleEffect(), false);
setLeavesTheBattlefieldTrigger(true);
} }
private NimDeathmantleTriggeredAbility(final NimDeathmantleTriggeredAbility ability) { private NimDeathmantleTriggeredAbility(final NimDeathmantleTriggeredAbility ability) {
@ -108,6 +110,11 @@ class NimDeathmantleTriggeredAbility extends TriggeredAbilityImpl {
public String getRule() { public String getRule() {
return "Whenever a nontoken creature is put into your graveyard from the battlefield, you may pay {4}. If you do, return that card to the battlefield and attach {this} to it."; return "Whenever a nontoken creature is put into your graveyard from the battlefield, you may pay {4}. If you do, return that card to the battlefield and attach {this} to it.";
} }
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
} }
class NimDeathmantleEffect extends OneShotEffect { class NimDeathmantleEffect extends OneShotEffect {

View file

@ -1,5 +1,6 @@
package mage.cards.p; package mage.cards.p;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
@ -97,6 +98,7 @@ class PiasRevolutionTriggeredAbility extends TriggeredAbilityImpl {
public PiasRevolutionTriggeredAbility() { public PiasRevolutionTriggeredAbility() {
super(Zone.BATTLEFIELD, new PiasRevolutionReturnEffect(), false); super(Zone.BATTLEFIELD, new PiasRevolutionReturnEffect(), false);
setTriggerPhrase("Whenever a nontoken artifact is put into your graveyard from the battlefield, "); setTriggerPhrase("Whenever a nontoken artifact is put into your graveyard from the battlefield, ");
setLeavesTheBattlefieldTrigger(true);
} }
private PiasRevolutionTriggeredAbility(final PiasRevolutionTriggeredAbility ability) { private PiasRevolutionTriggeredAbility(final PiasRevolutionTriggeredAbility ability) {
@ -127,4 +129,9 @@ class PiasRevolutionTriggeredAbility extends TriggeredAbilityImpl {
} }
return false; return false;
} }
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
} }

View file

@ -57,6 +57,7 @@ class PsychomancerTriggeredAbility extends TriggeredAbilityImpl {
this.setTriggerPhrase("Whenever {this} or another nontoken artifact you control is put " + this.setTriggerPhrase("Whenever {this} or another nontoken artifact you control is put " +
"into a graveyard from the battlefield or is put into exile from the battlefield, "); "into a graveyard from the battlefield or is put into exile from the battlefield, ");
this.withFlavorWord("Harbinger of Despair"); this.withFlavorWord("Harbinger of Despair");
this.setLeavesTheBattlefieldTrigger(true);
} }
private PsychomancerTriggeredAbility(final PsychomancerTriggeredAbility ability) { private PsychomancerTriggeredAbility(final PsychomancerTriggeredAbility ability) {

View file

@ -62,6 +62,7 @@ class PurgatoryTriggeredAbility extends TriggeredAbilityImpl {
PurgatoryTriggeredAbility() { PurgatoryTriggeredAbility() {
super(Zone.BATTLEFIELD, new PurgatoryExileEffect(), false); super(Zone.BATTLEFIELD, new PurgatoryExileEffect(), false);
setLeavesTheBattlefieldTrigger(true);
} }
private PurgatoryTriggeredAbility(final PurgatoryTriggeredAbility ability) { private PurgatoryTriggeredAbility(final PurgatoryTriggeredAbility ability) {
@ -103,6 +104,11 @@ class PurgatoryTriggeredAbility extends TriggeredAbilityImpl {
public String getRule() { public String getRule() {
return "Whenever a nontoken creature is put into your graveyard from the battlefield, exile that card."; return "Whenever a nontoken creature is put into your graveyard from the battlefield, exile that card.";
} }
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
} }
class PurgatoryExileEffect extends OneShotEffect { class PurgatoryExileEffect extends OneShotEffect {

View file

@ -1,5 +1,6 @@
package mage.cards.s; package mage.cards.s;
import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -40,6 +41,7 @@ class SacredGroundTriggeredAbility extends TriggeredAbilityImpl {
SacredGroundTriggeredAbility() { SacredGroundTriggeredAbility() {
super(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect()); super(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect());
setLeavesTheBattlefieldTrigger(true);
} }
private SacredGroundTriggeredAbility(final SacredGroundTriggeredAbility ability) { private SacredGroundTriggeredAbility(final SacredGroundTriggeredAbility ability) {
@ -75,4 +77,9 @@ class SacredGroundTriggeredAbility extends TriggeredAbilityImpl {
public String getRule() { public String getRule() {
return "Whenever a spell or ability an opponent controls causes a land to be put into your graveyard from the battlefield, return that card to the battlefield."; return "Whenever a spell or ability an opponent controls causes a land to be put into your graveyard from the battlefield, return that card to the battlefield.";
} }
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
} }

View file

@ -1,6 +1,7 @@
package mage.cards.s; package mage.cards.s;
import mage.MageInt; import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect;
@ -56,6 +57,7 @@ class ScrapTrawlerTriggeredAbility extends TriggeredAbilityImpl {
super(Zone.BATTLEFIELD, new ReturnToHandTargetEffect()); super(Zone.BATTLEFIELD, new ReturnToHandTargetEffect());
getEffects().get(0).setText("return to your hand target artifact card in your graveyard with lesser mana value"); getEffects().get(0).setText("return to your hand target artifact card in your graveyard with lesser mana value");
setTriggerPhrase("Whenever {this} or another artifact you control is put into a graveyard from the battlefield, "); setTriggerPhrase("Whenever {this} or another artifact you control is put into a graveyard from the battlefield, ");
setLeavesTheBattlefieldTrigger(true);
} }
private ScrapTrawlerTriggeredAbility(final ScrapTrawlerTriggeredAbility ability) { private ScrapTrawlerTriggeredAbility(final ScrapTrawlerTriggeredAbility ability) {
@ -90,4 +92,9 @@ class ScrapTrawlerTriggeredAbility extends TriggeredAbilityImpl {
} }
return false; return false;
} }
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
} }

View file

@ -1,6 +1,7 @@
package mage.cards.s; package mage.cards.s;
import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.GainLifeEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -45,12 +46,13 @@ class ScrapheapTriggeredAbility extends TriggeredAbilityImpl {
return new ScrapheapTriggeredAbility(this); return new ScrapheapTriggeredAbility(this);
} }
private ScrapheapTriggeredAbility(final ScrapheapTriggeredAbility ability){
super(ability);
}
public ScrapheapTriggeredAbility(){ public ScrapheapTriggeredAbility(){
super(Zone.BATTLEFIELD, new GainLifeEffect(1)); super(Zone.BATTLEFIELD, new GainLifeEffect(1));
setLeavesTheBattlefieldTrigger(true);
}
private ScrapheapTriggeredAbility(final ScrapheapTriggeredAbility ability){
super(ability);
} }
@Override @Override
@ -76,4 +78,9 @@ class ScrapheapTriggeredAbility extends TriggeredAbilityImpl {
public String getRule() { public String getRule() {
return "Whenever an artifact or enchantment is put into your graveyard from the battlefield, you gain 1 life."; return "Whenever an artifact or enchantment is put into your graveyard from the battlefield, you gain 1 life.";
} }
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
} }

View file

@ -2,6 +2,7 @@ package mage.cards.s;
import mage.MageInt; import mage.MageInt;
import mage.MageItem; import mage.MageItem;
import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.keyword.SurveilEffect; import mage.abilities.effects.keyword.SurveilEffect;
import mage.abilities.keyword.MenaceAbility; import mage.abilities.keyword.MenaceAbility;
@ -53,6 +54,7 @@ class SeerOfStolenSightTriggeredAbility extends TriggeredAbilityImpl {
SeerOfStolenSightTriggeredAbility() { SeerOfStolenSightTriggeredAbility() {
super(Zone.BATTLEFIELD, new SurveilEffect(1)); super(Zone.BATTLEFIELD, new SurveilEffect(1));
setTriggerPhrase("Whenever one or more artifacts and/or creatures you control are put into a graveyard from the battlefield, "); setTriggerPhrase("Whenever one or more artifacts and/or creatures you control are put into a graveyard from the battlefield, ");
setLeavesTheBattlefieldTrigger(true);
} }
private SeerOfStolenSightTriggeredAbility(final SeerOfStolenSightTriggeredAbility ability) { private SeerOfStolenSightTriggeredAbility(final SeerOfStolenSightTriggeredAbility ability) {
@ -85,4 +87,9 @@ class SeerOfStolenSightTriggeredAbility extends TriggeredAbilityImpl {
.map(Controllable::getControllerId) .map(Controllable::getControllerId)
.anyMatch(this::isControlledBy); .anyMatch(this::isControlledBy);
} }
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
} }

View file

@ -59,6 +59,7 @@ class ShardOfTheVoidDragonTriggeredAbility extends TriggeredAbilityImpl {
super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(2))); super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)));
setTriggerPhrase("Whenever an artifact is put into a graveyard from the battlefield or is put into exile from the battlefield, "); setTriggerPhrase("Whenever an artifact is put into a graveyard from the battlefield or is put into exile from the battlefield, ");
withFlavorWord("Matter Absorption"); withFlavorWord("Matter Absorption");
setLeavesTheBattlefieldTrigger(true);
} }
private ShardOfTheVoidDragonTriggeredAbility(final ShardOfTheVoidDragonTriggeredAbility ability) { private ShardOfTheVoidDragonTriggeredAbility(final ShardOfTheVoidDragonTriggeredAbility ability) {

View file

@ -1,5 +1,6 @@
package mage.cards.s; package mage.cards.s;
import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.CreateTokenEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -40,6 +41,7 @@ class SlagstoneRefineryTriggeredAbility extends TriggeredAbilityImpl {
SlagstoneRefineryTriggeredAbility() { SlagstoneRefineryTriggeredAbility() {
super(Zone.BATTLEFIELD, new CreateTokenEffect(new PowerstoneToken(), 1, true)); super(Zone.BATTLEFIELD, new CreateTokenEffect(new PowerstoneToken(), 1, true));
setLeavesTheBattlefieldTrigger(true);
} }
private SlagstoneRefineryTriggeredAbility(final SlagstoneRefineryTriggeredAbility ability) { private SlagstoneRefineryTriggeredAbility(final SlagstoneRefineryTriggeredAbility ability) {
@ -79,4 +81,9 @@ class SlagstoneRefineryTriggeredAbility extends TriggeredAbilityImpl {
return "Whenever {this} or another nontoken artifact you control is put into a graveyard from the battlefield " + return "Whenever {this} or another nontoken artifact you control is put into a graveyard from the battlefield " +
"or is put into exile from the battlefield, create a tapped Powerstone token."; "or is put into exile from the battlefield, create a tapped Powerstone token.";
} }
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
} }

View file

@ -112,6 +112,7 @@ class TheSkullsporeNexusTrigger extends TriggeredAbilityImpl {
TheSkullsporeNexusTrigger() { TheSkullsporeNexusTrigger() {
super(Zone.BATTLEFIELD, null, false); super(Zone.BATTLEFIELD, null, false);
setLeavesTheBattlefieldTrigger(true);
} }
private TheSkullsporeNexusTrigger(final TheSkullsporeNexusTrigger ability) { private TheSkullsporeNexusTrigger(final TheSkullsporeNexusTrigger ability) {

View file

@ -3,6 +3,7 @@ package mage.cards.t;
import java.util.UUID; import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
@ -65,6 +66,7 @@ class TianaShipsCaretakerTriggeredAbility extends TriggeredAbilityImpl {
TianaShipsCaretakerTriggeredAbility() { TianaShipsCaretakerTriggeredAbility() {
super(Zone.BATTLEFIELD, new TianaShipsCaretakerEffect(), true); super(Zone.BATTLEFIELD, new TianaShipsCaretakerEffect(), true);
setTriggerPhrase("Whenever an Aura or Equipment you control is put into a graveyard from the battlefield, "); setTriggerPhrase("Whenever an Aura or Equipment you control is put into a graveyard from the battlefield, ");
setLeavesTheBattlefieldTrigger(true);
} }
private TianaShipsCaretakerTriggeredAbility(final TianaShipsCaretakerTriggeredAbility ability) { private TianaShipsCaretakerTriggeredAbility(final TianaShipsCaretakerTriggeredAbility ability) {
@ -98,6 +100,11 @@ class TianaShipsCaretakerTriggeredAbility extends TriggeredAbilityImpl {
} }
return false; return false;
} }
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
} }
class TianaShipsCaretakerEffect extends OneShotEffect { class TianaShipsCaretakerEffect extends OneShotEffect {

View file

@ -3,6 +3,8 @@
package mage.cards.v; package mage.cards.v;
import java.util.UUID; import java.util.UUID;
import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.cards.Card; import mage.cards.Card;
@ -40,8 +42,10 @@ public final class ViridianRevel extends CardImpl {
} }
class ViridianRevelTriggeredAbility extends TriggeredAbilityImpl { class ViridianRevelTriggeredAbility extends TriggeredAbilityImpl {
ViridianRevelTriggeredAbility() { ViridianRevelTriggeredAbility() {
super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), true); super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), true);
setLeavesTheBattlefieldTrigger(true);
} }
private ViridianRevelTriggeredAbility(final ViridianRevelTriggeredAbility ability) { private ViridianRevelTriggeredAbility(final ViridianRevelTriggeredAbility ability) {
@ -75,4 +79,9 @@ class ViridianRevelTriggeredAbility extends TriggeredAbilityImpl {
public String getRule() { public String getRule() {
return "Whenever an artifact is put into an opponent's graveyard from the battlefield, you may draw a card."; return "Whenever an artifact is put into an opponent's graveyard from the battlefield, you may draw a card.";
} }
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game);
}
} }

View file

@ -1,4 +1,3 @@
package org.mage.test.cards.replacement; package org.mage.test.cards.replacement;
import mage.constants.PhaseStep; import mage.constants.PhaseStep;

View file

@ -0,0 +1,81 @@
package org.mage.test.cards.triggers.dies;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author JayDi85
*/
public class SeerOfStolenSightTest extends CardTestPlayerBase {
@Test
public void test_SingleDie() {
skipInitShuffling();
addCustomEffect_TargetDestroy(playerA);
// Whenever one or more artifacts and/or creatures you control are put into a graveyard from the battlefield, surveil 1.
addCard(Zone.BATTLEFIELD, playerA, "Seer of Stolen Sight");
addCard(Zone.LIBRARY, playerA, "Swamp", 2);
//
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "Grizzly Bears");
addTarget(playerA, "Swamp"); // surveil
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Grizzly Bears", 1);
assertGraveyardCount(playerA, "Swamp", 1);
}
@Test
public void test_MultipleDies() {
skipInitShuffling();
addCustomEffect_TargetDestroy(playerA, 2);
// Whenever one or more artifacts and/or creatures you control are put into a graveyard from the battlefield, surveil 1.
addCard(Zone.BATTLEFIELD, playerA, "Seer of Stolen Sight");
addCard(Zone.LIBRARY, playerA, "Swamp", 2);
//
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 2);
// must catch only one time trigger
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "Grizzly Bears^Grizzly Bears");
addTarget(playerA, "Swamp"); // surveil
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Grizzly Bears", 2);
assertGraveyardCount(playerA, "Swamp", 1);
}
@Test
public void test_OwnDie() {
skipInitShuffling();
addCustomEffect_TargetDestroy(playerA);
// Whenever one or more artifacts and/or creatures you control are put into a graveyard from the battlefield, surveil 1.
addCard(Zone.BATTLEFIELD, playerA, "Seer of Stolen Sight");
addCard(Zone.LIBRARY, playerA, "Swamp", 2);
//
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1);
// must trigger on own die
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "Seer of Stolen Sight");
addTarget(playerA, "Swamp"); // surveil
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Seer of Stolen Sight", 1);
assertGraveyardCount(playerA, "Swamp", 1);
}
}

View file

@ -8,7 +8,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
/** /**
* Tests the {@link mage.abilities.common.DiesOneOrMoreTriggeredAbility} batching. * Tests the {@link mage.abilities.common.DiesOneOrMoreTriggeredAbility} batching.
* *
* @author Susucr * @author Susucr, JayDi85
*/ */
public class VengefulTownsfolkTest extends CardTestPlayerBase { public class VengefulTownsfolkTest extends CardTestPlayerBase {
@ -145,4 +145,52 @@ public class VengefulTownsfolkTest extends CardTestPlayerBase {
assertPermanentCount(playerA, townsfolk, 1); assertPermanentCount(playerA, townsfolk, 1);
assertPowerToughness(playerA, townsfolk, 3 + 1, 3 + 1); assertPowerToughness(playerA, townsfolk, 3 + 1, 3 + 1);
} }
@Test
public void testReplacedDieEvent_Single() {
addCard(Zone.BATTLEFIELD, playerA, townsfolk);
//
// {T}, Sacrifice up to three permanents: If there were three or more card types among the sacrificed permanents, each opponent loses 3 life, you gain 3 life, and you draw three cards.
addCard(Zone.BATTLEFIELD, playerA, "Baba Lysaga, Night Witch");
addCard(Zone.BATTLEFIELD, playerA, "Angel of the God-Pharaoh"); // creature with cycle (will be exiled instead die)
//
// If a card with cycling would be put into your graveyard from anywhere and it wasn't cycled, exile it instead.
addCard(Zone.BATTLEFIELD, playerA, "Abandoned Sarcophagus");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice up to three");
setChoice(playerA, "Angel of the God-Pharaoh"); // sac cost
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertExileCount(playerA, "Angel of the God-Pharaoh", 1); // exiled instead die
assertPermanentCount(playerA, townsfolk, 1);
assertPowerToughness(playerA, townsfolk, 3, 3); // no triggers
}
@Test
public void testReplacedDieEvent_Multiple() {
addCard(Zone.BATTLEFIELD, playerA, townsfolk);
//
// {T}, Sacrifice up to three permanents: If there were three or more card types among the sacrificed permanents, each opponent loses 3 life, you gain 3 life, and you draw three cards.
addCard(Zone.BATTLEFIELD, playerA, "Baba Lysaga, Night Witch");
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 10); // creature
addCard(Zone.BATTLEFIELD, playerA, "Angel of the God-Pharaoh"); // creature with cycle (will be exiled instead die)
//
// If a card with cycling would be put into your graveyard from anywhere and it wasn't cycled, exile it instead.
addCard(Zone.BATTLEFIELD, playerA, "Abandoned Sarcophagus");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice up to three");
setChoice(playerA, "Grizzly Bears^Angel of the God-Pharaoh"); // sac cost
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Grizzly Bears", 1);
assertExileCount(playerA, "Angel of the God-Pharaoh", 1); // exiled instead die
assertPermanentCount(playerA, townsfolk, 1);
assertPowerToughness(playerA, townsfolk, 3 + 1, 3 + 1);
}
} }

View file

@ -351,8 +351,15 @@ public abstract class MageTestPlayerBase {
* Add target destroy ability that can be called by text "target destroy" * Add target destroy ability that can be called by text "target destroy"
*/ */
protected void addCustomEffect_TargetDestroy(TestPlayer controller) { protected void addCustomEffect_TargetDestroy(TestPlayer controller) {
addCustomEffect_TargetDestroy(controller, 1);
}
/**
* Add target destroy ability that can be called by text "target destroy"
*/
protected void addCustomEffect_TargetDestroy(TestPlayer controller, int numberOfTargets) {
Ability ability = new SimpleActivatedAbility(new DestroyTargetEffect().setText("target destroy"), new ManaCostsImpl<>("")); Ability ability = new SimpleActivatedAbility(new DestroyTargetEffect().setText("target destroy"), new ManaCostsImpl<>(""));
ability.addTarget(new TargetPermanent()); ability.addTarget(new TargetPermanent(numberOfTargets, StaticFilters.FILTER_PERMANENT));
addCustomCardWithAbility( addCustomCardWithAbility(
"target destroy for " + controller.getName(), "target destroy for " + controller.getName(),
controller, controller,

View file

@ -1990,12 +1990,21 @@ public class VerifyCardDataTest {
.filter(a -> a instanceof TriggeredAbility) .filter(a -> a instanceof TriggeredAbility)
.map(a -> (TriggeredAbility) a) .map(a -> (TriggeredAbility) a)
.filter(a -> !a.isLeavesTheBattlefieldTrigger()) .filter(a -> !a.isLeavesTheBattlefieldTrigger())
.filter(a -> a.getRule().contains("whenever") || a.getRule().contains("Whenever")) //.filter(a -> a.getRule().contains("whenever") || a.getRule().contains("Whenever")) // TODO: research failed cards
.filter(a -> a.getRule().contains("dies")) .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("with \"When")) // ignore token creating effects
.filter(a -> !a.getRule().contains("gains \"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("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("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 .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("Infested Thrinax")
&& !card.getName().equals("Xira, the Golden Sting") && !card.getName().equals("Xira, the Golden Sting")

View file

@ -8,7 +8,9 @@ import mage.constants.AbilityType;
import mage.constants.AbilityWord; import mage.constants.AbilityWord;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.events.BatchEvent;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeBatchEvent;
import mage.game.events.ZoneChangeEvent; import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken; import mage.game.permanent.PermanentToken;
@ -465,8 +467,14 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
public static boolean isInUseableZoneDiesTrigger(TriggeredAbility source, GameEvent event, Game game) { public static boolean isInUseableZoneDiesTrigger(TriggeredAbility source, GameEvent event, Game game) {
// runtime check: wrong trigger settings // runtime check: wrong trigger settings
if (!source.isLeavesTheBattlefieldTrigger()) { if (!source.isLeavesTheBattlefieldTrigger()) {
// TODO: enable after fix throw new IllegalArgumentException("Wrong code usage: all dies triggers must use setLeavesTheBattlefieldTrigger(true) and override isInUseableZone - "
// throw new IllegalArgumentException("Wrong code usage: all dies triggers must use setLeavesTheBattlefieldTrigger(true)"); + source.getSourceObject(game) + " - " + source);
}
// runtime check: wrong isInUseableZone for batch related triggers
if (event instanceof BatchEvent) {
throw new IllegalArgumentException("Wrong code usage: batch events unsupported here, possible miss of override isInUseableZone - "
+ source.getSourceObject(game) + " - " + source);
} }
// Get the source permanent of the ability // Get the source permanent of the ability

View file

@ -23,6 +23,7 @@ public class DiesOneOrMoreTriggeredAbility extends TriggeredAbilityImpl implemen
super(Zone.BATTLEFIELD, effect, optional); super(Zone.BATTLEFIELD, effect, optional);
this.filter = filter; this.filter = filter;
this.setTriggerPhrase("Whenever one or more " + filter.getMessage() + " die, "); this.setTriggerPhrase("Whenever one or more " + filter.getMessage() + " die, ");
setLeavesTheBattlefieldTrigger(true);
} }
private DiesOneOrMoreTriggeredAbility(final DiesOneOrMoreTriggeredAbility ability) { private DiesOneOrMoreTriggeredAbility(final DiesOneOrMoreTriggeredAbility ability) {
@ -59,6 +60,6 @@ public class DiesOneOrMoreTriggeredAbility extends TriggeredAbilityImpl implemen
return ((ZoneChangeBatchEvent) event) return ((ZoneChangeBatchEvent) event)
.getEvents() .getEvents()
.stream() .stream()
.allMatch(e -> TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, e, game)); .anyMatch(e -> TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, e, game));
} }
} }