diff --git a/Mage.Sets/src/mage/cards/a/AminatousAugury.java b/Mage.Sets/src/mage/cards/a/AminatousAugury.java index 9e230a447ca..78a047d2d3c 100644 --- a/Mage.Sets/src/mage/cards/a/AminatousAugury.java +++ b/Mage.Sets/src/mage/cards/a/AminatousAugury.java @@ -175,7 +175,8 @@ class AminatousAuguryCastFromExileEffect extends AsThoughEffectImpl { usedCardTypes.add(CardType.fromString(choice.getChoice())); } usedCardTypes.addAll(unusedCardTypes); - player.setCastSourceIdWithAlternateMana(sourceId, null, null); + player.setCastSourceIdWithAlternateMana(sourceId, null, card.getSpellAbility().getCosts()); + // TODO- This does not correctly work when you cancel the cast (has to be done by watcher I guess) game.getState().setValue(source.getSourceId().toString() + "cardTypes", usedCardTypes); } return true; diff --git a/Mage.Sets/src/mage/cards/b/BolassCitadel.java b/Mage.Sets/src/mage/cards/b/BolassCitadel.java index 2827e6a9358..00c6d096532 100644 --- a/Mage.Sets/src/mage/cards/b/BolassCitadel.java +++ b/Mage.Sets/src/mage/cards/b/BolassCitadel.java @@ -20,9 +20,12 @@ import mage.filter.predicate.Predicates; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetControlledPermanent; -import mage.util.CardUtil; import java.util.UUID; +import mage.cards.SplitCard; +import mage.cards.SplitCardHalf; +import mage.util.CardUtil; + /** * @author jeffwadsworth @@ -94,23 +97,43 @@ class BolassCitadelPlayTheTopCardEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { - Card cardToCheck = game.getCard(objectId); - objectId = CardUtil.getMainCardId(game, objectId); // for split cards + Player player = game.getPlayer(playerId); + if (player != null) { + Card topCard = player.getLibrary().getFromTop(game); + UUID objectIdToCast = CardUtil.getMainCardId(game, objectId); // for adventure cards + if (topCard == null || !topCard.getId().equals(objectIdToCast)) { + return false; + } + if (!topCard.isLand()) { + if (topCard instanceof SplitCard) { + SplitCardHalf leftCard = ((SplitCard) topCard).getLeftHalfCard(); + PayLifeCost lifeCost = new PayLifeCost(leftCard.getSpellAbility().getManaCosts().convertedManaCost()); + Costs leftCosts = new CostsImpl(); + leftCosts.add(lifeCost); + leftCosts.addAll(leftCard.getSpellAbility().getCosts()); + player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCosts); - if (!isAbilityAppliedForAlternateCast(cardToCheck, affectedAbility, playerId, source)) { - return false; - } - - Player controller = game.getPlayer(cardToCheck.getOwnerId()); - Card topCard = controller == null ? null : controller.getLibrary().getFromTop(game); - if (topCard != null && objectId.equals(topCard.getId())) { - // add the life cost first - PayLifeCost cost = new PayLifeCost(affectedAbility.getManaCosts().convertedManaCost()); - Costs costs = new CostsImpl(); - costs.add(cost); - costs.addAll(affectedAbility.getCosts()); - controller.setCastSourceIdWithAlternateMana(affectedAbility.getSourceId(), null, costs); + SplitCardHalf rightCard = ((SplitCard) topCard).getRightHalfCard(); + lifeCost = new PayLifeCost(rightCard.getSpellAbility().getManaCosts().convertedManaCost()); + Costs rightCosts = new CostsImpl(); + rightCosts.add(lifeCost); + rightCosts.addAll(rightCard.getSpellAbility().getCosts()); + player.setCastSourceIdWithAlternateMana(rightCard.getId(), null, rightCosts); + } else { + if (affectedAbility == null) { + affectedAbility = topCard.getSpellAbility(); + } else { + objectIdToCast = affectedAbility.getSourceId(); + } + PayLifeCost cost = new PayLifeCost(affectedAbility.getManaCosts().convertedManaCost()); + Costs costs = new CostsImpl(); + costs.add(cost); + costs.addAll(affectedAbility.getCosts()); + player.setCastSourceIdWithAlternateMana(objectIdToCast, null, costs); + } + } return true; + } return false; } diff --git a/Mage.Sets/src/mage/cards/f/FallenShinobi.java b/Mage.Sets/src/mage/cards/f/FallenShinobi.java index bc6cf45403d..6405ab06868 100644 --- a/Mage.Sets/src/mage/cards/f/FallenShinobi.java +++ b/Mage.Sets/src/mage/cards/f/FallenShinobi.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.NinjutsuAbility; @@ -12,9 +11,10 @@ import mage.cards.*; import mage.constants.*; import mage.game.Game; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.target.targetpointer.FixedTargets; /** * @author TheElk801 @@ -52,8 +52,8 @@ class FallenShinobiEffect extends OneShotEffect { FallenShinobiEffect() { super(Outcome.Benefit); - staticText = "that player exiles the top two cards of their library. " + - "Until end of turn, you may play those cards without paying their mana cost."; + staticText = "that player exiles the top two cards of their library. " + + "Until end of turn, you may play those cards without paying their mana cost."; } private FallenShinobiEffect(final FallenShinobiEffect effect) { @@ -71,53 +71,7 @@ class FallenShinobiEffect extends OneShotEffect { if (player == null) { return false; } - Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 2)); - player.moveCards(cards, Zone.EXILED, source, game); - for (Card card : cards.getCards(game)) { - ContinuousEffect effect = new UrzaLordHighArtificerFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card, game)); - game.addEffect(effect, source); - } - return true; - } -} - -class UrzaLordHighArtificerFromExileEffect extends AsThoughEffectImpl { - - UrzaLordHighArtificerFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - staticText = "you may play that card without paying its mana cost"; - } - - private UrzaLordHighArtificerFromExileEffect(final UrzaLordHighArtificerFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public UrzaLordHighArtificerFromExileEffect copy() { - return new UrzaLordHighArtificerFromExileEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (!affectedControllerId.equals(source.getControllerId()) - || !getTargetPointer().getTargets(game, source).contains(objectId)) { - return false; - } - Card card = game.getCard(objectId); - if (card == null || card.isLand() || card.getSpellAbility().getCosts() == null) { - return true; - } - Player player = game.getPlayer(affectedControllerId); - if (player == null) { - return false; - } - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - return true; + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, player.getLibrary().getTopCards(game, 2), + TargetController.YOU, Duration.EndOfTurn, true); } } diff --git a/Mage.Sets/src/mage/cards/g/GolosTirelessPilgrim.java b/Mage.Sets/src/mage/cards/g/GolosTirelessPilgrim.java index 1f4d3077e20..83d87fc44b3 100644 --- a/Mage.Sets/src/mage/cards/g/GolosTirelessPilgrim.java +++ b/Mage.Sets/src/mage/cards/g/GolosTirelessPilgrim.java @@ -7,9 +7,9 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -19,7 +19,7 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; -import mage.target.targetpointer.FixedTarget; +import mage.target.targetpointer.FixedTargets; /** * @author TheElk801 @@ -75,15 +75,8 @@ class GolosTirelessPilgrimEffect extends OneShotEffect { return false; } Set cards = player.getLibrary().getTopCards(game, 3); - player.moveCards(cards, Zone.EXILED, source, game); - cards.stream() - .filter(card -> game.getState().getZone(card.getId()) == Zone.EXILED) - .forEach(card -> { - ContinuousEffect effect = new GolosTirelessPilgrimCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card, game)); - game.addEffect(effect, source); - }); - return true; + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, cards, + TargetController.YOU, Duration.EndOfTurn, true); } @Override @@ -91,41 +84,3 @@ class GolosTirelessPilgrimEffect extends OneShotEffect { return new GolosTirelessPilgrimEffect(this); } } - -class GolosTirelessPilgrimCastFromExileEffect extends AsThoughEffectImpl { - - GolosTirelessPilgrimCastFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - } - - private GolosTirelessPilgrimCastFromExileEffect(final GolosTirelessPilgrimCastFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public GolosTirelessPilgrimCastFromExileEffect copy() { - return new GolosTirelessPilgrimCastFromExileEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (!objectId.equals(getTargetPointer().getFirst(game, source)) - || !affectedControllerId.equals(source.getControllerId())) { - return false; - } - Card card = game.getCard(objectId); - if (card == null || card.isLand() || card.getSpellAbility().getCosts() == null) { - return true; - } - Player player = game.getPlayer(affectedControllerId); - if (player != null) { - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/i/IgniteTheFuture.java b/Mage.Sets/src/mage/cards/i/IgniteTheFuture.java index 14cd2b90b27..926b3ca7068 100644 --- a/Mage.Sets/src/mage/cards/i/IgniteTheFuture.java +++ b/Mage.Sets/src/mage/cards/i/IgniteTheFuture.java @@ -2,7 +2,6 @@ package mage.cards.i; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlashbackAbility; @@ -13,10 +12,11 @@ import mage.constants.*; import mage.game.Game; import mage.game.stack.Spell; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; import java.util.Set; import java.util.UUID; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.target.targetpointer.FixedTargets; /** * @author TheElk801 @@ -69,81 +69,8 @@ class IgniteTheFutureEffect extends OneShotEffect { if (controller == null || spell == null) { return false; } - boolean forFree = spell.getFromZone() == Zone.GRAVEYARD; Set cards = controller.getLibrary().getTopCards(game, 3); - controller.moveCards(cards, Zone.EXILED, source, game); - - cards.stream().forEach(card -> { - ContinuousEffect effect = new IgniteTheFutureMayPlayEffect(forFree); - effect.setTargetPointer(new FixedTarget(card.getId(), game)); - game.addEffect(effect, source); - }); - - return true; - } -} - -class IgniteTheFutureMayPlayEffect extends AsThoughEffectImpl { - - private int castOnTurn = 0; - private final boolean forFree; - - IgniteTheFutureMayPlayEffect(boolean forFree) { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); - this.forFree = forFree; - if (forFree) { - this.staticText = "Until the end of your next turn, you may play that card without playing its mana cost."; - } else { - this.staticText = "Until the end of your next turn, you may play that card."; - } - } - - private IgniteTheFutureMayPlayEffect(final IgniteTheFutureMayPlayEffect effect) { - super(effect); - castOnTurn = effect.castOnTurn; - this.forFree = effect.forFree; - } - - @Override - public IgniteTheFutureMayPlayEffect copy() { - return new IgniteTheFutureMayPlayEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - castOnTurn = game.getTurnNum(); - } - - @Override - public boolean isInactive(Ability source, Game game) { - return castOnTurn != game.getTurnNum() - && game.getPhase().getStep().getType() == PhaseStep.END_TURN - && game.isActivePlayer(source.getControllerId()); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (!source.isControlledBy(affectedControllerId) - || !getTargetPointer().getTargets(game, source).contains(objectId)) { - return false; - } - if (!forFree) { - return true; - } - Card card = game.getCard(objectId); - if (card == null || card.isLand() || card.getSpellAbility().getCosts() == null) { - return true; - } - Player player = game.getPlayer(affectedControllerId); - if (player != null) { - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - } - return true; + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, cards, + TargetController.YOU, Duration.UntilEndOfYourNextTurn, Zone.GRAVEYARD.equals(spell.getFromZone())); } } diff --git a/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java b/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java index 5bf1a68777d..247fcad5090 100644 --- a/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java +++ b/Mage.Sets/src/mage/cards/i/IntetTheDreamer.java @@ -14,12 +14,16 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; import java.util.UUID; +import mage.abilities.condition.common.SourceOnBattlefieldCondition; +import mage.abilities.decorator.ConditionalAsThoughEffect; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.target.targetpointer.FixedTarget; /** * @author fireshoes @@ -39,15 +43,13 @@ public final class IntetTheDreamer extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Intet, the Dreamer deals combat damage to a player, you may pay {2}{U}. If you do, exile the top card of your library face down. + // You may play that card without paying its mana cost for as long as Intet remains on the battlefield. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( new DoIfCostPaid(new IntetTheDreamerExileEffect(), new ManaCostsImpl("{2}{U}")), false, true)); // You may look at that card for as long as it remains exiled. this.addAbility(new SimpleStaticAbility(Zone.ALL, new IntetTheDreamerLookEffect())); - // You may play that card without paying its mana cost for as long as Intet remains on the battlefield. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new IntetTheDreamerCastEffect())); - } public IntetTheDreamer(final IntetTheDreamer card) { @@ -64,7 +66,7 @@ class IntetTheDreamerExileEffect extends OneShotEffect { public IntetTheDreamerExileEffect() { super(Outcome.Discard); - staticText = "exile the top card of your library face down"; + staticText = "exile the top card of your library face down. You may play that card without paying its mana cost for as long as Intet remains on the battlefield"; } public IntetTheDreamerExileEffect(final IntetTheDreamerExileEffect effect) { @@ -77,20 +79,18 @@ class IntetTheDreamerExileEffect extends OneShotEffect { if (controller != null) { Card card = controller.getLibrary().getFromTop(game); MageObject sourceObject = source.getSourceObject(game); - if (card != null - && sourceObject != null) { + if (card != null && sourceObject != null) { card.setFaceDown(true, game); - controller.moveCardsToExile( - card, - source, - game, - false, - CardUtil.getExileZoneId(game, - source.getSourceId(), - sourceObject.getZoneChangeCounter(game)), // sourceObject must be used due to source not working correctly + controller.moveCardsToExile(card, source, game, false, + CardUtil.getExileZoneId(game, source.getSourceId(), sourceObject.getZoneChangeCounter(game)), // sourceObject must be used due to source not working correctly sourceObject.getIdName()); card.setFaceDown(true, game); - game.getState().setValue("Exiled_IntetTheDreamer" + card.getId(), Boolean.TRUE); + ContinuousEffect effect = new ConditionalAsThoughEffect( + new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.Custom, true), + SourceOnBattlefieldCondition.instance); + effect.setTargetPointer(new FixedTarget(card, game)); + game.getState().addEffect(effect, source); + game.getState().setValue("Exiled_IntetTheDreamer" + card.getId(), Boolean.TRUE); // TODO This value will never be removed return true; } } @@ -103,63 +103,6 @@ class IntetTheDreamerExileEffect extends OneShotEffect { } } -class IntetTheDreamerCastEffect extends AsThoughEffectImpl { - - public IntetTheDreamerCastEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "You may play the card from exile without paying its mana cost for as long as {this} remains on the battlefield"; - } - - public IntetTheDreamerCastEffect(final IntetTheDreamerCastEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public IntetTheDreamerCastEffect copy() { - return new IntetTheDreamerCastEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (affectedControllerId.equals(source.getControllerId())) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null - && sourceObject != null) { - Card card = game.getCard(objectId); - if (card != null - && card.isFaceDown(game)) { - ExileZone zone = game.getExile().getExileZone( - CardUtil.getExileZoneId(game, - source.getSourceId(), - sourceObject.getZoneChangeCounter(game))); // sourceObject must be used due to source not working correctly - if (zone != null - && zone.contains(card.getId())) { - if (card.isLand()) { - if (game.canPlaySorcery(controller.getId()) - && game.getPlayer(controller.getId()).canPlayLand()) { - return controller.chooseUse(outcome, "Play " + card.getIdName() + '?', source, game); - } - } else { - controller.setCastSourceIdWithAlternateMana(objectId, - null, - card.getSpellAbility().getCosts()); - return true; - } - } - } - } - } - return false; - - } -} - class IntetTheDreamerLookEffect extends AsThoughEffectImpl { public IntetTheDreamerLookEffect() { diff --git a/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java b/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java index f6779f259af..da5b92c4fc3 100644 --- a/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java +++ b/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java @@ -5,8 +5,6 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.MorphAbility; import mage.cards.Card; @@ -14,14 +12,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.stack.Spell; import mage.game.stack.StackObject; -import mage.players.Player; import mage.target.TargetSpell; -import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; import java.util.UUID; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; /** * @author emerald000 @@ -73,68 +68,14 @@ class KheruSpellsnatcherEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - UUID objectId = targetPointer.getFirst(game, source); - UUID sourceId = source.getSourceId(); - - StackObject stackObject = game.getStack().getStackObject(objectId); - if (stackObject != null + MageObject sourceObject = source.getSourceObject(game); + StackObject stackObject = game.getStack().getStackObject(targetPointer.getFirst(game, source)); + if (stackObject != null && sourceObject != null && game.getStack().counter(targetPointer.getFirst(game, source), source.getSourceId(), game, Zone.EXILED, false, ZoneDetail.NONE)) { if (!stackObject.isCopy()) { MageObject card = game.getObject(stackObject.getSourceId()); - if (card instanceof Card) { - UUID exileId = CardUtil.getCardExileZoneId(game, sourceId); - ((Card) card).moveToExile(exileId, "Kheru Spellsnatcher - cast without mana cost", sourceId, game); - ContinuousEffect effect = new KheruSpellsnatcherCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); - game.addEffect(effect, source); - } - } - return true; - } - return false; - } -} - -class KheruSpellsnatcherCastFromExileEffect extends AsThoughEffectImpl { - - KheruSpellsnatcherCastFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); - staticText = "You may cast that card without paying its mana cost as long as it remains exiled"; - } - - KheruSpellsnatcherCastFromExileEffect(final KheruSpellsnatcherCastFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public KheruSpellsnatcherCastFromExileEffect copy() { - return new KheruSpellsnatcherCastFromExileEffect(this); - } - - @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (affectedControllerId.equals(source.getControllerId())) { - if (getTargetPointer().getFirst(game, source) == null) { - this.discard(); - return false; - } - if (sourceId.equals(getTargetPointer().getFirst(game, source))) { - Card card = game.getCard(sourceId); - if (card != null) { - if (game.getState().getZone(sourceId) == Zone.EXILED) { - Player player = game.getPlayer(affectedControllerId); - if(player != null) { - player.setCastSourceIdWithAlternateMana(sourceId, null, card.getSpellAbility().getCosts()); - return true; - } - } else { - this.discard(); - } + if (card instanceof Card) { + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, (Card)card, TargetController.YOU, Duration.Custom, true); } } } diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheMind.java b/Mage.Sets/src/mage/cards/m/MagusOfTheMind.java index 26a6b6e3e2e..c3aa993fd2a 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheMind.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheMind.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.Set; @@ -10,21 +9,19 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.TargetController; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; import mage.watchers.common.CastSpellLastTurnWatcher; /** @@ -79,7 +76,7 @@ class MagusOfTheMindEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); - if(watcher == null){ + if (watcher == null) { return false; } int stormCount = watcher.getAmountOfSpellsAllPlayersCastOnCurrentTurn() + 1; @@ -87,60 +84,10 @@ class MagusOfTheMindEffect extends OneShotEffect { controller.shuffleLibrary(source, game); if (controller.getLibrary().hasCards()) { Set cards = controller.getLibrary().getTopCards(game, stormCount); - if (cards != null) { - for (Card card : cards) { - if (card != null) { - controller.moveCardToExileWithInfo(card, source.getSourceId(), sourceObject.getIdName(), source.getSourceId(), game, Zone.LIBRARY, true); - ContinuousEffect effect = new MagusOfTheMindCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); - game.addEffect(effect, source); - } - } - } + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, cards, TargetController.YOU, Duration.EndOfTurn, true); } return true; } return false; } } - -class MagusOfTheMindCastFromExileEffect extends AsThoughEffectImpl { - - MagusOfTheMindCastFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - staticText = "you may play that card without paying its mana cost"; - } - - MagusOfTheMindCastFromExileEffect(final MagusOfTheMindCastFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public MagusOfTheMindCastFromExileEffect copy() { - return new MagusOfTheMindCastFromExileEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (objectId != null && objectId.equals(getTargetPointer().getFirst(game, source))) { - if (affectedControllerId.equals(source.getControllerId())) { - Card card = game.getCard(objectId); - if (card != null && game.getState().getZone(objectId) == Zone.EXILED) { - if (!card.isLand() && card.getSpellAbility().getCosts() != null) { - Player player = game.getPlayer(affectedControllerId); - if (player != null) { - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - } - } - return true; - } - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/m/MindsDesire.java b/Mage.Sets/src/mage/cards/m/MindsDesire.java index eea1c4c7fae..d5cf67afd69 100644 --- a/Mage.Sets/src/mage/cards/m/MindsDesire.java +++ b/Mage.Sets/src/mage/cards/m/MindsDesire.java @@ -1,23 +1,19 @@ package mage.cards.m; import mage.abilities.Ability; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.StormAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; -import mage.target.targetpointer.FixedTargets; -import mage.util.CardUtil; import java.util.UUID; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.constants.TargetController; /** * @author emerald000 @@ -65,66 +61,9 @@ class MindsDesireEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { controller.shuffleLibrary(source, game); - Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - UUID exileId = CardUtil.getExileZoneId(controller.getId().toString() + "-" + game.getState().getTurnNum() + "-" + MindsDesire.class.toString(), game); - String exileName = "Mind's Desire free cast on " + game.getState().getTurnNum() + " turn for " + controller.getName(); - game.getExile().createZone(exileId, exileName).setCleanupOnEndTurn(true); - if (controller.moveCardsToExile(card, source, game, true, exileId, exileName)) { - ContinuousEffect effect = new MindsDesireCastFromExileEffect(); - effect.setTargetPointer(new FixedTargets(game.getExile().getExileZone(exileId).getCards(game), game)); - game.addEffect(effect, source); - } - } - return true; + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, controller.getLibrary().getFromTop(game), + TargetController.YOU, Duration.EndOfTurn, true); } return false; } -} - -class MindsDesireCastFromExileEffect extends AsThoughEffectImpl { - - MindsDesireCastFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - staticText = "you may play that card without paying its mana cost"; - } - - MindsDesireCastFromExileEffect(final MindsDesireCastFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public MindsDesireCastFromExileEffect copy() { - return new MindsDesireCastFromExileEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - return applies(objectId, null, source, game, affectedControllerId); - } - - @Override - public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { - Card cardToCheck = game.getCard(objectId); - objectId = CardUtil.getMainCardId(game, objectId); // for split cards - - if (!isAbilityAppliedForAlternateCast(cardToCheck, affectedAbility, playerId, source)) { - return false; - } - - Player controller = game.getPlayer(cardToCheck.getOwnerId()); - if (controller != null - && getTargetPointer().getTargets(game, source).contains(objectId)) { - controller.setCastSourceIdWithAlternateMana(affectedAbility.getSourceId(), null, affectedAbility.getCosts()); - return true; - } - - - return false; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/n/NarsetEnlightenedMaster.java b/Mage.Sets/src/mage/cards/n/NarsetEnlightenedMaster.java index 8301068ad25..195dea2f7b6 100644 --- a/Mage.Sets/src/mage/cards/n/NarsetEnlightenedMaster.java +++ b/Mage.Sets/src/mage/cards/n/NarsetEnlightenedMaster.java @@ -116,13 +116,9 @@ class NarsetEnlightenedMasterCastFromExileEffect extends AsThoughEffectImpl { public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { if (objectId.equals(getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId())) { - Card card = game.getCard(objectId); - if (card != null) { - Player player = game.getPlayer(affectedControllerId); - if (player != null) { - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - return true; - } + Player player = game.getPlayer(affectedControllerId); + if (player != null) { + return allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game); } } return false; diff --git a/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java b/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java index 476f219ad1c..9fdcb6b28f1 100644 --- a/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java +++ b/Mage.Sets/src/mage/cards/n/NicolBolasGodPharaoh.java @@ -21,11 +21,12 @@ import mage.target.Target; import mage.target.common.TargetAnyTarget; import mage.target.common.TargetCardInHand; import mage.target.common.TargetOpponent; -import mage.target.targetpointer.FixedTarget; import java.util.HashMap; import java.util.Map; import java.util.UUID; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.target.targetpointer.FixedTarget; /** * @author Will @@ -168,9 +169,8 @@ class NicolBolasGodPharaohPlusTwoEffect extends OneShotEffect { if (card.isLand()) { continue; } - ContinuousEffect effect = new NicolBolasGodPharaohFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), - game.getState().getZoneChangeCounter(card.getId()))); + ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.EndOfTurn, true); + effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); break; } while (library.hasCards() @@ -178,48 +178,3 @@ class NicolBolasGodPharaohPlusTwoEffect extends OneShotEffect { return true; } } - -class NicolBolasGodPharaohFromExileEffect extends AsThoughEffectImpl { - - NicolBolasGodPharaohFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - staticText = "You may cast card from exile"; - } - - private NicolBolasGodPharaohFromExileEffect(final NicolBolasGodPharaohFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public NicolBolasGodPharaohFromExileEffect copy() { - return new NicolBolasGodPharaohFromExileEffect(this); - } - - @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (sourceId == null - || !sourceId.equals(getTargetPointer().getFirst(game, source)) - || !affectedControllerId.equals(source.getControllerId())) { - return false; - } - Card card = game.getCard(sourceId); - if (card == null - || game.getState().getZone(sourceId) != Zone.EXILED) { - return false; - } - Player controller = game.getPlayer(affectedControllerId); - if (controller == null) { - return false; - } - controller.setCastSourceIdWithAlternateMana( - sourceId, - null, - card.getSpellAbility().getCosts()); - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/o/OraclesVault.java b/Mage.Sets/src/mage/cards/o/OraclesVault.java index 57ed7bfd9d4..e69ae9ac6e1 100644 --- a/Mage.Sets/src/mage/cards/o/OraclesVault.java +++ b/Mage.Sets/src/mage/cards/o/OraclesVault.java @@ -1,4 +1,3 @@ - package mage.cards.o; import java.util.UUID; @@ -11,8 +10,10 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.Card; import mage.cards.CardImpl; @@ -21,11 +22,13 @@ import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.players.Library; import mage.players.Player; +import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; /** @@ -84,13 +87,8 @@ class OraclesVaultEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - controller.moveCardsToExile(card, source, game, true, source.getSourceId(), - CardUtil.createObjectRealtedWindowTitle(source, game, "")); - game.addEffect(new OraclesVaultPlayEffect(new MageObjectReference(card, game)), source); - } - return true; + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, controller.getLibrary().getFromTop(game), + TargetController.YOU, Duration.EndOfTurn, false); } return false; } @@ -114,95 +112,9 @@ class OraclesVaultFreeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null && controller.getLibrary().hasCards()) { - Library library = controller.getLibrary(); - Card card = library.getFromTop(game); - if (card != null) { - controller.moveCardsToExile(card, source, game, true, source.getSourceId(), - CardUtil.createObjectRealtedWindowTitle(source, game, " ")); - game.addEffect(new OraclesVaultPlayForFreeEffect(new MageObjectReference(card, game)), source); - } - return true; - } - return false; - } -} - -class OraclesVaultPlayEffect extends AsThoughEffectImpl { - - private final MageObjectReference objectReference; - - public OraclesVaultPlayEffect(MageObjectReference objectReference) { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - this.objectReference = objectReference; - staticText = "Until end of turn, you may play that card"; - } - - public OraclesVaultPlayEffect(final OraclesVaultPlayEffect effect) { - super(effect); - this.objectReference = effect.objectReference; - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public OraclesVaultPlayEffect copy() { - return new OraclesVaultPlayEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (objectReference.refersTo(objectId, game) && affectedControllerId.equals(source.getControllerId())) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - return true; - } else { - discard(); - } - } - return false; - } -} - -class OraclesVaultPlayForFreeEffect extends AsThoughEffectImpl { - - private final MageObjectReference objectReference; - - public OraclesVaultPlayForFreeEffect(MageObjectReference objectReference) { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - this.objectReference = objectReference; - staticText = "Until end of turn, you may play that card without paying its mana cost"; - } - - public OraclesVaultPlayForFreeEffect(final OraclesVaultPlayForFreeEffect effect) { - super(effect); - this.objectReference = effect.objectReference; - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public OraclesVaultPlayForFreeEffect copy() { - return new OraclesVaultPlayForFreeEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (objectReference.refersTo(objectId, game) && affectedControllerId.equals(source.getControllerId())) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.setCastSourceIdWithAlternateMana(objectId, null, null); - return true; - } else { - discard(); - } + if (controller != null) { + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, controller.getLibrary().getFromTop(game), + TargetController.YOU, Duration.EndOfTurn, true); } return false; } diff --git a/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java b/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java index 1662e161975..c546bfda19e 100644 --- a/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java +++ b/Mage.Sets/src/mage/cards/p/PlaneswalkersMischief.java @@ -22,6 +22,7 @@ import mage.watchers.common.SpellsCastWatcher; import java.util.List; import java.util.UUID; +import mage.MageObject; /** * @author jeffwadsworth @@ -67,16 +68,15 @@ class PlaneswalkersMischiefEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player opponent = game.getPlayer(source.getFirstTarget()); - if (opponent != null - && opponent.getHand().size() > 0) { + if (opponent != null && opponent.getHand().size() > 0) { Card revealedCard = opponent.getHand().getRandom(game); if (revealedCard == null) { return false; } Cards cards = new CardsImpl(revealedCard); - opponent.revealCards("Planeswalker's Mischief Reveal", cards, game); + opponent.revealCards(source, cards, game); if (revealedCard.isInstant() - || revealedCard.isSorcery()) { + || revealedCard.isSorcery()) { opponent.moveCardToExileWithInfo(revealedCard, source.getSourceId(), "Planeswalker's Mischief", source.getSourceId(), game, Zone.HAND, true); AsThoughEffect effect = new PlaneswalkersMischiefCastFromExileEffect(); effect.setTargetPointer(new FixedTarget(revealedCard.getId())); @@ -123,8 +123,7 @@ class PlaneswalkersMischiefCastFromExileEffect extends AsThoughEffectImpl { Card card = game.getCard(objectId); if (player != null && card != null) { - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - return true; + return allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game); } } return false; diff --git a/Mage.Sets/src/mage/cards/r/ReleaseToTheWind.java b/Mage.Sets/src/mage/cards/r/ReleaseToTheWind.java index c8a9ebdf8d9..6ee617e186d 100644 --- a/Mage.Sets/src/mage/cards/r/ReleaseToTheWind.java +++ b/Mage.Sets/src/mage/cards/r/ReleaseToTheWind.java @@ -4,8 +4,8 @@ package mage.cards.r; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -13,7 +13,7 @@ import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.TargetController; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -67,58 +67,7 @@ class ReleaseToTheWindEffect extends OneShotEffect { if (controller != null) { Permanent targetPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (targetPermanent != null) { - if (controller.moveCards(targetPermanent, Zone.EXILED, source, game)) { - Card card = game.getCard(targetPermanent.getId()); - if (card != null) { - ContinuousEffect effect = new ReleaseToTheWindEffectCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); - game.addEffect(effect, source); - } - } - } - return true; - } - return false; - } -} - -class ReleaseToTheWindEffectCastFromExileEffect extends AsThoughEffectImpl { - - public ReleaseToTheWindEffectCastFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); - staticText = "For as long as that card remains exiled, its owner may cast it without paying its mana cost"; - } - - public ReleaseToTheWindEffectCastFromExileEffect(final ReleaseToTheWindEffectCastFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public ReleaseToTheWindEffectCastFromExileEffect copy() { - return new ReleaseToTheWindEffectCastFromExileEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - UUID ownerId = game.getOwnerId(objectId); - if (objectId.equals(getTargetPointer().getFirst(game, source))) { - if (affectedControllerId.equals(ownerId)) { - Card card = game.getCard(objectId); - Player player = game.getPlayer(ownerId); - if (player != null && card != null) { - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - return true; - } - } - } else { - if (((FixedTarget) getTargetPointer()).getTarget().equals(objectId)) { - // object has moved zone so effect can be discarted - this.discard(); + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, targetPermanent, TargetController.OWNER, Duration.Custom, true); } } return false; diff --git a/Mage.Sets/src/mage/cards/s/SinsOfThePast.java b/Mage.Sets/src/mage/cards/s/SinsOfThePast.java index 21aa8edb355..05441948f10 100644 --- a/Mage.Sets/src/mage/cards/s/SinsOfThePast.java +++ b/Mage.Sets/src/mage/cards/s/SinsOfThePast.java @@ -97,11 +97,12 @@ class SinsOfThePastCastFromGraveyardEffect extends AsThoughEffectImpl { } @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (sourceId.equals(this.getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId())) { + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (objectId.equals(this.getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId())) { Player player = game.getPlayer(affectedControllerId); - if(player != null) { - player.setCastSourceIdWithAlternateMana(sourceId, null, null); + Card cardToCast = game.getCard(objectId); + if(player != null && cardToCast != null) { + player.setCastSourceIdWithAlternateMana(objectId, null, cardToCast.getSpellAbility().getCosts()); return true; } } diff --git a/Mage.Sets/src/mage/cards/s/Spelljack.java b/Mage.Sets/src/mage/cards/s/Spelljack.java index f966eb20915..d15fcb80458 100644 --- a/Mage.Sets/src/mage/cards/s/Spelljack.java +++ b/Mage.Sets/src/mage/cards/s/Spelljack.java @@ -116,7 +116,7 @@ class SpelljackCastFromExileEffect extends AsThoughEffectImpl { if (game.getState().getZone(sourceId) == Zone.EXILED) { Player player = game.getPlayer(affectedControllerId); if(player != null) { - player.setCastSourceIdWithAlternateMana(sourceId, null, null); + player.setCastSourceIdWithAlternateMana(sourceId, null, card.getSpellAbility().getCosts()); } return true; } else { diff --git a/Mage.Sets/src/mage/cards/s/StolenGoods.java b/Mage.Sets/src/mage/cards/s/StolenGoods.java index c6588a53285..0cecd5c0876 100644 --- a/Mage.Sets/src/mage/cards/s/StolenGoods.java +++ b/Mage.Sets/src/mage/cards/s/StolenGoods.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -74,7 +73,7 @@ class StolenGoodsEffect extends OneShotEffect { if (card != null) { ContinuousEffect effect = new StolenGoodsCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + effect.setTargetPointer(new FixedTarget(card, game)); game.addEffect(effect, source); } return true; @@ -105,17 +104,11 @@ class StolenGoodsCastFromExileEffect extends AsThoughEffectImpl { } @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (sourceId != null && sourceId.equals(getTargetPointer().getFirst(game, source)) + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (objectId != null && objectId.equals(getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId())) { - Card card = game.getCard(sourceId); - if (card != null && game.getState().getZone(sourceId) == Zone.EXILED) { - Player player = game.getPlayer(affectedControllerId); - if (player != null) { - player.setCastSourceIdWithAlternateMana(sourceId, null, card.getSpellAbility().getCosts()); - return true; - } - } + allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game); + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/t/TemporalAperture.java b/Mage.Sets/src/mage/cards/t/TemporalAperture.java index 1bd65e5d275..9f6e7ae786a 100644 --- a/Mage.Sets/src/mage/cards/t/TemporalAperture.java +++ b/Mage.Sets/src/mage/cards/t/TemporalAperture.java @@ -1,4 +1,3 @@ - package mage.cards.t; import java.util.UUID; @@ -121,11 +120,8 @@ class TemporalApertureTopCardCastEffect extends AsThoughEffectImpl { if (controller != null && game.getState().getZone(objectId) == Zone.LIBRARY) { if (controller.getLibrary().getFromTop(game).equals(card)) { - if (objectCard == card - && objectCard.getSpellAbility() != null - && objectCard.getSpellAbility().spellCanBeActivatedRegularlyNow(controller.getId(), game) - || objectCard.isLand()) { - controller.setCastSourceIdWithAlternateMana(objectId, null, null); + if (objectCard == card && objectCard.getSpellAbility() != null) { // only if castable + allowCardToPlayWithoutMana(objectId, source, affectedControllerId, game); return true; } } diff --git a/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java b/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java index be880a8f951..616e8dbb6eb 100644 --- a/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java +++ b/Mage.Sets/src/mage/cards/u/UrzaLordHighArtificer.java @@ -7,7 +7,7 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapTargetCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.AsThoughEffectImpl; + import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -28,6 +28,8 @@ import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; import java.util.UUID; +import mage.MageObject; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; /** * @author TheElk801 @@ -92,68 +94,13 @@ class UrzaLordHighArtificerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { + MageObject sourceObject = source.getSourceObject(game); + if (controller == null || sourceObject == null) { return false; } controller.shuffleLibrary(source, game); Card card = controller.getLibrary().getFromTop(game); - if (card == null) { - return false; - } - UUID exileId = CardUtil.getExileZoneId( - controller.getId().toString() - + "-" + game.getState().getTurnNum() - + "-" + UrzaLordHighArtificer.class.toString(), game - ); - String exileName = "Urza, Lord High Artificer free cast on " - + game.getState().getTurnNum() + " turn for " + controller.getName(); - game.getExile().createZone(exileId, exileName).setCleanupOnEndTurn(true); - if (!controller.moveCardsToExile(card, source, game, true, exileId, exileName)) { - return false; - } - ContinuousEffect effect = new UrzaLordHighArtificerFromExileEffect(); - effect.setTargetPointer(new FixedTargets(game.getExile().getExileZone(exileId).getCards(game), game)); - game.addEffect(effect, source); - return true; - } -} - -class UrzaLordHighArtificerFromExileEffect extends AsThoughEffectImpl { - - UrzaLordHighArtificerFromExileEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - staticText = "you may play that card without paying its mana cost"; - } - - private UrzaLordHighArtificerFromExileEffect(final UrzaLordHighArtificerFromExileEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public UrzaLordHighArtificerFromExileEffect copy() { - return new UrzaLordHighArtificerFromExileEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - if (!affectedControllerId.equals(source.getControllerId()) - || !getTargetPointer().getTargets(game, source).contains(objectId)) { - return false; - } - Card card = game.getCard(objectId); - if (card == null || card.isLand() || card.getSpellAbility().getCosts() == null) { - return true; - } - Player player = game.getPlayer(affectedControllerId); - if (player == null) { - return false; - } - player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); - return true; + return PlayFromNotOwnHandZoneTargetEffect.exileAndPlayFromExile(game, source, card, + TargetController.YOU, Duration.EndOfTurn, true); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java index 763b5895000..1c0480fc1b8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java @@ -267,4 +267,166 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase { assertActivePlayer(playerA); } + /** + * #6580 + * Fallen Shinobi - In the second log, when Tormenting Voice is cast first, + * the discard was required. In the first log, when it was cast after + * Angelic Purge the discard was not required. + */ + + @Test + public void castFromExileButWithAdditionalCostTest() { + // Ninjutsu {2}{U}{B} + // Whenever Fallen Shinobi deals combat damage to a player, that player exiles the top two cards + // of their library. Until end of turn, you may play those cards without paying their mana cost. + addCard(Zone.BATTLEFIELD, playerB, "Fallen Shinobi", 1); // Creature 5/4 + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + addCard(Zone.HAND, playerB, "Pillarfield Ox"); + + addCard(Zone.LIBRARY, playerB, "Pillarfield Ox"); // Card to draw on turn 2 + // As an additional cost to cast Tormenting Voice, discard a card. + // Draw two cards. + addCard(Zone.LIBRARY, playerA, "Tormenting Voice"); // Sorcery {1}{R} + // As an additional cost to cast this spell, sacrifice a creature. + // Flying, Trample + addCard(Zone.LIBRARY, playerA, "Demon of Catastrophes"); // Creature {2}{B}{B} 6/6 + + skipInitShuffling(); + + attack(2, playerB, "Fallen Shinobi"); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Tormenting Voice"); + setChoice(playerB, "Pillarfield Ox"); // Discord for Tormenting Voice + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Demon of Catastrophes"); + setChoice(playerB, "Silvercoat Lion"); // Sacrifice for Demon + + setStopAt(2, PhaseStep.END_TURN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 15); + assertPermanentCount(playerB, "Fallen Shinobi", 1); + + assertGraveyardCount(playerA, "Tormenting Voice", 1); + assertGraveyardCount(playerB, "Pillarfield Ox", 1); // Discarded for Tormenting Voice + + + assertPermanentCount(playerB, "Demon of Catastrophes", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); // sacrificed for Demon + + assertHandCount(playerB, "Pillarfield Ox", 1); + assertHandCount(playerB, 3); // 2 from Tormenting Voice + 1 from Turn 2 + assertExileCount(playerA, 0); // Both exiled cards are cast + } + + + @Test + public void castFromExileButWithAdditionalCost2Test() { + // Ninjutsu {2}{U}{B} + // Whenever Fallen Shinobi deals combat damage to a player, that player exiles the top two cards + // of their library. Until end of turn, you may play those cards without paying their mana cost. + addCard(Zone.BATTLEFIELD, playerB, "Fallen Shinobi", 1); // Creature 5/4 + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + addCard(Zone.HAND, playerB, "Pillarfield Ox"); + + addCard(Zone.BATTLEFIELD, playerA, "Amulet of Kroog"); // Just to exile for Angelic Purge + + // As an additional cost to cast Tormenting Voice, discard a card. + // Draw two cards. + addCard(Zone.LIBRARY, playerA, "Tormenting Voice"); // Sorcery {1}{R} + + // As an additional cost to cast Angelic Purge, sacrifice a permanent. + // Exile target artifact, creature, or enchantment. + addCard(Zone.LIBRARY, playerA, "Angelic Purge"); // Sorcery {2}{W} + + skipInitShuffling(); + + attack(2, playerB, "Fallen Shinobi"); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Angelic Purge"); + setChoice(playerB, "Silvercoat Lion"); // Sacrifice for Purge + addTarget(playerB, "Amulet of Kroog"); // Exile with Purge + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Tormenting Voice"); + setChoice(playerB, "Pillarfield Ox"); // Discord for Tormenting Voice + + setStopAt(2, PhaseStep.END_TURN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 15); + assertPermanentCount(playerB, "Fallen Shinobi", 1); + + assertGraveyardCount(playerA, "Angelic Purge", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); // sacrificed for Purge + + assertGraveyardCount(playerA, "Tormenting Voice", 1); + assertGraveyardCount(playerB, "Pillarfield Ox", 1); // Discarded for Tormenting Voice + + assertHandCount(playerB, 3); // 2 from Tormenting Voice + 1 from Turn 2 draw + + assertExileCount(playerA, 1); // Both exiled cards are cast + assertExileCount(playerA, "Amulet of Kroog", 1); // Exiled with Purge + } + + @Test + public void castAdventureWithFallenShinobiTest() { + // Ninjutsu {2}{U}{B} + // Whenever Fallen Shinobi deals combat damage to a player, that player exiles the top two cards + // of their library. Until end of turn, you may play those cards without paying their mana cost. + addCard(Zone.BATTLEFIELD, playerB, "Fallen Shinobi", 1); // Creature 5/4 + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + addCard(Zone.BATTLEFIELD, playerA, "Amulet of Kroog"); // Just to exile for Angelic Purge + + /* Curious Pair {1}{G} + * Creature — Human Peasant + * 1/3 + * ---- + * Treats to Share {G} + * Sorcery — Adventure + * Create a Food token. + */ + addCard(Zone.LIBRARY, playerA, "Curious Pair"); + + // As an additional cost to cast Angelic Purge, sacrifice a permanent. + // Exile target artifact, creature, or enchantment. + addCard(Zone.LIBRARY, playerA, "Angelic Purge"); // Sorcery {2}{W} + + skipInitShuffling(); + + attack(2, playerB, "Fallen Shinobi"); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Angelic Purge"); + setChoice(playerB, "Silvercoat Lion"); // Sacrifice for Purge + addTarget(playerB, "Amulet of Kroog"); // Exile with Purge + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Treats to Share"); + + setStopAt(2, PhaseStep.END_TURN); + + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 15); + assertPermanentCount(playerB, "Fallen Shinobi", 1); + + assertGraveyardCount(playerA, "Angelic Purge", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); // sacrificed for Purge + + assertPermanentCount(playerB, "Food", 1); + assertExileCount(playerA, "Curious Pair", 1); + + assertHandCount(playerB, 1); // 1 from Turn 2 draw + + assertExileCount(playerA, 2); // Both exiled cards are cast + assertExileCount(playerA, "Amulet of Kroog", 1); // Exiled with Purge + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java index 034305cd91e..af231c27532 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/split/CastSplitCardsWithFuseTest.java @@ -80,12 +80,12 @@ public class CastSplitCardsWithFuseTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Absolute Grace"); // Enchantment addCard(Zone.BATTLEFIELD, playerB, "Juggernaut"); // Artifact - showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA); + // showAvaileableAbilities("abils", 1, PhaseStep.PRECOMBAT_MAIN, playerA); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "fused Wear // Tear"); addTarget(playerA, "Juggernaut"); addTarget(playerA, "Absolute Grace"); //playerA.addTarget("Absolute Grace"); - showBattlefield("after", 1, PhaseStep.BEGIN_COMBAT, playerB); + // showBattlefield("after", 1, PhaseStep.BEGIN_COMBAT, playerB); setStrictChooseMode(true); setStopAt(1, PhaseStep.BEGIN_COMBAT); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/TidehollowScullerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/TidehollowScullerTest.java index 2fd417ba202..6714b582846 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/TidehollowScullerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/TidehollowScullerTest.java @@ -104,7 +104,7 @@ public class TidehollowScullerTest extends CardTestPlayerBase { for (int i = 1; i <= 10; i++) { try { this.reset(); - System.out.println("run " + i); + // System.out.println("run " + i); test_CastTwoCardFromHandWillBeExiled(); } catch (Exception e) { // diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index a93b43236a6..d50d6888447 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -359,7 +359,7 @@ public class TestPlayer implements Player { } } - public boolean isAbilityHaveTargetNameOrAlias(Game game, Ability ability, String nameOrAlias) { + public boolean hasAbilityTargetNameOrAlias(Game game, Ability ability, String nameOrAlias) { // use cases: // * Cast cardName with extra // * Cast @ref @@ -379,7 +379,7 @@ public class TestPlayer implements Player { foundObject = true; foundAbility = ability.toString().startsWith(nameOrAlias); } else { - foundObject = isObjectHaveTargetNameOrAlias(game.getObject(ability.getSourceId()), searchObject); + foundObject = hasObjectTargetNameOrAlias(game.getObject(ability.getSourceId()), searchObject); foundAbility = searchObject.startsWith(ALIAS_PREFIX) || ability.toString().startsWith(nameOrAlias); } } else if (nameOrAlias.startsWith(ALIAS_PREFIX)) { @@ -388,7 +388,7 @@ public class TestPlayer implements Player { Assert.assertTrue("ability alias must contains space", nameOrAlias.contains(" ")); String searchObject = nameOrAlias.substring(0, nameOrAlias.indexOf(" ")); String searchAbility = nameOrAlias.substring(nameOrAlias.indexOf(" ") + 1); - foundObject = isObjectHaveTargetNameOrAlias(game.getObject(ability.getSourceId()), searchObject); + foundObject = hasObjectTargetNameOrAlias(game.getObject(ability.getSourceId()), searchObject); foundAbility = ability.toString().startsWith(searchAbility); } else { // ability text @@ -399,7 +399,7 @@ public class TestPlayer implements Player { return foundObject && foundAbility; } - public boolean isObjectHaveTargetNameOrAlias(MageObject object, String nameOrAlias) { + public boolean hasObjectTargetNameOrAlias(MageObject object, String nameOrAlias) { if (object == null || nameOrAlias == null) { return false; } @@ -505,7 +505,7 @@ public class TestPlayer implements Player { } // need by alias or by name - if (!isObjectHaveTargetNameOrAlias(object, targetName)) { + if (!hasObjectTargetNameOrAlias(object, targetName)) { continue; } @@ -578,7 +578,7 @@ public class TestPlayer implements Player { break; } for (ActivatedAbility ability : computerPlayer.getPlayable(game, true)) { // add wrong action log? - if (isAbilityHaveTargetNameOrAlias(game, ability, groups[0])) { + if (hasAbilityTargetNameOrAlias(game, ability, groups[0])) { int bookmark = game.bookmarkState(); ActivatedAbility newAbility = ability.copy(); if (groups.length > 1 && !groups[1].equals("target=" + NO_TARGET)) { @@ -606,7 +606,7 @@ public class TestPlayer implements Player { for (MageObject mageObject : manaObjects) { if (mageObject instanceof Permanent) { for (Ability manaAbility : ((Permanent) mageObject).getAbilities(game).getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) { - if (isAbilityHaveTargetNameOrAlias(game, manaAbility, groups[0])) { + if (hasAbilityTargetNameOrAlias(game, manaAbility, groups[0])) { Ability newManaAbility = manaAbility.copy(); computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); actions.remove(action); @@ -615,7 +615,7 @@ public class TestPlayer implements Player { } } else if (mageObject instanceof Card) { for (Ability manaAbility : ((Card) mageObject).getAbilities(game).getAvailableActivatedManaAbilities(game.getState().getZone(mageObject.getId()), game)) { - if (isAbilityHaveTargetNameOrAlias(game, manaAbility, groups[0])) { + if (hasAbilityTargetNameOrAlias(game, manaAbility, groups[0])) { Ability newManaAbility = manaAbility.copy(); computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); actions.remove(action); @@ -624,7 +624,7 @@ public class TestPlayer implements Player { } } else { for (Ability manaAbility : mageObject.getAbilities().getAvailableActivatedManaAbilities(game.getState().getZone(mageObject.getId()), game)) { - if (isAbilityHaveTargetNameOrAlias(game, manaAbility, groups[0])) { + if (hasAbilityTargetNameOrAlias(game, manaAbility, groups[0])) { Ability newManaAbility = manaAbility.copy(); computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); actions.remove(action); @@ -636,7 +636,7 @@ public class TestPlayer implements Player { List manaPermsWithCost = computerPlayer.getAvailableManaProducersWithCost(game); for (Permanent perm : manaPermsWithCost) { for (ActivatedManaAbilityImpl manaAbility : perm.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) { - if (isAbilityHaveTargetNameOrAlias(game, manaAbility, groups[0]) + if (hasAbilityTargetNameOrAlias(game, manaAbility, groups[0]) && manaAbility.canActivate(computerPlayer.getId(), game).canActivate()) { Ability newManaAbility = manaAbility.copy(); computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); @@ -650,7 +650,7 @@ public class TestPlayer implements Player { command = command.substring(command.indexOf("addCounters:") + 12); String[] groups = command.split("\\$"); for (Permanent permanent : game.getBattlefield().getAllActivePermanents()) { - if (isObjectHaveTargetNameOrAlias(permanent, groups[0])) { + if (hasObjectTargetNameOrAlias(permanent, groups[0])) { CounterType counterType = CounterType.findByName(groups[1]); Assert.assertNotNull("Invalid counter type " + groups[1], counterType); Counter counter = counterType.createInstance(Integer.parseInt(groups[2])); @@ -989,7 +989,7 @@ public class TestPlayer implements Player { } // need by alias or by name - if (!isObjectHaveTargetNameOrAlias(perm, cardName)) { + if (!hasObjectTargetNameOrAlias(perm, cardName)) { continue; } @@ -1198,7 +1198,7 @@ public class TestPlayer implements Player { private void assertPermanentCount(PlayerAction action, Game game, Player player, String permanentName, int count) { int foundedCount = 0; for (Permanent perm : game.getBattlefield().getAllPermanents()) { - if (isObjectHaveTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId())) { + if (hasObjectTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId())) { foundedCount++; } } @@ -1209,7 +1209,7 @@ public class TestPlayer implements Player { private void assertPermanentCounters(PlayerAction action, Game game, Player player, String permanentName, CounterType counterType, int count) { int foundedCount = 0; for (Permanent perm : game.getBattlefield().getAllPermanents()) { - if (isObjectHaveTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId())) { + if (hasObjectTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId())) { foundedCount = perm.getCounters(game).getCount(counterType); } } @@ -1220,7 +1220,7 @@ public class TestPlayer implements Player { private void assertExileCount(PlayerAction action, Game game, Player player, String permanentName, int count) { int foundedCount = 0; for (Card card : game.getExile().getAllCards(game)) { - if (isObjectHaveTargetNameOrAlias(card, permanentName) && card.isOwnedBy(player.getId())) { + if (hasObjectTargetNameOrAlias(card, permanentName) && card.isOwnedBy(player.getId())) { foundedCount++; } } @@ -1231,7 +1231,7 @@ public class TestPlayer implements Player { private void assertGraveyardCount(PlayerAction action, Game game, Player player, String permanentName, int count) { int foundedCount = 0; for (Card card : player.getGraveyard().getCards(game)) { - if (isObjectHaveTargetNameOrAlias(card, permanentName) && card.isOwnedBy(player.getId())) { + if (hasObjectTargetNameOrAlias(card, permanentName) && card.isOwnedBy(player.getId())) { foundedCount++; } } @@ -1247,7 +1247,7 @@ public class TestPlayer implements Player { int realCount = 0; for (UUID cardId : player.getHand()) { Card card = game.getCard(cardId); - if (isObjectHaveTargetNameOrAlias(card, cardName)) { + if (hasObjectTargetNameOrAlias(card, cardName)) { realCount++; } } @@ -1259,7 +1259,7 @@ public class TestPlayer implements Player { int realCount = 0; for (UUID cardId : game.getCommandersIds(player)) { Card card = game.getCard(cardId); - if (isObjectHaveTargetNameOrAlias(card, cardName) && Zone.COMMAND.equals(game.getState().getZone(cardId))) { + if (hasObjectTargetNameOrAlias(card, cardName) && Zone.COMMAND.equals(game.getState().getZone(cardId))) { realCount++; } } @@ -1476,7 +1476,7 @@ public class TestPlayer implements Player { if (group.startsWith("planeswalker=")) { String planeswalkerName = group.substring(group.indexOf("planeswalker=") + 13); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, game)) { - if (isObjectHaveTargetNameOrAlias(permanent, planeswalkerName)) { + if (hasObjectTargetNameOrAlias(permanent, planeswalkerName)) { defenderId = permanent.getId(); } } @@ -1832,7 +1832,7 @@ public class TestPlayer implements Player { if (target.getTargets().contains(permanent.getId())) { continue; } - if (isObjectHaveTargetNameOrAlias(permanent, targetName)) { + if (hasObjectTargetNameOrAlias(permanent, targetName)) { if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) { if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { target.add(permanent.getId(), game); @@ -1897,7 +1897,7 @@ public class TestPlayer implements Player { CheckTargetsList: for (UUID targetId : possibleCards) { MageObject targetObject = game.getObject(targetId); - if (isObjectHaveTargetNameOrAlias(targetObject, possibleChoice)) { + if (hasObjectTargetNameOrAlias(targetObject, possibleChoice)) { if (target.canTarget(targetObject.getId(), game)) { // only unique targets if (usedTargets.contains(targetObject.getId())) { @@ -1950,7 +1950,7 @@ public class TestPlayer implements Player { for (UUID targetId : possibleTargets) { MageObject targetObject = game.getObject(targetId); if (targetObject != null) { - if (isObjectHaveTargetNameOrAlias(targetObject, targetName)) { + if (hasObjectTargetNameOrAlias(targetObject, targetName)) { List alreadyTargetted = target.getTargets(); if (t.canTarget(targetObject.getId(), game)) { if (alreadyTargetted != null && !alreadyTargetted.contains(targetObject.getId())) { @@ -2086,7 +2086,7 @@ public class TestPlayer implements Player { filter = ((FilterPlaneswalkerOrPlayer) filter).getFilterPermanent(); } for (Permanent permanent : game.getBattlefield().getActivePermanents((FilterPermanent) filter, abilityControllerId, sourceId, game)) { - if (isObjectHaveTargetNameOrAlias(permanent, targetName) || (permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { // TODO: remove exp code search? + if (hasObjectTargetNameOrAlias(permanent, targetName) || (permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { // TODO: remove exp code search? if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.getTargets().contains(permanent.getId())) { if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { target.addTarget(permanent.getId(), source, game); @@ -2112,7 +2112,7 @@ public class TestPlayer implements Player { boolean targetFound = false; for (String targetName : targetList) { for (Card card : computerPlayer.getHand().getCards(((TargetCardInHand) target.getOriginalTarget()).getFilter(), game)) { - if (isObjectHaveTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? + if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) { target.addTarget(card.getId(), source, game); targetFound = true; @@ -2137,7 +2137,7 @@ public class TestPlayer implements Player { boolean targetFound = false; for (String targetName : targetList) { for (Card card : game.getExile().getCards(targetFull.getFilter(), game)) { - if (isObjectHaveTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? + if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) { target.addTarget(card.getId(), source, game); targetFound = true; @@ -2162,7 +2162,7 @@ public class TestPlayer implements Player { boolean targetFound = false; for (String targetName : targetList) { for (Card card : game.getBattlefield().getAllActivePermanents()) { - if (isObjectHaveTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? + if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? if (targetFull.canTarget(abilityControllerId, card.getId(), source, game) && !targetFull.getTargets().contains(card.getId())) { targetFull.add(card.getId(), game); targetFound = true; @@ -2212,7 +2212,7 @@ public class TestPlayer implements Player { for (UUID playerId : needPlayers) { Player player = game.getPlayer(playerId); for (Card card : player.getGraveyard().getCards(targetFull.getFilter(), game)) { - if (isObjectHaveTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? + if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search? if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) { target.addTarget(card.getId(), source, game); targetFound = true; @@ -2239,7 +2239,7 @@ public class TestPlayer implements Player { boolean targetFound = false; for (String targetName : targetList) { for (StackObject stackObject : game.getStack()) { - if (isObjectHaveTargetNameOrAlias(stackObject, targetName)) { + if (hasObjectTargetNameOrAlias(stackObject, targetName)) { if (target.canTarget(abilityControllerId, stackObject.getId(), source, game) && !target.getTargets().contains(stackObject.getId())) { target.addTarget(stackObject.getId(), source, game); targetFound = true; @@ -2289,7 +2289,7 @@ public class TestPlayer implements Player { boolean targetFound = false; for (String targetName : targetList) { for (Card card : cards.getCards(game)) { - if (isObjectHaveTargetNameOrAlias(card, targetName) && !target.getTargets().contains(card.getId())) { + if (hasObjectTargetNameOrAlias(card, targetName) && !target.getTargets().contains(card.getId())) { target.addTarget(card.getId(), source, game); targetFound = true; break; @@ -3517,7 +3517,7 @@ public class TestPlayer implements Player { if (target.getTargets().contains(card.getId())) { continue; } - if (isObjectHaveTargetNameOrAlias(card, targetName)) { + if (hasObjectTargetNameOrAlias(card, targetName)) { if (target.isNotTarget() || target.canTarget(card.getId(), game)) { target.add(card.getId(), game); targetFound = true; diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index 3d3e4e8edaf..57a4c26ac6e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -1819,7 +1819,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement private boolean isObjectHaveTargetNameOrAlias(Player player, MageObject object, String nameOrAlias) { TestPlayer testPlayer = (TestPlayer) player; if (player != null) { // TODO: remove null check and replace all null-player calls in tests by player - return testPlayer.isObjectHaveTargetNameOrAlias(object, nameOrAlias); + return testPlayer.hasObjectTargetNameOrAlias(object, nameOrAlias); } else { return object.getName().equals(nameOrAlias); } diff --git a/Mage/src/main/java/mage/abilities/SpellAbility.java b/Mage/src/main/java/mage/abilities/SpellAbility.java index a0ee47b51c2..82593588e4a 100644 --- a/Mage/src/main/java/mage/abilities/SpellAbility.java +++ b/Mage/src/main/java/mage/abilities/SpellAbility.java @@ -105,7 +105,7 @@ public class SpellAbility extends ActivatedAbilityImpl { return ActivationStatus.getFalse(); } } - if (costs.canPay(this, sourceId, controllerId, game)) { + if (costs.canPay(this, sourceId, playerId, game)) { if (getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) { SplitCard splitCard = (SplitCard) game.getCard(getSourceId()); if (splitCard != null) { diff --git a/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java index d17ca0e2633..52bf7f44362 100644 --- a/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/AsThoughEffectImpl.java @@ -7,6 +7,9 @@ import mage.constants.*; import mage.game.Game; import java.util.UUID; +import mage.cards.SplitCard; +import mage.cards.SplitCardHalf; +import mage.players.Player; /** * @author BetaSteward_at_googlemail.com @@ -42,7 +45,14 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements } /** - * Helper to check that affectedAbility is compatible for alternative cast modifications by setCastSourceIdWithAlternateMana + * Helper to check that affectedAbility is compatible for alternative cast + * modifications by setCastSourceIdWithAlternateMana + * + * @param cardToCheck + * @param affectedAbilityToCheck + * @param playerToCheck + * @param source + * @return */ public boolean isAbilityAppliedForAlternateCast(Card cardToCheck, Ability affectedAbilityToCheck, UUID playerToCheck, Ability source) { return cardToCheck != null @@ -52,4 +62,35 @@ public abstract class AsThoughEffectImpl extends ContinuousEffectImpl implements && (affectedAbilityToCheck.getAbilityType() == AbilityType.SPELL || affectedAbilityToCheck.getAbilityType() == AbilityType.PLAY_LAND); } + + /** + * Internal method to do the neccessary to allow the card from objectId to be cast or played (if it's a land) without paying any mana. + * Additional costs (like sacrificing or discarding) have still to be payed. + * Checks if the card is of the correct type or in the correct zone have to be done before. + * + * @param objectId sourceId of the card to play + * @param source source ability that allows this effect + * @param affectedControllerId player allowed to play the card + * @param game + * @return + */ + protected boolean allowCardToPlayWithoutMana(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + Player player = game.getPlayer(affectedControllerId); + Card card = game.getCard(objectId); + if (card == null || player == null) { + return false; + } + if (!card.isLand()) { + if (card instanceof SplitCard) { + SplitCardHalf leftCard = ((SplitCard) card).getLeftHalfCard(); + player.setCastSourceIdWithAlternateMana(leftCard.getId(), null, leftCard.getSpellAbility().getCosts()); + SplitCardHalf rightCard = ((SplitCard) card).getRightHalfCard(); + player.setCastSourceIdWithAlternateMana(rightCard.getId(), null, rightCard.getSpellAbility().getCosts()); + } else { + player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); + } + } + return true; + } + } diff --git a/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java index ec878eb5b14..2bbb910e5f9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/asthought/PlayFromNotOwnHandZoneTargetEffect.java @@ -1,15 +1,23 @@ package mage.abilities.effects.common.asthought; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.cards.Card; import mage.constants.AsThoughEffectType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; import mage.game.Game; +import mage.players.Player; +import mage.target.targetpointer.FixedTargets; +import mage.util.CardUtil; /** * @@ -19,6 +27,7 @@ public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl { private final Zone fromZone; private final TargetController allowedCaster; + private final boolean withoutMana; public PlayFromNotOwnHandZoneTargetEffect() { this(Duration.EndOfTurn); @@ -33,15 +42,21 @@ public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl { } public PlayFromNotOwnHandZoneTargetEffect(Zone fromZone, TargetController allowedCaster, Duration duration) { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, Outcome.Benefit); + this(fromZone, allowedCaster, duration, false); + } + + public PlayFromNotOwnHandZoneTargetEffect(Zone fromZone, TargetController allowedCaster, Duration duration, boolean withoutMana) { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, withoutMana ? Outcome.PlayForFree : Outcome.PutCardInPlay); this.fromZone = fromZone; this.allowedCaster = allowedCaster; + this.withoutMana = withoutMana; } public PlayFromNotOwnHandZoneTargetEffect(final PlayFromNotOwnHandZoneTargetEffect effect) { super(effect); this.fromZone = effect.fromZone; this.allowedCaster = effect.allowedCaster; + this.withoutMana = effect.withoutMana; } @Override @@ -56,27 +71,91 @@ public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + return applies(objectId, null, source, game, affectedControllerId); + } + + @Override + public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { + + List targets = getTargetPointer().getTargets(game, source); + if (targets.isEmpty()) { + this.discard(); + return false; + } switch (allowedCaster) { case YOU: - if (affectedControllerId != source.getControllerId()) { + if (playerId != source.getControllerId()) { return false; } break; case OPPONENT: - if (!game.getOpponents(source.getControllerId()).contains(affectedControllerId)) { + if (!game.getOpponents(source.getControllerId()).contains(playerId)) { return false; } break; case ANY: break; } - List targets = getTargetPointer().getTargets(game, source); - if (targets.isEmpty()) { - this.discard(); + UUID objectIdToCast = CardUtil.getMainCardId(game, objectId); + if (targets.contains(objectIdToCast) + && playerId.equals(source.getControllerId()) + && game.getState().getZone(objectId).match(fromZone)) { + if (withoutMana) { + if (affectedAbility != null) { + objectIdToCast = affectedAbility.getSourceId(); + } + return allowCardToPlayWithoutMana(objectIdToCast, source, playerId, game); + } + return true; + } + return false; + } + + public static boolean exileAndPlayFromExile(Game game, Ability source, Card card, TargetController allowedCaster, Duration duration, boolean withoutMana) { + if (card == null) { + return true; + } + Set cards = new HashSet<>(); + cards.add(card); + return exileAndPlayFromExile(game, source, cards, allowedCaster, duration, withoutMana); + } + /** + * Exiles the cards and let the allowed player play them from exile for the given duration + * + * @param game + * @param source + * @param cards + * @param allowedCaster + * @param duration + * @param withoutMana + * @return + */ + public static boolean exileAndPlayFromExile(Game game, Ability source, Set cards, TargetController allowedCaster, Duration duration, boolean withoutMana) { + if (cards == null || cards.isEmpty()) { + return true; + } + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller == null || sourceObject == null) { return false; } - return targets.contains(objectId) - && affectedControllerId.equals(source.getControllerId()) - && game.getState().getZone(objectId).match(fromZone); + UUID exileId = CardUtil.getExileZoneId( + controller.getId().toString() + + "-" + game.getState().getTurnNum() + + "-" + sourceObject.getIdName(), game + ); + String exileName = sourceObject.getIdName() + " free play" + + (Duration.EndOfTurn.equals(duration) ? " on turn " + game.getState().getTurnNum():"") + + " for " + controller.getName(); + if (Duration.EndOfTurn.equals(duration)) { + game.getExile().createZone(exileId, exileName).setCleanupOnEndTurn(true); + } + if (!controller.moveCardsToExile(cards, source, game, true, exileId, exileName)) { + return false; + } + ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, allowedCaster, duration, withoutMana); + effect.setTargetPointer(new FixedTargets(cards, game)); + game.addEffect(effect, source); + return true; } } diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java index 3bb996652b8..364415d96d5 100644 --- a/Mage/src/main/java/mage/cards/Card.java +++ b/Mage/src/main/java/mage/cards/Card.java @@ -29,6 +29,8 @@ public interface Card extends MageObject { /** * For cards: return all basic and dynamic abilities * For permanents: return all basic and dynamic abilities + * @param game + * @return */ Abilities getAbilities(Game game); diff --git a/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java b/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java index 137303c8c55..d125bcc84b2 100644 --- a/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java +++ b/Mage/src/main/java/mage/target/targetpointer/FixedTarget.java @@ -27,6 +27,12 @@ public class FixedTarget implements TargetPointer { this(mor.getSourceId(), mor.getZoneChangeCounter()); } + /** + * Target counter is immediatly initialised with current zoneChangeCounter value from the GameState + * Sets fixed the currect zone chnage counter + * @param card used to get the objectId + * @param game + */ public FixedTarget(Card card, Game game) { this.targetId = card.getId(); this.zoneChangeCounter = card.getZoneChangeCounter(game);