* Copy spell - improved support for some cards and abilities (#8074);

This commit is contained in:
Oleg Agafonov 2021-07-31 15:48:57 +04:00
parent 6e0184a38d
commit 530cd627cc
5 changed files with 108 additions and 50 deletions

View file

@ -240,7 +240,6 @@ public class ManaWasSpentToCastTest extends CardTestPlayerBase {
assertPermanentCount(playerA, "Sliver Construct", 1); assertPermanentCount(playerA, "Sliver Construct", 1);
} }
@Ignore // currently fails
@Test @Test
public void testManaDrainCopied() { public void testManaDrainCopied() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 11); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 11);

View file

@ -24,13 +24,15 @@ public class AdventureCardsTest extends CardTestPlayerBase {
* Sorcery Adventure * Sorcery Adventure
* Create a Food token. * Create a Food token.
*/ */
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.HAND, playerA, "Curious Pair"); addCard(Zone.HAND, playerA, "Curious Pair");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
assertHandCount(playerA, 0); assertHandCount(playerA, 0);
assertPermanentCount(playerA, "Food", 1); assertPermanentCount(playerA, "Food", 1);
assertExileCount(playerA, "Curious Pair", 1); assertExileCount(playerA, "Curious Pair", 1);
@ -61,14 +63,17 @@ public class AdventureCardsTest extends CardTestPlayerBase {
@Test @Test
public void testCastCuriousPair() { public void testCastCuriousPair() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.HAND, playerA, "Curious Pair"); addCard(Zone.HAND, playerA, "Curious Pair");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
assertHandCount(playerA, 0); assertHandCount(playerA, 0);
assertPermanentCount(playerA, "Food", 0); assertPermanentCount(playerA, "Food", 0);
assertPermanentCount(playerA, "Curious Pair", 1); assertPermanentCount(playerA, "Curious Pair", 1);
@ -78,16 +83,19 @@ public class AdventureCardsTest extends CardTestPlayerBase {
@Test @Test
public void testCastTreatsToShareAndCuriousPair() { public void testCastTreatsToShareAndCuriousPair() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.HAND, playerA, "Curious Pair"); addCard(Zone.HAND, playerA, "Curious Pair");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
assertHandCount(playerA, 0); assertHandCount(playerA, 0);
assertPermanentCount(playerA, "Food", 1); assertPermanentCount(playerA, "Food", 1);
assertPermanentCount(playerA, "Curious Pair", 1); assertPermanentCount(playerA, "Curious Pair", 1);
@ -103,16 +111,19 @@ public class AdventureCardsTest extends CardTestPlayerBase {
* Whenever you cast a creature spell that has an Adventure, draw a card. * Whenever you cast a creature spell that has an Adventure, draw a card.
* 1/1 * 1/1
*/ */
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerA, "Edgewall Innkeeper"); addCard(Zone.BATTLEFIELD, playerA, "Edgewall Innkeeper");
addCard(Zone.HAND, playerA, "Curious Pair"); addCard(Zone.HAND, playerA, "Curious Pair");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
assertHandCount(playerA, 0); assertHandCount(playerA, 0);
assertPermanentCount(playerA, "Food", 1); assertPermanentCount(playerA, "Food", 1);
assertPermanentCount(playerA, "Curious Pair", 0); assertPermanentCount(playerA, "Curious Pair", 0);
@ -122,14 +133,18 @@ public class AdventureCardsTest extends CardTestPlayerBase {
@Test @Test
public void testCastCuriousPairWithEdgewallInnkeeper() { public void testCastCuriousPairWithEdgewallInnkeeper() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerA, "Edgewall Innkeeper"); addCard(Zone.BATTLEFIELD, playerA, "Edgewall Innkeeper");
addCard(Zone.HAND, playerA, "Curious Pair"); addCard(Zone.HAND, playerA, "Curious Pair");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed();
assertHandCount(playerA, 1); assertHandCount(playerA, 1);
assertPermanentCount(playerA, "Food", 0); assertPermanentCount(playerA, "Food", 0);
assertPermanentCount(playerA, "Curious Pair", 1); assertPermanentCount(playerA, "Curious Pair", 1);
@ -139,17 +154,20 @@ public class AdventureCardsTest extends CardTestPlayerBase {
@Test @Test
public void testCastTreatsToShareAndCuriousPairWithEdgewallInnkeeper() { public void testCastTreatsToShareAndCuriousPairWithEdgewallInnkeeper() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerA, "Edgewall Innkeeper"); addCard(Zone.BATTLEFIELD, playerA, "Edgewall Innkeeper");
addCard(Zone.HAND, playerA, "Curious Pair"); addCard(Zone.HAND, playerA, "Curious Pair");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
assertHandCount(playerA, 1); assertHandCount(playerA, 1);
assertPermanentCount(playerA, "Food", 1); assertPermanentCount(playerA, "Food", 1);
assertPermanentCount(playerA, "Curious Pair", 1); assertPermanentCount(playerA, "Curious Pair", 1);
@ -159,17 +177,17 @@ public class AdventureCardsTest extends CardTestPlayerBase {
@Test @Test
public void testCastCuriousPairWithMysteriousPathlighter() { public void testCastCuriousPairWithMysteriousPathlighter() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
addCard(Zone.BATTLEFIELD, playerA, "Mysterious Pathlighter"); addCard(Zone.BATTLEFIELD, playerA, "Mysterious Pathlighter");
addCard(Zone.HAND, playerA, "Curious Pair"); addCard(Zone.HAND, playerA, "Curious Pair");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
assertHandCount(playerA, 0); assertHandCount(playerA, 0);
assertPermanentCount(playerA, "Food", 0); assertPermanentCount(playerA, "Food", 0);
assertPermanentCount(playerA, "Curious Pair", 1); assertPermanentCount(playerA, "Curious Pair", 1);
@ -186,20 +204,22 @@ public class AdventureCardsTest extends CardTestPlayerBase {
* Target opponent reveals their hand. You choose a nonland card from it. That player discards that card. * Target opponent reveals their hand. You choose a nonland card from it. That player discards that card.
* You may put a card that has an Adventure that player owns from exile into that player's graveyard. * You may put a card that has an Adventure that player owns from exile into that player's graveyard.
*/ */
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.HAND, playerA, "Curious Pair"); addCard(Zone.HAND, playerA, "Curious Pair");
addCard(Zone.HAND, playerA, "Opt"); addCard(Zone.HAND, playerA, "Opt");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); //
addCard(Zone.BATTLEFIELD, playerB, "Swamp"); addCard(Zone.BATTLEFIELD, playerB, "Swamp");
addCard(Zone.BATTLEFIELD, playerB, "Swamp"); addCard(Zone.BATTLEFIELD, playerB, "Swamp");
addCard(Zone.BATTLEFIELD, playerB, "Swamp"); addCard(Zone.BATTLEFIELD, playerB, "Swamp");
addCard(Zone.HAND, playerB, "Memory Theft"); addCard(Zone.HAND, playerB, "Memory Theft");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Memory Theft", playerA);
playerB.addChoice("Opt");
playerB.addChoice("Curious Pair");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Memory Theft", playerA);
setChoice(playerB, "Opt");
setChoice(playerB, "Curious Pair");
setStrictChooseMode(true);
setStopAt(2, PhaseStep.BEGIN_COMBAT); setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
@ -216,12 +236,13 @@ public class AdventureCardsTest extends CardTestPlayerBase {
* Artifact * Artifact
* Whenever you cast an Adventure instant or sorcery spell, copy it. You may choose new targets for the copy. * Whenever you cast an Adventure instant or sorcery spell, copy it. You may choose new targets for the copy.
*/ */
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerA, "Lucky Clover"); addCard(Zone.BATTLEFIELD, playerA, "Lucky Clover");
addCard(Zone.HAND, playerA, "Curious Pair"); addCard(Zone.HAND, playerA, "Curious Pair");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
@ -240,15 +261,16 @@ public class AdventureCardsTest extends CardTestPlayerBase {
* Instant * Instant
* Copy target instant or sorcery spell, except that the copy is red. You may choose new targets for the copy. * Copy target instant or sorcery spell, except that the copy is red. You may choose new targets for the copy.
*/ */
setStrictChooseMode(true); addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
addCard(Zone.HAND, playerA, "Curious Pair"); addCard(Zone.HAND, playerA, "Curious Pair");
addCard(Zone.HAND, playerA, "Fork"); addCard(Zone.HAND, playerA, "Fork");
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fork", "Treats to Share"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fork", "Treats to Share");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
@ -269,15 +291,16 @@ public class AdventureCardsTest extends CardTestPlayerBase {
* Instant * Instant
* Counter target spell. * Counter target spell.
*/ */
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerB, "Island"); addCard(Zone.BATTLEFIELD, playerB, "Island");
addCard(Zone.BATTLEFIELD, playerB, "Island"); addCard(Zone.BATTLEFIELD, playerB, "Island");
addCard(Zone.HAND, playerA, "Curious Pair"); addCard(Zone.HAND, playerA, "Curious Pair");
addCard(Zone.HAND, playerB, "Counterspell"); addCard(Zone.HAND, playerB, "Counterspell");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Counterspell", "Treats to Share"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Counterspell", "Treats to Share");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
@ -300,7 +323,6 @@ public class AdventureCardsTest extends CardTestPlayerBase {
* Target opponent reveals their hand. You choose a nonland card from that player's graveyard or hand and exile it. * Target opponent reveals their hand. You choose a nonland card from that player's graveyard or hand and exile it.
* You may cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast that spell. * You may cast that card for as long as it remains exiled, and you may spend mana as though it were mana of any color to cast that spell.
*/ */
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Island", 1); addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); addCard(Zone.BATTLEFIELD, playerA, "Forest", 6);
@ -313,10 +335,11 @@ public class AdventureCardsTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
assertHandCount(playerA, 0); assertHandCount(playerA, 0);
assertHandCount(playerB, 0); assertHandCount(playerB, 0);
assertPermanentCount(playerB, 0); assertPermanentCount(playerB, 0);
@ -346,7 +369,6 @@ public class AdventureCardsTest extends CardTestPlayerBase {
* Target creature gets +2/+0 until end of turn. * Target creature gets +2/+0 until end of turn.
*/ */
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6);
addCard(Zone.BATTLEFIELD, playerA, "Eager Cadet"); addCard(Zone.BATTLEFIELD, playerA, "Eager Cadet");
addCard(Zone.HAND, playerA, "Rimrock Knight", 2); addCard(Zone.HAND, playerA, "Rimrock Knight", 2);
@ -356,10 +378,11 @@ public class AdventureCardsTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rimrock Knight"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rimrock Knight");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rimrock Knight"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rimrock Knight");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
assertHandCount(playerA, 0); assertHandCount(playerA, 0);
assertPermanentCount(playerA, "Rimrock Knight", 2); assertPermanentCount(playerA, "Rimrock Knight", 2);
assertPermanentCount(playerA, "Eager Cadet", 1); assertPermanentCount(playerA, "Eager Cadet", 1);
@ -370,16 +393,16 @@ public class AdventureCardsTest extends CardTestPlayerBase {
@Test @Test
public void testRimrockKnightPermanentText() { public void testRimrockKnightPermanentText() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
addCard(Zone.HAND, playerA, "Rimrock Knight"); addCard(Zone.HAND, playerA, "Rimrock Knight");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rimrock Knight"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rimrock Knight");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
assertHandCount(playerA, 0); assertHandCount(playerA, 0);
assertPermanentCount(playerA, "Rimrock Knight", 1); assertPermanentCount(playerA, "Rimrock Knight", 1);
assertExileCount(playerA, 0); assertExileCount(playerA, 0);
@ -414,7 +437,6 @@ public class AdventureCardsTest extends CardTestPlayerBase {
* Whenever you cast an instant or sorcery spell from your library, copy it. You may choose new targets for the copy. * Whenever you cast an instant or sorcery spell from your library, copy it. You may choose new targets for the copy.
* 2/4 * 2/4
*/ */
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Melek, Izzet Paragon"); addCard(Zone.BATTLEFIELD, playerA, "Melek, Izzet Paragon");
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
removeAllCardsFromLibrary(playerA); removeAllCardsFromLibrary(playerA);
@ -422,10 +444,11 @@ public class AdventureCardsTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
assertHandCount(playerA, 0); assertHandCount(playerA, 0);
assertPermanentCount(playerA, 4); assertPermanentCount(playerA, 4);
assertPermanentCount(playerA, "Food", 2); assertPermanentCount(playerA, "Food", 2);
@ -465,7 +488,6 @@ public class AdventureCardsTest extends CardTestPlayerBase {
* You may cast the top card of your library if it's a creature card. * You may cast the top card of your library if it's a creature card.
* 7/7 * 7/7
*/ */
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Garruk's Horde"); addCard(Zone.BATTLEFIELD, playerA, "Garruk's Horde");
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
removeAllCardsFromLibrary(playerA); removeAllCardsFromLibrary(playerA);
@ -473,10 +495,11 @@ public class AdventureCardsTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curious Pair");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
assertHandCount(playerA, 0); assertHandCount(playerA, 0);
assertPermanentCount(playerA, "Food", 0); assertPermanentCount(playerA, "Food", 0);
assertPermanentCount(playerA, "Curious Pair", 1); assertPermanentCount(playerA, "Curious Pair", 1);
@ -516,7 +539,6 @@ public class AdventureCardsTest extends CardTestPlayerBase {
* 7: You get an emblem with "Instant and sorcery cards in your graveyard have retrace." * 7: You get an emblem with "Instant and sorcery cards in your graveyard have retrace."
* Loyalty: 3 * Loyalty: 3
*/ */
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.BATTLEFIELD, playerA, "Wrenn and Six"); addCard(Zone.BATTLEFIELD, playerA, "Wrenn and Six");
addCard(Zone.GRAVEYARD, playerA, "Curious Pair"); addCard(Zone.GRAVEYARD, playerA, "Curious Pair");
@ -531,10 +553,11 @@ public class AdventureCardsTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Treats to Share");
setChoice(playerA, "Forest"); setChoice(playerA, "Forest");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
assertHandCount(playerA, 0); assertHandCount(playerA, 0);
assertPermanentCount(playerA, "Food", 1); assertPermanentCount(playerA, "Food", 1);
assertPermanentCount(playerA, "Curious Pair", 0); assertPermanentCount(playerA, "Curious Pair", 0);
@ -555,7 +578,6 @@ public class AdventureCardsTest extends CardTestPlayerBase {
* 3: Return up to one target artifact, creature, or enchantment to its owner's hand. Draw a card. * 3: Return up to one target artifact, creature, or enchantment to its owner's hand. Draw a card.
* Loyalty: 4 * Loyalty: 4
*/ */
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Teferi, Time Raveler"); addCard(Zone.BATTLEFIELD, playerA, "Teferi, Time Raveler");
addCard(Zone.BATTLEFIELD, playerA, "Forest"); addCard(Zone.BATTLEFIELD, playerA, "Forest");
addCard(Zone.HAND, playerA, "Curious Pair"); addCard(Zone.HAND, playerA, "Curious Pair");
@ -565,10 +587,11 @@ public class AdventureCardsTest extends CardTestPlayerBase {
// showAvailableAbilities("abils", 1, PhaseStep.BEGIN_COMBAT, playerA); // showAvailableAbilities("abils", 1, PhaseStep.BEGIN_COMBAT, playerA);
castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Treats to Share"); castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Treats to Share");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute(); execute();
assertAllCommandsUsed(); assertAllCommandsUsed();
assertHandCount(playerA, 0); assertHandCount(playerA, 0);
assertPermanentCount(playerA, 3); assertPermanentCount(playerA, 3);
assertPermanentCount(playerA, "Food", 1); assertPermanentCount(playerA, "Food", 1);

View file

@ -65,7 +65,6 @@ import mage.util.MessageToClient;
import mage.util.RandomUtil; import mage.util.RandomUtil;
import mage.util.functions.CopyApplier; import mage.util.functions.CopyApplier;
import mage.watchers.Watcher; import mage.watchers.Watcher;
import mage.watchers.Watchers;
import mage.watchers.common.*; import mage.watchers.common.*;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
@ -2051,6 +2050,7 @@ public abstract class GameImpl implements Game, Serializable {
// to exist the next time state-based actions are checked. // to exist the next time state-based actions are checked.
// //
// Copied cards can be stored in GameState.copiedCards or in game state value (until LKI rework) // Copied cards can be stored in GameState.copiedCards or in game state value (until LKI rework)
// Copied cards list contains all parts of split/adventure/mdfc
Set<Card> allCopiedCards = new HashSet<>(); Set<Card> allCopiedCards = new HashSet<>();
allCopiedCards.addAll(this.getState().getCopiedCards()); allCopiedCards.addAll(this.getState().getCopiedCards());
Map<String, Object> stateSavedCopiedCards = this.getState().getValues(GameState.COPIED_CARD_KEY); Map<String, Object> stateSavedCopiedCards = this.getState().getValues(GameState.COPIED_CARD_KEY);
@ -2060,6 +2060,7 @@ public abstract class GameImpl implements Game, Serializable {
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toList()) .collect(Collectors.toList())
); );
Set<Card> copiedCardsToRemove = new HashSet<>();
for (Card copiedCard : allCopiedCards) { for (Card copiedCard : allCopiedCards) {
// 1. Zone must be checked from main card only cause mdf parts can have different zones // 1. Zone must be checked from main card only cause mdf parts can have different zones
// (one side on battlefield, another side on outside) // (one side on battlefield, another side on outside)
@ -2070,13 +2071,25 @@ public abstract class GameImpl implements Game, Serializable {
Zone zone = state.getZone(copiedCard.getMainCard().getId()); Zone zone = state.getZone(copiedCard.getMainCard().getId());
// TODO: remember LKI of copied cards here after LKI rework // TODO: remember LKI of copied cards here after LKI rework
switch (zone) { switch (zone) {
case BATTLEFIELD: case OUTSIDE:
case BATTLEFIELD: {
// keep in battlefield
// keep in outside (it's a final zone for all copied cards)
continue; continue;
case STACK: }
if (getStack().getStackObject(copiedCard.getId()) != null) {
case STACK: {
// copied cards aren't moves and keeps in Stack zone after resolve,
// so it must be moved manually as SBA (see Outside zone change at the end)
MageObject object = getStack().getStackObject(copiedCard.getId());
if (object != null) {
// keep in stack until resolve
continue; continue;
} }
case GRAVEYARD: break;
}
case GRAVEYARD: {
for (Player player : getPlayers().values()) { for (Player player : getPlayers().values()) {
if (player.getGraveyard().contains(copiedCard.getId())) { if (player.getGraveyard().contains(copiedCard.getId())) {
player.getGraveyard().remove(copiedCard); player.getGraveyard().remove(copiedCard);
@ -2084,7 +2097,9 @@ public abstract class GameImpl implements Game, Serializable {
} }
} }
break; break;
case HAND: }
case HAND: {
for (Player player : getPlayers().values()) { for (Player player : getPlayers().values()) {
if (player.getHand().contains(copiedCard.getId())) { if (player.getHand().contains(copiedCard.getId())) {
player.getHand().remove(copiedCard); player.getHand().remove(copiedCard);
@ -2092,7 +2107,9 @@ public abstract class GameImpl implements Game, Serializable {
} }
} }
break; break;
case LIBRARY: }
case LIBRARY: {
for (Player player : getPlayers().values()) { for (Player player : getPlayers().values()) {
if (player.getLibrary().getCard(copiedCard.getId(), this) != null) { if (player.getLibrary().getCard(copiedCard.getId(), this) != null) {
player.getLibrary().remove(copiedCard.getId(), this); player.getLibrary().remove(copiedCard.getId(), this);
@ -2100,15 +2117,30 @@ public abstract class GameImpl implements Game, Serializable {
} }
} }
break; break;
case EXILED: }
case EXILED: {
getExile().removeCard(copiedCard, this); getExile().removeCard(copiedCard, this);
break; break;
}
case COMMAND:
default: {
break;
}
} }
// remove copied card info // copied card can be removed to Outside
this.getState().getCopiedCards().remove(copiedCard); copiedCardsToRemove.add(copiedCard);
this.getState().removeValue(GameState.COPIED_CARD_KEY + copiedCard.getId().toString());
} }
// real remove
copiedCardsToRemove.forEach(card -> {
card.setZone(Zone.OUTSIDE, this);
this.getState().getCopiedCards().remove(card);
// must keep card in game state as LKI alternative until LKI rework, so don't remove from it
// TODO: change after LKI rework
//this.getState().removeValue(GameState.COPIED_CARD_KEY + copiedCard.getId().toString());
});
List<Permanent> legendary = new ArrayList<>(); List<Permanent> legendary = new ArrayList<>();
List<Permanent> worldEnchantment = new ArrayList<>(); List<Permanent> worldEnchantment = new ArrayList<>();

View file

@ -233,7 +233,10 @@ public class Spell extends StackObjectImpl implements Card {
} }
} }
if (game.getState().getZone(card.getMainCard().getId()) == Zone.STACK) { if (game.getState().getZone(card.getMainCard().getId()) == Zone.STACK) {
if (!isCopy()) { if (isCopy()) {
// copied spell, only remove from stack
game.getStack().remove(this, game);
} else {
controller.moveCards(card, Zone.GRAVEYARD, ability, game); controller.moveCards(card, Zone.GRAVEYARD, ability, game);
} }
} }
@ -438,7 +441,7 @@ public class Spell extends StackObjectImpl implements Card {
} }
} }
} else { } else {
// Copied spell, only remove from stack // copied spell, only remove from stack
game.getStack().remove(this, game); game.getStack().remove(this, game);
} }
} }
@ -847,6 +850,7 @@ public class Spell extends StackObjectImpl implements Card {
@Override @Override
public boolean moveToExile(UUID exileId, String name, Ability source, Game game, List<UUID> appliedEffects) { public boolean moveToExile(UUID exileId, String name, Ability source, Game game, List<UUID> appliedEffects) {
if (this.isCopy()) { if (this.isCopy()) {
// copied spell, only remove from stack
game.getStack().remove(this, game); game.getStack().remove(this, game);
return true; return true;
} }

View file

@ -4472,7 +4472,7 @@ public abstract class PlayerImpl implements Player, Serializable {
} else if (card instanceof Spell) { } else if (card instanceof Spell) {
final Spell spell = (Spell) card; final Spell spell = (Spell) card;
if (spell.isCopy()) { if (spell.isCopy()) {
// Copied spell, only remove from stack // copied spell, only remove from stack
game.getStack().remove(spell, game); game.getStack().remove(spell, game);
} }
} }