mirror of
https://github.com/magefree/mage.git
synced 2025-12-23 20:11:59 -08:00
Rework drawing cards and associated replacement effects; implement [WHO] River Song (#12700)
* remove unused scoring system code * add test for Alms Collector replacement effect * flatten draw cards into single method in PlayerImpl * remove outdated MageAction framework * clarify game event for drawing two or more cards * clarify methods for getting cards from library * implement [WHO] River Song * fix error * adjust library methods * add lots of test cases for draw replacement effects * fix #12616 * track cards drawn this way through multi draw replacement as well * add test for River Song * remove redundant comment
This commit is contained in:
parent
34ae226130
commit
9fcbfdeac6
22 changed files with 645 additions and 345 deletions
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package org.mage.test.cards.replacement;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
|
|
@ -8,11 +7,343 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
* @author LevelX2, xenohedron
|
||||
*/
|
||||
public class DrawEffectsTest extends CardTestPlayerBase {
|
||||
|
||||
private static final String drawOne = "Radical Idea"; // 1U instant
|
||||
private static final String drawTwo = "Quick Study"; // 2U instant
|
||||
private static final String drawThree = "Jace's Ingenuity"; // 3UU instant
|
||||
private static final String excavation = "Ancient Excavation"; // 2UB instant
|
||||
// Draw cards equal to the number of cards in your hand, then discard a card for each card drawn this way.
|
||||
|
||||
private static final String reflection = "Thought Reflection";
|
||||
// If you would draw a card, draw two cards instead.
|
||||
private static final String scrivener = "Blood Scrivener";
|
||||
// If you would draw a card while you have no cards in hand, instead you draw two cards and you lose 1 life.
|
||||
private static final String notionThief = "Notion Thief";
|
||||
// If an opponent would draw a card except the first one they draw in each of their draw steps,
|
||||
// instead that player skips that draw and you draw a card.
|
||||
private static final String asmodeus = "Asmodeus the Archfiend";
|
||||
// If you would draw a card, exile the top card of your library face down instead.
|
||||
private static final String almsCollector = "Alms Collector";
|
||||
// If an opponent would draw two or more cards, instead you and that player each draw a card.
|
||||
|
||||
private void testBase(String cardDraw, int handPlayerA, int handPlayerB) {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Island", 5);
|
||||
addCard(Zone.HAND, playerA, cardDraw);
|
||||
addCard(Zone.HAND, playerB, cardDraw);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cardDraw);
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, cardDraw);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, cardDraw, 1);
|
||||
assertGraveyardCount(playerB, cardDraw, 1);
|
||||
assertHandCount(playerA, handPlayerA);
|
||||
assertHandCount(playerB, handPlayerB);
|
||||
}
|
||||
|
||||
private void testSingle(String cardDraw, int handPlayerA, int handPlayerB, String... choices) {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
|
||||
addCard(Zone.HAND, playerA, cardDraw);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cardDraw);
|
||||
for (String choice : choices) {
|
||||
setChoice(playerA, choice);
|
||||
}
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, cardDraw, 1);
|
||||
assertHandCount(playerA, handPlayerA);
|
||||
assertHandCount(playerB, handPlayerB);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReflection1() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testBase(drawOne, 2, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReflection2() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testBase(drawTwo, 4, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReflection3() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testBase(drawThree, 6, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScrivener1() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, scrivener);
|
||||
testBase(drawOne, 2, 1);
|
||||
assertLife(playerA, 19);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScrivener2() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, scrivener);
|
||||
testBase(drawTwo, 3, 2);
|
||||
assertLife(playerA, 19);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScrivener3() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, scrivener);
|
||||
testBase(drawThree, 4, 3);
|
||||
assertLife(playerA, 19);
|
||||
}
|
||||
|
||||
/*
|
||||
* Each additional Blood Scrivener you control will effectively add one card and 1 life lost.
|
||||
* Say you control two Blood Scriveners and would draw a card while you have no cards in hand.
|
||||
* The effect of one Blood Scrivener will replace the event “draw a card” with “draw two cards and lose 1 life.”
|
||||
* The effect of the other Blood Scrivener will replace the drawing of the first of those two cards with
|
||||
* “draw two cards and lose 1 life.” You’ll draw two cards and lose 1 life,
|
||||
* then draw another card and lose another 1 life. (2013-04-15)
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testDoubleScrivener1() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, scrivener, 2);
|
||||
testSingle(drawOne, 3, 0, scrivener);
|
||||
assertLife(playerA, 18);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleScrivener2() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, scrivener, 2);
|
||||
testSingle(drawTwo, 4, 0, scrivener);
|
||||
assertLife(playerA, 18);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleScrivener3() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, scrivener, 2);
|
||||
testSingle(drawThree, 5, 0, scrivener);
|
||||
assertLife(playerA, 18);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsmodeus1() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, asmodeus);
|
||||
testBase(drawOne, 0, 1);
|
||||
assertExileCount(playerA, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsmodeus2() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, asmodeus);
|
||||
testBase(drawTwo, 0, 2);
|
||||
assertExileCount(playerA, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsmodeus3() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, asmodeus);
|
||||
testBase(drawThree, 0, 3);
|
||||
assertExileCount(playerA, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReflectionAsmodeus1() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, asmodeus);
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testSingle(drawOne, 0, 0, reflection);
|
||||
assertExileCount(playerA, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReflectionAsmodeus2() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, asmodeus);
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testSingle(drawTwo, 0, 0, reflection, reflection);
|
||||
assertExileCount(playerA, 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReflectionAsmodeus3() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, asmodeus);
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testSingle(drawThree, 0, 0, reflection, reflection, reflection);
|
||||
assertExileCount(playerA, 6);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsmodeusReflection1() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, asmodeus);
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testSingle(drawOne, 0, 0, asmodeus);
|
||||
assertExileCount(playerA, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsmodeusReflection2() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, asmodeus);
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testSingle(drawTwo, 0, 0, asmodeus, asmodeus);
|
||||
assertExileCount(playerA, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsmodeusReflection3() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, asmodeus);
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testSingle(drawThree, 0, 0, asmodeus, asmodeus, asmodeus);
|
||||
assertExileCount(playerA, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlmsCollectorReflection1() {
|
||||
addCard(Zone.BATTLEFIELD, playerB, almsCollector);
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testSingle(drawOne, 1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlmsCollectorReflection2() {
|
||||
addCard(Zone.BATTLEFIELD, playerB, almsCollector);
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testSingle(drawTwo, 2, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlmsCollectorReflection3() {
|
||||
addCard(Zone.BATTLEFIELD, playerB, almsCollector);
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testSingle(drawThree, 2, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotionThiefReflection1() {
|
||||
addCard(Zone.BATTLEFIELD, playerB, notionThief);
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testSingle(drawOne, 0, 1, notionThief);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotionThiefReflection2() {
|
||||
addCard(Zone.BATTLEFIELD, playerB, notionThief);
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testSingle(drawTwo, 0, 2, notionThief, notionThief);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotionThiefReflection3() {
|
||||
addCard(Zone.BATTLEFIELD, playerB, notionThief);
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testSingle(drawThree, 0, 3, notionThief, notionThief, notionThief);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReflectionNotionThief1() {
|
||||
addCard(Zone.BATTLEFIELD, playerB, notionThief);
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
testSingle(drawOne, 0, 2, reflection);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAncientExcavation() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 4);
|
||||
addCard(Zone.HAND, playerA, excavation);
|
||||
addCard(Zone.HAND, playerA, "Shock");
|
||||
skipInitShuffling();
|
||||
addCard(Zone.LIBRARY, playerA, "Healing Salve");
|
||||
addCard(Zone.LIBRARY, playerA, "Giant Growth");
|
||||
addCard(Zone.BATTLEFIELD, playerA, reflection);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, excavation);
|
||||
// 1 card in hand, thought reflection -> draw 2, then discard two
|
||||
setChoice(playerA, "Shock"); // to discard
|
||||
setChoice(playerA, "Healing Salve"); // to discard
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, excavation, 1);
|
||||
assertGraveyardCount(playerA, "Shock", 1);
|
||||
assertGraveyardCount(playerA, "Healing Salve", 1);
|
||||
assertHandCount(playerA, "Giant Growth", 1);
|
||||
assertHandCount(playerA, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAncientExcavationNotionThief() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 4);
|
||||
addCard(Zone.HAND, playerA, excavation);
|
||||
addCard(Zone.HAND, playerA, "Shock");
|
||||
addCard(Zone.HAND, playerA, "Dark Ritual");
|
||||
addCard(Zone.HAND, playerA, "Ornithopter");
|
||||
skipInitShuffling();
|
||||
addCard(Zone.LIBRARY, playerA, "Healing Salve");
|
||||
addCard(Zone.LIBRARY, playerA, "Giant Growth");
|
||||
addCard(Zone.BATTLEFIELD, playerB, notionThief);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, excavation);
|
||||
// 3 cards in hand, notion thief -> instead opponent draws three
|
||||
// but cards were still drawn this way, so discard all three (no choice to make)
|
||||
// if this turns out to be incorrect, modify the test accordingly
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, excavation, 1);
|
||||
assertGraveyardCount(playerA, "Shock", 1);
|
||||
assertGraveyardCount(playerA, "Dark Ritual", 1);
|
||||
assertGraveyardCount(playerA, "Ornithopter", 1);
|
||||
assertHandCount(playerA, 0);
|
||||
assertLibraryCount(playerA, "Healing Salve", 1);
|
||||
assertLibraryCount(playerA, "Giant Growth", 1);
|
||||
assertHandCount(playerB, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAncientExcavationAlmsCollector() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 4);
|
||||
addCard(Zone.HAND, playerA, excavation);
|
||||
addCard(Zone.HAND, playerA, "Shock");
|
||||
addCard(Zone.HAND, playerA, "Dark Ritual");
|
||||
addCard(Zone.HAND, playerA, "Ornithopter");
|
||||
skipInitShuffling();
|
||||
addCard(Zone.LIBRARY, playerA, "Healing Salve");
|
||||
addCard(Zone.LIBRARY, playerA, "Giant Growth");
|
||||
addCard(Zone.BATTLEFIELD, playerB, almsCollector);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, excavation);
|
||||
// 3 cards in hand, alms collector -> instead each player draws one
|
||||
// interpret as two cards were drawn this way in total
|
||||
// if this turns out to be incorrect, modify the test accordingly
|
||||
setChoice(playerA, "Shock"); // to discard
|
||||
setChoice(playerA, "Giant Growth"); // to discard
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, excavation, 1);
|
||||
assertGraveyardCount(playerA, "Shock", 1);
|
||||
assertGraveyardCount(playerA, "Giant Growth", 1);
|
||||
assertHandCount(playerA, "Dark Ritual", 1);
|
||||
assertHandCount(playerA, "Ornithopter", 1);
|
||||
assertHandCount(playerA, 2);
|
||||
assertLibraryCount(playerA, "Healing Salve", 1);
|
||||
assertHandCount(playerB, 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The effects of multiple Thought Reflections are cumulative. For example,
|
||||
* if you have three Thought Reflections on the battlefield, you'll draw
|
||||
|
|
@ -104,4 +435,49 @@ public class DrawEffectsTest extends CardTestPlayerBase {
|
|||
assertPermanentCount(playerA, "Bear Token", 1);
|
||||
assertHandCount(playerA, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlmsCollector() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Alms Collector");
|
||||
// If an opponent would draw two or more cards, instead you and that player each draw a card.
|
||||
|
||||
// Draw two cards.
|
||||
addCard(Zone.HAND, playerA, "Counsel of the Soratami", 1); // Sorcery {2}{U}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Counsel of the Soratami");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Counsel of the Soratami", 1);
|
||||
assertHandCount(playerA, 1);
|
||||
assertHandCount(playerB, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRiverSong() {
|
||||
skipInitShuffling();
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
addCard(Zone.LIBRARY, playerA, "Healing Salve"); // bottom
|
||||
addCard(Zone.LIBRARY, playerA, "Giant Growth");
|
||||
addCard(Zone.LIBRARY, playerA, "Shock"); // top
|
||||
addCard(Zone.BATTLEFIELD, playerA, "River Song");
|
||||
// You draw cards from the bottom of your library rather than the top.
|
||||
addCard(Zone.HAND, playerA, drawOne, 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, drawOne);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, drawOne, 1);
|
||||
assertHandCount(playerA, 1);
|
||||
assertHandCount(playerA, "Healing Salve", 1);
|
||||
assertLibraryCount(playerA, "Shock", 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue