diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/PossibilityStorm.java b/Mage.Sets/src/mage/sets/dragonsmaze/PossibilityStorm.java index a937018ec3..1c6e13edb2 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/PossibilityStorm.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/PossibilityStorm.java @@ -53,7 +53,6 @@ import mage.target.targetpointer.FixedTarget; * * @author LevelX2 */ - public class PossibilityStorm extends CardImpl { public PossibilityStorm(UUID ownerId) { @@ -77,7 +76,6 @@ public class PossibilityStorm extends CardImpl { } } - class PossibilityStormTriggeredAbility extends TriggeredAbilityImpl { public PossibilityStormTriggeredAbility() { @@ -132,11 +130,11 @@ class PossibilityStormEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); - MageObject sourceObject = source.getSourceObject(game); + MageObject sourceObject = source.getSourceObject(game); if (sourceObject != null && spell != null) { Player spellController = game.getPlayer(spell.getControllerId()); - if (spellController != null && - spellController.moveCardToExileWithInfo(spell, source.getSourceId(), sourceObject.getIdName(), source.getSourceId(), game, Zone.STACK, true)) { + if (spellController != null + && spellController.moveCardToExileWithInfo(spell, source.getSourceId(), sourceObject.getIdName(), source.getSourceId(), game, Zone.STACK, true)) { if (spellController.getLibrary().size() > 0) { Library library = spellController.getLibrary(); Card card; @@ -147,9 +145,9 @@ class PossibilityStormEffect extends OneShotEffect { } } while (library.size() > 0 && card != null && !sharesType(card, spell.getCardType())); - if (card != null && sharesType(card, spell.getCardType()) && - !card.getCardType().contains(CardType.LAND) && - card.getSpellAbility().getTargets().canChoose(spellController.getId(), game)) { + if (card != null && sharesType(card, spell.getCardType()) + && !card.getCardType().contains(CardType.LAND) + && card.getSpellAbility().canChooseTarget(game)) { if (spellController.chooseUse(Outcome.PlayForFree, "Cast " + card.getLogName() + " without paying cost?", source, game)) { spellController.cast(card.getSpellAbility(), game, true); } @@ -171,7 +169,7 @@ class PossibilityStormEffect extends OneShotEffect { return false; } - private boolean sharesType (Card card, List cardTypes) { + private boolean sharesType(Card card, List cardTypes) { for (CardType type : card.getCardType()) { if (cardTypes.contains(type)) { return true; diff --git a/Mage.Sets/src/mage/sets/theros/SteamAugury.java b/Mage.Sets/src/mage/sets/theros/SteamAugury.java index dfdd983e2f..f73f15876f 100644 --- a/Mage.Sets/src/mage/sets/theros/SteamAugury.java +++ b/Mage.Sets/src/mage/sets/theros/SteamAugury.java @@ -48,6 +48,7 @@ import mage.players.Player; import mage.target.Target; import mage.target.TargetCard; import mage.target.common.TargetOpponent; +import mage.util.GameLog; /** * @@ -59,7 +60,6 @@ public class SteamAugury extends CardImpl { super(ownerId, 205, "Steam Augury", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{2}{U}{R}"); this.expansionSetCode = "THS"; - // Reveal the top five cards of your library and separate them into two piles. An opponent chooses one of those piles. Put that pile into your hand and the other into your graveyard. this.getSpellAbility().addEffect(new SteamAuguryEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); @@ -102,7 +102,7 @@ class SteamAuguryEffect extends OneShotEffect { Cards cards = new CardsImpl(); cards.addAll(controller.getLibrary().getTopCards(game, 5)); - controller.revealCards(sourceObject.getName(), cards, game); + controller.revealCards(sourceObject.getIdName(), cards, game); Player opponent; Set opponents = game.getOpponents(controller.getId()); @@ -131,14 +131,14 @@ class SteamAuguryEffect extends OneShotEffect { } List pile2 = new ArrayList<>(); Cards pile2CardsIds = new CardsImpl(); - for (UUID cardId :cards) { + for (UUID cardId : cards) { Card card = game.getCard(cardId); if (card != null && !pile1.contains(card)) { pile2.add(card); pile2CardsIds.add(card.getId()); } } - boolean choice = opponent.choosePile(Outcome.Detriment, new StringBuilder("Choose a pile to put into ").append(controller.getLogName()).append("'s hand.").toString(), pile1, pile2, game); + boolean choice = opponent.choosePile(Outcome.Detriment, "Choose a pile to put into " + controller.getName() + "'s hand.", pile1, pile2, game); Zone pile1Zone = Zone.GRAVEYARD; Zone pile2Zone = Zone.HAND; @@ -147,13 +147,13 @@ class SteamAuguryEffect extends OneShotEffect { pile2Zone = Zone.GRAVEYARD; } - StringBuilder sb = new StringBuilder(sourceObject.getLogName() + ": Pile 1, going to ").append(pile1Zone.equals(Zone.HAND)?"Hand":"Graveyard").append (": "); + StringBuilder sb = new StringBuilder(sourceObject.getLogName() + ": Pile 1, going to ").append(pile1Zone.equals(Zone.HAND) ? "Hand" : "Graveyard").append(": "); int i = 0; for (UUID cardUuid : pile1CardsIds) { i++; Card card = game.getCard(cardUuid); if (card != null) { - sb.append(card.getName()); + sb.append(GameLog.getColoredObjectName(card)); if (i < pile1CardsIds.size()) { sb.append(", "); } @@ -162,13 +162,13 @@ class SteamAuguryEffect extends OneShotEffect { } game.informPlayers(sb.toString()); - sb = new StringBuilder(sourceObject.getLogName() + ": Pile 2, going to ").append(pile2Zone.equals(Zone.HAND)?"Hand":"Graveyard").append (":"); + sb = new StringBuilder(sourceObject.getLogName() + ": Pile 2, going to ").append(pile2Zone.equals(Zone.HAND) ? "Hand" : "Graveyard").append(":"); i = 0; for (UUID cardUuid : pile2CardsIds) { Card card = game.getCard(cardUuid); if (card != null) { i++; - sb.append(" ").append(card.getName()); + sb.append(" ").append(GameLog.getColoredObjectName(card)); if (i < pile2CardsIds.size()) { sb.append(", "); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/PossibilityStormTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/PossibilityStormTest.java index 6501fbf2ab..a5ccb16193 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/PossibilityStormTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/PossibilityStormTest.java @@ -27,7 +27,6 @@ */ package org.mage.test.cards.triggers; - import mage.cards.Card; import mage.constants.PhaseStep; import mage.constants.Zone; @@ -39,7 +38,6 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * * @author LevelX2 */ - public class PossibilityStormTest extends CardTestPlayerBase { /** @@ -56,18 +54,17 @@ public class PossibilityStormTest extends CardTestPlayerBase { * because the filter on this site claims it makes my post look too * "spammy". Here's a screenshot of it instead(in spoiler tag). */ - @Test public void TestWithZoeticCavern() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); // Whenever a player casts a spell from his or her hand, that player exiles it, then exiles cards from // the top of his or her library until he or she exiles a card that shares a card type with it. That // player may cast that card without paying its mana cost. Then he or she puts all cards exiled with - // Possibility Storm on the bottom of his or her library in a random order. + // Possibility Storm on the bottom of his or her library in a random order. addCard(Zone.BATTLEFIELD, playerA, "Possibility Storm", 2); - + // {T}: Add {1} to your mana pool. - // Morph {2} + // Morph {2} addCard(Zone.HAND, playerA, "Zoetic Cavern"); addCard(Zone.LIBRARY, playerA, "Silvercoat Lion"); @@ -80,16 +77,80 @@ public class PossibilityStormTest extends CardTestPlayerBase { execute(); assertPermanentCount(playerA, "Zoetic Cavern", 0); - + boolean zoeticCavernInLibrary = false; - for (Card card: playerA.getLibrary().getCards(currentGame)) { + for (Card card : playerA.getLibrary().getCards(currentGame)) { if (card.getName().equals("Zoetic Cavern")) { zoeticCavernInLibrary = true; } } Assert.assertEquals("Zoetic Cavern has to be in the library", true, zoeticCavernInLibrary); - + assertPermanentCount(playerA, "Silvercoat Lion", 1); } -} \ No newline at end of file + + /* + * Having another Possibility Storm issue(shocking, I know). This time it + * occurred when trying to finish off my opponent that was at 3 life. + * I cast an Izzet Charm choosing draw 2 discard 2 for mode, + * responded to the Possibility Storm trigger with Remand. + * Remand's trigger revealed a Pact of Negation I chose not to cast, then the trigger for Izzet Charm + * resolved apparently revealing a Cryptic Command. It automatically moved + * the spell from exile to my library, apparently because it believed it did + * not have a target(no spells remaining on the stack). I've seen this + * happen with Mana Leaks that get revealed with no targets, but Cryptic + * being modal means it should always be able to be cast. It's worth + * mentioning that I've revealed Cryptics off triggers with other spells on + * the stack and properly been asked if I wish to cast them. Thanks! + */ + @Test + public void TestWithCrypticCommand() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + // Whenever a player casts a spell from his or her hand, that player exiles it, then exiles cards from + // the top of his or her library until he or she exiles a card that shares a card type with it. That + // player may cast that card without paying its mana cost. Then he or she puts all cards exiled with + // Possibility Storm on the bottom of his or her library in a random order. + addCard(Zone.BATTLEFIELD, playerA, "Possibility Storm", 1); + + // Choose one — Counter target noncreature spell unless its controller pays {2}; + // or Izzet Charm deals 2 damage to target creature; + // or draw two cards, then discard two cards. + addCard(Zone.HAND, playerA, "Izzet Charm"); // {U}{R} + // Counter target spell. If that spell is countered this way, put it into its owner's hand instead of into that player's graveyard. + // Draw a card. + addCard(Zone.HAND, playerA, "Remand"); + + // Choose two - + // Counter target spell; + // or return target permanent to its owner's hand; + // or tap all creatures your opponents control; + // or draw a card. + addCard(Zone.LIBRARY, playerA, "Cryptic Command"); + addCard(Zone.LIBRARY, playerA, "Pact of Negation"); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Izzet Charm"); + setModeChoice(playerA, "3"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Remand", "Izzet Charm", "Whenever a player casts"); + setChoice(playerA, "No"); // Don't play Pact of Negotiation + + setChoice(playerA, "Yes"); // Play Cryptic Command + setModeChoice(playerA, "3"); + setModeChoice(playerA, "4"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertExileCount(playerA, 0); + assertGraveyardCount(playerA, "Cryptic Command", 1); + + assertTapped("Silvercoat Lion", true); + assertHandCount(playerA, 1); // from Cryptic Command Draw + + } +} diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index a255e8a0c4..bc6ccd434b 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -872,9 +872,13 @@ public abstract class AbilityImpl implements Ability { @Override public boolean canChooseTarget(Game game) { + int found = 0; for (Mode mode : modes.values()) { if (mode.getTargets().canChoose(sourceId, controllerId, game)) { - return true; + found++; + if (found >= getModes().getMinModes()) { + return true; + } } } return false;