* Exile card and return it from exile - fixed rollback error on commander creature exile (#7250);

[CMR] fixed PromiseOfTomorrow - not working ability;
This commit is contained in:
Oleg Agafonov 2020-12-17 10:07:15 +04:00
parent a6f79580d7
commit 3f44d9eef3
19 changed files with 232 additions and 116 deletions

View file

@ -21,8 +21,7 @@ public final class AcrobaticManeuver extends CardImpl {
// Exile target creature you control, then return that card to the battlefield under its owner's control.
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent());
Effect effect = new ExileTargetForSourceEffect();
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addEffect(new ExileTargetForSourceEffect());
this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false));
// Draw a card.

View file

@ -123,16 +123,11 @@ class AshioksErasureReplacementEffect extends ContinuousRuleModifyingEffectImpl
}
UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
if (exileZone == null) {
return false;
}
ExileZone exile = game.getExile().getExileZone(exileZone);
if (exile == null) {
// try without ZoneChangeCounter - that is useful for tokens
exileZone = CardUtil.getCardExileZoneId(game, source);
if (exileZone != null) {
exile = game.getExile().getExileZone(exileZone);
}
exile = game.getExile().getExileZone(exileZone);
}
if (exile == null) {

View file

@ -20,8 +20,7 @@ public final class Cloudshift extends CardImpl {
// Exile target creature you control, then return that card to the battlefield under your control.
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent());
Effect effect = new ExileTargetForSourceEffect();
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addEffect(new ExileTargetForSourceEffect());
this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderYourControlTargetEffect(false));
}

View file

@ -21,11 +21,9 @@ public final class Displace extends CardImpl {
// Exile up to two target creatures you control, then return those cards to the battlefield under their owner's control.
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(0, 2, new FilterControlledCreaturePermanent("creatures you control"), false));
Effect effect = new ExileTargetForSourceEffect();
this.getSpellAbility().addEffect(effect);
effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)
.withReturnNames("those cards", "their owner's").concatBy(", then");
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addEffect(new ExileTargetForSourceEffect());
this.getSpellAbility().addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false)
.withReturnNames("those cards", "their owner's").concatBy(", then"));
}
public Displace(final Displace card) {

View file

@ -40,11 +40,9 @@ public final class EldraziDisplacer extends CardImpl {
this.addAbility(new DevoidAbility(this.color));
// {2}{C}: Exile another target creature, then return it to the battlefield tapped under its owner's control.
Effect effect = new ExileTargetForSourceEffect();
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl<>("{2}{C}"));
effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(true, false)
.withReturnNames("it", "its owner's").concatBy(", then");
ability.addEffect(effect);
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetForSourceEffect(), new ManaCostsImpl<>("{2}{C}"));
ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(true, false)
.withReturnNames("it", "its owner's").concatBy(", then"));
ability.addTarget(new TargetCreaturePermanent(FILTER));
this.addAbility(ability);
}

View file

@ -29,8 +29,7 @@ public final class EssenceFlux extends CardImpl {
// Exile target creature you control, then return that card to the battlefield under its owner's control. If it's a Spirit, put a +1/+1 counter on it.
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent());
Effect effect = new ExileTargetForSourceEffect();
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addEffect(new ExileTargetForSourceEffect());
this.getSpellAbility().addEffect(new EssenceFluxEffect());
}

View file

@ -39,15 +39,12 @@ public final class FacelessButcher extends CardImpl {
this.toughness = new MageInt(3);
// When Faceless Butcher enters the battlefield, exile target creature other than Faceless Butcher.
Effect effect = new ExileTargetForSourceEffect();
Ability ability1 = new EntersBattlefieldTriggeredAbility(effect, false);
Target target = new TargetPermanent(filter);
ability1.addTarget(target);
this.addAbility(ability1);
Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), false);
ability.addTarget(new TargetPermanent(filter));
this.addAbility(ability);
// When Faceless Butcher leaves the battlefield, return the exiled card to the battlefield under its owner's control.
Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false);
this.addAbility(ability2);
this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false));
}

View file

@ -44,10 +44,8 @@ public final class FacelessDevourer extends CardImpl {
this.addAbility(ShadowAbility.getInstance());
// When Faceless Devourer enters the battlefield, exile another target creature with shadow.
Effect effect = new ExileTargetForSourceEffect();
Ability ability = new EntersBattlefieldTriggeredAbility(effect, false);
Target target = new TargetPermanent(filter);
ability.addTarget(target);
Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), false);
ability.addTarget(new TargetPermanent(filter));
this.addAbility(ability);
// When Faceless Devourer leaves the battlefield, return the exiled card to the battlefield under its owner's control.

View file

@ -36,12 +36,10 @@ public final class FelidarGuardian extends CardImpl {
this.toughness = new MageInt(4);
// When Felidar Guardian enters the battlefield, you may exile another target permanent you control, then return that card to the battlefield under its owner's control.
Effect effect = new ExileTargetForSourceEffect();
Ability ability = new EntersBattlefieldTriggeredAbility(effect, true);
Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), true);
ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false));
ability.addTarget(new TargetControlledPermanent(filter));
this.addAbility(ability);
}
public FelidarGuardian(final FelidarGuardian card) {

View file

@ -71,7 +71,7 @@ class GhastlyDemiseEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
int affectedTargets = 0;
if (source.getTargets().size() > 1 && targetPointer instanceof FirstTargetPointer) { // for Rain of Thorns
if (source.getTargets().size() > 1 && this.targetPointer instanceof FirstTargetPointer) { // for Rain of Thorns
for (Target target : source.getTargets()) {
for (UUID permanentId : target.getTargets()) {
Permanent permanent = game.getPermanent(permanentId);
@ -81,8 +81,8 @@ class GhastlyDemiseEffect extends OneShotEffect {
}
}
}
} else if (!targetPointer.getTargets(game, source).isEmpty()) {
for (UUID permanentId : targetPointer.getTargets(game, source)) {
} else if (this.targetPointer != null && !this.targetPointer.getTargets(game, source).isEmpty()) {
for (UUID permanentId : this.targetPointer.getTargets(game, source)) {
Permanent permanent = game.getPermanent(permanentId);
if (permanent != null && permanent.getToughness().getValue() <= game.getPlayer(source.getControllerId()).getGraveyard().size()) {
permanent.destroy(source, game, noRegen);

View file

@ -34,14 +34,12 @@ public final class OblivionRing extends CardImpl {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}");
// When Oblivion Ring enters the battlefield, exile another target nonland permanent.
Ability ability1 = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), false);
Target target = new TargetPermanent(anotherNonlandPermanent);
ability1.addTarget(target);
this.addAbility(ability1);
Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), false);
ability.addTarget(new TargetPermanent(anotherNonlandPermanent));
this.addAbility(ability);
// When Oblivion Ring leaves the battlefield, return the exiled card to the battlefield under its owner's control.
Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false);
this.addAbility(ability2);
this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false));
}
public OblivionRing(final OblivionRing card) {

View file

@ -39,13 +39,12 @@ public final class PromiseOfTomorrow extends CardImpl {
));
// At the beginning of each end step, if you control no creatures, sacrifice Promise of Tomorrow and return all cards exiled with it to the battlefield under your control.
BeginningOfEndStepTriggeredAbility returnAbility = new BeginningOfEndStepTriggeredAbility(new SacrificeSourceEffect(), TargetController.ANY, false);
returnAbility.addEffect(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD));
Ability ability = new ConditionalInterveningIfTriggeredAbility(
new BeginningOfEndStepTriggeredAbility(
new SacrificeSourceEffect(), TargetController.ANY, false
), condition, "At the beginning of each end step, if you control no creatures, " +
returnAbility, condition, "At the beginning of each end step, if you control no creatures, " +
"sacrifice {this} and return all cards exiled with it to the battlefield under your control."
);
ability.addEffect(new ReturnFromExileForSourceEffect(Zone.EXILED));
this.addAbility(ability);
}

View file

@ -113,8 +113,8 @@ class WerewolfRansackerEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
int affectedTargets = 0;
if (!targetPointer.getTargets(game, source).isEmpty()) {
for (UUID permanentId : targetPointer.getTargets(game, source)) {
if (this.targetPointer != null && !this.targetPointer.getTargets(game, source).isEmpty()) {
for (UUID permanentId : this.targetPointer.getTargets(game, source)) {
Permanent permanent = game.getPermanent(permanentId);
if (permanent != null) {
if (permanent.destroy(source, game, false)) {

View file

@ -25,11 +25,9 @@ public class ExileTargetTest extends CardTestCommander4Players {
addCard(Zone.BATTLEFIELD, playerC, "Balduvian Bears", 1); // 2/2
// must select opponent's Balduvian Bears
// showAvailableAbilities("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Oblivion Ring");
//addTarget(playerA, "Balduvian Bears"); // disable to activate AI target choose
// showAvailableAbilities("after", 1, PhaseStep.BEGIN_COMBAT, playerA);
//setStrictChooseMode(true); // disable strict mode to activate AI for choosing
setStopAt(1, PhaseStep.END_COMBAT);
execute();

View file

@ -0,0 +1,121 @@
package org.mage.test.cards.single.cmr;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestCommander4Players;
/**
* @author JayDi85
*/
public class PromiseOfTomorrowTest extends CardTestCommander4Players {
@Test
public void test_NormalCard() {
// bug: https://github.com/magefree/mage/issues/7250
// Whenever a creature you control dies, exile it.
// At the beginning of each end step, if you control no creatures, sacrifice Promise of Tomorrow and return all
// cards exiled with it to the battlefield under your control.
addCard(Zone.BATTLEFIELD, playerA, "Promise of Tomorrow", 1);
//
addCard(Zone.HAND, playerA, "Lightning Bolt", 2);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears@bear", 2);
// destroy two creatures
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "@bear.1");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "@bear.2");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkExileCount("after die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 2);
// must return
checkPermanentCount("after return", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 2);
checkGraveyardCount("after return", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Promise of Tomorrow", 1);
setStrictChooseMode(true);
setStopAt(2, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
}
@Test
public void test_Commander_LeaveInZone() {
// bug: https://github.com/magefree/mage/issues/7250
// Whenever a creature you control dies, exile it.
// At the beginning of each end step, if you control no creatures, sacrifice Promise of Tomorrow and return all
// cards exiled with it to the battlefield under your control.
addCard(Zone.BATTLEFIELD, playerA, "Promise of Tomorrow", 1);
//
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
//
addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
// prepare commander
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
checkPermanentCount("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1);
// destroy creature
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkExileCount("after die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1);
setChoice(playerA, "No"); // move commander from graveyard to command zone
setChoice(playerA, "No"); // move commander from exile to command zone
// must return
checkPermanentCount("after return", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1);
checkGraveyardCount("after return", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Promise of Tomorrow", 1);
setStrictChooseMode(true);
setStopAt(2, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
}
@Test
public void test_Commander_MoveToCommandZoneFirst() {
// bug: https://github.com/magefree/mage/issues/7250
// Whenever a creature you control dies, exile it.
// At the beginning of each end step, if you control no creatures, sacrifice Promise of Tomorrow and return all
// cards exiled with it to the battlefield under your control.
addCard(Zone.BATTLEFIELD, playerA, "Promise of Tomorrow", 1);
//
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
//
addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
// prepare commander
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
checkPermanentCount("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1);
// destroy creature
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
checkExileCount("after die", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1);
// possible bug: Promise of Tomorrow tries to move commander card to exile from command zone with error
setChoice(playerA, "Yes"); // move commander from graveyard to command zone
setChoice(playerA, "No"); // move commander from exile to command zone
// must return
checkPermanentCount("after return", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1);
checkGraveyardCount("after return", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Promise of Tomorrow", 1);
setStrictChooseMode(true);
setStopAt(2, PhaseStep.END_TURN);
execute();
assertAllCommandsUsed();
}
}

View file

@ -3,6 +3,7 @@ package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.Outcome;
@ -10,15 +11,19 @@ import mage.game.Game;
import mage.players.Player;
import mage.target.Target;
import mage.target.targetpointer.FirstTargetPointer;
import mage.util.CardUtil;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;
import mage.abilities.effects.Effect;
import mage.target.targetpointer.FixedTarget;
import mage.target.targetpointer.TargetPointer;
import mage.util.CardUtil;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* Use it for combo with ReturnFromExileForSourceEffect (exile and return exiled later)
*
* @author BetaSteward_at_googlemail.com
*/
public class ExileTargetForSourceEffect extends OneShotEffect {
@ -45,46 +50,50 @@ public class ExileTargetForSourceEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (controller == null || sourceObject == null) {
return false;
}
if (controller != null
&& sourceObject != null) {
Set<Card> cards = new LinkedHashSet<>();
if (source.getTargets().size() > 1
&& targetPointer instanceof FirstTargetPointer) {
for (Target target : source.getTargets()) {
for (UUID targetId : target.getTargets()) {
MageObject mageObject = game.getObject(targetId);
if (mageObject instanceof Card) {
cards.add((Card) mageObject);
}
Set<UUID> objectsToMove = new LinkedHashSet<>();
if (this.targetPointer instanceof FirstTargetPointer
&& source.getTargets().size() > 1) {
for (Target target : source.getTargets()) {
objectsToMove.addAll(target.getTargets());
}
} else {
if (this.targetPointer != null && !this.targetPointer.getTargets(game, source).isEmpty()) {
objectsToMove.addAll(this.targetPointer.getTargets(game, source));
} else {
// issue with Madness keyword #6889
UUID fixedTargetId = null;
for (Effect effect : source.getEffects()) {
TargetPointer targetPointerId = effect.getTargetPointer();
if (targetPointerId instanceof FixedTarget) {
fixedTargetId = (((FixedTarget) targetPointerId).getTarget());
}
}
} else {
if (!targetPointer.getTargets(game, source).isEmpty()) {
for (UUID targetId : targetPointer.getTargets(game, source)) {
MageObject mageObject = game.getObject(targetId);
if (mageObject != null) {
cards.add((Card) mageObject);
}
}
} else {
// issue with Madness keyword #6889
UUID fixedTargetId = null;
for (Effect effect : source.getEffects()) {
TargetPointer targetPointerId = effect.getTargetPointer();
if (targetPointerId instanceof FixedTarget) {
fixedTargetId = (((FixedTarget) targetPointerId).getTarget());
}
}
if (fixedTargetId != null) {
cards.add((Card) game.getObject(fixedTargetId));
}
if (fixedTargetId != null) {
objectsToMove.add(fixedTargetId);
}
}
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
return controller.moveCardsToExile(cards, source, game, true, exileId, sourceObject.getIdName());
}
return false;
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
// it can target permanents on battlefield, so use objects first
Set<Card> cardsToMove = objectsToMove.stream()
.map(game::getObject)
.filter(Objects::nonNull)
.map(object -> {
if (object instanceof Card) {
return (Card) object;
} else {
return game.getCard(object.getId());
}
})
.filter(Objects::nonNull)
.collect(Collectors.toSet());
return controller.moveCardsToExile(cardsToMove, source, game, true, exileId, sourceObject.getIdName());
}
@Override

View file

@ -1,6 +1,5 @@
package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
@ -10,16 +9,19 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;
import org.apache.log4j.Logger;
import java.util.UUID;
/**
* Use it for combo with ExileTargetForSourceEffect (exile and return exiled later)
*
* @author BetaSteward_at_googlemail.com
*/
public class ReturnFromExileForSourceEffect extends OneShotEffect {
private Zone returnToZone;
private boolean tapped;
private boolean previousZone;
private final Zone returnToZone;
private final boolean tapped;
private final boolean previousZone;
private String returnName = "cards";
private String returnControlName;
@ -78,24 +80,32 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (sourceObject != null && controller != null) {
Permanent permanentLeftBattlefield = (Permanent) getValue("permanentLeftBattlefield");
if (permanentLeftBattlefield == null) {
Logger.getLogger(ReturnFromExileForSourceEffect.class).error("Permanent not found: " + sourceObject.getName());
return false;
}
ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), permanentLeftBattlefield.getZoneChangeCounter(game)));
if (exile != null) { // null is valid if source left battlefield before enters the battlefield effect resolved
if (returnToZone == Zone.BATTLEFIELD) {
controller.moveCards(exile.getCards(game), returnToZone, source, game, false, false, true, null);
} else {
controller.moveCards(exile, returnToZone, source, game);
}
}
return true;
if (controller == null) {
return false;
}
return false;
// effect uses in two use cases:
// * on battlefield
// * after leaves the battlefield
// so ZCC must be different in different use cases
UUID exileId;
Permanent permanentLeftBattlefield = (Permanent) getValue("permanentLeftBattlefield");
if (permanentLeftBattlefield != null) {
exileId = CardUtil.getExileZoneId(game, source.getSourceId(), permanentLeftBattlefield.getZoneChangeCounter(game));
} else {
exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
}
ExileZone exile = game.getExile().getExileZone(exileId);
if (exile != null) { // null is valid if source left battlefield before enters the battlefield effect resolved
if (returnToZone == Zone.BATTLEFIELD) {
controller.moveCards(exile.getCards(game), returnToZone, source, game, false, false, true, null);
} else {
controller.moveCards(exile, returnToZone, source, game);
}
}
return true;
}
private void updateText() {

View file

@ -61,7 +61,7 @@ public class SkipNextPlayerUntapStepEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player player = null;
if (targetPointer != null) {
if (!targetPointer.getTargets(game, source).isEmpty()) {
if (!this.targetPointer.getTargets(game, source).isEmpty()) {
player = game.getPlayer(targetPointer.getFirst(game, source));
} else {
player = game.getPlayer(source.getControllerId());

View file

@ -154,7 +154,7 @@ class ChampionExileCost extends CostImpl {
MageObject sourceObject = ability.getSourceObject(game);
if (controller != null && sourceObject != null) {
if (targets.choose(Outcome.Exile, controllerId, source.getSourceId(), game)) {
UUID exileId = CardUtil.getExileZoneId(game, ability.getSourceId(), ability.getSourceObjectZoneChangeCounter());
UUID exileId = CardUtil.getExileZoneId(game, ability.getSourceId(), ability.getSourceObjectZoneChangeCounter()); // exileId important for return effect
for (UUID targetId : targets.get(0).getTargets()) {
Permanent permanent = game.getPermanent(targetId);
if (permanent == null) {