* 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).

This commit is contained in:
LevelX2 2020-08-20 16:48:13 +02:00
parent 89639f5e9b
commit 783239e79e
3 changed files with 85 additions and 10 deletions

View file

@ -134,21 +134,20 @@ class AminatousAuguryCastFromExileEffect extends AsThoughEffectImpl {
} }
@Override @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); Player player = game.getPlayer(affectedControllerId);
EnumSet<CardType> usedCardTypes = EnumSet.noneOf(CardType.class); EnumSet<CardType> usedCardTypes = EnumSet.noneOf(CardType.class);
if (game.getState().getValue(source.getSourceId().toString() + "cardTypes") != null) { if (game.getState().getValue(source.getSourceId().toString() + "cardTypes") != null) {
usedCardTypes = (EnumSet<CardType>) game.getState().getValue(source.getSourceId().toString() + "cardTypes"); usedCardTypes = (EnumSet<CardType>) game.getState().getValue(source.getSourceId().toString() + "cardTypes");
} }
//TODO add code for adding additional costs to the card
if (player != null if (player != null
&& sourceId != null && objectId != null
&& sourceId.equals(getTargetPointer().getFirst(game, source)) && objectId.equals(getTargetPointer().getFirst(game, source))
&& affectedControllerId.equals(source.getControllerId())) { && affectedControllerId.equals(source.getControllerId())) {
Card card = game.getCard(sourceId); Card card = game.getCard(objectId);
if (card != null if (card != null
&& game.getState().getZone(sourceId) == Zone.EXILED) { && game.getState().getZone(objectId) == Zone.EXILED) {
EnumSet<CardType> unusedCardTypes = EnumSet.noneOf(CardType.class); EnumSet<CardType> unusedCardTypes = EnumSet.noneOf(CardType.class);
for (CardType cardT : card.getCardType()) { for (CardType cardT : card.getCardType()) {
if (!usedCardTypes.contains(cardT)) { if (!usedCardTypes.contains(cardT)) {
@ -175,10 +174,9 @@ class AminatousAuguryCastFromExileEffect extends AsThoughEffectImpl {
usedCardTypes.add(CardType.fromString(choice.getChoice())); usedCardTypes.add(CardType.fromString(choice.getChoice()));
} }
usedCardTypes.addAll(unusedCardTypes); 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)
game.getState().setValue(source.getSourceId().toString() + "cardTypes", usedCardTypes); game.getState().setValue(source.getSourceId().toString() + "cardTypes", usedCardTypes);
} }
player.setCastSourceIdWithAlternateMana(objectId, null, card.getSpellAbility().getCosts());
return true; return true;
} }
} }

View file

@ -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);
}
}

View file

@ -163,6 +163,8 @@ public class GameState implements Serializable, Copyable<GameState> {
for (Map.Entry<String, Object> entry : state.values.entrySet()) { for (Map.Entry<String, Object> entry : state.values.entrySet()) {
if (entry.getValue() instanceof HashSet) { if (entry.getValue() instanceof HashSet) {
this.values.put(entry.getKey(), ((HashSet) entry.getValue()).clone()); 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 { } else {
this.values.put(entry.getKey(), entry.getValue()); this.values.put(entry.getKey(), entry.getValue());
} }
@ -986,7 +988,7 @@ public class GameState implements Serializable, Copyable<GameState> {
* object may be changed by AI simulation or rollbacks, because the Value * object may be changed by AI simulation or rollbacks, because the Value
* objects are not copied as the state class is copied. Mutable supported: * objects are not copied as the state class is copied. Mutable supported:
* HashSet with immutable entries (e.g. HashSet< UUID > or HashSet< String * HashSet with immutable entries (e.g. HashSet< UUID > or HashSet< String
* >) * > and EnumSets)
* *
* @param valueId * @param valueId
* @param value * @param value