updated various cards to improve how they handle exiling with info (#7615)

This commit is contained in:
Evan Kranzler 2021-02-22 15:26:58 -05:00 committed by GitHub
parent bb0a995541
commit bd3777997e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 349 additions and 442 deletions

View file

@ -3,8 +3,7 @@ package mage.cards.b;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.effects.Effect; import mage.abilities.effects.common.ExileTargetForSourceEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.effects.common.ReturnFromExileEffect; import mage.abilities.effects.common.ReturnFromExileEffect;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -13,9 +12,10 @@ import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.SuperType; import mage.constants.SuperType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterControlledPermanent;
import mage.filter.predicate.Predicates; import mage.filter.predicate.Predicates;
import mage.target.common.TargetControlledPermanent; import mage.target.TargetPermanent;
import java.util.UUID; import java.util.UUID;
@ -24,6 +24,12 @@ import java.util.UUID;
*/ */
public final class BragoKingEternal extends CardImpl { public final class BragoKingEternal extends CardImpl {
private static final FilterPermanent filter = new FilterControlledPermanent("nonland permanents you control");
static {
filter.add(Predicates.not(CardType.LAND.getPredicate()));
}
public BragoKingEternal(UUID ownerId, CardSetInfo setInfo) { public BragoKingEternal(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}");
addSuperType(SuperType.LEGENDARY); addSuperType(SuperType.LEGENDARY);
@ -34,14 +40,11 @@ public final class BragoKingEternal extends CardImpl {
// Flying // Flying
this.addAbility(FlyingAbility.getInstance()); this.addAbility(FlyingAbility.getInstance());
// When Brago, King Eternal deals combat damage to a player, exile any number of target nonland permanents you control, then return those cards to the battlefield under their owner's control. // When Brago, King Eternal deals combat damage to a player, exile any number of target nonland permanents you control, then return those cards to the battlefield under their owner's control.
Effect effect = new ExileTargetEffect(this.getId(), this.getName(), Zone.BATTLEFIELD); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new ExileTargetForSourceEffect().setText("exile any number of target nonland permanents you control"), false);
effect.setText("exile any number of target nonland permanents you control"); ability.addTarget(new TargetPermanent(0, Integer.MAX_VALUE, filter, false));
Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, false); ability.addEffect(new ReturnFromExileEffect(Zone.BATTLEFIELD, ", then return those cards to the battlefield under their owner's control"));
FilterControlledPermanent filterControlledNonlandPermanent = new FilterControlledPermanent();
filterControlledNonlandPermanent.add(Predicates.not(CardType.LAND.getPredicate()));
ability.addTarget(new TargetControlledPermanent(0, Integer.MAX_VALUE, filterControlledNonlandPermanent, false));
ability.addEffect(new ReturnFromExileEffect(this.getId(), Zone.BATTLEFIELD, ", then return those cards to the battlefield under their owner's control"));
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -144,7 +144,7 @@ class BreechesBrazenPlundererEffect extends OneShotEffect {
return false; return false;
} }
for (Card card : cards.getCards(game)) { for (Card card : cards.getCards(game)) {
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.EndOfTurn); CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, true);
} }
return true; return true;
} }

View file

@ -91,7 +91,7 @@ class CovetousUrgeEffect extends OneShotEffect {
if (card.getSpellAbility() == null) { if (card.getSpellAbility() == null) {
return true; return true;
} }
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.Custom); CardUtil.makeCardPlayable(game, source, card, Duration.Custom, true);
return true; return true;
} }
} }

View file

@ -1,4 +1,3 @@
package mage.cards.c; package mage.cards.c;
import mage.abilities.Ability; import mage.abilities.Ability;
@ -12,19 +11,16 @@ import mage.constants.CardType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterCard; import mage.filter.StaticFilters;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPlayer; import mage.target.TargetPlayer;
import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.FixedTarget;
import java.util.UUID; import java.util.UUID;
/** /**
*
* @author BetaSteward * @author BetaSteward
*/ */
public final class CurseOfOblivion extends CardImpl { public final class CurseOfOblivion extends CardImpl {
@ -56,11 +52,11 @@ public final class CurseOfOblivion extends CardImpl {
class CurseOfOblivionAbility extends TriggeredAbilityImpl { class CurseOfOblivionAbility extends TriggeredAbilityImpl {
public CurseOfOblivionAbility() { CurseOfOblivionAbility() {
super(Zone.BATTLEFIELD, new ExileFromZoneTargetEffect(Zone.GRAVEYARD, null, "", new FilterCard(), 2)); super(Zone.BATTLEFIELD, new ExileFromZoneTargetEffect(Zone.GRAVEYARD, StaticFilters.FILTER_CARD_CARDS, 2, false));
} }
public CurseOfOblivionAbility(final CurseOfOblivionAbility ability) { private CurseOfOblivionAbility(final CurseOfOblivionAbility ability) {
super(ability); super(ability);
} }
@ -76,20 +72,16 @@ class CurseOfOblivionAbility extends TriggeredAbilityImpl {
@Override @Override
public boolean checkTrigger(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) {
Permanent enchantment = game.getPermanent(this.sourceId); Permanent enchantment = getSourcePermanentOrLKI(game);
if (enchantment != null && enchantment.getAttachedTo() != null) { if (enchantment == null || !game.isActivePlayer(enchantment.getAttachedTo())) {
Player player = game.getPlayer(enchantment.getAttachedTo());
if (player != null && game.isActivePlayer(player.getId())) {
this.getEffects().get(0).setTargetPointer(new FixedTarget(player.getId()));
return true;
}
}
return false; return false;
} }
this.getEffects().setTargetPointer(new FixedTarget(enchantment.getAttachedTo()));
return true;
}
@Override @Override
public String getRule() { public String getRule() {
return "At the beginning of enchanted player's upkeep, that player exiles two cards from their graveyard."; return "At the beginning of enchanted player's upkeep, that player exiles two cards from their graveyard.";
} }
} }

View file

@ -90,7 +90,7 @@ class DaxosOfMeletisEffect extends OneShotEffect {
if (card.getSpellAbility() != null) { if (card.getSpellAbility() != null) {
// allow to cast the card // allow to cast the card
// and you may spend mana as though it were mana of any color to cast it // and you may spend mana as though it were mana of any color to cast it
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.EndOfTurn); CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, true);
} }
} }
return true; return true;

View file

@ -15,7 +15,6 @@ import mage.filter.FilterCard;
import mage.filter.predicate.Predicates; import mage.filter.predicate.Predicates;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent; import mage.game.events.ZoneChangeEvent;
import mage.players.Player; import mage.players.Player;
import mage.target.common.TargetCardInOpponentsGraveyard; import mage.target.common.TargetCardInOpponentsGraveyard;
@ -94,7 +93,7 @@ class DireFleetDaredevilEffect extends OneShotEffect {
targetCard = game.getCard(targetCard.getId()); targetCard = game.getCard(targetCard.getId());
if (targetCard != null) { if (targetCard != null) {
// you may play and spend any mana // you may play and spend any mana
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, targetCard, Duration.EndOfTurn); CardUtil.makeCardPlayable(game, source, targetCard, Duration.EndOfTurn, true);
// exile from graveyard // exile from graveyard
ContinuousEffect effect = new DireFleetDaredevilReplacementEffect(); ContinuousEffect effect = new DireFleetDaredevilReplacementEffect();
effect.setTargetPointer(new FixedTarget(targetCard, game)); effect.setTargetPointer(new FixedTarget(targetCard, game));

View file

@ -1,39 +1,34 @@
package mage.cards.f; package mage.cards.f;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ExileFromZoneTargetEffect;
import mage.abilities.effects.common.RegenerateSourceEffect; import mage.abilities.effects.common.RegenerateSourceEffect;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.*;
import mage.filter.FilterCard; import mage.filter.common.FilterControlledPermanent;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.game.ExileZone;
import mage.game.Game; import mage.game.Game;
import mage.target.common.TargetControlledCreaturePermanent; import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetDiscard;
import mage.util.CardUtil;
import java.util.UUID;
/** /**
*
* @author BetaSteward * @author BetaSteward
*/ */
public final class FiendOfTheShadows extends CardImpl { public final class FiendOfTheShadows extends CardImpl {
private UUID exileId = UUID.randomUUID(); private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.HUMAN, "a Human");
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a Human");
static {
filter.add(SubType.HUMAN.getPredicate());
}
public FiendOfTheShadows(UUID ownerId, CardSetInfo setInfo) { public FiendOfTheShadows(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}");
@ -44,12 +39,12 @@ public final class FiendOfTheShadows extends CardImpl {
this.toughness = new MageInt(3); this.toughness = new MageInt(3);
this.addAbility(FlyingAbility.getInstance()); this.addAbility(FlyingAbility.getInstance());
// Whenever Fiend of the Shadows deals combat damage to a player, that player exiles a card from their hand. You may play that card for as long as it remains exiled. // Whenever Fiend of the Shadows deals combat damage to a player, that player exiles a card from their hand. You may play that card for as long as it remains exiled.
this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new ExileFromZoneTargetEffect(Zone.HAND, exileId, "Fiend of the Shadows", new FilterCard()), false, true)); this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new FiendOfTheShadowsEffect(), false, true));
this.addAbility(new SimpleStaticAbility(Zone.ALL, new FiendOfTheShadowsEffect(exileId)));
// Sacrifice a Human: Regenerate Fiend of the Shadows. // Sacrifice a Human: Regenerate Fiend of the Shadows.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, filter, false)))); this.addAbility(new SimpleActivatedAbility(new RegenerateSourceEffect(), new SacrificeTargetCost(new TargetControlledPermanent(filter))));
} }
private FiendOfTheShadows(final FiendOfTheShadows card) { private FiendOfTheShadows(final FiendOfTheShadows card) {
@ -62,24 +57,16 @@ public final class FiendOfTheShadows extends CardImpl {
} }
} }
class FiendOfTheShadowsEffect extends AsThoughEffectImpl { class FiendOfTheShadowsEffect extends OneShotEffect {
private final UUID exileId; FiendOfTheShadowsEffect() {
super(Outcome.Discard);
public FiendOfTheShadowsEffect(UUID exileId) { staticText = "that player exiles a card from their hand. " +
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit); "You may play that card for as long as it remains exiled";
this.exileId = exileId;
staticText = "You may play that card for as long as it remains exiled";
} }
public FiendOfTheShadowsEffect(final FiendOfTheShadowsEffect effect) { private FiendOfTheShadowsEffect(final FiendOfTheShadowsEffect effect) {
super(effect); super(effect);
this.exileId = effect.exileId;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
} }
@Override @Override
@ -88,14 +75,23 @@ class FiendOfTheShadowsEffect extends AsThoughEffectImpl {
} }
@Override @Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { public boolean apply(Game game, Ability source) {
if (affectedControllerId.equals(source.getControllerId())) { Player player = game.getPlayer(targetPointer.getFirst(game, source));
ExileZone zone = game.getExile().getExileZone(exileId); MageObject sourceObject = source.getSourceObject(game);
if (zone != null && zone.contains(objectId)) { if (player == null || sourceObject == null || player.getHand().isEmpty()) {
return false;
}
TargetCard targetCard = new TargetDiscard(player.getId());
player.choose(outcome, targetCard, source.getSourceId(), game);
Card card = game.getCard(targetCard.getFirstTarget());
if (card == null) {
return false;
}
player.moveCardToExileWithInfo(
card, CardUtil.getExileZoneId(game, source), sourceObject.getName(),
source, game, Zone.HAND, true
);
CardUtil.makeCardPlayable(game, source, card, Duration.Custom, false);
return true; return true;
} }
} }
return false;
}
}

View file

@ -157,7 +157,7 @@ class GrenzoHavocRaiserEffect extends OneShotEffect {
if (card.getSpellAbility() != null) { if (card.getSpellAbility() != null) {
// allow to cast the card // allow to cast the card
// and you may spend mana as though it were mana of any color to cast it // and you may spend mana as though it were mana of any color to cast it
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.EndOfTurn); CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, true);
} }
} }
return true; return true;

View file

@ -1,8 +1,5 @@
package mage.cards.h; package mage.cards.h;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
@ -15,18 +12,18 @@ import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.common.FilterArtifactCard; import mage.filter.common.FilterArtifactCard;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetCardInLibrary;
import mage.util.CardUtil;
import java.util.UUID;
/** /**
*
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
public final class HoardingDragon extends CardImpl { public final class HoardingDragon extends CardImpl {
@ -41,10 +38,10 @@ public final class HoardingDragon extends CardImpl {
this.addAbility(FlyingAbility.getInstance()); this.addAbility(FlyingAbility.getInstance());
// When Hoarding Dragon enters the battlefield, you may search your library for an artifact card, exile it, then shuffle your library. // When Hoarding Dragon enters the battlefield, you may search your library for an artifact card, exile it, then shuffle your library.
this.addAbility(new EntersBattlefieldTriggeredAbility(new HoardingDragonEffect(this.getId()), true)); this.addAbility(new EntersBattlefieldTriggeredAbility(new HoardingDragonEffect(), true));
// When Hoarding Dragon dies, you may put the exiled card into its owner's hand. // When Hoarding Dragon dies, you may put the exiled card into its owner's hand.
this.addAbility(new DiesSourceTriggeredAbility(new ReturnFromExileEffect(this.getId(), Zone.HAND), false)); this.addAbility(new DiesSourceTriggeredAbility(new ReturnFromExileEffect(Zone.HAND), false));
} }
private HoardingDragon(final HoardingDragon card) { private HoardingDragon(final HoardingDragon card) {
@ -60,42 +57,35 @@ public final class HoardingDragon extends CardImpl {
class HoardingDragonEffect extends OneShotEffect { class HoardingDragonEffect extends OneShotEffect {
private final UUID exileId; HoardingDragonEffect() {
public HoardingDragonEffect(UUID exileId) {
super(Outcome.Exile); super(Outcome.Exile);
this.exileId = exileId;
this.staticText = "you may search your library for an artifact card, exile it, then shuffle your library"; this.staticText = "you may search your library for an artifact card, exile it, then shuffle your library";
} }
public HoardingDragonEffect(final HoardingDragonEffect effect) { private HoardingDragonEffect(final HoardingDragonEffect effect) {
super(effect); super(effect);
this.exileId = effect.exileId;
} }
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game); MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null) { UUID exileId = CardUtil.getExileZoneId(game, source);
if (controller == null || sourceObject == null) {
return false;
}
TargetCardInLibrary target = new TargetCardInLibrary(new FilterArtifactCard()); TargetCardInLibrary target = new TargetCardInLibrary(new FilterArtifactCard());
if (controller.searchLibrary(target, source, game)) { controller.searchLibrary(target, source, game);
if (!target.getTargets().isEmpty()) {
Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); Card card = controller.getLibrary().getCard(target.getFirstTarget(), game);
if (card != null) { if (card != null) {
controller.moveCardToExileWithInfo(card, exileId, sourceObject.getIdName(), source, game, Zone.LIBRARY, true); controller.moveCardToExileWithInfo(card, exileId, sourceObject.getIdName(), source, game, Zone.LIBRARY, true);
} }
}
}
controller.shuffleLibrary(source, game); controller.shuffleLibrary(source, game);
return true; return true;
} }
return false;
}
@Override @Override
public HoardingDragonEffect copy() { public HoardingDragonEffect copy() {
return new HoardingDragonEffect(this); return new HoardingDragonEffect(this);
} }
} }

View file

@ -93,7 +93,7 @@ class HostageTakerExileEffect extends OneShotEffect {
UUID exileId = CardUtil.getCardExileZoneId(game, source); UUID exileId = CardUtil.getCardExileZoneId(game, source);
controller.moveCardToExileWithInfo(card, exileId, permanent.getIdName(), source, game, Zone.BATTLEFIELD, true); controller.moveCardToExileWithInfo(card, exileId, permanent.getIdName(), source, game, Zone.BATTLEFIELD, true);
// allow to cast the card and you may spend mana as though it were mana of any color to cast it // allow to cast the card and you may spend mana as though it were mana of any color to cast it
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.Custom); CardUtil.makeCardPlayable(game, source, card, Duration.Custom, true);
return true; return true;
} }
} }

View file

@ -1,27 +1,27 @@
package mage.cards.i; package mage.cards.i;
import java.util.UUID;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.ReturnFromExileEffect; import mage.abilities.effects.common.ReturnFromExileEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.cards.Cards; import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.ExileZone;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.util.CardUtil; import mage.util.CardUtil;
import java.util.Objects;
import java.util.UUID;
/** /**
*
* @author jeffwadsworth * @author jeffwadsworth
*/ */
public final class IgnorantBliss extends CardImpl { public final class IgnorantBliss extends CardImpl {
@ -30,9 +30,7 @@ public final class IgnorantBliss extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}");
// Exile all cards from your hand face down. At the beginning of the next end step, return those cards to your hand, then draw a card. // Exile all cards from your hand face down. At the beginning of the next end step, return those cards to your hand, then draw a card.
this.getSpellAbility().addEffect(new IgnorantBlissExileEffect()); this.getSpellAbility().addEffect(new IgnorantBlissEffect());
this.getSpellAbility().addEffect(new IgnorantBlissReturnEffect());
} }
private IgnorantBliss(final IgnorantBliss card) { private IgnorantBliss(final IgnorantBliss card) {
@ -45,71 +43,40 @@ public final class IgnorantBliss extends CardImpl {
} }
} }
class IgnorantBlissExileEffect extends OneShotEffect { class IgnorantBlissEffect extends OneShotEffect {
IgnorantBlissExileEffect() { IgnorantBlissEffect() {
super(Outcome.Exile); super(Outcome.Exile);
this.staticText = "Exile all cards from your hand face down"; this.staticText = "Exile all cards from your hand face down. At the beginning of the next end step, " +
"return those cards to your hand, then draw a card";
} }
IgnorantBlissExileEffect(final IgnorantBlissExileEffect effect) { private IgnorantBlissEffect(final IgnorantBlissEffect effect) {
super(effect); super(effect);
} }
@Override @Override
public IgnorantBlissExileEffect copy() { public IgnorantBlissEffect copy() {
return new IgnorantBlissExileEffect(this); return new IgnorantBlissEffect(this);
} }
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game); MageObject sourceObject = source.getSourceObject(game);
if (controller != null if (controller == null || sourceObject == null) {
&& sourceObject != null) {
Cards hand = controller.getHand();
hand.getCards(game).stream().filter((card) -> (card != null)).map((card) -> {
UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), 0);
controller.moveCardsToExile(card, source, game, false, exileZoneId, sourceObject.getIdName());
return card;
}).forEachOrdered((card) -> {
card.setFaceDown(true, game);
});
return true;
}
return false; return false;
} }
} Cards hand = new CardsImpl(controller.getHand());
controller.moveCardsToExile(hand.getCards(game), source, game, false, CardUtil.getExileZoneId(game, source), sourceObject.getIdName());
class IgnorantBlissReturnEffect extends OneShotEffect { hand.getCards(game)
.stream()
IgnorantBlissReturnEffect() { .filter(Objects::nonNull)
super(Outcome.DrawCard); .filter(card -> game.getState().getZone(card.getId()) == Zone.EXILED)
this.staticText = "At the beginning of the next end step, return those cards to your hand, then draw a card"; .forEach(card -> card.setFaceDown(true, game));
} DelayedTriggeredAbility ability = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(Zone.HAND));
IgnorantBlissReturnEffect(final IgnorantBlissReturnEffect effect) {
super(effect);
}
@Override
public IgnorantBlissReturnEffect copy() {
return new IgnorantBlissReturnEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), 0));
if (exileZone != null) {
Effect effect = new ReturnFromExileEffect(exileZone.getId(), Zone.HAND);
AtTheBeginOfNextEndStepDelayedTriggeredAbility ability = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect);
ability.addEffect(new DrawCardSourceControllerEffect(1)); ability.addEffect(new DrawCardSourceControllerEffect(1));
game.addDelayedTriggeredAbility(ability, source); game.addDelayedTriggeredAbility(ability, source);
return true; return true;
} }
} }
return false;
}
}

View file

@ -146,7 +146,7 @@ class KingNarfisBetrayalSecondEffect extends OneShotEffect {
ExileZone zone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); ExileZone zone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source));
if (zone != null) { if (zone != null) {
for (Card card : zone.getCards(game)) { for (Card card : zone.getCards(game)) {
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.EndOfTurn); CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, true);
} }
} }

View file

@ -1,7 +1,5 @@
package mage.cards.k; package mage.cards.k;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility;
@ -12,12 +10,12 @@ import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.SuperType; import mage.constants.SuperType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.target.common.TargetOpponent; import mage.target.common.TargetOpponent;
import java.util.UUID;
/** /**
*
* @author LevelX2 * @author LevelX2
*/ */
public final class KyokiSanitysEclipse extends CardImpl { public final class KyokiSanitysEclipse extends CardImpl {
@ -32,10 +30,12 @@ public final class KyokiSanitysEclipse extends CardImpl {
this.toughness = new MageInt(4); this.toughness = new MageInt(4);
// Whenever you cast a Spirit or Arcane spell, target opponent exiles a card from their hand. // Whenever you cast a Spirit or Arcane spell, target opponent exiles a card from their hand.
Ability ability = new SpellCastControllerTriggeredAbility(new ExileFromZoneTargetEffect(Zone.HAND, null, "", new FilterCard()), StaticFilters.SPIRIT_OR_ARCANE_CARD, false); Ability ability = new SpellCastControllerTriggeredAbility(
new ExileFromZoneTargetEffect(Zone.HAND, false),
StaticFilters.SPIRIT_OR_ARCANE_CARD, false
);
ability.addTarget(new TargetOpponent()); ability.addTarget(new TargetOpponent());
this.addAbility(ability); this.addAbility(ability);
} }
private KyokiSanitysEclipse(final KyokiSanitysEclipse card) { private KyokiSanitysEclipse(final KyokiSanitysEclipse card) {

View file

@ -14,7 +14,6 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.FilterSpell; import mage.filter.FilterSpell;
import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.mageobject.ColorPredicate;
import mage.target.TargetPlayer; import mage.target.TargetPlayer;
@ -41,13 +40,14 @@ public final class MerrowBonegnawer extends CardImpl {
this.toughness = new MageInt(1); this.toughness = new MageInt(1);
// {tap}: Target player exiles a card from their graveyard. // {tap}: Target player exiles a card from their graveyard.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileFromZoneTargetEffect(Zone.GRAVEYARD, null, getIdName(), new FilterCard()), new TapSourceCost()); Ability ability = new SimpleActivatedAbility(
new ExileFromZoneTargetEffect(Zone.GRAVEYARD, false), new TapSourceCost()
);
ability.addTarget(new TargetPlayer()); ability.addTarget(new TargetPlayer());
this.addAbility(ability); this.addAbility(ability);
// Whenever you cast a black spell, you may untap Merrow Bonegnawer. // Whenever you cast a black spell, you may untap Merrow Bonegnawer.
this.addAbility(new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new UntapSourceEffect(), filter, true, false)); this.addAbility(new SpellCastControllerTriggeredAbility(new UntapSourceEffect(), filter, true, false));
} }
private MerrowBonegnawer(final MerrowBonegnawer card) { private MerrowBonegnawer(final MerrowBonegnawer card) {

View file

@ -1,12 +1,9 @@
package mage.cards.m; package mage.cards.m;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.ActivateAsSorceryActivatedAbility; import mage.abilities.common.ActivateAsSorceryActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.ExileFromZoneTargetEffect; import mage.abilities.effects.common.ExileFromZoneTargetEffect;
import mage.abilities.keyword.CantBeBlockedSourceAbility; import mage.abilities.keyword.CantBeBlockedSourceAbility;
import mage.abilities.keyword.DevoidAbility; import mage.abilities.keyword.DevoidAbility;
@ -15,11 +12,11 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.target.common.TargetOpponent; import mage.target.common.TargetOpponent;
import java.util.UUID;
/** /**
*
* @author fireshoes * @author fireshoes
*/ */
public final class Mindmelter extends CardImpl { public final class Mindmelter extends CardImpl {
@ -38,9 +35,9 @@ public final class Mindmelter extends CardImpl {
this.addAbility(new CantBeBlockedSourceAbility()); this.addAbility(new CantBeBlockedSourceAbility());
// {3}{C}: Target opponent exiles a card from their hand. Activate this ability only any time you could cast a sorcery. // {3}{C}: Target opponent exiles a card from their hand. Activate this ability only any time you could cast a sorcery.
Effect effect = new ExileFromZoneTargetEffect(Zone.HAND, null, "", new FilterCard()); Ability ability = new ActivateAsSorceryActivatedAbility(
effect.setText("Target opponent exiles a card from their hand"); Zone.BATTLEFIELD, new ExileFromZoneTargetEffect(Zone.HAND, false), new ManaCostsImpl("{3}{C}")
Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{3}{C}")); );
ability.addTarget(new TargetOpponent()); ability.addTarget(new TargetOpponent());
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -101,7 +101,7 @@ class OppositionAgentReplacementEffect extends ReplacementEffectImpl {
// You may play those cards for as long as they remain exiled, and you may spend mana as though it were mana of any color to cast them // You may play those cards for as long as they remain exiled, and you may spend mana as though it were mana of any color to cast them
for (Card card : cardsToExile) { for (Card card : cardsToExile) {
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.Custom); CardUtil.makeCardPlayable(game, source, card, Duration.Custom, true);
} }
// return false all the time // return false all the time

View file

@ -1,12 +1,9 @@
package mage.cards.p; package mage.cards.p;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.ActivateAsSorceryActivatedAbility; import mage.abilities.common.ActivateAsSorceryActivatedAbility;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.RemoveCountersSourceCost;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.ExileFromZoneTargetEffect; import mage.abilities.effects.common.ExileFromZoneTargetEffect;
import mage.abilities.effects.common.ReturnFromExileEffect; import mage.abilities.effects.common.ReturnFromExileEffect;
import mage.abilities.keyword.FadingAbility; import mage.abilities.keyword.FadingAbility;
@ -15,17 +12,15 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.counters.CounterType; import mage.counters.CounterType;
import mage.filter.FilterCard;
import mage.target.common.TargetOpponent; import mage.target.common.TargetOpponent;
import java.util.UUID;
/** /**
*
* @author spjspj * @author spjspj
*/ */
public final class ParallaxNexus extends CardImpl { public final class ParallaxNexus extends CardImpl {
private UUID exileId = UUID.randomUUID();
public ParallaxNexus(UUID ownerId, CardSetInfo setInfo) { public ParallaxNexus(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}");
@ -33,14 +28,16 @@ public final class ParallaxNexus extends CardImpl {
this.addAbility(new FadingAbility(5, this)); this.addAbility(new FadingAbility(5, this));
// Remove a fade counter from Parallax Nexus: Target opponent exiles a card from their hand. Activate this ability only any time you could cast a sorcery. // Remove a fade counter from Parallax Nexus: Target opponent exiles a card from their hand. Activate this ability only any time you could cast a sorcery.
Effect effect = new ExileFromZoneTargetEffect(Zone.HAND, exileId, "Parallax Nexus", new FilterCard()); Ability ability = new ActivateAsSorceryActivatedAbility(
effect.setText("Target opponent exiles a card from their hand"); Zone.BATTLEFIELD,
Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, effect, new RemoveCountersSourceCost(CounterType.FADE.createInstance())); new ExileFromZoneTargetEffect(Zone.HAND, true),
new RemoveCountersSourceCost(CounterType.FADE.createInstance())
);
ability.addTarget(new TargetOpponent()); ability.addTarget(new TargetOpponent());
this.addAbility(ability); this.addAbility(ability);
// When Parallax Nexus leaves the battlefield, each player returns to their hand all cards they own exiled with Parallax Nexus. // When Parallax Nexus leaves the battlefield, each player returns to their hand all cards they own exiled with Parallax Nexus.
this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileEffect(exileId, Zone.HAND), false)); this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileEffect(Zone.HAND), false));
} }
private ParallaxNexus(final ParallaxNexus card) { private ParallaxNexus(final ParallaxNexus card) {

View file

@ -80,7 +80,7 @@ class PlaneswalkersMischiefEffect extends OneShotEffect {
AsThoughEffect effect = new PlaneswalkersMischiefCastFromExileEffect(); AsThoughEffect effect = new PlaneswalkersMischiefCastFromExileEffect();
effect.setTargetPointer(new FixedTarget(revealedCard.getId())); effect.setTargetPointer(new FixedTarget(revealedCard.getId()));
game.addEffect(effect, source); game.addEffect(effect, source);
OneShotEffect effect2 = new ReturnFromExileEffect(source.getSourceId(), Zone.HAND); OneShotEffect effect2 = new ReturnFromExileEffect(Zone.HAND);
Condition condition = new PlaneswalkersMischiefCondition(source.getSourceId(), revealedCard.getId()); Condition condition = new PlaneswalkersMischiefCondition(source.getSourceId(), revealedCard.getId());
ConditionalOneShotEffect effect3 = new ConditionalOneShotEffect(effect2, condition, "if you haven't cast it, return it to its owner's hand."); ConditionalOneShotEffect effect3 = new ConditionalOneShotEffect(effect2, condition, "if you haven't cast it, return it to its owner's hand.");
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect3); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect3);

View file

@ -1,13 +1,11 @@
package mage.cards.p; package mage.cards.p;
import mage.MageObject; import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.condition.Condition; import mage.abilities.condition.Condition;
import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.effects.AsThoughEffect;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ReturnFromExileEffect; import mage.abilities.effects.common.ReturnFromExileEffect;
import mage.cards.Card; import mage.cards.Card;
@ -16,15 +14,15 @@ import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.*;
import mage.filter.common.FilterInstantOrSorceryCard; import mage.filter.common.FilterInstantOrSorceryCard;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.players.Player; import mage.players.Player;
import mage.target.TargetCard; import mage.target.TargetCard;
import mage.target.TargetPlayer; import mage.target.TargetPlayer;
import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil;
import mage.watchers.common.SpellsCastWatcher; import mage.watchers.Watcher;
import java.util.List; import java.util.*;
import java.util.UUID;
/** /**
* @author L_J (significantly based on code by jeffwadsworth and Styxo) * @author L_J (significantly based on code by jeffwadsworth and Styxo)
@ -35,9 +33,9 @@ public final class PsychicTheft extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{U}");
// Target player reveals their hand. You choose an instant or sorcery card from it and exile that card. You may cast that card for as long as it remains exiled. At the beginning of the next end step, if you haven't cast the card, return it to its owner's hand. // Target player reveals their hand. You choose an instant or sorcery card from it and exile that card. You may cast that card for as long as it remains exiled. At the beginning of the next end step, if you haven't cast the card, return it to its owner's hand.
this.getSpellAbility().addTarget(new TargetPlayer());
this.getSpellAbility().addEffect(new PsychicTheftEffect()); this.getSpellAbility().addEffect(new PsychicTheftEffect());
this.getSpellAbility().addTarget(new TargetPlayer());
this.getSpellAbility().addWatcher(new PsychicTheftWatcher());
} }
private PsychicTheft(final PsychicTheft card) { private PsychicTheft(final PsychicTheft card) {
@ -54,12 +52,14 @@ class PsychicTheftEffect extends OneShotEffect {
private static final FilterInstantOrSorceryCard filter = new FilterInstantOrSorceryCard(); private static final FilterInstantOrSorceryCard filter = new FilterInstantOrSorceryCard();
public PsychicTheftEffect() { PsychicTheftEffect() {
super(Outcome.Benefit); super(Outcome.Benefit);
this.staticText = "Target player reveals their hand. You choose an instant or sorcery card from it and exile that card. You may cast that card for as long as it remains exiled. At the beginning of the next end step, if you haven't cast the card, return it to its owner's hand."; this.staticText = "Target player reveals their hand. You choose an instant or sorcery card from it " +
"and exile that card. You may cast that card for as long as it remains exiled. " +
"At the beginning of the next end step, if you haven't cast the card, return it to its owner's hand.";
} }
public PsychicTheftEffect(final PsychicTheftEffect effect) { private PsychicTheftEffect(final PsychicTheftEffect effect) {
super(effect); super(effect);
} }
@ -72,10 +72,14 @@ class PsychicTheftEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player opponent = game.getPlayer(targetPointer.getFirst(game, source)); Player opponent = game.getPlayer(targetPointer.getFirst(game, source));
MageObject sourceObject = game.getObject(source.getSourceId()); MageObject sourceObject = game.getObject(source.getSourceId());
if (opponent != null && sourceObject != null) { if (opponent == null || sourceObject == null) {
return false;
}
opponent.revealCards(sourceObject.getName(), opponent.getHand(), game); opponent.revealCards(sourceObject.getName(), opponent.getHand(), game);
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (controller != null) { if (controller == null) {
return false;
}
int cardsHand = opponent.getHand().count(filter, game); int cardsHand = opponent.getHand().count(filter, game);
Card chosenCard = null; Card chosenCard = null;
if (cardsHand > 0) { if (cardsHand > 0) {
@ -84,89 +88,60 @@ class PsychicTheftEffect extends OneShotEffect {
chosenCard = opponent.getHand().get(target.getFirstTarget(), game); chosenCard = opponent.getHand().get(target.getFirstTarget(), game);
} }
} }
if (chosenCard != null) { if (chosenCard == null) {
opponent.moveCardToExileWithInfo(chosenCard, source.getSourceId(), sourceObject.getIdName(), source, game, Zone.HAND, true);
AsThoughEffect effect = new PsychicTheftCastFromExileEffect();
effect.setTargetPointer(new FixedTarget(chosenCard.getId()));
game.addEffect(effect, source);
OneShotEffect effect2 = new ReturnFromExileEffect(source.getSourceId(), Zone.HAND);
Condition condition = new PsychicTheftCondition(source.getSourceId(), chosenCard.getId());
ConditionalOneShotEffect effect3 = new ConditionalOneShotEffect(effect2, condition, "if you haven't cast it, return it to its owner's hand.");
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect3);
delayedAbility.addWatcher(new SpellsCastWatcher());
game.addDelayedTriggeredAbility(delayedAbility, source);
return true;
}
}
}
return false; return false;
} }
} UUID exileId = CardUtil.getExileZoneId(game, source);
controller.moveCardToExileWithInfo(chosenCard, exileId, sourceObject.getIdName(), source, game, Zone.HAND, true);
class PsychicTheftCastFromExileEffect extends AsThoughEffectImpl { CardUtil.makeCardPlayable(game, source, chosenCard, Duration.Custom, false);
PsychicTheftCastFromExileEffect() { game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); new ConditionalOneShotEffect(
staticText = "You may cast that card for as long as it remains exiled"; new ReturnFromExileEffect(Zone.HAND),
} new PsychicTheftCondition(chosenCard, game),
"if you haven't cast it, return it to its owner's hand."
PsychicTheftCastFromExileEffect(final PsychicTheftCastFromExileEffect effect) { )
super(effect); ), source);
}
@Override
public boolean apply(Game game, Ability source) {
return true; return true;
} }
@Override
public PsychicTheftCastFromExileEffect copy() {
return new PsychicTheftCastFromExileEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (targetPointer.getTargets(game, source).contains(objectId)
&& game.getState().getZone(objectId) == Zone.EXILED) {
Player player = game.getPlayer(source.getControllerId());
Card card = game.getCard(objectId);
return player != null
&& card != null;
}
return false;
}
} }
class PsychicTheftCondition implements Condition { class PsychicTheftCondition implements Condition {
private final MageObjectReference mor;
protected UUID exileId; PsychicTheftCondition(Card card, Game game) {
protected UUID cardId; this.mor = new MageObjectReference(card.getId(), card.getZoneChangeCounter(game) + 1, game);
public PsychicTheftCondition(UUID exileId, UUID cardId) {
this.exileId = exileId;
this.cardId = cardId;
} }
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
if (!game.getExile().getExileZone(exileId).contains(cardId)) { PsychicTheftWatcher watcher = game.getState().getWatcher(PsychicTheftWatcher.class);
return false; return watcher != null && !watcher.checkPlayer(source.getSourceId(), mor);
}
SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class);
if (watcher != null) {
List<Spell> spells = watcher.getSpellsCastThisTurn(source.getControllerId());
if (spells != null) {
for (Spell spell : spells) {
if (spell.getSourceId().equals(cardId)) {
return false;
} }
} }
}
} class PsychicTheftWatcher extends Watcher {
return true;
private final Map<UUID, Set<MageObjectReference>> map = new HashMap<>();
PsychicTheftWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() != GameEvent.EventType.SPELL_CAST) {
return;
}
Spell spell = game.getSpell(event.getTargetId());
if (spell == null || spell.getCard() == null || spell.getCard().getMainCard() == null) {
return;
}
map.computeIfAbsent(event.getPlayerId(), x -> new HashSet<>()).add(new MageObjectReference(spell.getCard().getMainCard(), game));
}
boolean checkPlayer(UUID playerId, MageObjectReference mor) {
return map.computeIfAbsent(playerId, x -> new HashSet<>()).contains(mor);
} }
} }

View file

@ -6,8 +6,6 @@ import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.condition.Condition; import mage.abilities.condition.Condition;
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.AsThoughManaEffect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.hint.ConditionHint; import mage.abilities.hint.ConditionHint;
import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.HasteAbility;
@ -16,20 +14,12 @@ import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.*;
import mage.game.ExileZone;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.ManaPoolItem;
import mage.players.Player; import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil; import mage.util.CardUtil;
import mage.watchers.Watcher;
import mage.watchers.common.AttackedThisTurnWatcher; import mage.watchers.common.AttackedThisTurnWatcher;
import java.util.HashSet;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
/** /**
@ -145,7 +135,7 @@ class RobberOfTheRichEffect extends OneShotEffect {
if (card.getSpellAbility() != null) { if (card.getSpellAbility() != null) {
// allow to cast the card // allow to cast the card
// and you may spend mana as though it were mana of any color to cast it // and you may spend mana as though it were mana of any color to cast it
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.Custom, RobberOfTheRichAnyTurnAttackedCondition.instance); CardUtil.makeCardPlayable(game, source, card, Duration.Custom, true, RobberOfTheRichAnyTurnAttackedCondition.instance);
} }
return true; return true;
} }

View file

@ -7,7 +7,7 @@ import mage.abilities.costs.common.SacrificeSourceCost;
import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DoIfCostPaid;
import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.ExileTargetForSourceEffect;
import mage.abilities.effects.common.ReturnFromExileEffect; import mage.abilities.effects.common.ReturnFromExileEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -27,21 +27,18 @@ public final class SafeHaven extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
// {2}, {tap}: Exile target creature you control. // {2}, {tap}: Exile target creature you control.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(this.getId(), Ability ability = new SimpleActivatedAbility(new ExileTargetForSourceEffect(), new ManaCostsImpl("{2}"));
this.getIdName()), new ManaCostsImpl("{2}"));
ability.addCost(new TapSourceCost()); ability.addCost(new TapSourceCost());
ability.addTarget(new TargetControlledCreaturePermanent()); ability.addTarget(new TargetControlledCreaturePermanent());
this.addAbility(ability); this.addAbility(ability);
// At the beginning of your upkeep, you may sacrifice Safe Haven. If you do, return each card exiled with Safe Haven to the battlefield under its owner's control. // At the beginning of your upkeep, you may sacrifice Safe Haven. If you do, return each card exiled with Safe Haven to the battlefield under its owner's control.
ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, this.addAbility(new BeginningOfUpkeepTriggeredAbility(
new DoIfCostPaid( new DoIfCostPaid(new ReturnFromExileEffect(
new ReturnFromExileEffect(this.getId(), Zone.BATTLEFIELD, "return each card exiled with {this} to the battlefield under its owner's control"), Zone.BATTLEFIELD, "return each card exiled with " +
new SacrificeSourceCost() "{this} to the battlefield under its owner's control"
), ), new SacrificeSourceCost()), TargetController.YOU, false
TargetController.YOU, ));
false);
this.addAbility(ability);
} }
private SafeHaven(final SafeHaven card) { private SafeHaven(final SafeHaven card) {

View file

@ -3,13 +3,11 @@ package mage.cards.s;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.*;
import mage.game.ExileZone;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.util.CardUtil; import mage.util.CardUtil;
@ -80,7 +78,7 @@ class StolenStrategyEffect extends OneShotEffect {
if (!card.isLand() && card.getSpellAbility() != null) { if (!card.isLand() && card.getSpellAbility() != null) {
// allow to cast the card // allow to cast the card
// and you may spend mana as though it were mana of any color to cast it // and you may spend mana as though it were mana of any color to cast it
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.EndOfTurn); CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, true);
} }
} }
} }

View file

@ -83,7 +83,7 @@ class TobiasBeckettEffect extends OneShotEffect {
if (card.getSpellAbility() != null) { if (card.getSpellAbility() != null) {
// allow to cast the card // allow to cast the card
// and you may spend mana as though it were mana of any color to cast it // and you may spend mana as though it were mana of any color to cast it
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, card, Duration.Custom); CardUtil.makeCardPlayable(game, source, card, Duration.Custom, true);
} }
} }
return true; return true;

View file

@ -186,7 +186,7 @@ class UrzaAcademyHeadmasterRandomEffect extends OneShotEffect {
break; break;
case 11: // KARN LIBERATED 1 case 11: // KARN LIBERATED 1
sb.append("Target player exiles a card from their hand."); sb.append("Target player exiles a card from their hand.");
effects.add(new ExileFromZoneTargetEffect(Zone.HAND, null, "", new FilterCard())); effects.add(new ExileFromZoneTargetEffect(Zone.HAND, true));
target = new TargetPlayer(); target = new TargetPlayer();
break; break;
case 12: // NISSA SAGE ANIMIST 1 case 12: // NISSA SAGE ANIMIST 1

View file

@ -1,7 +1,5 @@
package mage.cards.v; package mage.cards.v;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.ActivateAsSorceryActivatedAbility; import mage.abilities.common.ActivateAsSorceryActivatedAbility;
import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.SacrificeSourceCost;
@ -11,11 +9,12 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterCard; import mage.filter.StaticFilters;
import mage.target.common.TargetOpponent; import mage.target.common.TargetOpponent;
import java.util.UUID;
/** /**
*
* @author fireshoes * @author fireshoes
*/ */
public final class VesselOfMalignity extends CardImpl { public final class VesselOfMalignity extends CardImpl {
@ -24,9 +23,12 @@ public final class VesselOfMalignity extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}");
// {1}{B}, Sacrifice Vessel of Malignity: Target opponent exiles two cards from their hand. Activate this ability only any time you could cast a sorcery. // {1}{B}, Sacrifice Vessel of Malignity: Target opponent exiles two cards from their hand. Activate this ability only any time you could cast a sorcery.
Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, Ability ability = new ActivateAsSorceryActivatedAbility(
new ExileFromZoneTargetEffect(Zone.HAND, null, "", new FilterCard("cards"), 2), Zone.BATTLEFIELD,
new ManaCostsImpl("{1}{B}")); new ExileFromZoneTargetEffect(
Zone.HAND, StaticFilters.FILTER_CARD_CARDS, 2, false
), new ManaCostsImpl("{1}{B}")
);
ability.addCost(new SacrificeSourceCost()); ability.addCost(new SacrificeSourceCost());
ability.addTarget(new TargetOpponent()); ability.addTarget(new TargetOpponent());
this.addAbility(ability); this.addAbility(ability);

View file

@ -1,7 +1,5 @@
package mage.cards.w; package mage.cards.w;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.common.EndOfCombatTriggeredAbility; import mage.abilities.common.EndOfCombatTriggeredAbility;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
@ -16,8 +14,9 @@ import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.BlockedByIdPredicate; import mage.filter.predicate.permanent.BlockedByIdPredicate;
import java.util.UUID;
/** /**
*
* @author LoneFox * @author LoneFox
*/ */
public final class WallOfNets extends CardImpl { public final class WallOfNets extends CardImpl {
@ -30,13 +29,14 @@ public final class WallOfNets extends CardImpl {
// Defender // Defender
this.addAbility(DefenderAbility.getInstance()); this.addAbility(DefenderAbility.getInstance());
// At end of combat, exile all creatures blocked by Wall of Nets. // At end of combat, exile all creatures blocked by Wall of Nets.
FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures blocked by {this}"); FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures blocked by {this}");
filter.add(new BlockedByIdPredicate(this.getId())); filter.add(new BlockedByIdPredicate(this.getId()));
this.addAbility(new EndOfCombatTriggeredAbility(new ExileAllEffect(filter, this.getId(), this.getIdName()), false)); this.addAbility(new EndOfCombatTriggeredAbility(new ExileAllEffect(filter, true), false));
// When Wall of Nets leaves the battlefield, return all cards exiled with Wall of Nets to the battlefield under their owners' control. // When Wall of Nets leaves the battlefield, return all cards exiled with Wall of Nets to the battlefield under their owners' control.
this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileEffect(this.getId(), this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileEffect(Zone.BATTLEFIELD, "return all cards exiled with {this} to the battlefield under their owners' control"), false));
Zone.BATTLEFIELD, "return all cards exiled with {this} to the battlefield under their owners' control"), false));
} }
private WallOfNets(final WallOfNets card) { private WallOfNets(final WallOfNets card) {

View file

@ -1,8 +1,5 @@
package mage.cards.w; package mage.cards.w;
import java.util.UUID;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.ExileFromZoneTargetEffect; import mage.abilities.effects.common.ExileFromZoneTargetEffect;
import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.LoseLifeTargetEffect;
import mage.abilities.keyword.DevoidAbility; import mage.abilities.keyword.DevoidAbility;
@ -10,11 +7,12 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterCard; import mage.filter.StaticFilters;
import mage.target.common.TargetOpponent; import mage.target.common.TargetOpponent;
import java.util.UUID;
/** /**
*
* @author LevelX2 * @author LevelX2
*/ */
public final class WitnessTheEnd extends CardImpl { public final class WitnessTheEnd extends CardImpl {
@ -24,12 +22,13 @@ public final class WitnessTheEnd extends CardImpl {
// Devoid // Devoid
this.addAbility(new DevoidAbility(this.color)); this.addAbility(new DevoidAbility(this.color));
// Target opponent exiles two cards from their hand and loses 2 life. // Target opponent exiles two cards from their hand and loses 2 life.
getSpellAbility().addEffect(new ExileFromZoneTargetEffect(Zone.HAND, null, "", new FilterCard("cards"), 2)); this.getSpellAbility().addEffect(new ExileFromZoneTargetEffect(
Effect effect = new LoseLifeTargetEffect(2); Zone.HAND, StaticFilters.FILTER_CARD_CARDS, 2, false
effect.setText("and loses 2 life"); ));
getSpellAbility().addTarget(new TargetOpponent()); this.getSpellAbility().addEffect(new LoseLifeTargetEffect(2).setText("and loses 2 life"));
getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetOpponent());
} }
private WitnessTheEnd(final WitnessTheEnd card) { private WitnessTheEnd(final WitnessTheEnd card) {

View file

@ -45,7 +45,7 @@ public class FiendOfTheShadowsTest extends CardTestPlayerBase {
addCard(Zone.HAND, playerB, "Swamp"); addCard(Zone.HAND, playerB, "Swamp");
attack(1, playerA, "Fiend of the Shadows"); attack(1, playerA, "Fiend of the Shadows");
addTarget(playerB, "Swamp"); setChoice(playerB, "Swamp");
playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Swamp"); playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Swamp");
setStrictChooseMode(true); setStrictChooseMode(true);

View file

@ -1,44 +1,40 @@
package mage.abilities.effects.common; package mage.abilities.effects.common;
import java.util.List; import mage.MageObject;
import java.util.UUID;
import mage.constants.Outcome;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.Outcome;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterPermanent; import mage.filter.FilterPermanent;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player; import mage.players.Player;
import mage.util.CardUtil;
/** /**
*
* @author LevelX2 * @author LevelX2
*/ */
public class ExileAllEffect extends OneShotEffect { public class ExileAllEffect extends OneShotEffect {
private FilterPermanent filter; private final FilterPermanent filter;
private String exileZone = null; private final boolean forSource;
private UUID exileId = null;
public ExileAllEffect(FilterPermanent filter) { public ExileAllEffect(FilterPermanent filter) {
this(filter, null, null); this(filter, false);
} }
public ExileAllEffect(FilterPermanent filter, UUID exileId, String exileZone) { public ExileAllEffect(FilterPermanent filter, boolean forSource) {
super(Outcome.Exile); super(Outcome.Exile);
this.filter = filter; this.filter = filter;
this.exileZone = exileZone; this.forSource = forSource;
this.exileId = exileId;
setText(); setText();
} }
public ExileAllEffect(final ExileAllEffect effect) { public ExileAllEffect(final ExileAllEffect effect) {
super(effect); super(effect);
this.filter = effect.filter.copy(); this.filter = effect.filter.copy();
this.exileZone = effect.exileZone; this.forSource = effect.forSource;
this.exileId = effect.exileId;
} }
@Override @Override
@ -49,14 +45,18 @@ public class ExileAllEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (controller != null) { MageObject sourceObject = source.getSourceObject(game);
List<Permanent> permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game); if (controller == null || sourceObject == null) {
for (Permanent permanent : permanents) {
controller.moveCardToExileWithInfo(permanent, exileId, exileZone, source, game, Zone.BATTLEFIELD, true);
}
return true;
}
return false; return false;
}
Cards cards = new CardsImpl();
game.getBattlefield().getActivePermanents(
filter, source.getControllerId(), source.getSourceId(), game
).stream().forEach(cards::add);
if (forSource) {
return controller.moveCardsToExile(cards.getCards(game), source, game, true, CardUtil.getExileZoneId(game, source), sourceObject.getName());
}
return controller.moveCards(cards, Zone.EXILED, source, game);
} }

View file

@ -1,14 +1,15 @@
package mage.abilities.effects.common; package mage.abilities.effects.common;
import java.util.Locale; import mage.MageObject;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.cards.Cards;
import mage.cards.CardsImpl; import mage.cards.CardsImpl;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.target.Target; import mage.target.Target;
@ -16,29 +17,32 @@ import mage.target.common.TargetCardInHand;
import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil; import mage.util.CardUtil;
import java.util.Locale;
/** /**
*
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
public class ExileFromZoneTargetEffect extends OneShotEffect { public class ExileFromZoneTargetEffect extends OneShotEffect {
private Zone zone; private final Zone zone;
private FilterCard filter; private final FilterCard filter;
private UUID exileId; private final int amount;
private String exileName; private final boolean withSource;
private int amount;
public ExileFromZoneTargetEffect(Zone zone, UUID exileId, String exileName, FilterCard filter) { public ExileFromZoneTargetEffect(Zone zone, boolean withSource) {
this(zone, exileId, exileName, filter, 1); this(zone, StaticFilters.FILTER_CARD, withSource);
} }
public ExileFromZoneTargetEffect(Zone zone, UUID exileId, String exileName, FilterCard filter, int amount) { public ExileFromZoneTargetEffect(Zone zone, FilterCard filter, boolean withSource) {
this(zone, filter, 1, withSource);
}
public ExileFromZoneTargetEffect(Zone zone, FilterCard filter, int amount, boolean withSource) {
super(Outcome.Exile); super(Outcome.Exile);
this.zone = zone; this.zone = zone;
this.filter = filter; this.filter = filter;
this.exileId = exileId;
this.exileName = exileName;
this.amount = amount; this.amount = amount;
this.withSource = withSource;
setText(); setText();
} }
@ -46,15 +50,17 @@ public class ExileFromZoneTargetEffect extends OneShotEffect {
super(effect); super(effect);
this.zone = effect.zone; this.zone = effect.zone;
this.filter = effect.filter.copy(); this.filter = effect.filter.copy();
this.exileId = effect.exileId;
this.exileName = effect.exileName;
this.amount = effect.amount; this.amount = effect.amount;
this.withSource = effect.withSource;
} }
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(targetPointer.getFirst(game, source)); Player player = game.getPlayer(targetPointer.getFirst(game, source));
if (player != null) { MageObject mageObject = source.getSourceObject(game);
if (player == null) {
return false;
}
Target target = null; Target target = null;
switch (zone) { switch (zone) {
case HAND: case HAND:
@ -65,14 +71,15 @@ public class ExileFromZoneTargetEffect extends OneShotEffect {
break; break;
default: default:
} }
if (target != null && target.canChoose(source.getSourceId(), player.getId(), game)) { if (target == null || !target.canChoose(source.getSourceId(), player.getId(), game)) {
if (target.chooseTarget(Outcome.Exile, player.getId(), source, game)) {
player.moveCardsToExile(new CardsImpl(target.getTargets()).getCards(game), source, game, true, exileId, exileName);
}
}
return true; return true;
} }
return false; target.chooseTarget(Outcome.Exile, player.getId(), source, game);
Cards cards = new CardsImpl(target.getTargets());
if (withSource) {
return player.moveCardsToExile(cards.getCards(game), source, game, true, CardUtil.getExileZoneId(game, source), mageObject.getName());
}
return player.moveCards(cards, Zone.EXILED, source, game);
} }
@Override @Override
@ -81,6 +88,7 @@ public class ExileFromZoneTargetEffect extends OneShotEffect {
} }
private void setText() { private void setText() {
staticText = "target player exiles " + CardUtil.numberToText(amount, "a") + ' ' + filter.getMessage() + " from their " + zone.toString().toLowerCase(Locale.ENGLISH); staticText = "target player exiles " + CardUtil.numberToText(amount, "a")
+ ' ' + filter.getMessage() + " from their " + zone.toString().toLowerCase(Locale.ENGLISH);
} }
} }

View file

@ -1,5 +1,6 @@
package mage.abilities.effects.common; package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
@ -7,6 +8,8 @@ import mage.constants.Outcome;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;
/** /**
* Created by Eric on 9/24/2016. * Created by Eric on 9/24/2016.
@ -18,23 +21,22 @@ public class MistmeadowWitchEffect extends OneShotEffect {
staticText = "Exile target creature. Return that card to the battlefield under its owner's control at the beginning of the next end step"; staticText = "Exile target creature. Return that card to the battlefield under its owner's control at the beginning of the next end step";
} }
public MistmeadowWitchEffect(final MistmeadowWitchEffect effect) { private MistmeadowWitchEffect(final MistmeadowWitchEffect effect) {
super(effect); super(effect);
} }
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getFirstTarget()); Permanent permanent = game.getPermanent(source.getFirstTarget());
if (permanent != null) { MageObject sourceObject = source.getSourceObject(game);
if (permanent.moveToExile(source.getSourceId(), "Mistmeadow Witch Exile", source, game)) { if (player == null || permanent == null || sourceObject == null) {
//create delayed triggered ability
AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(source.getSourceId(), Zone.BATTLEFIELD));
game.addDelayedTriggeredAbility(delayedAbility, source);
return true;
}
}
return false; return false;
} }
player.moveCardsToExile(permanent, source, game, true, CardUtil.getExileZoneId(game, source), sourceObject.getName());
game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnFromExileEffect(Zone.BATTLEFIELD, "return the exiled card to the battlefield under its owner's control")), source);
return true;
}
@Override @Override
public MistmeadowWitchEffect copy() { public MistmeadowWitchEffect copy() {

View file

@ -1,7 +1,6 @@
package mage.abilities.effects.common; package mage.abilities.effects.common;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome; import mage.constants.Outcome;
@ -9,29 +8,27 @@ import mage.constants.Zone;
import mage.game.ExileZone; import mage.game.ExileZone;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.util.CardUtil;
/** /**
*
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
public class ReturnFromExileEffect extends OneShotEffect { public class ReturnFromExileEffect extends OneShotEffect {
private UUID exileId; private final Zone zone;
private Zone zone; private final boolean tapped;
private boolean tapped;
public ReturnFromExileEffect(UUID exileId, Zone zone) { public ReturnFromExileEffect(Zone zone) {
this(exileId, zone, false); this(zone, false);
} }
public ReturnFromExileEffect(UUID exileId, Zone zone, String text) { public ReturnFromExileEffect(Zone zone, String text) {
this(exileId, zone, false); this(zone, false);
staticText = text; staticText = text;
} }
public ReturnFromExileEffect(UUID exileId, Zone zone, boolean tapped) { public ReturnFromExileEffect(Zone zone, boolean tapped) {
super(Outcome.PutCardInPlay); super(Outcome.PutCardInPlay);
this.exileId = exileId;
this.zone = zone; this.zone = zone;
this.tapped = tapped; this.tapped = tapped;
setText(); setText();
@ -39,7 +36,6 @@ public class ReturnFromExileEffect extends OneShotEffect {
public ReturnFromExileEffect(final ReturnFromExileEffect effect) { public ReturnFromExileEffect(final ReturnFromExileEffect effect) {
super(effect); super(effect);
this.exileId = effect.exileId;
this.zone = effect.zone; this.zone = effect.zone;
this.tapped = effect.tapped; this.tapped = effect.tapped;
} }
@ -51,20 +47,16 @@ public class ReturnFromExileEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
ExileZone exile = game.getExile().getExileZone(exileId); ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source));
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (controller != null && exile != null) { if (controller == null || exile == null) {
switch (zone) {
case LIBRARY:
controller.putCardsOnTopOfLibrary(exile, game, source, false);
break;
default:
controller.moveCards(exile.getCards(game), zone, source, game, tapped, false, true, null);
}
return true;
}
return false; return false;
} }
if (zone == Zone.LIBRARY) {
return controller.putCardsOnTopOfLibrary(exile, game, source, false);
}
return controller.moveCards(exile.getCards(game), zone, source, game, tapped, false, true, null);
}
private void setText() { private void setText() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();

View file

@ -74,7 +74,7 @@ class TibaltCosmicImpostorPlayFromExileEffect extends AsThoughEffectImpl {
if (exile.contains(mainCardId) if (exile.contains(mainCardId)
&& affectedControllerId.equals(source.getControllerId()) && affectedControllerId.equals(source.getControllerId())
&& game.getState().getZone(mainCardId).equals(Zone.EXILED)) { && game.getState().getZone(mainCardId).equals(Zone.EXILED)) {
CardUtil.makeCardPlayableAndSpendManaAsAnyColor(game, source, cardInExile, Duration.Custom); CardUtil.makeCardPlayable(game, source, cardInExile, Duration.Custom, true);
return true; return true;
} }
return false; return false;

View file

@ -1102,8 +1102,8 @@ public final class CardUtil {
} }
} }
public static void makeCardPlayableAndSpendManaAsAnyColor(Game game, Ability source, Card card, Duration duration) { public static void makeCardPlayable(Game game, Ability source, Card card, Duration duration, boolean anyColor) {
makeCardPlayableAndSpendManaAsAnyColor(game, source, card, duration, null); makeCardPlayable(game, source, card, duration, anyColor, null);
} }
/** /**
@ -1115,9 +1115,10 @@ public final class CardUtil {
* @param game * @param game
* @param card * @param card
* @param duration * @param duration
* @param anyColor
* @param condition can be null * @param condition can be null
*/ */
public static void makeCardPlayableAndSpendManaAsAnyColor(Game game, Ability source, Card card, Duration duration, Condition condition) { public static void makeCardPlayable(Game game, Ability source, Card card, Duration duration, boolean anyColor, Condition condition) {
// Effect can be used for cards in zones and permanents on battlefield // Effect can be used for cards in zones and permanents on battlefield
// PermanentCard's ZCC is static, but we need updated ZCC from the card (after moved to another zone) // PermanentCard's ZCC is static, but we need updated ZCC from the card (after moved to another zone)
// So there is a workaround to get actual card's ZCC // So there is a workaround to get actual card's ZCC
@ -1125,8 +1126,10 @@ public final class CardUtil {
UUID objectId = card.getMainCard().getId(); UUID objectId = card.getMainCard().getId();
int zcc = game.getState().getZoneChangeCounter(objectId); int zcc = game.getState().getZoneChangeCounter(objectId);
game.addEffect(new CanPlayCardControllerEffect(game, objectId, zcc, duration, condition), source); game.addEffect(new CanPlayCardControllerEffect(game, objectId, zcc, duration, condition), source);
if (anyColor) {
game.addEffect(new YouMaySpendManaAsAnyColorToCastTargetEffect(duration, condition).setTargetPointer(new FixedTarget(objectId, zcc)), source); game.addEffect(new YouMaySpendManaAsAnyColorToCastTargetEffect(duration, condition).setTargetPointer(new FixedTarget(objectId, zcc)), source);
} }
}
/** /**
* Pay life in effects * Pay life in effects