From 783239e79e57bfc8b9079f69c900bbad57e4018e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 20 Aug 2020 16:48:13 +0200 Subject: [PATCH] * Aminatou's Augury - Fixed that spells in exile where not shown as castable and that AI usage prevented casting of multiple spells from exile (fixes #6987). --- .../src/mage/cards/a/AminatousAugury.java | 16 ++-- .../cards/single/c18/AminatousAuguryTest.java | 75 +++++++++++++++++++ Mage/src/main/java/mage/game/GameState.java | 4 +- 3 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/c18/AminatousAuguryTest.java diff --git a/Mage.Sets/src/mage/cards/a/AminatousAugury.java b/Mage.Sets/src/mage/cards/a/AminatousAugury.java index 2c46320553..de8cb0942e 100644 --- a/Mage.Sets/src/mage/cards/a/AminatousAugury.java +++ b/Mage.Sets/src/mage/cards/a/AminatousAugury.java @@ -134,21 +134,20 @@ class AminatousAuguryCastFromExileEffect extends AsThoughEffectImpl { } @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { Player player = game.getPlayer(affectedControllerId); EnumSet usedCardTypes = EnumSet.noneOf(CardType.class); if (game.getState().getValue(source.getSourceId().toString() + "cardTypes") != null) { usedCardTypes = (EnumSet) game.getState().getValue(source.getSourceId().toString() + "cardTypes"); } - //TODO add code for adding additional costs to the card if (player != null - && sourceId != null - && sourceId.equals(getTargetPointer().getFirst(game, source)) + && objectId != null + && objectId.equals(getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId())) { - Card card = game.getCard(sourceId); + Card card = game.getCard(objectId); if (card != null - && game.getState().getZone(sourceId) == Zone.EXILED) { + && game.getState().getZone(objectId) == Zone.EXILED) { EnumSet unusedCardTypes = EnumSet.noneOf(CardType.class); for (CardType cardT : card.getCardType()) { if (!usedCardTypes.contains(cardT)) { @@ -174,11 +173,10 @@ class AminatousAuguryCastFromExileEffect extends AsThoughEffectImpl { } usedCardTypes.add(CardType.fromString(choice.getChoice())); } - usedCardTypes.addAll(unusedCardTypes); - 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) + usedCardTypes.addAll(unusedCardTypes); game.getState().setValue(source.getSourceId().toString() + "cardTypes", usedCardTypes); } + player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts()); return true; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/c18/AminatousAuguryTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/c18/AminatousAuguryTest.java new file mode 100644 index 0000000000..8dff191188 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/c18/AminatousAuguryTest.java @@ -0,0 +1,75 @@ + +package org.mage.test.cards.single.c18; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class AminatousAuguryTest extends CardTestPlayerBase { + + @Test + public void testCastMultiple() { + setStrictChooseMode(true); + + + addCard(Zone.LIBRARY, playerA, "Pillarfield Ox"); // Creature (2/4) + // As an additional cost to cast this spell, discard a card. + // Draw two cards. + addCard(Zone.LIBRARY, playerA, "Tormenting Voice"); // Sorcery + // {1}: Adarkar Sentinel gets +0/+1 until end of turn. + addCard(Zone.LIBRARY, playerA, "Adarkar Sentinel"); // Artifact Creature {5} (3/3) + addCard(Zone.LIBRARY, playerA, "Storm Crow"); + // You have hexproof. (You can't be the target of spells or abilities your opponents control.) + addCard(Zone.LIBRARY, playerA, "Aegis of the Gods"); // Enchantment Creature {1}{W} (2/1) + addCard(Zone.LIBRARY, playerA, "Lightning Bolt"); // Instant + addCard(Zone.LIBRARY, playerA, "Badlands"); + skipInitShuffling(); + // Exile the top eight cards of your library. You may put a land card from among them onto the battlefield. + // Until end of turn, for each nonland card type, you may cast a card of that type from among the exiled cards + // without paying its mana cost. + addCard(Zone.HAND, playerA, "Aminatou's Augury"); // SORCERY {6}{U}{U} + addCard(Zone.HAND, playerA, "Mountain"); + addCard(Zone.HAND, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerA, "Island", 8); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Aminatou's Augury"); + setChoice(playerA, "Yes"); // Put a land from among the exiled cards into play? + setChoice(playerA, "Badlands"); // Select a land card + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Adarkar Sentinel"); + setChoice(playerA, "Artifact"); // Which card type do you want to consume? + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Aegis of the Gods"); + setChoice(playerA, "Enchantment"); // Which card type do you want to consume? + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Storm Crow"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tormenting Voice"); + setChoice(playerA, "Silvercoat Lion"); // Select a card (discard cost) + + checkPlayableAbility("Cannot cast second creature from exile", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Pillarfield Ox", Boolean.FALSE); // Type Creature type is already consumed + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Aminatou's Augury", 1); + assertPermanentCount(playerA, "Mountain", 1); + assertPermanentCount(playerA, "Badlands", 1); + assertPermanentCount(playerA, "Adarkar Sentinel", 1); + assertPermanentCount(playerA, "Aegis of the Gods", 1); + assertPermanentCount(playerA, "Storm Crow", 1); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + + assertLife(playerA, 20); + assertLife(playerB, 17); + + assertHandCount(playerA, 2); + assertGraveyardCount(playerA, "Silvercoat Lion",1); + assertExileCount(playerA, 2); + } + +} diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index 7ab0ae2076..0496002adc 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -163,6 +163,8 @@ public class GameState implements Serializable, Copyable { for (Map.Entry entry : state.values.entrySet()) { if (entry.getValue() instanceof HashSet) { this.values.put(entry.getKey(), ((HashSet) entry.getValue()).clone()); + } else if (entry.getValue() instanceof EnumSet) { + this.values.put(entry.getKey(), ((EnumSet) entry.getValue()).clone()); } else { this.values.put(entry.getKey(), entry.getValue()); } @@ -986,7 +988,7 @@ public class GameState implements Serializable, Copyable { * object may be changed by AI simulation or rollbacks, because the Value * objects are not copied as the state class is copied. Mutable supported: * HashSet with immutable entries (e.g. HashSet< UUID > or HashSet< String - * >) + * > and EnumSets) * * @param valueId * @param value