mirror of
https://github.com/correl/mage.git
synced 2024-12-24 03:00:14 +00:00
* Possibility Storm - Fixed a bug that it was not correctly checked if the player was able to cast modal spells.
This commit is contained in:
parent
4aebcd2399
commit
a33ed68c74
4 changed files with 91 additions and 28 deletions
|
@ -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<CardType> cardTypes) {
|
||||
private boolean sharesType(Card card, List<CardType> cardTypes) {
|
||||
for (CardType type : card.getCardType()) {
|
||||
if (cardTypes.contains(type)) {
|
||||
return true;
|
||||
|
|
|
@ -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<UUID> opponents = game.getOpponents(controller.getId());
|
||||
|
@ -131,14 +131,14 @@ class SteamAuguryEffect extends OneShotEffect {
|
|||
}
|
||||
List<Card> 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(", ");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue