fixes Phelia, Exuberant Shepherd return trigger

fixes #13722
This commit is contained in:
Susucre 2025-06-03 20:50:07 +02:00 committed by Failure
parent a552208b92
commit d17f0561b0
3 changed files with 75 additions and 11 deletions

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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