diff --git a/Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java b/Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java index 63f6fdba3df..851b8af4e35 100644 --- a/Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java +++ b/Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java @@ -1,6 +1,7 @@ package mage.cards.p; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -49,10 +50,7 @@ public final class PheliaExuberantShepherd extends CardImpl { this.addAbility(FlashAbility.getInstance()); // Whenever Phelia, Exuberant Shepherd attacks, exile up to one other target nonland permanent. At the beginning of the next end step, return that card to the battlefield under its owner's control. If it entered under your control, put a +1/+1 counter on Phelia. - Ability ability = new AttacksTriggeredAbility(new ExileTargetEffect().setToSourceExileZone(true)); - ability.addEffect(new CreateDelayedTriggeredAbilityEffect( - new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new PheliaExuberantShepherdEffect()), false - )); + Ability ability = new AttacksTriggeredAbility(new PheliaExuberantShepherdExileEffect()); ability.addTarget(new TargetPermanent(0, 1, filter)); this.addAbility(ability); } @@ -67,16 +65,55 @@ public final class PheliaExuberantShepherd extends CardImpl { } } +class PheliaExuberantShepherdExileEffect extends ExileTargetEffect { + + PheliaExuberantShepherdExileEffect() { + super(); + outcome = Outcome.Neutral; // quite contextual outcome. + staticText = "exile up to one other target nonland permanent. At the beginning of the next end step, " + + "return that card to the battlefield under its owner's control. If it entered under your control, " + + "put a +1/+1 counter on {this}."; + } + + private PheliaExuberantShepherdExileEffect(final PheliaExuberantShepherdExileEffect effect) { + super(effect); + } + + @Override + public PheliaExuberantShepherdExileEffect copy() { + return new PheliaExuberantShepherdExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + MageObject sourceObject = source.getSourceObject(game); + this.exileId = UUID.randomUUID(); + this.exileZone = sourceObject == null ? null : sourceObject.getIdName(); + // attempting to exile to the fresh exileId + boolean didSomething = super.apply(game, source); + // delayed trigger is created even if exiling failed. + didSomething |= new CreateDelayedTriggeredAbilityEffect( + new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new PheliaExuberantShepherdEffect(this.exileId)), false + ).apply(game, source); + return didSomething; + } +} + class PheliaExuberantShepherdEffect extends OneShotEffect { - PheliaExuberantShepherdEffect() { + private final UUID zoneId; // the exile zone's id for cards to return. + + PheliaExuberantShepherdEffect(UUID zoneId) { super(Outcome.Benefit); staticText = "return that card to the battlefield under its owner's control. " + "If it entered under your control, put a +1/+1 counter on {this}"; + this.zoneId = zoneId; } private PheliaExuberantShepherdEffect(final PheliaExuberantShepherdEffect effect) { super(effect); + this.zoneId = effect.zoneId; } @Override @@ -90,7 +127,6 @@ class PheliaExuberantShepherdEffect extends OneShotEffect { if (player == null) { return false; } - UUID zoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); ExileZone exileZone = game.getExile().getExileZone(zoneId); if (exileZone == null || exileZone.isEmpty()) { return false; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/PheliaExuberantShepherdTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/PheliaExuberantShepherdTest.java index 1abb61f00cf..64b36e9b337 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/PheliaExuberantShepherdTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/PheliaExuberantShepherdTest.java @@ -149,4 +149,35 @@ public class PheliaExuberantShepherdTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Memnite", 1); } + // bug: return trigger should not return cards exiled the turn before and not + // returned due to a Stifle effect + @Test + public void test_Stifle() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, phelia, 1); + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerA, "Ornithopter", 1); + addCard(Zone.HAND, playerB, "Stifle", 1); + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + + attack(1, playerA, phelia, playerB); + addTarget(playerA, "Memnite"); + + castSpell(1, PhaseStep.END_TURN, playerB, "Stifle", "stack ability (At the beginning"); + + checkExileCount("Memnite still exiled", 2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + + attack(3, playerA, phelia, playerB); + addTarget(playerA, "Ornithopter"); + + // end of turn trigger: Ornithopter returns. + + setStopAt(4, PhaseStep.UPKEEP); + execute(); + + assertPowerToughness(playerA, phelia, 2 + 1, 2 + 1); + assertExileCount(playerA, "Memnite", 1); + assertPermanentCount(playerA, "Ornithopter", 1); + } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java index 8c404a46482..edc5433bba3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java @@ -12,9 +12,6 @@ import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; -import mage.target.Target; -import mage.target.targetpointer.FirstTargetPointer; -import mage.target.targetpointer.SecondTargetPointer; import mage.util.CardUtil; import java.util.LinkedHashSet; @@ -27,8 +24,8 @@ import java.util.UUID; public class ExileTargetEffect extends OneShotEffect { private final Zone onlyFromZone; - private String exileZone = null; - private UUID exileId = null; + protected String exileZone = null; + protected UUID exileId = null; private boolean toSourceExileZone = false; // exile the targets to a source object specific exile zone (takes care of zone change counter) private boolean withName = true; // for face down - allows to hide card name in game logs before real face down apply