From b5b4b38cc6f42e54fd34828ddbc79d450fd1efc7 Mon Sep 17 00:00:00 2001 From: "Alex W. Jackson" Date: Mon, 5 Sep 2022 19:44:09 -0400 Subject: [PATCH] Fix ShuffleIntoLibraryTargetEffect to support multiple target cards with possibly different owners --- .../src/mage/cards/g/GhostlyCastigator.java | 4 +- .../src/mage/cards/p/PerpetualTimepiece.java | 42 +------------- .../src/mage/cards/s/StreamOfThought.java | 46 +++++++++++---- Mage.Sets/src/mage/cards/t/TurnTheEarth.java | 10 ++-- .../ShuffleIntoLibraryTargetEffect.java | 57 +++++++++++++------ .../common/TargetCardInYourGraveyard.java | 2 +- 6 files changed, 86 insertions(+), 75 deletions(-) diff --git a/Mage.Sets/src/mage/cards/g/GhostlyCastigator.java b/Mage.Sets/src/mage/cards/g/GhostlyCastigator.java index f18d529962..44868f2dd4 100644 --- a/Mage.Sets/src/mage/cards/g/GhostlyCastigator.java +++ b/Mage.Sets/src/mage/cards/g/GhostlyCastigator.java @@ -32,8 +32,8 @@ public final class GhostlyCastigator extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - // When Ghostly Castigator enters the battlefield, shuffle up to three target cards from your graveyard into your library. - Ability ability = new EntersBattlefieldTriggeredAbility(new ShuffleIntoLibraryTargetEffect()); + // When Ghostly Castigator enters the battlefield, you may shuffle up to three target cards from your graveyard into your library. + Ability ability = new EntersBattlefieldTriggeredAbility(new ShuffleIntoLibraryTargetEffect(), true); ability.addTarget(new TargetCardInYourGraveyard(0, 3)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/p/PerpetualTimepiece.java b/Mage.Sets/src/mage/cards/p/PerpetualTimepiece.java index b7711a0e18..0d2682e497 100644 --- a/Mage.Sets/src/mage/cards/p/PerpetualTimepiece.java +++ b/Mage.Sets/src/mage/cards/p/PerpetualTimepiece.java @@ -1,4 +1,3 @@ - package mage.cards.p; import java.util.UUID; @@ -7,18 +6,11 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.ExileSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.MillCardsControllerEffect; import mage.abilities.effects.common.ShuffleIntoLibraryTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; /** @@ -31,12 +23,12 @@ public final class PerpetualTimepiece extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); // {T}: Put the top two cards of your library into your graveyard. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new MillCardsControllerEffect(2), new TapSourceCost())); + this.addAbility(new SimpleActivatedAbility(new MillCardsControllerEffect(2), new TapSourceCost())); // {2}, Exile Perpetual Timepiece: Shuffle any number of target cards from your graveyard into your library. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ShuffleIntoLibraryTargetEffect(), new GenericManaCost(2)); + Ability ability = new SimpleActivatedAbility(new ShuffleIntoLibraryTargetEffect(), new GenericManaCost(2)); ability.addCost(new ExileSourceCost()); - ability.addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, new FilterCard("cards to shuffle into your library"))); + ability.addTarget(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE)); this.addAbility(ability); } @@ -49,31 +41,3 @@ public final class PerpetualTimepiece extends CardImpl { return new PerpetualTimepiece(this); } } - -class PerpetualTimepieceShuffleEffect extends OneShotEffect { - - PerpetualTimepieceShuffleEffect() { - super(Outcome.Neutral); - this.staticText = "Shuffle any number of target cards from your graveyard into your library"; - } - - PerpetualTimepieceShuffleEffect(final PerpetualTimepieceShuffleEffect effect) { - super(effect); - } - - @Override - public PerpetualTimepieceShuffleEffect copy() { - return new PerpetualTimepieceShuffleEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.moveCards(new CardsImpl(this.getTargetPointer().getTargets(game, source)), Zone.LIBRARY, source, game); - controller.shuffleLibrary(source, game); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/s/StreamOfThought.java b/Mage.Sets/src/mage/cards/s/StreamOfThought.java index 79dc9257a0..8d3d129838 100644 --- a/Mage.Sets/src/mage/cards/s/StreamOfThought.java +++ b/Mage.Sets/src/mage/cards/s/StreamOfThought.java @@ -2,12 +2,10 @@ package mage.cards.s; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.PutLibraryIntoGraveTargetEffect; -import mage.abilities.effects.common.ShuffleIntoLibraryTargetEffect; +import mage.abilities.effects.common.MillCardsTargetEffect; import mage.abilities.keyword.ReplicateAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -16,7 +14,6 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPlayer; import mage.target.common.TargetCardInYourGraveyard; -import mage.target.targetpointer.SecondTargetPointer; import java.util.UUID; @@ -28,14 +25,11 @@ public final class StreamOfThought extends CardImpl { public StreamOfThought(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{U}"); - // Target player puts the top four cards of their library into their graveyard. - this.getSpellAbility().addEffect(new PutLibraryIntoGraveTargetEffect(4)); + // Target player mills four cards. You shuffle up to four cards from your graveyard into your library. + this.getSpellAbility().addEffect(new MillCardsTargetEffect(4)); + this.getSpellAbility().addEffect(new StreamOfThoughtEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); - // You shuffle up to four cards from your graveyard into your library. - this.getSpellAbility().addEffect(new ShuffleIntoLibraryTargetEffect().setTargetPointer(new SecondTargetPointer())); - this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(0, 4)); - // Replicate {2}{U}{U} this.addAbility(new ReplicateAbility("{2}{U}{U}")); } @@ -49,3 +43,35 @@ public final class StreamOfThought extends CardImpl { return new StreamOfThought(this); } } + +class StreamOfThoughtEffect extends OneShotEffect { + + StreamOfThoughtEffect() { + super(Outcome.Benefit); + staticText = "You shuffle up to four cards from your graveyard into your library."; + } + + private StreamOfThoughtEffect(final StreamOfThoughtEffect effect) { + super(effect); + } + + @Override + public StreamOfThoughtEffect copy() { + return new StreamOfThoughtEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetCard target = new TargetCardInYourGraveyard(0, 4); + target.setNotTarget(true); + if (!player.choose(outcome, player.getGraveyard(), target, game)) { + return false; + } + player.shuffleCardsToLibrary(new CardsImpl(target.getTargets()), game, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TurnTheEarth.java b/Mage.Sets/src/mage/cards/t/TurnTheEarth.java index a7c2fc214b..b6040f89e7 100644 --- a/Mage.Sets/src/mage/cards/t/TurnTheEarth.java +++ b/Mage.Sets/src/mage/cards/t/TurnTheEarth.java @@ -7,7 +7,7 @@ import mage.abilities.keyword.FlashbackAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TimingRule; +import mage.filter.FilterCard; import mage.target.common.TargetCardInGraveyard; import java.util.UUID; @@ -17,15 +17,15 @@ import java.util.UUID; */ public final class TurnTheEarth extends CardImpl { + private static final FilterCard filter = new FilterCard("cards in graveyards"); + public TurnTheEarth(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); // Choose up to three target cards in graveyards. The owners of those cards shuffle them into their libraries. You gain 2 life. - this.getSpellAbility().addEffect(new ShuffleIntoLibraryTargetEffect() - .setText("choose up to three target cards in graveyards. " + - "The owners of those cards shuffle them into their libraries.")); + this.getSpellAbility().addEffect(new ShuffleIntoLibraryTargetEffect()); this.getSpellAbility().addEffect(new GainLifeEffect(2)); - this.getSpellAbility().addTarget(new TargetCardInGraveyard(0, 3)); + this.getSpellAbility().addTarget(new TargetCardInGraveyard(0, 3, filter)); // Flashback {1}{G} this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{1}{G}"))); diff --git a/Mage/src/main/java/mage/abilities/effects/common/ShuffleIntoLibraryTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ShuffleIntoLibraryTargetEffect.java index f75be6580b..0e779d63b5 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ShuffleIntoLibraryTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ShuffleIntoLibraryTargetEffect.java @@ -1,17 +1,22 @@ package mage.abilities.effects.common; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + /** * - * @author Styxo + * @author awjackson */ public class ShuffleIntoLibraryTargetEffect extends OneShotEffect { @@ -38,30 +43,46 @@ public class ShuffleIntoLibraryTargetEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - MageObject cardObject = game.getObject(getTargetPointer().getFirst(game, source)); Player controller = game.getPlayer(source.getControllerId()); - if (cardObject != null && controller != null && cardObject instanceof Card) { - if (!optional - || controller.chooseUse(Outcome.Benefit, "Shuffle " + cardObject.getIdName() + " into " - + (((Card) cardObject).getOwnerId().equals(source.getControllerId()) ? "your" : "its owners") - + " library?", source, game)) { - Player owner = game.getPlayer(((Card) cardObject).getOwnerId()); - if (owner != null) { - return owner.shuffleCardsToLibrary(((Card) cardObject), game, source); - } - return true; + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(getTargetPointer().getTargets(game, source)); + if (cards.isEmpty()) { + return true; + } + if (optional && !controller.chooseUse(Outcome.Benefit, "Shuffle the target card" + (cards.size() > 1 ? "s" : "") + " into your library?", source, game)) { + return true; + } + // sort the target cards by owner + Map cardsByOwner = new HashMap<>(); + for (Card card : cards.getCards(game)) { + cardsByOwner.computeIfAbsent(card.getOwnerId(), x -> new CardsImpl()).add(card); + } + // then each player shuffles the cards they own + for (Map.Entry entry : cardsByOwner.entrySet()) { + Player owner = game.getPlayer(entry.getKey()); + if (owner != null) { + owner.shuffleCardsToLibrary(entry.getValue(), game, source); } } - return false; + return true; } @Override - public String getText(Mode mode - ) { + public String getText(Mode mode) + { if (staticText != null && !staticText.isEmpty()) { return staticText; - } else { - return "choose target " + mode.getTargets().get(0).getTargetName() + ". Its owner shuffles it into their library"; } + String targetDescription = getTargetPointer().describeTargets(mode.getTargets(), ""); + if (targetDescription.contains("your graveyard")) { + return (optional ? "you may shuffle " : "shuffle ") + targetDescription + " into your library"; + } + return "choose " + targetDescription + ( + getTargetPointer().isPlural(mode.getTargets()) ? + ". The owners of those cards shuffle them into their libraries" : + ". Its owner shuffles it into their library" + ); } } diff --git a/Mage/src/main/java/mage/target/common/TargetCardInYourGraveyard.java b/Mage/src/main/java/mage/target/common/TargetCardInYourGraveyard.java index c6bf601890..d5a0cadf25 100644 --- a/Mage/src/main/java/mage/target/common/TargetCardInYourGraveyard.java +++ b/Mage/src/main/java/mage/target/common/TargetCardInYourGraveyard.java @@ -33,7 +33,7 @@ public class TargetCardInYourGraveyard extends TargetCard { } public TargetCardInYourGraveyard(int minNumTargets, int maxNumTargets) { - this(minNumTargets, maxNumTargets, StaticFilters.FILTER_CARD_FROM_YOUR_GRAVEYARD); + this(minNumTargets, maxNumTargets, maxNumTargets > 1 ? StaticFilters.FILTER_CARDS_FROM_YOUR_GRAVEYARD : StaticFilters.FILTER_CARD_FROM_YOUR_GRAVEYARD); } public TargetCardInYourGraveyard(int minNumTargets, int maxNumTargets, FilterCard filter) {