mirror of
https://github.com/correl/mage.git
synced 2024-11-14 19:19:32 +00:00
* 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:
parent
a6f79580d7
commit
3f44d9eef3
19 changed files with 232 additions and 116 deletions
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue