* 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

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