From 4a09654743f9ca52e866e263f1ed7231d3dac299 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Sun, 28 Feb 2021 16:10:23 -0500 Subject: [PATCH] refactored card.moveToExile usages A through D heavily reworked Dark Impostor and Dimensional Breach --- Mage.Sets/src/mage/cards/a/ArgentSphinx.java | 40 ++-- Mage.Sets/src/mage/cards/a/AshesToAshes.java | 50 +---- .../src/mage/cards/b/BaneAlleyBroker.java | 6 +- .../src/mage/cards/c/CryptIncursion.java | 43 ++--- Mage.Sets/src/mage/cards/d/DarkImpostor.java | 72 +++---- .../mage/cards/d/DecreeOfAnnihilation.java | 54 +++--- .../src/mage/cards/d/DimensionalBreach.java | 179 +++++++++++------- .../mage/cards/d/DimensionalInfiltrator.java | 51 +++-- 8 files changed, 248 insertions(+), 247 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/ArgentSphinx.java b/Mage.Sets/src/mage/cards/a/ArgentSphinx.java index c99f92483d..1386549a10 100644 --- a/Mage.Sets/src/mage/cards/a/ArgentSphinx.java +++ b/Mage.Sets/src/mage/cards/a/ArgentSphinx.java @@ -1,22 +1,22 @@ package mage.cards.a; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.condition.common.MetalcraftCondition; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderYourControlTargetEffect; import mage.abilities.hint.common.MetalcraftHint; import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -37,7 +37,10 @@ public final class ArgentSphinx extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Metalcraft — {U}: Exile Argent Sphinx. Return it to the battlefield under your control at the beginning of the next end step. Activate this ability only if you control three or more artifacts. - Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, new ArgentSphinxEffect(), new ManaCostsImpl("{U}"), MetalcraftCondition.instance); + Ability ability = new ActivateIfConditionActivatedAbility( + Zone.BATTLEFIELD, new ArgentSphinxEffect(), + new ManaCostsImpl("{U}"), MetalcraftCondition.instance + ); ability.setAbilityWord(AbilityWord.METALCRAFT); ability.addHint(MetalcraftHint.instance); this.addAbility(ability); @@ -56,37 +59,38 @@ public final class ArgentSphinx extends CardImpl { class ArgentSphinxEffect extends OneShotEffect { - private static final String effectText = "Exile {this}. Return it to the battlefield under your control at the beginning of the next end step"; + private static final String effectText = "Exile {this}. Return it to the battlefield " + + "under your control at the beginning of the next end step"; ArgentSphinxEffect() { super(Outcome.Benefit); staticText = effectText; } - ArgentSphinxEffect(ArgentSphinxEffect effect) { + private ArgentSphinxEffect(ArgentSphinxEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (permanent != null && sourceObject != null) { - if (permanent.moveToExile(source.getSourceId(), sourceObject.getIdName(), source, game)) { - //create delayed triggered ability - Effect effect = new ReturnToBattlefieldUnderYourControlTargetEffect(); - effect.setText("Return it to the battlefield under your control at the beginning of the next end step"); - effect.setTargetPointer(new FixedTarget(source.getSourceId(), game)); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); - return true; - } + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (player == null || permanent == null) { + return false; } - return false; + Card card = permanent.getMainCard(); + player.moveCards(permanent, Zone.EXILED, source, game); + //create delayed triggered ability + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new ReturnToBattlefieldUnderYourControlTargetEffect().setText( + "Return it to the battlefield under your control at the beginning of the next end step" + ).setTargetPointer(new FixedTarget(card, game)) + ), source); + return true; } @Override public ArgentSphinxEffect copy() { return new ArgentSphinxEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/a/AshesToAshes.java b/Mage.Sets/src/mage/cards/a/AshesToAshes.java index 3330c5b16d..8687a4c847 100644 --- a/Mage.Sets/src/mage/cards/a/AshesToAshes.java +++ b/Mage.Sets/src/mage/cards/a/AshesToAshes.java @@ -1,38 +1,33 @@ - package mage.cards.a; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageControllerEffect; +import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class AshesToAshes extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonartifact creature"); + + private static final FilterPermanent filter = new FilterCreaturePermanent("nonartifact creatures"); static { filter.add(Predicates.not(CardType.ARTIFACT.getPredicate())); } public AshesToAshes(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); // Exile two target nonartifact creatures. Ashes to Ashes deals 5 damage to you. - this.getSpellAbility().addEffect(new AshesToAshesEffect()); + this.getSpellAbility().addEffect(new ExileTargetEffect()); this.getSpellAbility().addTarget(new TargetPermanent(2, filter)); this.getSpellAbility().addEffect(new DamageControllerEffect(5)); } @@ -46,32 +41,3 @@ public final class AshesToAshes extends CardImpl { return new AshesToAshes(this); } } - -class AshesToAshesEffect extends OneShotEffect { - - public AshesToAshesEffect() { - super(Outcome.Benefit); - staticText = "Exile two target nonartifact creatures"; - } - - public AshesToAshesEffect(final AshesToAshesEffect effect) { - super(effect); - } - - @Override - public AshesToAshesEffect copy() { - return new AshesToAshesEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - UUID exileId = source.getSourceId(); - for (UUID permanentId : targetPointer.getTargets(game, source)) { - Permanent target = game.getPermanent(permanentId); - if (target != null) { - target.moveToExile(exileId, "Ashes to Ashes", source, game); - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java b/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java index d7b330c09b..32ee4804fb 100644 --- a/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java +++ b/Mage.Sets/src/mage/cards/b/BaneAlleyBroker.java @@ -115,7 +115,9 @@ class BaneAlleyBrokerDrawExileEffect extends OneShotEffect { if (card == null || sourceObject == null) { return false; } - if (!card.moveToExile(CardUtil.getExileZoneId(game, source), sourceObject.getName(), source, game)) { + if (!controller.moveCardsToExile( + card, source, game, false, CardUtil.getExileZoneId(game, source), sourceObject.getIdName() + )) { return false; } card.setFaceDown(true, game); @@ -157,7 +159,7 @@ class BaneAlleyBrokerReturnToHandEffect extends OneShotEffect { } TargetCardInExile target = new TargetCardInExile(StaticFilters.FILTER_CARD, exile.getId()); target.setNotTarget(true); - player.chooseTarget(outcome, exile,target, source, game); + player.chooseTarget(outcome, exile, target, source, game); Card card = game.getCard(target.getFirstTarget()); if (card == null) { return false; diff --git a/Mage.Sets/src/mage/cards/c/CryptIncursion.java b/Mage.Sets/src/mage/cards/c/CryptIncursion.java index bdc0f68ff4..68e573fa56 100644 --- a/Mage.Sets/src/mage/cards/c/CryptIncursion.java +++ b/Mage.Sets/src/mage/cards/c/CryptIncursion.java @@ -1,20 +1,22 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class CryptIncursion extends CardImpl { @@ -23,9 +25,8 @@ public final class CryptIncursion extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); // Exile all creature cards from target player's graveyard. You gain 3 life for each card exiled this way. - this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addEffect(new CryptIncursionEffect()); - + this.getSpellAbility().addTarget(new TargetPlayer()); } private CryptIncursion(final CryptIncursion card) { @@ -36,17 +37,17 @@ public final class CryptIncursion extends CardImpl { public CryptIncursion copy() { return new CryptIncursion(this); } - } class CryptIncursionEffect extends OneShotEffect { - public CryptIncursionEffect() { + CryptIncursionEffect() { super(Outcome.Detriment); - staticText = "Exile all creature cards from target player's graveyard. You gain 3 life for each card exiled this way"; + staticText = "Exile all creature cards from target player's graveyard. " + + "You gain 3 life for each card exiled this way"; } - public CryptIncursionEffect(final CryptIncursionEffect effect) { + private CryptIncursionEffect(final CryptIncursionEffect effect) { super(effect); } @@ -54,24 +55,20 @@ class CryptIncursionEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(source.getFirstTarget()); - if (player != null && targetPlayer != null) { - int exiledCards = 0; - for (Card card : targetPlayer.getGraveyard().getCards(game)) { - if (StaticFilters.FILTER_CARD_CREATURE.match(card, game)) { - if (card.moveToExile(null, "", source, game)) { - exiledCards++; - } - } - } - player.gainLife(exiledCards * 3, game, source); - return true; - } - return false; + Cards cards = new CardsImpl(targetPlayer.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game)); + player.moveCards(cards, Zone.EXILED, source, game); + int count = cards + .stream() + .map(game.getState()::getZone) + .map(Zone.EXILED::equals) + .mapToInt(x -> x ? 1 : 0) + .sum(); + player.gainLife(3 * count, game, source); + return true; } @Override public CryptIncursionEffect copy() { return new CryptIncursionEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/d/DarkImpostor.java b/Mage.Sets/src/mage/cards/d/DarkImpostor.java index 6f37ec593c..b3b21f423d 100644 --- a/Mage.Sets/src/mage/cards/d/DarkImpostor.java +++ b/Mage.Sets/src/mage/cards/d/DarkImpostor.java @@ -1,11 +1,8 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.ActivatedAbility; -import mage.abilities.Mode; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -17,14 +14,18 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author noxx - * */ public final class DarkImpostor extends CardImpl { @@ -37,12 +38,16 @@ public final class DarkImpostor extends CardImpl { this.toughness = new MageInt(2); // {4}{B}{B}: Exile target creature and put a +1/+1 counter on Dark Impostor. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DarkImpostorExileTargetEffect(), new ManaCostsImpl("{4}{B}{B}")); + Ability ability = new SimpleActivatedAbility( + new DarkImpostorExileTargetEffect(), new ManaCostsImpl("{4}{B}{B}") + ); + ability.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("and put a +1/+1 counter on {this}")); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); // Dark Impostor has all activated abilities of all creature cards exiled with it. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DarkImpostorContinuousEffect())); + this.addAbility(new SimpleStaticAbility(new DarkImpostorContinuousEffect())); } private DarkImpostor(final DarkImpostor card) { @@ -57,11 +62,12 @@ public final class DarkImpostor extends CardImpl { class DarkImpostorExileTargetEffect extends OneShotEffect { - public DarkImpostorExileTargetEffect() { + DarkImpostorExileTargetEffect() { super(Outcome.Exile); + staticText = "exile target creature"; } - public DarkImpostorExileTargetEffect(final DarkImpostorExileTargetEffect effect) { + private DarkImpostorExileTargetEffect(final DarkImpostorExileTargetEffect effect) { super(effect); } @@ -72,46 +78,43 @@ class DarkImpostorExileTargetEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); - MageObject sourceObject = source.getSourceObject(game); - if (permanent != null) { - permanent.moveToExile(null, null, source, game); - if (sourceObject instanceof Permanent) { - ((Permanent) sourceObject).imprint(permanent.getId(), game); - } + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (player == null || permanent == null) { + return false; } - return new AddCountersSourceEffect(CounterType.P1P1.createInstance()).apply(game, source); - } - - @Override - public String getText(Mode mode) { - return "exile target creature and put a +1/+1 counter on {this}"; + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + if (sourcePermanent == null) { + return player.moveCards(permanent, Zone.EXILED, source, game); + } + return player.moveCardsToExile( + permanent, source, game, true, CardUtil.getExileZoneId(game, source), sourcePermanent.getIdName() + ); } } class DarkImpostorContinuousEffect extends ContinuousEffectImpl { - public DarkImpostorContinuousEffect() { + DarkImpostorContinuousEffect() { super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); staticText = "{this} has all activated abilities of all creature cards exiled with it"; } - public DarkImpostorContinuousEffect(final DarkImpostorContinuousEffect effect) { + private DarkImpostorContinuousEffect(final DarkImpostorContinuousEffect effect) { super(effect); } @Override public boolean apply(Game game, Ability source) { - Permanent perm = game.getPermanent(source.getSourceId()); - if (perm != null) { - for (UUID imprintedId : perm.getImprinted()) { - Card card = game.getCard(imprintedId); - if (card != null) { - for (Ability ability : card.getAbilities(game)) { - if (ability instanceof ActivatedAbility) { - perm.addAbility(ability.copy(), source.getSourceId(), game); - } - } + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (permanent == null || exileZone == null || exileZone.isEmpty()) { + return false; + } + for (Card card : exileZone.getCards(StaticFilters.FILTER_CARD_CREATURE, game)) { + for (Ability ability : card.getAbilities(game)) { + if (ability instanceof ActivatedAbility) { + permanent.addAbility(ability, source.getSourceId(), game); } } } @@ -122,5 +125,4 @@ class DarkImpostorContinuousEffect extends ContinuousEffectImpl { public DarkImpostorContinuousEffect copy() { return new DarkImpostorContinuousEffect(this); } - } diff --git a/Mage.Sets/src/mage/cards/d/DecreeOfAnnihilation.java b/Mage.Sets/src/mage/cards/d/DecreeOfAnnihilation.java index a3428894e6..a52cc656a0 100644 --- a/Mage.Sets/src/mage/cards/d/DecreeOfAnnihilation.java +++ b/Mage.Sets/src/mage/cards/d/DecreeOfAnnihilation.java @@ -1,18 +1,18 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.CycleTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.keyword.CyclingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; @@ -20,8 +20,9 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author fireshoes */ public final class DecreeOfAnnihilation extends CardImpl { @@ -36,8 +37,7 @@ public final class DecreeOfAnnihilation extends CardImpl { this.addAbility(new CyclingAbility(new ManaCostsImpl("{5}{R}{R}"))); // When you cycle Decree of Annihilation, destroy all lands. - Ability ability = new CycleTriggeredAbility(new DestroyAllEffect(StaticFilters.FILTER_LANDS), false); - this.addAbility(ability); + this.addAbility(new CycleTriggeredAbility(new DestroyAllEffect(StaticFilters.FILTER_LANDS), false)); } private DecreeOfAnnihilation(final DecreeOfAnnihilation card) { @@ -52,21 +52,23 @@ public final class DecreeOfAnnihilation extends CardImpl { class DecreeOfAnnihilationEffect extends OneShotEffect { - private static final FilterPermanent filter = new FilterPermanent(""); + private static final FilterPermanent filter = new FilterPermanent(); static { filter.add(Predicates.or( CardType.ARTIFACT.getPredicate(), CardType.CREATURE.getPredicate(), - CardType.LAND.getPredicate())); + CardType.LAND.getPredicate() + )); } - public DecreeOfAnnihilationEffect() { + DecreeOfAnnihilationEffect() { super(Outcome.Detriment); - staticText = "Exile all artifacts, creatures, and lands from the battlefield, all cards from all graveyards, and all cards from all hands"; + staticText = "Exile all artifacts, creatures, and lands from the battlefield, " + + "all cards from all graveyards, and all cards from all hands"; } - public DecreeOfAnnihilationEffect(final DecreeOfAnnihilationEffect effect) { + private DecreeOfAnnihilationEffect(final DecreeOfAnnihilationEffect effect) { super(effect); } @@ -77,26 +79,24 @@ class DecreeOfAnnihilationEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - permanent.moveToExile(null, "", source, game); + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(); + for (Permanent permanent : game.getBattlefield().getActivePermanents( + filter, source.getControllerId(), source.getSourceId(), game + )) { + cards.add(permanent); } for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { Player player = game.getPlayer(playerId); - if (player != null) { - for (UUID cid : player.getHand().copy()) { - Card c = game.getCard(cid); - if (c != null) { - c.moveToExile(null, null, source, game); - } - } - for (UUID cid : player.getGraveyard().copy()) { - Card c = game.getCard(cid); - if (c != null) { - c.moveToExile(null, null, source, game); - } - } + if (player == null) { + continue; } + cards.addAll(player.getHand()); + cards.addAll(player.getGraveyard()); } - return true; + return controller.moveCards(cards, Zone.EXILED, source, game); } } diff --git a/Mage.Sets/src/mage/cards/d/DimensionalBreach.java b/Mage.Sets/src/mage/cards/d/DimensionalBreach.java index 5a2a399955..f8e8000a1e 100644 --- a/Mage.Sets/src/mage/cards/d/DimensionalBreach.java +++ b/Mage.Sets/src/mage/cards/d/DimensionalBreach.java @@ -1,31 +1,32 @@ - package mage.cards.d; -import java.util.UUID; -import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.predicate.card.OwnerIdPredicate; -import mage.game.ExileZone; +import mage.filter.StaticFilters; import mage.game.Game; +import mage.game.events.GameEvent; import mage.players.Player; +import mage.target.TargetCard; import mage.target.common.TargetCardInExile; -import mage.util.CardUtil; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; /** - * - * @author jeffwadsworth + * @author TheElk801 */ public final class DimensionalBreach extends CardImpl { @@ -34,8 +35,6 @@ public final class DimensionalBreach extends CardImpl { // Exile all permanents. For as long as any of those cards remain exiled, at the beginning of each player's upkeep, that player returns one of the exiled cards they own to the battlefield. this.getSpellAbility().addEffect(new DimensionalBreachExileEffect()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new BeginningOfUpkeepTriggeredAbility(Zone.ALL, new DimensionalBreachReturnFromExileEffect(), TargetController.ANY, false, true, null), new CardsStillInExileCondition(), null)); - } private DimensionalBreach(final DimensionalBreach card) { @@ -50,12 +49,14 @@ public final class DimensionalBreach extends CardImpl { class DimensionalBreachExileEffect extends OneShotEffect { - public DimensionalBreachExileEffect() { + DimensionalBreachExileEffect() { super(Outcome.Exile); - staticText = "Exile all permanents."; + staticText = "Exile all permanents. For as long as any of those cards remain exiled, " + + "at the beginning of each player's upkeep, that player returns " + + "one of the exiled cards they own to the battlefield."; } - public DimensionalBreachExileEffect(final DimensionalBreachExileEffect effect) { + private DimensionalBreachExileEffect(final DimensionalBreachExileEffect effect) { super(effect); } @@ -66,75 +67,107 @@ class DimensionalBreachExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - MageObject sourceObject = source.getSourceObject(game); - Player controller = game.getPlayer(source.getControllerId()); - if (sourceObject != null - && controller != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), 0); - if (exileId != null) { - game.getBattlefield().getAllActivePermanents().forEach((permanent) -> { - permanent.moveToExile(exileId, sourceObject.getName(), source, game); - }); - return true; - } + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; } - return false; + Cards cards = new CardsImpl(); + game.getBattlefield().getActivePermanents(source.getControllerId(), game).stream().forEach(cards::add); + player.moveCards(cards, Zone.EXILED, source, game); + Set morSet = cards + .getCards(game) + .stream() + .filter(c -> game.getState().getZone(c.getId()) == Zone.EXILED) + .map(c -> new MageObjectReference(c, game)) + .collect(Collectors.toSet()); + game.addDelayedTriggeredAbility(new DimensionalBreachDelayedTriggeredAbility(morSet), source); + return true; } } -class DimensionalBreachReturnFromExileEffect extends OneShotEffect { +class DimensionalBreachDelayedTriggeredAbility extends DelayedTriggeredAbility { - public DimensionalBreachReturnFromExileEffect() { - super(Outcome.PutCardInPlay); - staticText = "For as long as any of those cards remain exiled, at the beginning of each player's upkeep, that player returns one of the exiled cards they own to the battlefield."; + private final Set morSet = new HashSet<>(); + + DimensionalBreachDelayedTriggeredAbility(Set morSet) { + super(new DimensionalBreachReturnEffect(morSet), Duration.Custom, false, false); + this.morSet.addAll(morSet); } - public DimensionalBreachReturnFromExileEffect(final DimensionalBreachReturnFromExileEffect effect) { - super(effect); + private DimensionalBreachDelayedTriggeredAbility(final DimensionalBreachDelayedTriggeredAbility ability) { + super(ability); + this.morSet.addAll(ability.morSet); } @Override - public DimensionalBreachReturnFromExileEffect copy() { - return new DimensionalBreachReturnFromExileEffect(this); + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return morSet.stream().map(mor -> mor.getCard(game)).anyMatch(Objects::nonNull); + } + + @Override + public DimensionalBreachDelayedTriggeredAbility copy() { + return new DimensionalBreachDelayedTriggeredAbility(this); + } + + @Override + public boolean isInactive(Game game) { + return morSet.stream().map(mor -> mor.getCard(game)).noneMatch(Objects::nonNull); + } + + @Override + public String getRule() { + return "For as long as any of those cards remain exiled, at the beginning of each player's upkeep, " + + "that player returns one of the exiled cards they own to the battlefield."; + } +} + +class DimensionalBreachReturnEffect extends OneShotEffect { + + private final Set morSet = new HashSet<>(); + + DimensionalBreachReturnEffect(Set morSet) { + super(Outcome.PutCardInPlay); + this.morSet.addAll(morSet); + } + + private DimensionalBreachReturnEffect(final DimensionalBreachReturnEffect effect) { + super(effect); + this.morSet.addAll(effect.morSet); + } + + @Override + public DimensionalBreachReturnEffect copy() { + return new DimensionalBreachReturnEffect(this); } @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null) { - FilterCard filter = new FilterCard("card you own from the Dimensional Breach exile"); - filter.add(new OwnerIdPredicate(player.getId())); - - TargetCardInExile target = new TargetCardInExile(filter, CardUtil.getExileZoneId(game, source.getSourceId(), 0)); - target.setNotTarget(true); - - if (target.canChoose(source.getSourceId(), player.getId(), game)) { - if (player.chooseTarget(Outcome.PutCardInPlay, target, source, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null - && player.moveCards(card, Zone.BATTLEFIELD, source, game)) { - return true; - } - } - } - } - return false; - } -} - -class CardsStillInExileCondition implements Condition { - - @Override - public final boolean apply(Game game, Ability source) { Player player = game.getPlayer(game.getActivePlayerId()); - if (player != null) { - FilterCard filter = new FilterCard(); - filter.add(new OwnerIdPredicate(player.getId())); - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), 0); - ExileZone exileZone = game.getExile().getExileZone(exileId); - return (exileZone != null - && !exileZone.getCards(filter, game).isEmpty()); + if (player == null) { + return false; } - return false; + Cards cards = new CardsImpl( + morSet.stream() + .map(mor -> mor.getCard(game)) + .filter(Objects::nonNull) + .filter(c -> c.isOwnedBy(game.getActivePlayerId())) + .collect(Collectors.toSet()) + ); + if (cards.isEmpty()) { + return false; + } + if (cards.size() > 1) { + TargetCard target = new TargetCardInExile(StaticFilters.FILTER_CARD); + target.setNotTarget(true); + player.choose(outcome, cards, target, game); + cards.clear(); + cards.add(target.getFirstTarget()); + } + return player.moveCards(cards, Zone.BATTLEFIELD, source, game); } } diff --git a/Mage.Sets/src/mage/cards/d/DimensionalInfiltrator.java b/Mage.Sets/src/mage/cards/d/DimensionalInfiltrator.java index 8a4568c5f8..d20cc6f96e 100644 --- a/Mage.Sets/src/mage/cards/d/DimensionalInfiltrator.java +++ b/Mage.Sets/src/mage/cards/d/DimensionalInfiltrator.java @@ -1,14 +1,10 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.keyword.DevoidAbility; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.FlyingAbility; @@ -16,21 +12,23 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class DimensionalInfiltrator extends CardImpl { public DimensionalInfiltrator(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.ELDRAZI); this.power = new MageInt(2); this.toughness = new MageInt(1); @@ -45,7 +43,7 @@ public final class DimensionalInfiltrator extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // {1}{C}: Exile the top card of target opponent's library. If it's a land card, you may return Dimensional Infiltrator to its owner's hand. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DimensionalInfiltratorEffect(), new ManaCostsImpl("{1}{C}")); + Ability ability = new SimpleActivatedAbility(new DimensionalInfiltratorEffect(), new ManaCostsImpl("{1}{C}")); ability.addTarget(new TargetOpponent()); this.addAbility(ability); } @@ -62,12 +60,13 @@ public final class DimensionalInfiltrator extends CardImpl { class DimensionalInfiltratorEffect extends OneShotEffect { - public DimensionalInfiltratorEffect() { - super(Outcome.PutCreatureInPlay); - this.staticText = "Exile the top card of target opponent's library. If it's a land card, you may return Dimensional Infiltrator to its owner's hand"; + DimensionalInfiltratorEffect() { + super(Outcome.ReturnToHand); + this.staticText = "exile the top card of target opponent's library. " + + "If it's a land card, you may return {this} to its owner's hand"; } - public DimensionalInfiltratorEffect(final DimensionalInfiltratorEffect effect) { + private DimensionalInfiltratorEffect(final DimensionalInfiltratorEffect effect) { super(effect); } @@ -78,24 +77,22 @@ class DimensionalInfiltratorEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(source.getControllerId()); Player opponent = game.getPlayer(source.getFirstTarget()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (opponent == null || controller == null || sourceObject == null) { + if (player == null || opponent == null) { return false; } - - if (opponent.getLibrary().hasCards()) { - Card card = opponent.getLibrary().getFromTop(game); - if (card != null) { - card.moveToExile(null, "Dimensional Infiltrator", source, game); - if (card.isLand()) { - if (controller.chooseUse(Outcome.Neutral, "Return " + sourceObject.getIdName() + " to its owner's hand?", source, game)) { - new ReturnToHandSourceEffect(true).apply(game, source); - } - } - } + Card card = opponent.getLibrary().getFromTop(game); + if (card == null) { + return false; } - return true; + player.moveCards(card, Zone.EXILED, source, game); + if (!card.isLand()) { + return true; + } + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return permanent != null + && player.chooseUse(outcome, "Return " + permanent.getName() + " to its owner's hand?", source, game) + && player.moveCards(permanent, Zone.HAND, source, game); } }