mirror of
https://github.com/correl/mage.git
synced 2024-11-15 03:00:16 +00:00
* Fix of some problems of zone change related triggered abilities that had not been correctly implemented (fixes #6586).
This commit is contained in:
parent
40c01a04c4
commit
32ce1d85e9
5 changed files with 102 additions and 94 deletions
|
@ -1,7 +1,6 @@
|
||||||
package mage.cards.a;
|
package mage.cards.a;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.TriggeredAbilityImpl;
|
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.dynamicvalue.common.DevotionCount;
|
import mage.abilities.dynamicvalue.common.DevotionCount;
|
||||||
import mage.abilities.effects.common.CreateTokenEffect;
|
import mage.abilities.effects.common.CreateTokenEffect;
|
||||||
|
@ -12,17 +11,27 @@ import mage.constants.*;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
import mage.game.events.ZoneChangeEvent;
|
import mage.game.events.ZoneChangeEvent;
|
||||||
import mage.game.permanent.PermanentToken;
|
|
||||||
import mage.game.permanent.token.SatyrCantBlockToken;
|
import mage.game.permanent.token.SatyrCantBlockToken;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
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
|
* @author TheElk801
|
||||||
*/
|
*/
|
||||||
public final class AnaxHardenedInTheForge extends CardImpl {
|
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) {
|
public AnaxHardenedInTheForge(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{R}{R}");
|
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")
|
.setText("{this}'s power is equal to your devotion to red")
|
||||||
).addHint(DevotionCount.R.getHint()));
|
).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.
|
// Whenever Anax or another nontoken creature you control dies, create a 1/1 red Satyr creature token
|
||||||
this.addAbility(new AnaxHardenedInTheForgeTriggeredAbility());
|
// 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) {
|
private AnaxHardenedInTheForge(final AnaxHardenedInTheForge card) {
|
||||||
|
@ -52,10 +62,10 @@ public final class AnaxHardenedInTheForge extends CardImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AnaxHardenedInTheForgeTriggeredAbility extends TriggeredAbilityImpl {
|
class AnaxHardenedInTheForgeTriggeredAbility extends DiesThisOrAnotherCreatureTriggeredAbility {
|
||||||
|
|
||||||
AnaxHardenedInTheForgeTriggeredAbility() {
|
AnaxHardenedInTheForgeTriggeredAbility(Effect effect, boolean optional, FilterCreaturePermanent filter) {
|
||||||
super(Zone.BATTLEFIELD, null, false);
|
super(effect, optional, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AnaxHardenedInTheForgeTriggeredAbility(final AnaxHardenedInTheForgeTriggeredAbility ability) {
|
private AnaxHardenedInTheForgeTriggeredAbility(final AnaxHardenedInTheForgeTriggeredAbility ability) {
|
||||||
|
@ -67,33 +77,21 @@ class AnaxHardenedInTheForgeTriggeredAbility extends TriggeredAbilityImpl {
|
||||||
return new AnaxHardenedInTheForgeTriggeredAbility(this);
|
return new AnaxHardenedInTheForgeTriggeredAbility(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean checkEventType(GameEvent event, Game game) {
|
|
||||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkTrigger(GameEvent event, Game game) {
|
public boolean checkTrigger(GameEvent event, Game game) {
|
||||||
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
if (super.checkTrigger(event, game)) {
|
||||||
if (!zEvent.isDiesEvent()) {
|
int tokenCount = ((ZoneChangeEvent) event).getTarget().getPower().getValue() > 3 ? 2 : 1;
|
||||||
return false;
|
this.getEffects().clear();
|
||||||
|
this.addEffect(new CreateTokenEffect(new SatyrCantBlockToken(), tokenCount));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (!zEvent.getTarget().getId().equals(getSourceId())
|
return false;
|
||||||
&& (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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRule() {
|
public String getRule() {
|
||||||
return "Whenever {this} or another nontoken creature you control dies, " +
|
return "Whenever {this} or another nontoken creature you control dies, "
|
||||||
"create a 1/1 red Satyr creature token with \"This creature can't block.\" " +
|
+ "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.";
|
+ "If the creature had power 4 or greater, create two of those tokens instead.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,17 @@
|
||||||
|
|
||||||
package mage.cards.p;
|
package mage.cards.p;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.MageObject;
|
import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility;
|
||||||
import mage.abilities.TriggeredAbilityImpl;
|
|
||||||
import mage.abilities.effects.common.CreateTokenEffect;
|
import mage.abilities.effects.common.CreateTokenEffect;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.constants.Zone;
|
import mage.constants.TargetController;
|
||||||
import mage.game.Game;
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
import mage.game.events.GameEvent;
|
import mage.filter.predicate.Predicates;
|
||||||
import mage.game.events.GameEvent.EventType;
|
import mage.filter.predicate.permanent.TokenPredicate;
|
||||||
import mage.game.events.ZoneChangeEvent;
|
|
||||||
import mage.game.permanent.Permanent;
|
|
||||||
import mage.game.permanent.PermanentToken;
|
|
||||||
import mage.game.permanent.token.EldraziSpawnToken;
|
import mage.game.permanent.token.EldraziSpawnToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,15 +20,24 @@ import mage.game.permanent.token.EldraziSpawnToken;
|
||||||
*/
|
*/
|
||||||
public final class PawnOfUlamog extends CardImpl {
|
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) {
|
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.VAMPIRE);
|
||||||
this.subtype.add(SubType.SHAMAN);
|
this.subtype.add(SubType.SHAMAN);
|
||||||
|
|
||||||
this.power = new MageInt(2);
|
this.power = new MageInt(2);
|
||||||
this.toughness = 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) {
|
public PawnOfUlamog(final PawnOfUlamog card) {
|
||||||
|
@ -45,48 +49,3 @@ public final class PawnOfUlamog extends CardImpl {
|
||||||
return new PawnOfUlamog(this);
|
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}.\"";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,10 +9,17 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
* Created by alexsandro on 06/03/17.
|
* Created by alexsandro on 06/03/17.
|
||||||
*/
|
*/
|
||||||
public class SakashimaTheImpostorTest extends CardTestPlayerBase {
|
public class SakashimaTheImpostorTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void copySpellStutterTest() {
|
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, playerA, "Spellstutter Sprite", 1);
|
||||||
addCard(Zone.BATTLEFIELD, playerB, "Island", 4);
|
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);
|
addCard(Zone.HAND, playerB, "Sakashima the Impostor", 4);
|
||||||
|
|
||||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Sakashima the Impostor");
|
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);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,21 +171,24 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (isLeavesTheBattlefieldTrigger()) {
|
||||||
|
source = zce.getTarget();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case DESTROYED_PERMANENT:
|
case DESTROYED_PERMANENT:
|
||||||
if (isLeavesTheBattlefieldTrigger()) {
|
if (isLeavesTheBattlefieldTrigger()) {
|
||||||
if (event.getType() == EventType.DESTROYED_PERMANENT) {
|
source = game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD);
|
||||||
source = game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD);
|
|
||||||
} else if (((ZoneChangeEvent) event).getTarget() != null) {
|
|
||||||
source = ((ZoneChangeEvent) event).getTarget();
|
|
||||||
} else {
|
|
||||||
source = game.getLastKnownInformation(getSourceId(), event.getZone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
case PHASED_OUT:
|
case PHASED_OUT:
|
||||||
case PHASED_IN:
|
case PHASED_IN:
|
||||||
|
if (isLeavesTheBattlefieldTrigger()) {
|
||||||
|
source = game.getLastKnownInformation(getSourceId(), event.getZone());
|
||||||
|
}
|
||||||
if (this.zone == Zone.ALL || game.getLastKnownInformation(getSourceId(), zone) != null) {
|
if (this.zone == Zone.ALL || game.getLastKnownInformation(getSourceId(), zone) != null) {
|
||||||
return this.hasSourceObjectAbility(game, source, event);
|
return this.hasSourceObjectAbility(game, source, event);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return super.isInUseableZone(game, source, event);
|
return super.isInUseableZone(game, source, event);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
public ZoneChangeAllTriggeredAbility(Zone zone, Zone fromZone, Zone toZone, Effect effect, FilterPermanent filter, String rule, boolean optional) {
|
||||||
super(zone, effect, optional);
|
super(zone, effect, optional);
|
||||||
|
if (fromZone == Zone.BATTLEFIELD) {
|
||||||
|
setLeavesTheBattlefieldTrigger(true);
|
||||||
|
}
|
||||||
this.fromZone = fromZone;
|
this.fromZone = fromZone;
|
||||||
this.toZone = toZone;
|
this.toZone = toZone;
|
||||||
this.rule = rule;
|
this.rule = rule;
|
||||||
|
|
Loading…
Reference in a new issue