diff --git a/Mage.Sets/src/mage/cards/p/PanglacialWurm.java b/Mage.Sets/src/mage/cards/p/PanglacialWurm.java new file mode 100644 index 00000000000..889eba921a5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PanglacialWurm.java @@ -0,0 +1,66 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.p; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.WhileSearchingPlayFromLibraryAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * + * @author L_J + */ +public class PanglacialWurm extends CardImpl { + + public PanglacialWurm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{G}"); + this.subtype.add(SubType.WURM); + this.power = new MageInt(9); + this.toughness = new MageInt(5); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // While you're searching your library, you may cast Panglacial Wurm from your library. + this.addAbility(WhileSearchingPlayFromLibraryAbility.getInstance()); + } + + public PanglacialWurm(final PanglacialWurm card) { + super(card); + } + + @Override + public PanglacialWurm copy() { + return new PanglacialWurm(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Coldsnap.java b/Mage.Sets/src/mage/sets/Coldsnap.java index 5d924af0ca9..1709fb4e29e 100644 --- a/Mage.Sets/src/mage/sets/Coldsnap.java +++ b/Mage.Sets/src/mage/sets/Coldsnap.java @@ -139,6 +139,7 @@ public class Coldsnap extends ExpansionSet { cards.add(new SetCardInfo("Ohran Viper", 115, Rarity.RARE, mage.cards.o.OhranViper.class)); cards.add(new SetCardInfo("Ohran Yeti", 93, Rarity.COMMON, mage.cards.o.OhranYeti.class)); cards.add(new SetCardInfo("Orcish Bloodpainter", 94, Rarity.COMMON, mage.cards.o.OrcishBloodpainter.class)); + cards.add(new SetCardInfo("Panglacial Wurm", 116, Rarity.RARE, mage.cards.p.PanglacialWurm.class)); cards.add(new SetCardInfo("Perilous Research", 41, Rarity.UNCOMMON, mage.cards.p.PerilousResearch.class)); cards.add(new SetCardInfo("Phobian Phantasm", 66, Rarity.UNCOMMON, mage.cards.p.PhobianPhantasm.class)); cards.add(new SetCardInfo("Phyrexian Etchings", 67, Rarity.RARE, mage.cards.p.PhyrexianEtchings.class)); diff --git a/Mage/src/main/java/mage/abilities/common/WhileSearchingPlayFromLibraryAbility.java b/Mage/src/main/java/mage/abilities/common/WhileSearchingPlayFromLibraryAbility.java new file mode 100644 index 00000000000..4de04282897 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/WhileSearchingPlayFromLibraryAbility.java @@ -0,0 +1,67 @@ +/* + * Copyright 2011 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.abilities.common; + +import mage.abilities.MageSingleton; +import mage.abilities.StaticAbility; + +import java.io.ObjectStreamException; + +import mage.constants.AbilityType; +import mage.constants.Zone; + +/** + * @author L_J + */ +public class WhileSearchingPlayFromLibraryAbility extends StaticAbility implements MageSingleton { + + private static final WhileSearchingPlayFromLibraryAbility instance = new WhileSearchingPlayFromLibraryAbility(); + + private Object readResolve() throws ObjectStreamException { + return instance; + } + + public static WhileSearchingPlayFromLibraryAbility getInstance() { + return instance; + } + + private WhileSearchingPlayFromLibraryAbility() { + super(AbilityType.STATIC, Zone.LIBRARY); + } + + @Override + public String getRule() { + return "While you're searching your library, you may cast {this} from your library."; + } + + @Override + public WhileSearchingPlayFromLibraryAbility copy() { + return instance; + } + +} diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 6233a4ed6f2..1301f196972 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -36,6 +36,7 @@ import mage.MageObject; import mage.Mana; import mage.abilities.*; import mage.abilities.common.PassAbility; +import mage.abilities.common.WhileSearchingPlayFromLibraryAbility; import mage.abilities.common.delayed.AtTheEndOfTurnStepPostDelayedTriggeredAbility; import mage.abilities.costs.*; import mage.abilities.costs.mana.ManaCost; @@ -53,6 +54,7 @@ import mage.cards.Cards; import mage.cards.CardsImpl; import mage.cards.SplitCard; import mage.cards.decks.Deck; +import mage.choices.ChoiceImpl; import mage.constants.*; import static mage.constants.Zone.BATTLEFIELD; import static mage.constants.Zone.EXILED; @@ -2307,28 +2309,101 @@ public abstract class PlayerImpl implements Player, Serializable { TargetCardInLibrary newTarget = target.copy(); int count; int librarySearchLimit = event.getAmount(); - if (librarySearchLimit == Integer.MAX_VALUE) { - count = searchedLibrary.count(target.getFilter(), game); - } else { - newTarget.setCardLimit(librarySearchLimit); - count = Math.min(searchedLibrary.count(target.getFilter(), game), librarySearchLimit); - } - - if (count < target.getNumberOfTargets()) { - newTarget.setMinNumberOfTargets(count); - } - if (newTarget.choose(Outcome.Neutral, playerId, targetPlayerId, game)) { - target.getTargets().clear(); - for (UUID targetId : newTarget.getTargets()) { - target.add(targetId, game); + List cardsFromTop = null; + do { + // TODO: prevent shuffling from moving the visualized cards + if (librarySearchLimit == Integer.MAX_VALUE) { + count = searchedLibrary.count(target.getFilter(), game); + } else { + Player targetPlayer = game.getPlayer(targetPlayerId); + if (targetPlayer != null) { + if (cardsFromTop == null) { + cardsFromTop = new ArrayList<>(targetPlayer.getLibrary().getTopCards(game, librarySearchLimit)); + } else { + cardsFromTop.retainAll(targetPlayer.getLibrary().getCards(game)); + } + } + newTarget.setCardLimit(Math.min(librarySearchLimit, cardsFromTop.size())); + count = Math.min(searchedLibrary.count(target.getFilter(), game), librarySearchLimit); } - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LIBRARY_SEARCHED, targetPlayerId, playerId)); - } + + if (count < target.getNumberOfTargets()) { + newTarget.setMinNumberOfTargets(count); + } + if (newTarget.choose(Outcome.Neutral, playerId, targetPlayerId, game)) { + if (targetPlayerId.equals(playerId) && handleLibraryCastableCards(library, game, targetPlayerId)) { // for handling Panglacial Wurm + newTarget.clearChosen(); + continue; + } + target.getTargets().clear(); + for (UUID targetId : newTarget.getTargets()) { + target.add(targetId, game); + } + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LIBRARY_SEARCHED, targetPlayerId, playerId)); + } else if (targetPlayerId.equals(playerId) && handleLibraryCastableCards(library, game, targetPlayerId)) { // for handling Panglacial Wurm + newTarget.clearChosen(); + continue; + } + break; + } while (true); return true; } return false; } + private boolean handleLibraryCastableCards(Library library, Game game, UUID targetPlayerId) { + // for handling Panglacial Wurm + boolean alreadyChosenUse = false; + Map libraryCastableCardTracker = new HashMap<>(); + searchForCards: + do { + for (Card card : library.getCards(game)) { + for (Ability ability : card.getAbilities()) { + if (ability.getClass() == WhileSearchingPlayFromLibraryAbility.class) { + libraryCastableCardTracker.put(card.getId(), card.getName() + " [" + card.getId().toString().substring(0, 3) + "]"); + } + } + } + if (!libraryCastableCardTracker.isEmpty()) { + Player player = game.getPlayer(targetPlayerId); + if (player != null) { + if (player.isHuman() && (alreadyChosenUse || player.chooseUse(Outcome.AIDontUseIt, "Cast a creature card from your library? (choose \"No\" to finish search)", null, game))) { + ChoiceImpl chooseCard = new ChoiceImpl(); + chooseCard.setMessage("Which creature do you wish to cast from your library?"); + Set choice = new LinkedHashSet<>(); + for (Entry entry : libraryCastableCardTracker.entrySet()) { + choice.add(new AbstractMap.SimpleEntry(entry).getValue()); + } + chooseCard.setChoices(choice); + while (!choice.isEmpty()) { + if (player.choose(Outcome.AIDontUseIt, chooseCard, game)) { + String chosenCard = chooseCard.getChoice(); + for (Entry entry : libraryCastableCardTracker.entrySet()) { + if (chosenCard.equals(entry.getValue())) { + Card card = game.getCard(entry.getKey()); + if (card != null) { + // TODO: fix costs (why is Panglacial Wurm automatically accepting payment?) + player.cast(card.getSpellAbility(), game, false); + } + chooseCard.clearChoice(); + libraryCastableCardTracker.clear(); + alreadyChosenUse = true; + continue searchForCards; + } + } + continue; + } + break; + } + return true; + } + } + } + break; + } while (alreadyChosenUse); + return alreadyChosenUse; + } + @Override public boolean flipCoin(Game game) { return this.flipCoin(game, null);