* Fix of some problems of zone change related triggered abilities that had not been correctly implemented (fixes #6586).

This commit is contained in:
LevelX2 2020-05-29 14:41:24 +02:00
parent 40c01a04c4
commit 32ce1d85e9
5 changed files with 102 additions and 94 deletions

View file

@ -1,7 +1,6 @@
package mage.cards.a;
import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.dynamicvalue.common.DevotionCount;
import mage.abilities.effects.common.CreateTokenEffect;
@ -12,17 +11,27 @@ import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.PermanentToken;
import mage.game.permanent.token.SatyrCantBlockToken;
import java.util.Objects;
import java.util.UUID;
import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.permanent.TokenPredicate;
/**
* @author TheElk801
*/
public final class AnaxHardenedInTheForge extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nontoken creature you control");
static {
filter.add(TargetController.YOU.getControllerPredicate());
filter.add(Predicates.not(TokenPredicate.instance));
}
public AnaxHardenedInTheForge(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{R}{R}");
@ -38,8 +47,9 @@ public final class AnaxHardenedInTheForge extends CardImpl {
.setText("{this}'s power is equal to your devotion to red")
).addHint(DevotionCount.R.getHint()));
// Whenever Anax or another nontoken creature you control dies, create a 1/1 red Satyr creature token with "This creature can't block." If the creature had power 4 or greater, create two of those tokens instead.
this.addAbility(new AnaxHardenedInTheForgeTriggeredAbility());
// Whenever Anax or another nontoken creature you control dies, create a 1/1 red Satyr creature token
// with "This creature can't block." If the creature had power 4 or greater, create two of those tokens instead.
this.addAbility(new AnaxHardenedInTheForgeTriggeredAbility(null, false, filter));
}
private AnaxHardenedInTheForge(final AnaxHardenedInTheForge card) {
@ -52,10 +62,10 @@ public final class AnaxHardenedInTheForge extends CardImpl {
}
}
class AnaxHardenedInTheForgeTriggeredAbility extends TriggeredAbilityImpl {
class AnaxHardenedInTheForgeTriggeredAbility extends DiesThisOrAnotherCreatureTriggeredAbility {
AnaxHardenedInTheForgeTriggeredAbility() {
super(Zone.BATTLEFIELD, null, false);
AnaxHardenedInTheForgeTriggeredAbility(Effect effect, boolean optional, FilterCreaturePermanent filter) {
super(effect, optional, filter);
}
private AnaxHardenedInTheForgeTriggeredAbility(final AnaxHardenedInTheForgeTriggeredAbility ability) {
@ -67,33 +77,21 @@ class AnaxHardenedInTheForgeTriggeredAbility extends TriggeredAbilityImpl {
return new AnaxHardenedInTheForgeTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (!zEvent.isDiesEvent()) {
return false;
if (super.checkTrigger(event, game)) {
int tokenCount = ((ZoneChangeEvent) event).getTarget().getPower().getValue() > 3 ? 2 : 1;
this.getEffects().clear();
this.addEffect(new CreateTokenEffect(new SatyrCantBlockToken(), tokenCount));
return true;
}
if (!zEvent.getTarget().getId().equals(getSourceId())
&& (zEvent.getTarget() instanceof PermanentToken
|| !zEvent.getTarget().isCreature()
|| !Objects.equals(zEvent.getTarget().getControllerId(), getControllerId()))) {
return false;
}
int tokenCount = zEvent.getTarget().getPower().getValue() > 3 ? 2 : 1;
this.getEffects().clear();
this.addEffect(new CreateTokenEffect(new SatyrCantBlockToken(), tokenCount));
return true;
return false;
}
@Override
public String getRule() {
return "Whenever {this} or another nontoken creature you control dies, " +
"create a 1/1 red Satyr creature token with \"This creature can't block.\" " +
"If the creature had power 4 or greater, create two of those tokens instead.";
return "Whenever {this} or another nontoken creature you control dies, "
+ "create a 1/1 red Satyr creature token with \"This creature can't block.\" "
+ "If the creature had power 4 or greater, create two of those tokens instead.";
}
}

View file

@ -1,22 +1,17 @@
package mage.cards.p;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.permanent.TokenPredicate;
import mage.game.permanent.token.EldraziSpawnToken;
/**
@ -24,16 +19,25 @@ import mage.game.permanent.token.EldraziSpawnToken;
* @author North
*/
public final class PawnOfUlamog extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nontoken creature you control");
static {
filter.add(TargetController.YOU.getControllerPredicate());
filter.add(Predicates.not(TokenPredicate.instance));
}
public PawnOfUlamog(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}");
this.subtype.add(SubType.VAMPIRE);
this.subtype.add(SubType.SHAMAN);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
this.addAbility(new PawnOfUlamogTriggeredAbility());
// Whenever Pawn of Ulamog or another nontoken creature you control dies, you may create a 0/1 colorless
// Eldrazi Spawn creature token. It has "Sacrifice this creature: Add {C}."
this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility(new CreateTokenEffect(new EldraziSpawnToken()), true, filter));
}
public PawnOfUlamog(final PawnOfUlamog card) {
@ -44,49 +48,4 @@ public final class PawnOfUlamog extends CardImpl {
public PawnOfUlamog copy() {
return new PawnOfUlamog(this);
}
}
class PawnOfUlamogTriggeredAbility extends TriggeredAbilityImpl {
public PawnOfUlamogTriggeredAbility() {
super(Zone.BATTLEFIELD, new CreateTokenEffect(new EldraziSpawnToken()), true);
}
public PawnOfUlamogTriggeredAbility(PawnOfUlamogTriggeredAbility ability) {
super(ability);
}
@Override
public PawnOfUlamogTriggeredAbility copy() {
return new PawnOfUlamogTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.ZONE_CHANGE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
UUID targetId = event.getTargetId();
MageObject card = game.getLastKnownInformation(targetId, Zone.BATTLEFIELD);
if (card instanceof Permanent && !(card instanceof PermanentToken)) {
Permanent permanent = (Permanent) card;
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.isDiesEvent()
&& permanent.isControlledBy(this.controllerId)
&& (targetId.equals(this.getSourceId())
|| (permanent.isCreature()
&& !targetId.equals(this.getSourceId())
&& !(permanent instanceof PermanentToken)))) {
return true;
}
}
return false;
}
@Override
public String getRule() {
return "Whenever Pawn of Ulamog or another nontoken creature you control dies, you may create a 0/1 colorless Eldrazi Spawn creature token. It has \"Sacrifice this creature: Add {C}.\"";
}
}
}

View file

@ -9,10 +9,17 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
* Created by alexsandro on 06/03/17.
*/
public class SakashimaTheImpostorTest extends CardTestPlayerBase {
@Test
public void copySpellStutterTest() {
// Flash, Flying
// When Spellstutter Sprite enters the battlefield, counter target spell with converted mana cost X or less,
// where X is the number of Faeries you control.
addCard(Zone.BATTLEFIELD, playerA, "Spellstutter Sprite", 1);
addCard(Zone.BATTLEFIELD, playerB, "Island", 4);
// You may have Sakashima the Impostor enter the battlefield as a copy of any creature on the battlefield,
// except its name is Sakashima the Impostor, it's legendary in addition to its other types,
// and it has "{2}{U}{U}: Return Sakashima the Impostor to its owner's hand at the beginning of the next end step."
addCard(Zone.HAND, playerB, "Sakashima the Impostor", 4);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Sakashima the Impostor");
@ -25,4 +32,42 @@ public class SakashimaTheImpostorTest extends CardTestPlayerBase {
assertPowerToughness(playerB, "Sakashima the Impostor", 1, 1);
}
/**
* I played Sakashima the Imposter copying an opponents Pawn of Ulamaog.
* Sakashima gained the following ability: "Whenever Pawn of Ulamog or
* another nontoken creature you control dies, you may create a 0/1
* colorless Eldrazi Spawn creature token. It has "Sacrifice this creature:
* Add {C}." Then Sakashima died due to combat damage and the ability did
* not trigger.
*
*/
@Test
public void copyDiesTriggeredTest() {
// Whenever Pawn of Ulamog or another nontoken creature you control dies, you may create a 0/1 colorless
// Eldrazi Spawn creature token. It has "Sacrifice this creature: Add {C}."
addCard(Zone.BATTLEFIELD, playerA, "Pawn of Ulamog", 1); // Creature 2/2
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); // Creature 2/2
addCard(Zone.BATTLEFIELD, playerB, "Island", 4);
// You may have Sakashima the Impostor enter the battlefield as a copy of any creature on the battlefield,
// except its name is Sakashima the Impostor, it's legendary in addition to its other types,
// and it has "{2}{U}{U}: Return Sakashima the Impostor to its owner's hand at the beginning of the next end step."
addCard(Zone.HAND, playerB, "Sakashima the Impostor", 4);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Sakashima the Impostor");
setChoice(playerB, "Pawn of Ulamog");
attack(4, playerB, "Sakashima the Impostor");
block(4, playerA, "Silvercoat Lion", "Sakashima the Impostor");
setStopAt(4, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
assertGraveyardCount(playerB, "Sakashima the Impostor", 1);
assertPermanentCount(playerA, "Eldrazi Spawn", 1);
assertPermanentCount(playerB, "Eldrazi Spawn", 1);
}
}

View file

@ -171,21 +171,24 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
}
}
}
if (isLeavesTheBattlefieldTrigger()) {
source = zce.getTarget();
}
break;
case DESTROYED_PERMANENT:
if (isLeavesTheBattlefieldTrigger()) {
if (event.getType() == EventType.DESTROYED_PERMANENT) {
source = game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD);
} else if (((ZoneChangeEvent) event).getTarget() != null) {
source = ((ZoneChangeEvent) event).getTarget();
} else {
source = game.getLastKnownInformation(getSourceId(), event.getZone());
}
source = game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD);
}
break;
case PHASED_OUT:
case PHASED_IN:
if (isLeavesTheBattlefieldTrigger()) {
source = game.getLastKnownInformation(getSourceId(), event.getZone());
}
if (this.zone == Zone.ALL || game.getLastKnownInformation(getSourceId(), zone) != null) {
return this.hasSourceObjectAbility(game, source, event);
}
break;
}
return super.isInUseableZone(game, source, event);
}

View file

@ -27,6 +27,9 @@ public class ZoneChangeAllTriggeredAbility extends TriggeredAbilityImpl {
public ZoneChangeAllTriggeredAbility(Zone zone, Zone fromZone, Zone toZone, Effect effect, FilterPermanent filter, String rule, boolean optional) {
super(zone, effect, optional);
if (fromZone == Zone.BATTLEFIELD) {
setLeavesTheBattlefieldTrigger(true);
}
this.fromZone = fromZone;
this.toZone = toZone;
this.rule = rule;