mirror of
https://github.com/magefree/mage.git
synced 2025-12-23 12:02:01 -08:00
other: reworked target selection: (#13638)
- WIP: AI and multi targets, human and X=0 use cases, human and impossible targets use cases;
- improved stability and shared logic (related to #13606, #11134, #11666, continue from a53eb66b58, close #13617, close #13613);
- improved test logs and debug info to show more target info on errors;
- improved test framework to support multiple addTarget calls;
- improved test framework to find bad commands order for targets (related to #11666);
- fixed game freezes on auto-choice usages with disconnected or under control players (related to #11285);
- gui, game: fixed that player doesn't mark avatar as selected/green in "up to" targeting;
- gui, game: fixed small font in some popup messages on big screens (related to #969);
- gui, game: added min targets info for target selection dialog;
- for devs: added new cheat option to call and test any game dialog (define own dialogs, targets, etc in HumanDialogsTester);
- for devs: now tests require complete an any or up to target selection by addTarget + TestPlayer.TARGET_SKIP or setChoice + TestPlayer.CHOICE_SKIP (if not all max/possible targets used);
- for devs: added detail targets info for activate/trigger/cast, can be useful to debug unit tests, auto-choose or AI (see DebugUtil.GAME_SHOW_CHOOSE_TARGET_LOGS)
This commit is contained in:
parent
80d62727e1
commit
133e4fe425
84 changed files with 2737 additions and 743 deletions
|
|
@ -33,9 +33,10 @@ public class LightningStormTest extends CardTestPlayerBase {
|
|||
|
||||
// B discard and re-target
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Discard");
|
||||
setChoice(playerB, "Mountain"); // discard cost
|
||||
setChoice(playerB, true); // change target
|
||||
addTarget(playerB, playerA); // new target
|
||||
setChoice(playerB, "Mountain"); // discard cost
|
||||
|
||||
|
||||
// A discard and re-target
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Discard");
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.abilities.enters;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
|
|
@ -61,18 +62,19 @@ public class ValakutTheMoltenPinnacleTest extends CardTestPlayerBase {
|
|||
@Test
|
||||
public void sixEnterWithScapeshiftDamageToPlayerB() {
|
||||
|
||||
addCard(Zone.LIBRARY, playerA, "Mountain", 6);
|
||||
addCard(Zone.LIBRARY, playerA, "Mountain", 10);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Valakut, the Molten Pinnacle");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 6);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 10);
|
||||
addCard(Zone.HAND, playerA, "Scapeshift");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scapeshift");
|
||||
setChoice(playerA, "Forest^Forest^Forest^Forest^Forest^Forest");
|
||||
addTarget(playerA, "Mountain^Mountain^Mountain^Mountain^Mountain^Mountain");
|
||||
setChoice(playerA, "Whenever", 5); // order triggers
|
||||
setChoice(playerA, true, 6); // yes to deal damage
|
||||
setChoice(playerA, "Forest^Forest^Forest^Forest^Forest^Forest"); // to sac
|
||||
addTarget(playerA, "Mountain^Mountain^Mountain^Mountain^Mountain^Mountain"); // to search
|
||||
setChoice(playerA, "Whenever a Mountain", 6 - 1); // x6 triggers from valakut
|
||||
addTarget(playerA, playerB, 6); // to deal damage
|
||||
setChoice(playerA, true, 6); // yes to deal damage
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
|
|
@ -81,7 +83,7 @@ public class ValakutTheMoltenPinnacleTest extends CardTestPlayerBase {
|
|||
assertPermanentCount(playerA, "Mountain", 6);
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 2); // 6 * 3 damage = 18
|
||||
assertLife(playerB, 20 - 18); // 6 * 3 damage = 18
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.abilities.keywords;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||
|
||||
/**
|
||||
|
|
@ -26,9 +27,10 @@ public class ImproviseTest extends CardTestPlayerBaseWithAIHelps {
|
|||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 4);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bastion Inventor");
|
||||
setChoice(playerA, "Blue", 4); // pay 1-4
|
||||
// improvise pay by one card
|
||||
// improvise pay by one card (after 2025 addTarget improve - Improvise can be pay by multiple addTarget)
|
||||
setChoice(playerA, "Improvise");
|
||||
addTarget(playerA, "Alpha Myr"); // pay 5 as improvise
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP); // choose only 1 of 2 possible permanent
|
||||
setChoice(playerA, "Improvise");
|
||||
addTarget(playerA, "Alpha Myr"); // pay 6 as improvise
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import mage.constants.PhaseStep;
|
|||
import mage.constants.Zone;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
|
|
@ -33,6 +34,7 @@ public class TakeControlWhileSearchingLibraryTest extends CardTestPlayerBase {
|
|||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 3);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Buried Alive");
|
||||
addTarget(playerA, "Balduvian Bears");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
// after
|
||||
|
|
@ -73,6 +75,7 @@ public class TakeControlWhileSearchingLibraryTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Buried Alive");
|
||||
setChoice(playerB, true); // continue
|
||||
addTarget(playerB, "Balduvian Bears"); // player B must take control for searching
|
||||
addTarget(playerB, TestPlayer.TARGET_SKIP);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
// after
|
||||
|
|
@ -116,6 +119,7 @@ public class TakeControlWhileSearchingLibraryTest extends CardTestPlayerBase {
|
|||
setChoice(playerA, true); // yes, try to cast a creature card from lib
|
||||
setChoice(playerA, "Panglacial Wurm"); // try to cast
|
||||
addTarget(playerA, "Balduvian Bears"); // choice for searching
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
// after
|
||||
|
|
@ -170,6 +174,7 @@ public class TakeControlWhileSearchingLibraryTest extends CardTestPlayerBase {
|
|||
setChoice(playerB, true); // yes, try to cast a creature card from lib
|
||||
setChoice(playerB, "Panglacial Wurm"); // try to cast
|
||||
addTarget(playerB, "Balduvian Bears"); // choice for searching
|
||||
addTarget(playerB, TestPlayer.TARGET_SKIP);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
// after
|
||||
|
|
@ -230,6 +235,7 @@ public class TakeControlWhileSearchingLibraryTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Buried Alive");
|
||||
setChoice(playerB, true); // continue after new control
|
||||
addTarget(playerB, "Balduvian Bears");
|
||||
addTarget(playerB, TestPlayer.TARGET_SKIP);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkGraveyardCount("after grave a", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 0);
|
||||
checkGraveyardCount("after grave b", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Balduvian Bears", 0);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import mage.util.CardUtil;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
|
@ -509,6 +510,7 @@ public class AdjusterCostTest extends CardTestPlayerBaseWithAIHelps {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fireball");
|
||||
setChoice(playerA, "X=2");
|
||||
addTarget(playerA, "Arbor Elf^Arbor Elf");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import mage.counters.CounterType;
|
|||
import mage.game.stack.StackObject;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
|
|
@ -280,6 +281,7 @@ public class CollectEvidenceTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, monitor);
|
||||
setChoice(playerA, true);
|
||||
setChoice(playerA, giant);
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.cost.additional;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
|
|
@ -22,6 +23,7 @@ public class RemoveCounterCostTest extends CardTestPlayerBase {
|
|||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, Remove two +1/+1 counters");
|
||||
setChoice(playerA, "Novijen Sages"); // counters to remove
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||
setChoice(playerA, "X=2"); // counters to remove
|
||||
|
||||
setStrictChooseMode(true);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public class AddManaOfAnyTypeProducedTest extends CardTestPlayerBase {
|
|||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vedalken Mastermind");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
|
|
@ -52,6 +53,7 @@ public class AddManaOfAnyTypeProducedTest extends CardTestPlayerBase {
|
|||
|
||||
activateManaAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
|
|
@ -71,19 +73,26 @@ public class AddManaOfAnyTypeProducedTest extends CardTestPlayerBase {
|
|||
// If Gemstone Caverns is in your opening hand and you're not playing first, you may begin the game with Gemstone Caverns on the battlefield with a luck counter on it. If you do, exile a card from your hand.
|
||||
// {T}: Add {C}. If Gemstone Caverns has a luck counter on it, instead add one mana of any color.
|
||||
addCard(Zone.HAND, playerB, "Gemstone Caverns", 1);
|
||||
addCard(Zone.HAND, playerB, "Swamp", 1);
|
||||
|
||||
addCard(Zone.HAND, playerB, "Silvercoat Lion", 2);
|
||||
|
||||
// pay and put Gemstone to battlefield on starting
|
||||
setChoice(playerB, true);
|
||||
setChoice(playerB, "Swamp");
|
||||
|
||||
activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add");
|
||||
setChoice(playerB, "White");
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerB, "Gemstone Caverns", 1);
|
||||
assertCounterCount("Gemstone Caverns", CounterType.LUCK, 1);
|
||||
assertPermanentCount(playerB, "Silvercoat Lion", 1);
|
||||
assertExileCount("Silvercoat Lion", 1);
|
||||
assertExileCount("Swamp", 1);
|
||||
assertTapped("Gemstone Caverns", true);
|
||||
|
||||
}
|
||||
|
|
@ -99,12 +108,19 @@ public class AddManaOfAnyTypeProducedTest extends CardTestPlayerBase {
|
|||
// If Gemstone Caverns is in your opening hand and you're not playing first, you may begin the game with Gemstone Caverns on the battlefield with a luck counter on it. If you do, exile a card from your hand.
|
||||
// {T}: Add {C}. If Gemstone Caverns has a luck counter on it, instead add one mana of any color.
|
||||
addCard(Zone.HAND, playerB, "Gemstone Caverns", 1);
|
||||
addCard(Zone.HAND, playerB, "Swamp", 1);
|
||||
|
||||
addCard(Zone.HAND, playerB, "Silvercoat Lion", 2);
|
||||
|
||||
activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add");
|
||||
// pay and put Gemstone to battlefield on starting
|
||||
setChoice(playerB, true);
|
||||
setChoice(playerB, "Swamp");
|
||||
|
||||
activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add {C}");
|
||||
setChoice(playerB, "White");
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Vorinclex, Voice of Hunger");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
|
|
@ -112,7 +128,6 @@ public class AddManaOfAnyTypeProducedTest extends CardTestPlayerBase {
|
|||
assertCounterCount("Gemstone Caverns", CounterType.LUCK, 1);
|
||||
assertPermanentCount(playerB, "Vorinclex, Voice of Hunger", 1);
|
||||
assertTapped("Gemstone Caverns", true);
|
||||
|
||||
}
|
||||
|
||||
private static final String kinnan = "Kinnan, Bonder Prodigy";
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.single.afc;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestCommander4Players;
|
||||
|
||||
|
||||
|
|
@ -313,6 +314,7 @@ public class ShareTheSpoilsTest extends CardTestCommander4Players {
|
|||
// Cast an adventure card from hand
|
||||
castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Dizzying Swoop");
|
||||
addTarget(playerA, "Prosper, Tome-Bound");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP); // tap 1 of 2 targets
|
||||
waitStackResolved(5, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
// Make sure the creature card can't be played from exile since there isn't the {W}{W} for it
|
||||
|
|
|
|||
|
|
@ -169,15 +169,25 @@ public class ApproachOfTheSecondSunTest extends CardTestPlayerBase {
|
|||
@Test
|
||||
public void testCastFromGraveyard() {
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
|
||||
addCard(Zone.LIBRARY, playerA, "Plains", 6);
|
||||
addCard(Zone.HAND, playerA, "Approach of the Second Sun", 1);
|
||||
addCard(Zone.GRAVEYARD, playerA, "Approach of the Second Sun", 2);
|
||||
addCard(Zone.HAND, playerA, "Finale of Promise", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mystic Monastery", 25);
|
||||
//
|
||||
// If this spell was cast from your hand and you've cast another spell named Approach of the Second Sun this game,
|
||||
// you win the game. Otherwise, put Approach of the Second Sun into its owner's library seventh from the top
|
||||
// and you gain 7 life.
|
||||
addCard(Zone.HAND, playerA, "Approach of the Second Sun", 1); // {6}{W}
|
||||
addCard(Zone.GRAVEYARD, playerA, "Approach of the Second Sun", 2); // {6}{W}
|
||||
//
|
||||
// You may cast up to one target instant card and/or up to one target sorcery card from your graveyard each
|
||||
// with mana value X or less without paying their mana costs. If a spell cast this way would be put into
|
||||
// your graveyard, exile it instead. If X is 10 or more, copy each of those spells twice. You may choose
|
||||
// new targets for the copies.
|
||||
addCard(Zone.HAND, playerA, "Finale of Promise", 2); // {X}{R}{R}
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mystic Monastery", 25); // for mana
|
||||
|
||||
// first may have been cast from anywhere.
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Finale of Promise", true);
|
||||
// You may cast up to one target instant card and/or up to one target sorcery card from your graveyard each with mana value X or less without paying their mana costs. If a spell cast this way would be put into your graveyard, exile it instead. If X is 10 or more, copy each of those spells twice. You may choose new targets for the copies.
|
||||
setChoice(playerA, "X=7"); // each with mana value X or less
|
||||
setChoice(playerA, "Yes"); // You may cast
|
||||
addTarget(playerA, TARGET_SKIP); // up to one target instant card
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ public class ChainerNightmareAdeptTest extends CardTestPlayerBase {
|
|||
addCard(Zone.GRAVEYARD, playerA, maaka, 2);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Discard");
|
||||
setChoice(playerA, mountain); // discard cost
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, maaka);
|
||||
|
||||
|
|
@ -33,8 +34,8 @@ public class ChainerNightmareAdeptTest extends CardTestPlayerBase {
|
|||
|
||||
attack(1, playerA, maaka);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, maaka, 1);
|
||||
|
|
@ -52,7 +53,10 @@ public class ChainerNightmareAdeptTest extends CardTestPlayerBase {
|
|||
addCard(Zone.GRAVEYARD, playerA, khenra);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Discard");
|
||||
setChoice(playerA, true);
|
||||
setChoice(playerA, mountain); // discard cost
|
||||
setChoice(playerA, true); // use copy
|
||||
setChoice(playerA, "0"); // for casting main spell - x2 permitting object
|
||||
setChoice(playerA, "0"); // for casting copied spell - x2 permitting object
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, maaka, true);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, khenra);
|
||||
|
|
@ -60,8 +64,8 @@ public class ChainerNightmareAdeptTest extends CardTestPlayerBase {
|
|||
attack(1, playerA, maaka);
|
||||
attack(1, playerA, khenra);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, maaka, 1);
|
||||
|
|
@ -88,8 +92,8 @@ public class ChainerNightmareAdeptTest extends CardTestPlayerBase {
|
|||
|
||||
attack(1, playerA, maaka);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, maaka, 1);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.single.clb;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
|
|
@ -29,6 +30,7 @@ public class BabaLysagaNightWitchTest extends CardTestPlayerBase {
|
|||
activateAbility(1, PhaseStep.UPKEEP, playerA, "{1}: {this} becomes a 2/2 Assembly-Worker artifact creature until end of turn. It's still a land.");
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice up to three permanents: If there ");
|
||||
setChoice(playerA, "Mishra's Factory");
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
|
@ -48,6 +50,7 @@ public class BabaLysagaNightWitchTest extends CardTestPlayerBase {
|
|||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice up to three permanents: If there ");
|
||||
setChoice(playerA, "Mishra's Factory");
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import mage.constants.PhaseStep;
|
|||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
|
|
@ -45,6 +46,7 @@ public class MuYanlingWindRiderTest extends CardTestPlayerBase {
|
|||
|
||||
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 1");
|
||||
setChoice(playerA, "Memnite");
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||
|
||||
attack(3, playerA, "Vehicle Token", playerB);
|
||||
attack(3, playerA, muyanling, playerB);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package org.mage.test.cards.single.emn;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
|
|
@ -77,6 +78,7 @@ public class TamiyoFieldResearcherTest extends CardTestPlayerBase {
|
|||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Choose up to two");
|
||||
addTarget(playerA, "Bronze Sable");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
|
||||
attack(1, playerA, "Bronze Sable");
|
||||
|
||||
|
|
@ -135,6 +137,7 @@ public class TamiyoFieldResearcherTest extends CardTestPlayerBase {
|
|||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Choose up to two");
|
||||
addTarget(playerA, "Sylvan Advocate");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
|
||||
attack(1, playerA, "Sylvan Advocate");
|
||||
attack(2, playerB, "Memnite");
|
||||
|
|
@ -167,6 +170,7 @@ public class TamiyoFieldResearcherTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tamiyo, Field Researcher", true);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Choose up to two");
|
||||
addTarget(playerA, "Sylvan Advocate");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
|
||||
attack(1, playerA, "Sylvan Advocate");
|
||||
|
||||
|
|
@ -236,6 +240,7 @@ public class TamiyoFieldResearcherTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tamiyo, Field Researcher", true);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Choose up to two");
|
||||
addTarget(playerA, "Bronze Sable");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
|
||||
attack(2, playerB, "Bronze Sable");
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.single.grn;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
|
|
@ -31,6 +32,7 @@ public class WandOfVertebraeTest extends CardTestPlayerBase {
|
|||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}");
|
||||
addTarget(playerA, lavaCoil);
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP); // must choose 1 of 5
|
||||
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.single.j22;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
|
|
@ -80,13 +81,17 @@ public class AgrusKosEternalSoldierTest extends CardTestPlayerBase {
|
|||
addCard(Zone.HAND, playerA, "Smoldering Werewolf");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Smoldering Werewolf");
|
||||
setChoice(playerA, "When {this} enters, it deals");
|
||||
addTarget(playerA, agrus);
|
||||
addTarget(playerA, agrus);
|
||||
setChoice(playerB, true); // gain life
|
||||
setChoice(playerB, "Whenever {this} becomes");
|
||||
setChoice(playerB, true); // pay to copy
|
||||
setChoice(playerB, true); // pay to copy
|
||||
setChoice(playerB, true); // gain life on cast
|
||||
//
|
||||
setChoice(playerA, "When {this} enters, it deals"); // x2 triggers from Panharmonicon
|
||||
addTarget(playerA, agrus); // x1 trigger
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP); // x1 trigger
|
||||
addTarget(playerA, agrus); // x2 trigger
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP); // x2 trigger
|
||||
//
|
||||
setChoice(playerB, "Whenever {this} becomes"); // x2 triggers from Panharmonicon
|
||||
setChoice(playerB, true); // x1 pay to copy
|
||||
setChoice(playerB, true); // x2 pay to copy
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.single.m21;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
public class AlpineHoundmasterTest extends CardTestPlayerBase {
|
||||
|
|
@ -22,6 +23,7 @@ public class AlpineHoundmasterTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpine Houndmaster");
|
||||
setChoice(playerA, true);
|
||||
addTarget(playerA, "Alpine Watchdog");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP); // only single card
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
|
|
@ -44,6 +46,7 @@ public class AlpineHoundmasterTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpine Houndmaster");
|
||||
setChoice(playerA, true);
|
||||
addTarget(playerA, "Igneous Cur");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP); // only single card
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
|
|
@ -54,7 +57,7 @@ public class AlpineHoundmasterTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void searchBoth() {
|
||||
public void searchBoth_TestFramework_AddTargetsAsSingle() {
|
||||
// When Alpine Houndmaster enters the battlefield, you may search your library for a card named
|
||||
// Alpine Watchdog and/or a card named Igneous Cur, reveal them, put them into your hand, then shuffle your library.
|
||||
addCard(Zone.HAND, playerA, "Alpine Houndmaster", 1);
|
||||
|
|
@ -72,7 +75,30 @@ public class AlpineHoundmasterTest extends CardTestPlayerBase {
|
|||
execute();
|
||||
|
||||
assertHandCount(playerA, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchBoth_TestFramework_AddTargetsAsMultiple() {
|
||||
// test framework must support both
|
||||
|
||||
// When Alpine Houndmaster enters the battlefield, you may search your library for a card named
|
||||
// Alpine Watchdog and/or a card named Igneous Cur, reveal them, put them into your hand, then shuffle your library.
|
||||
addCard(Zone.HAND, playerA, "Alpine Houndmaster", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains");
|
||||
addCard(Zone.LIBRARY, playerA, "Alpine Watchdog");
|
||||
addCard(Zone.LIBRARY, playerA, "Igneous Cur");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpine Houndmaster");
|
||||
setChoice(playerA, true);
|
||||
addTarget(playerA, "Igneous Cur");
|
||||
addTarget(playerA, "Alpine Watchdog");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertHandCount(playerA, 2);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.single.mh3;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||
|
||||
/**
|
||||
|
|
@ -72,6 +73,7 @@ public class NethergoyfTest extends CardTestPlayerBaseWithAIHelps {
|
|||
checkPlayableAbility("can escape", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", true);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, nethergoyf + " with Escape");
|
||||
setChoice(playerA, "Memnite^Memnite^Memnite^Memnite^Bitterblossom"); // cards exiled for escape cost: Exile all the Memnite but one.
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import mage.constants.PhaseStep;
|
|||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
|
|
@ -39,6 +40,7 @@ public class SuppressionRayTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Suppression Ray", playerB);
|
||||
setChoiceAmount(playerA, 3); // decide to pay 3 energy
|
||||
setChoice(playerA, "Zodiac Pig^Zodiac Rabbit"); // put stun on those 2 creatures
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ package org.mage.test.cards.single.mkm;
|
|||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.target.TargetPlayer;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
public class CovetedFalconTest extends CardTestPlayerBase {
|
||||
|
|
@ -31,6 +33,7 @@ public class CovetedFalconTest extends CardTestPlayerBase {
|
|||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, "Grizzly Bears");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
|
|
@ -57,6 +60,7 @@ public class CovetedFalconTest extends CardTestPlayerBase {
|
|||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, "Grizzly Bears");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Turn Against", "Grizzly Bears");
|
||||
|
|
@ -82,6 +86,7 @@ public class CovetedFalconTest extends CardTestPlayerBase {
|
|||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, "Putrid Goblin");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Murder", "Putrid Goblin");
|
||||
|
|
@ -111,6 +116,7 @@ public class CovetedFalconTest extends CardTestPlayerBase {
|
|||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, "Treacherous Pit-Dweller");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Murder", "Treacherous Pit-Dweller");
|
||||
|
|
@ -151,6 +157,7 @@ public class CovetedFalconTest extends CardTestPlayerBase {
|
|||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||
addTarget(playerA, playerB);
|
||||
addTarget(playerA, "Darksteel Relic^Grizzly Bears");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
|
||||
castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, "Murder", "Guardian Beast");
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import mage.constants.PhaseStep;
|
|||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
|
|
@ -65,6 +66,7 @@ public class TenthDistrictHeroTest extends CardTestPlayerBase {
|
|||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}");
|
||||
setChoice(playerA, giant);
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
|
|
@ -92,6 +94,7 @@ public class TenthDistrictHeroTest extends CardTestPlayerBase {
|
|||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}");
|
||||
setChoice(playerA, giant);
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import mage.constants.PhaseStep;
|
|||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
|
|
@ -37,6 +38,7 @@ public class TheWiseMothmanTest extends CardTestPlayerBase {
|
|||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}");
|
||||
addTarget(playerA, mothman + "^Grizzly Bears"); // up to three targets => choosing 2
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
package org.mage.test.cards.asthough;
|
||||
package org.mage.test.cards.targets;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
|
|
@ -7,13 +6,11 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
* @author LevelX2, JayDi85
|
||||
*/
|
||||
public class CastAsInstantTest extends CardTestPlayerBase {
|
||||
public class AutoChooseTargetsAndCastAsInstantTest extends CardTestPlayerBase {
|
||||
|
||||
@Test
|
||||
public void testEffectOnlyForOneTurn() {
|
||||
private void run_WithAutoSelection(int selectedTargets, int possibleTargets) {
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Island");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 4);
|
||||
// The next sorcery card you cast this turn can be cast as though it had flash.
|
||||
|
|
@ -23,22 +20,45 @@ public class CastAsInstantTest extends CardTestPlayerBase {
|
|||
// Target opponent exiles two cards from their hand and loses 2 life.
|
||||
addCard(Zone.HAND, playerB, "Witness the End"); // {3}{B}
|
||||
|
||||
addCard(Zone.HAND, playerA, "Silvercoat Lion", 2);
|
||||
addCard(Zone.HAND, playerA, "Silvercoat Lion", possibleTargets);
|
||||
|
||||
castSpell(1, PhaseStep.UPKEEP, playerB, "Quicken", true);
|
||||
castSpell(1, PhaseStep.UPKEEP, playerB, "Witness the End", playerA);
|
||||
|
||||
// it uses auto-choose logic inside, so disable strict mode
|
||||
// logic for possible targets with min/max = 2:
|
||||
// 0, 1, 2 - auto-choose all possible targets
|
||||
// 3+ - AI choose best targets
|
||||
setStrictChooseMode(false);
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerB, "Quicken", 1);
|
||||
assertGraveyardCount(playerB, "Witness the End", 1);
|
||||
|
||||
assertExileCount("Silvercoat Lion", 2);
|
||||
assertExileCount("Silvercoat Lion", selectedTargets);
|
||||
|
||||
assertLife(playerA, 18);
|
||||
assertLife(playerB, 20);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AutoChoose_0_of_0() {
|
||||
run_WithAutoSelection(0, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AutoChoose_1_of_1() {
|
||||
run_WithAutoSelection(1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AutoChoose_2_of_2() {
|
||||
run_WithAutoSelection(2, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AutoChoose_2_of_5() {
|
||||
run_WithAutoSelection(2, 5);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
package org.mage.test.cards.targets;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.TapAttachedCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileTargetEffect;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* Helper class for target selection tests in diff use cases like selection on targets declare or on resolve
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class TargetsSelectionBaseTest extends CardTestPlayerBaseWithAIHelps {
|
||||
|
||||
static final boolean DEBUG_ENABLE_DETAIL_LOGS = true;
|
||||
|
||||
protected void run_PlayerChooseTarget_OnActivate(int chooseCardsCount, int availableCardsCount) {
|
||||
// custom effect - exile and gain life due selected targets
|
||||
int minTarget = 0;
|
||||
int maxTarget = 3;
|
||||
String startingText = "exile and gain";
|
||||
Ability ability = new SimpleActivatedAbility(
|
||||
new ExileTargetEffect().setText("exile"),
|
||||
new ManaCostsImpl<>("")
|
||||
);
|
||||
ability.addEffect(new GainLifeEffect(TotalTargetsValue.instance).concatBy("and").setText("gain life"));
|
||||
ability.addTarget(new TargetCardInHand(minTarget, maxTarget, StaticFilters.FILTER_CARD).withNotTarget(false));
|
||||
addCustomCardWithAbility("test choice", playerA, ability);
|
||||
|
||||
addCard(Zone.HAND, playerA, "Swamp", availableCardsCount);
|
||||
|
||||
checkHandCardCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swamp", availableCardsCount);
|
||||
checkExileCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swamp", 0);
|
||||
|
||||
if (availableCardsCount > 0) {
|
||||
checkPlayableAbility("can activate on non-zero targets", 1, PhaseStep.PRECOMBAT_MAIN, playerA, startingText, true);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, startingText);
|
||||
} else {
|
||||
checkPlayableAbility("can't activate on zero targets", 1, PhaseStep.PRECOMBAT_MAIN, playerA, startingText, false);
|
||||
}
|
||||
|
||||
if (chooseCardsCount > 0) {
|
||||
// need selection
|
||||
List<String> targetCards = new ArrayList<>();
|
||||
IntStream.rangeClosed(1, chooseCardsCount).forEach(x -> {
|
||||
targetCards.add("Swamp");
|
||||
});
|
||||
addTarget(playerA, String.join("^", targetCards));
|
||||
// end selection:
|
||||
// - x of 0 - no
|
||||
// - 1 of 3 - yes
|
||||
// - 2 of 3 - yes
|
||||
// - 3 of 3 - no, it's auto-finish on last select
|
||||
// - 3 of 5 - no, it's auto-finish on last select
|
||||
if (chooseCardsCount < maxTarget) {
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
}
|
||||
} else {
|
||||
// need skip
|
||||
// on 0 cards there are not valid targets, so no any dialogs
|
||||
if (availableCardsCount > 0) {
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG_ENABLE_DETAIL_LOGS) {
|
||||
System.out.println("planning actions:");
|
||||
playerA.getActions().forEach(System.out::println);
|
||||
System.out.println("planning targets:");
|
||||
playerA.getTargets().forEach(System.out::println);
|
||||
System.out.println("planning choices:");
|
||||
playerA.getChoices().forEach(System.out::println);
|
||||
}
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertExileCount(playerA, "Swamp", chooseCardsCount);
|
||||
assertLife(playerA, 20 + chooseCardsCount);
|
||||
}
|
||||
|
||||
protected void run_PlayerChoose_OnResolve(int chooseCardsCount, int availableCardsCount) {
|
||||
// custom effect - select, exile and gain life
|
||||
int minTarget = 0;
|
||||
int maxTarget = 3;
|
||||
String startingText = "select, exile and gain life";
|
||||
Ability ability = new SimpleActivatedAbility(
|
||||
new SelectExileAndGainLifeCustomEffect(minTarget, maxTarget, Outcome.Benefit),
|
||||
new ManaCostsImpl<>("")
|
||||
);
|
||||
addCustomCardWithAbility("test choice", playerA, ability);
|
||||
|
||||
addCard(Zone.HAND, playerA, "Swamp", availableCardsCount);
|
||||
|
||||
checkHandCardCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swamp", availableCardsCount);
|
||||
checkExileCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swamp", 0);
|
||||
|
||||
checkPlayableAbility("can activate any time (even with zero cards)", 1, PhaseStep.PRECOMBAT_MAIN, playerA, startingText, true);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, startingText);
|
||||
|
||||
if (chooseCardsCount > 0) {
|
||||
// need selection
|
||||
List<String> targetCards = new ArrayList<>();
|
||||
IntStream.rangeClosed(1, chooseCardsCount).forEach(x -> {
|
||||
targetCards.add("Swamp");
|
||||
});
|
||||
setChoice(playerA, String.join("^", targetCards));
|
||||
} else {
|
||||
// need skip
|
||||
// on 0 cards there are must be dialog with done button anyway
|
||||
|
||||
// end selection:
|
||||
// - x of 0 - yes
|
||||
// - 1 of 3 - yes
|
||||
// - 2 of 3 - yes
|
||||
// - 3 of 3 - no, it's auto-finish on last select
|
||||
// - 3 of 5 - no, it's auto-finish on last select
|
||||
if (chooseCardsCount < maxTarget) {
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (DEBUG_ENABLE_DETAIL_LOGS) {
|
||||
System.out.println("planning actions:");
|
||||
playerA.getActions().forEach(System.out::println);
|
||||
System.out.println("planning targets:");
|
||||
playerA.getTargets().forEach(System.out::println);
|
||||
System.out.println("planning choices:");
|
||||
playerA.getChoices().forEach(System.out::println);
|
||||
}
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertExileCount(playerA, "Swamp", chooseCardsCount);
|
||||
assertLife(playerA, 20 + chooseCardsCount);
|
||||
}
|
||||
|
||||
protected void run_PlayerChoose_OnResolve_AI(Outcome outcome, int minTargets, int maxTargets, int aiMustChooseCardsCount, int availableCardsCount) {
|
||||
// custom effect - select, exile and gain life
|
||||
String startingText = "{T}: Select, exile and gain life";
|
||||
Ability ability = new SimpleActivatedAbility(
|
||||
new SelectExileAndGainLifeCustomEffect(minTargets, maxTargets, outcome),
|
||||
new ManaCostsImpl<>("")
|
||||
);
|
||||
ability.addCost(new TapSourceCost());
|
||||
addCustomCardWithAbility("test choice", playerA, ability);
|
||||
|
||||
addCard(Zone.HAND, playerA, "Swamp", availableCardsCount);
|
||||
addCard(Zone.HAND, playerA, "Forest", 1);
|
||||
|
||||
// Forest play is workaround to disable lands play in ai priority
|
||||
// {T} cost is workaround to disable multiple calls of the ability
|
||||
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest");
|
||||
|
||||
checkPlayableAbility("can activate any time (even with zero cards)", 1, PhaseStep.PRECOMBAT_MAIN, playerA, startingText, true);
|
||||
|
||||
// AI must see bad effect and select only halves of the max targets, e.g. 1 of 3
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertExileCount(playerA, "Swamp", aiMustChooseCardsCount);
|
||||
assertLife(playerA, 20 + aiMustChooseCardsCount);
|
||||
}
|
||||
}
|
||||
|
||||
enum TotalTargetsValue implements DynamicValue {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public TotalTargetsValue copy() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
return effect.getTargetPointer().getTargets(game, sourceAbility).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "total targets";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "X";
|
||||
}
|
||||
}
|
||||
|
||||
class SelectExileAndGainLifeCustomEffect extends OneShotEffect {
|
||||
|
||||
private final int minTargets;
|
||||
private final int maxTargets;
|
||||
|
||||
SelectExileAndGainLifeCustomEffect(int minTargets, int maxTargets, Outcome outcome) {
|
||||
super(outcome);
|
||||
this.minTargets = minTargets;
|
||||
this.maxTargets = maxTargets;
|
||||
staticText = "select, exile and gain life";
|
||||
}
|
||||
|
||||
private SelectExileAndGainLifeCustomEffect(final SelectExileAndGainLifeCustomEffect effect) {
|
||||
super(effect);
|
||||
this.minTargets = effect.minTargets;
|
||||
this.maxTargets = effect.maxTargets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectExileAndGainLifeCustomEffect copy() {
|
||||
return new SelectExileAndGainLifeCustomEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Target target = new TargetCardInHand(this.minTargets, this.maxTargets, StaticFilters.FILTER_CARD).withNotTarget(true);
|
||||
if (!player.choose(outcome, target, source, game)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
player.moveCardsToExile(new CardsImpl(target.getTargets()).getCards(game), source, game, false, source.getSourceId(), player.getLogName());
|
||||
player.gainLife(target.getTargets().size(), game, source);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package org.mage.test.cards.targets;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Testing targets selection on activate/cast (player.chooseTarget)
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class TargetsSelectionOnActivateTest extends TargetsSelectionBaseTest {
|
||||
|
||||
// no selects
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_0_of_0() {
|
||||
run_PlayerChooseTarget_OnActivate(0, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_0_of_1() {
|
||||
run_PlayerChooseTarget_OnActivate(0, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_0_of_2() {
|
||||
run_PlayerChooseTarget_OnActivate(0, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_0_of_3() {
|
||||
run_PlayerChooseTarget_OnActivate(0, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_0_of_10() {
|
||||
run_PlayerChooseTarget_OnActivate(0, 10);
|
||||
}
|
||||
|
||||
// 1 select
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_1_of_1() {
|
||||
run_PlayerChooseTarget_OnActivate(1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_1_of_2() {
|
||||
run_PlayerChooseTarget_OnActivate(1, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_1_of_3() {
|
||||
run_PlayerChooseTarget_OnActivate(1, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_1_of_10() {
|
||||
run_PlayerChooseTarget_OnActivate(1, 10);
|
||||
}
|
||||
|
||||
// 2 selects
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_2_of_2() {
|
||||
run_PlayerChooseTarget_OnActivate(2, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_2_of_3() {
|
||||
run_PlayerChooseTarget_OnActivate(2, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_2_of_10() {
|
||||
run_PlayerChooseTarget_OnActivate(2, 10);
|
||||
}
|
||||
|
||||
// 3 selects
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_3_of_3() {
|
||||
run_PlayerChooseTarget_OnActivate(3, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_3_of_10() {
|
||||
run_PlayerChooseTarget_OnActivate(3, 10);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package org.mage.test.cards.targets;
|
||||
|
||||
import mage.constants.Outcome;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Testing targets selection on resolve (player.choose) for AI
|
||||
* <p>
|
||||
* AI must use logic like:
|
||||
* - for good effects - choose as much as possible targets
|
||||
* - for bad effects - choose as much as lower targets
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class TargetsSelectionOnResolveAITest extends TargetsSelectionBaseTest {
|
||||
|
||||
@Test
|
||||
public void test_OnResolve_Good_0_of_0() {
|
||||
run_PlayerChoose_OnResolve_AI(Outcome.Benefit, 0, 3, 0, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnResolve_Good_1_of_1() {
|
||||
run_PlayerChoose_OnResolve_AI(Outcome.Benefit, 0, 3, 1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnResolve_Good_2_of_2() {
|
||||
run_PlayerChoose_OnResolve_AI(Outcome.Benefit, 0, 3, 2, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnResolve_Good_3_of_3() {
|
||||
run_PlayerChoose_OnResolve_AI(Outcome.Benefit, 0, 3, 3, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnResolve_Good_3_of_10() {
|
||||
run_PlayerChoose_OnResolve_AI(Outcome.Benefit, 0, 3, 3, 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnResolve_Bad_0_of_0() {
|
||||
run_PlayerChoose_OnResolve_AI(Outcome.Detriment, 0, 3, 0, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnResolve_Bad_0_of_1() {
|
||||
run_PlayerChoose_OnResolve_AI(Outcome.Detriment, 0, 3, 0, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnResolve_Bad_0_of_2() {
|
||||
run_PlayerChoose_OnResolve_AI(Outcome.Detriment, 0, 3, 0, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnResolve_Bad_0_of_3() {
|
||||
run_PlayerChoose_OnResolve_AI(Outcome.Detriment, 0, 3, 0, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnResolve_Bad_0_of_10() {
|
||||
run_PlayerChoose_OnResolve_AI(Outcome.Detriment, 0, 3, 0, 10);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
package org.mage.test.cards.targets;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Testing targets selection on resolve (player.choose)
|
||||
* <p>
|
||||
* Player can use any logic and choose any number of targets
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class TargetsSelectionOnResolveTest extends TargetsSelectionBaseTest {
|
||||
|
||||
// no selects
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_0_of_0() {
|
||||
run_PlayerChoose_OnResolve(0, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_0_of_1() {
|
||||
run_PlayerChoose_OnResolve(0, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_0_of_2() {
|
||||
run_PlayerChoose_OnResolve(0, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_0_of_3() {
|
||||
run_PlayerChoose_OnResolve(0, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_0_of_10() {
|
||||
run_PlayerChoose_OnResolve(0, 10);
|
||||
}
|
||||
|
||||
// 1 select
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_1_of_1() {
|
||||
run_PlayerChoose_OnResolve(1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_1_of_2() {
|
||||
run_PlayerChoose_OnResolve(1, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_1_of_3() {
|
||||
run_PlayerChoose_OnResolve(1, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_1_of_10() {
|
||||
run_PlayerChoose_OnResolve(1, 10);
|
||||
}
|
||||
|
||||
// 2 selects
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_2_of_2() {
|
||||
run_PlayerChoose_OnResolve(2, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_2_of_3() {
|
||||
run_PlayerChoose_OnResolve(2, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_2_of_10() {
|
||||
run_PlayerChoose_OnResolve(2, 10);
|
||||
}
|
||||
|
||||
// 3 selects
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_3_of_3() {
|
||||
run_PlayerChoose_OnResolve(3, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_OnActivate_3_of_10() {
|
||||
run_PlayerChoose_OnResolve(3, 10);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.triggers;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
|
|
@ -23,6 +24,7 @@ public class EnterLeaveBattlefieldExileTargetTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Serenity");
|
||||
addTarget(playerA, "Silvercoat Lion^Pillarfield Ox");
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
setChoice(playerA, true);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class WorldgorgerDragonTest extends CardTestPlayerBase {
|
||||
|
|
@ -83,62 +82,48 @@ public class WorldgorgerDragonTest extends CardTestPlayerBase {
|
|||
// When Staunch Defenders enters the battlefield, you gain 4 life.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Staunch Defenders", 1);
|
||||
|
||||
// 1 cast and resolve
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Dead", "Worldgorger Dragon");
|
||||
setChoice(playerA, "Worldgorger Dragon"); // attach
|
||||
setChoice(playerA, "When {this} enters, if it's"); // x2 triggers (gain life from Staunch Defenders and etb from Animate Dead)
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3);
|
||||
|
||||
// 2 etb and attach
|
||||
setChoice(playerA, "Worldgorger Dragon");
|
||||
setChoice(playerA, "When {this} enters, if it's");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3);
|
||||
|
||||
// 3 etb and attach
|
||||
setChoice(playerA, "Worldgorger Dragon");
|
||||
setChoice(playerA, "When {this} enters, if it's");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3);
|
||||
|
||||
// 4 etb and attach
|
||||
setChoice(playerA, "Worldgorger Dragon");
|
||||
setChoice(playerA, "When {this} enters, if it's");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3);
|
||||
|
||||
// 5 etb and attach
|
||||
setChoice(playerA, "Worldgorger Dragon");
|
||||
setChoice(playerA, false); // no draws on infinite loop
|
||||
setChoice(playerA, "When {this} enters, if it's");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3);
|
||||
|
||||
// 6 etb and attach
|
||||
setChoice(playerA, "Worldgorger Dragon");
|
||||
setChoice(playerA, "When {this} enters, if it's");
|
||||
setChoice(playerA, false);
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3);
|
||||
|
||||
setChoice(playerA, "Worldgorger Dragon");
|
||||
setChoice(playerA, "When {this} enters, if it's");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3 - 1);
|
||||
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
|
||||
setChoice(playerA, "Worldgorger Dragon");
|
||||
setChoice(playerA, "When {this} enters, if it's");
|
||||
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
|
||||
setChoice(playerA, "Worldgorger Dragon");
|
||||
setChoice(playerA, "When {this} enters, if it's");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Volcanic Geyser", playerB, 22);
|
||||
// cast spell and stop infinite loop after 20+ mana in pool
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Volcanic Geyser", playerB, 20);
|
||||
setChoice(playerA, "X=20");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 44);
|
||||
assertLife(playerA, 20 + 5 * 4);
|
||||
assertLife(playerB, 0);
|
||||
|
||||
assertGraveyardCount(playerA, "Volcanic Geyser", 1);
|
||||
|
|
@ -154,12 +139,11 @@ public class WorldgorgerDragonTest extends CardTestPlayerBase {
|
|||
* you choose to skip or pick a different creature, it always returns the
|
||||
* first creature you picked. Kind of hard to explain, but here's how to
|
||||
* reproduce:
|
||||
*
|
||||
* <p>
|
||||
* 1) Cast Animate Dead, targeting Worldgorger Dragon 2) Worldgorger Dragon
|
||||
* will exile Animate Dead, killing the dragon and returning the permanents
|
||||
* 3) Select Worldgorger again 4) Step 2 repeats 5) Attempt to select a
|
||||
* different creature. Worldgorger Dragon is returned instead.
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testWithAnimateDeadDifferentTargets() {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.triggers.dies;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
|
|
@ -159,6 +160,7 @@ public class VengefulTownsfolkTest extends CardTestPlayerBase {
|
|||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice up to three");
|
||||
setChoice(playerA, "Angel of the God-Pharaoh"); // sac cost
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
|
|
@ -183,6 +185,7 @@ public class VengefulTownsfolkTest extends CardTestPlayerBase {
|
|||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice up to three");
|
||||
setChoice(playerA, "Grizzly Bears^Angel of the God-Pharaoh"); // sac cost
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public class TestPlayer implements Player {
|
|||
|
||||
private static final Logger LOGGER = Logger.getLogger(TestPlayer.class);
|
||||
|
||||
private static final int takeMaxTargetsPerChoose = Integer.MAX_VALUE; // TODO: set 1, fix broken tests and replace all "for (String targetDefinition" by targets.get(0)
|
||||
private static final int takeMaxTargetsPerChoose = Integer.MAX_VALUE; // TODO: set 1 here, fix broken tests and replace all "for (String targetDefinition" by targets.get(0)
|
||||
|
||||
public static final String TARGET_SKIP = "[target_skip]"; // stop/skip targeting
|
||||
public static final String CHOICE_SKIP = "[choice_skip]"; // stop/skip choice
|
||||
|
|
@ -113,7 +113,7 @@ public class TestPlayer implements Player {
|
|||
// (example: card call TestPlayer's choice, but it uses another choices, see docs in TestComputerPlayer)
|
||||
private boolean strictChooseMode = false;
|
||||
|
||||
private String[] groupsForTargetHandling = null;
|
||||
private String[] groupsForTargetHandling = null; // predefined targets list from cast/activate command
|
||||
|
||||
// Tracks the initial turns (turn 0s) both players are given at the start of the game.
|
||||
// Before actual turns start. Needed for checking attacker/blocker legality in the tests
|
||||
|
|
@ -516,7 +516,7 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
}
|
||||
for (UUID id : currentTarget.possibleTargets(ability.getControllerId(), ability, game)) {
|
||||
if (!currentTarget.getTargets().contains(id)) {
|
||||
if (!currentTarget.contains(id)) {
|
||||
MageObject object = game.getObject(id);
|
||||
|
||||
if (object == null) {
|
||||
|
|
@ -594,7 +594,7 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
|
||||
// fake test ability for triggers and events
|
||||
Ability source = new SimpleStaticAbility(Zone.OUTSIDE, new InfoEffect("adding testing cards"));
|
||||
Ability source = new SimpleStaticAbility(Zone.OUTSIDE, new InfoEffect("fake ability"));
|
||||
source.setControllerId(this.getId());
|
||||
|
||||
int numberOfActions = actions.size();
|
||||
|
|
@ -2099,8 +2099,18 @@ public class TestPlayer implements Player {
|
|||
return "Ability: null";
|
||||
}
|
||||
|
||||
private String getInfo(Target o, Game game) {
|
||||
return "Target: " + (o != null ? o.getClass().getSimpleName() + ": " + o.getMessage(game) : "null");
|
||||
private String getInfo(Target target, Ability source, Game game) {
|
||||
if (target == null) {
|
||||
return "Target: null";
|
||||
}
|
||||
UUID abilityControllerId = getId();
|
||||
if (target.getTargetController() != null && target.getAbilityController() != null) {
|
||||
abilityControllerId = target.getAbilityController();
|
||||
}
|
||||
Set<UUID> possibleTargets = target.possibleTargets(abilityControllerId, source, game);
|
||||
|
||||
return "Target: selected " + target.getSize() + ", possible " + possibleTargets.size()
|
||||
+ ", " + target.getClass().getSimpleName() + ": " + target.getMessage(game);
|
||||
}
|
||||
|
||||
private void assertAliasSupportInChoices(boolean methodSupportAliases) {
|
||||
|
|
@ -2211,7 +2221,7 @@ public class TestPlayer implements Player {
|
|||
// skip choices
|
||||
if (possibleChoice.equals(CHOICE_SKIP)) {
|
||||
choices.remove(0);
|
||||
return true;
|
||||
return false; // false - stop to choose
|
||||
}
|
||||
|
||||
if (choice.setChoiceByAnswers(choices, true)) {
|
||||
|
|
@ -2264,27 +2274,28 @@ public class TestPlayer implements Player {
|
|||
abilityControllerId = target.getAbilityController();
|
||||
}
|
||||
|
||||
// TODO: warning, some cards call player.choose methods instead target.choose, see #8254
|
||||
// most use cases - discard and other cost with choice like that method
|
||||
// must migrate all choices.remove(xxx) to choices.remove(0), takeMaxTargetsPerChoose can help to find it
|
||||
|
||||
// ignore player select
|
||||
if (target.getMessage(game).equals("Select a starting player")) {
|
||||
return computerPlayer.choose(outcome, target, source, game, options);
|
||||
}
|
||||
|
||||
boolean isAddedSomething = false; // must return true on any changes in targets, so game can ask next choose dialog until finish
|
||||
|
||||
assertAliasSupportInChoices(true);
|
||||
if (!choices.isEmpty()) {
|
||||
|
||||
// skip choices
|
||||
if (choices.get(0).equals(CHOICE_SKIP)) {
|
||||
if (tryToSkipSelection(choices, CHOICE_SKIP)) {
|
||||
Assert.assertTrue("found skip choice, but it require more choices, needs "
|
||||
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
||||
target.getTargets().size() >= target.getMinNumberOfTargets());
|
||||
choices.remove(0);
|
||||
return true;
|
||||
return false; // false - stop to choose
|
||||
}
|
||||
|
||||
List<Integer> usedChoices = new ArrayList<>();
|
||||
List<UUID> usedTargets = new ArrayList<>();
|
||||
|
||||
|
||||
// TODO: Allow to choose a player with TargetPermanentOrPlayer
|
||||
if ((target.getOriginalTarget() instanceof TargetPermanent)
|
||||
|| (target.getOriginalTarget() instanceof TargetPermanentOrPlayer)) { // player target not implemented yet
|
||||
|
|
@ -2294,10 +2305,13 @@ public class TestPlayer implements Player {
|
|||
} else {
|
||||
filterPermanent = ((TargetPermanent) target.getOriginalTarget()).getFilter();
|
||||
}
|
||||
while (!choices.isEmpty()) {
|
||||
while (!choices.isEmpty()) { // TODO: remove cycle after main commits
|
||||
if (tryToSkipSelection(choices, CHOICE_SKIP)) {
|
||||
return false; // stop dialog
|
||||
}
|
||||
String choiceRecord = choices.get(0);
|
||||
String[] targetList = choiceRecord.split("\\^");
|
||||
boolean targetFound = false;
|
||||
isAddedSomething = false;
|
||||
for (String targetName : targetList) {
|
||||
boolean originOnly = false;
|
||||
boolean copyOnly = false;
|
||||
|
|
@ -2312,14 +2326,14 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
}
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterPermanent, abilityControllerId, source, game)) {
|
||||
if (target.getTargets().contains(permanent.getId())) {
|
||||
if (target.contains(permanent.getId())) {
|
||||
continue;
|
||||
}
|
||||
if (hasObjectTargetNameOrAlias(permanent, targetName)) {
|
||||
if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) {
|
||||
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
|
||||
target.add(permanent.getId(), game);
|
||||
targetFound = true;
|
||||
isAddedSomething = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -2327,7 +2341,7 @@ public class TestPlayer implements Player {
|
|||
if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) {
|
||||
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
|
||||
target.add(permanent.getId(), game);
|
||||
targetFound = true;
|
||||
isAddedSomething = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -2335,51 +2349,50 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
}
|
||||
|
||||
if (!targetFound) {
|
||||
//failOnLastBadChoice(game, source, target, choiceRecord, "unknown or can't target");
|
||||
}
|
||||
|
||||
try {
|
||||
if (target.isChosen(game)) {
|
||||
return true;
|
||||
} else {
|
||||
// TODO: move check above and fix all fail tests (not after target.isChosen)
|
||||
if (!targetFound) {
|
||||
failOnLastBadChoice(game, source, target, choiceRecord, "selected, but not all required targets");
|
||||
if (isAddedSomething) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
failOnLastBadChoice(game, source, target, choiceRecord, "invalid target or miss skip command");
|
||||
}
|
||||
} finally {
|
||||
choices.remove(0);
|
||||
}
|
||||
}
|
||||
return isAddedSomething;
|
||||
} // choices
|
||||
}
|
||||
|
||||
if (target instanceof TargetPlayer) {
|
||||
while (!choices.isEmpty()) {
|
||||
while (!choices.isEmpty()) { // TODO: remove cycle after main commits
|
||||
if (tryToSkipSelection(choices, CHOICE_SKIP)) {
|
||||
return false; // stop dialog
|
||||
}
|
||||
String choiceRecord = choices.get(0);
|
||||
boolean targetFound = false;
|
||||
isAddedSomething = false;
|
||||
for (Player player : game.getPlayers().values()) {
|
||||
if (player.getName().equals(choiceRecord)) {
|
||||
if (target.canTarget(abilityControllerId, player.getId(), null, game) && !target.getTargets().contains(player.getId())) {
|
||||
if (target.canTarget(abilityControllerId, player.getId(), null, game) && !target.contains(player.getId())) {
|
||||
target.add(player.getId(), game);
|
||||
targetFound = true;
|
||||
} else {
|
||||
failOnLastBadChoice(game, source, target, choiceRecord, "can't target");
|
||||
isAddedSomething = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (target.isChosen(game)) {
|
||||
return true;
|
||||
}
|
||||
if (!targetFound) {
|
||||
failOnLastBadChoice(game, source, target, choiceRecord, "unknown target");
|
||||
if (isAddedSomething) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
failOnLastBadChoice(game, source, target, choiceRecord, "invalid target or miss skip command");
|
||||
}
|
||||
} finally {
|
||||
choices.remove(0);
|
||||
}
|
||||
}
|
||||
return isAddedSomething;
|
||||
} // while choices
|
||||
}
|
||||
|
||||
// TODO: add same choices fixes for other target types (one choice must uses only one time for one target)
|
||||
|
|
@ -2388,102 +2401,78 @@ public class TestPlayer implements Player {
|
|||
// only unique targets
|
||||
//TargetCard targetFull = ((TargetCard) target);
|
||||
|
||||
usedChoices.clear();
|
||||
usedTargets.clear();
|
||||
boolean targetCompleted = false;
|
||||
|
||||
CheckAllChoices:
|
||||
for (int choiceIndex = 0; choiceIndex < choices.size(); choiceIndex++) {
|
||||
String choiceRecord = choices.get(choiceIndex);
|
||||
if (targetCompleted) {
|
||||
break CheckAllChoices;
|
||||
for (String choiceRecord : new ArrayList<>(choices)) { // TODO: remove cycle after main commits
|
||||
if (tryToSkipSelection(choices, CHOICE_SKIP)) {
|
||||
return false; // stop dialog
|
||||
}
|
||||
|
||||
boolean targetFound = false;
|
||||
isAddedSomething = false;
|
||||
String[] possibleChoices = choiceRecord.split("\\^");
|
||||
|
||||
CheckOneChoice:
|
||||
for (String possibleChoice : possibleChoices) {
|
||||
Set<UUID> possibleCards = target.possibleTargets(abilityControllerId, source, game);
|
||||
CheckTargetsList:
|
||||
for (UUID targetId : possibleCards) {
|
||||
MageObject targetObject = game.getCard(targetId);
|
||||
if (hasObjectTargetNameOrAlias(targetObject, possibleChoice)) {
|
||||
if (target.canTarget(targetObject.getId(), game)) {
|
||||
// only unique targets
|
||||
if (usedTargets.contains(targetObject.getId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// OK, can use it
|
||||
if (target.canTarget(targetObject.getId(), game) && !target.contains(targetObject.getId())) {
|
||||
target.add(targetObject.getId(), game);
|
||||
targetFound = true;
|
||||
usedTargets.add(targetObject.getId());
|
||||
|
||||
// break on full targets list
|
||||
if (target.getTargets().size() >= target.getMaxNumberOfTargets()) {
|
||||
targetCompleted = true;
|
||||
break CheckOneChoice;
|
||||
}
|
||||
|
||||
// restart search
|
||||
break CheckTargetsList;
|
||||
isAddedSomething = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetFound) {
|
||||
usedChoices.add(choiceIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// apply only on ALL targets or revert
|
||||
if (usedChoices.size() > 0) {
|
||||
if (target.isChosen(game)) {
|
||||
// remove all used choices
|
||||
for (int i = choices.size(); i >= 0; i--) {
|
||||
if (usedChoices.contains(i)) {
|
||||
choices.remove(i);
|
||||
try {
|
||||
if (isAddedSomething) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
failOnLastBadChoice(game, source, target, choiceRecord, "invalid target or miss skip command");
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
Assert.fail("Not full targets list.");
|
||||
target.clearChosen();
|
||||
} finally {
|
||||
choices.remove(0);
|
||||
}
|
||||
}
|
||||
return isAddedSomething;
|
||||
} // for choices
|
||||
}
|
||||
|
||||
if (target.getOriginalTarget() instanceof TargetSource) {
|
||||
Set<UUID> possibleTargets;
|
||||
TargetSource t = ((TargetSource) target.getOriginalTarget());
|
||||
possibleTargets = t.possibleTargets(abilityControllerId, source, game);
|
||||
for (String choiceRecord : choices) {
|
||||
Set<UUID> possibleTargets = t.possibleTargets(abilityControllerId, source, game);
|
||||
// TODO: enable choices.get first instead all
|
||||
for (String choiceRecord : new ArrayList<>(choices)) { // TODO: remove cycle after main commits
|
||||
if (tryToSkipSelection(choices, CHOICE_SKIP)) {
|
||||
return false; // stop dialog
|
||||
}
|
||||
String[] targetList = choiceRecord.split("\\^");
|
||||
boolean targetFound = false;
|
||||
isAddedSomething = false;
|
||||
for (String targetName : targetList) {
|
||||
for (UUID targetId : possibleTargets) {
|
||||
MageObject targetObject = game.getObject(targetId);
|
||||
if (targetObject != null) {
|
||||
if (hasObjectTargetNameOrAlias(targetObject, targetName)) {
|
||||
List<UUID> alreadyTargetted = target.getTargets();
|
||||
if (t.canTarget(targetObject.getId(), game)) {
|
||||
if (alreadyTargetted != null && !alreadyTargetted.contains(targetObject.getId())) {
|
||||
target.add(targetObject.getId(), game);
|
||||
choices.remove(choiceRecord);
|
||||
targetFound = true;
|
||||
}
|
||||
if (t.canTarget(targetObject.getId(), game) && !target.contains(targetObject.getId())) {
|
||||
target.add(targetObject.getId(), game);
|
||||
isAddedSomething = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (targetFound) {
|
||||
choices.remove(choiceRecord);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (isAddedSomething) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
failOnLastBadChoice(game, source, target, choiceRecord, "invalid target or miss skip command");
|
||||
}
|
||||
} finally {
|
||||
choices.remove(choiceRecord);
|
||||
}
|
||||
return isAddedSomething;
|
||||
} // for choices
|
||||
}
|
||||
|
||||
// TODO: enable fail checks and fix tests
|
||||
|
|
@ -2492,7 +2481,7 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
}
|
||||
|
||||
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target, game));
|
||||
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target, source, game));
|
||||
return computerPlayer.choose(outcome, target, source, game, options);
|
||||
}
|
||||
|
||||
|
|
@ -2535,7 +2524,7 @@ public class TestPlayer implements Player {
|
|||
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
||||
target.getTargets().size() >= target.getMinNumberOfTargets());
|
||||
targets.remove(0);
|
||||
return true;
|
||||
return false; // false - stop to choose
|
||||
}
|
||||
|
||||
Set<Zone> targetCardZonesChecked = new HashSet<>(); // control miss implementation
|
||||
|
|
@ -2594,7 +2583,7 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents((FilterPermanent) filter, abilityControllerId, source, game)) {
|
||||
if (hasObjectTargetNameOrAlias(permanent, targetName) || (permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { // TODO: remove exp code search?
|
||||
if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.getTargets().contains(permanent.getId())) {
|
||||
if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.contains(permanent.getId())) {
|
||||
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
|
||||
target.addTarget(permanent.getId(), source, game);
|
||||
targetFound = true;
|
||||
|
|
@ -2624,7 +2613,7 @@ public class TestPlayer implements Player {
|
|||
for (String targetName : targetList) {
|
||||
for (Card card : computerPlayer.getHand().getCards(((TargetCard) target.getOriginalTarget()).getFilter(), game)) {
|
||||
if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
|
||||
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
|
||||
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.contains(card.getId())) {
|
||||
target.addTarget(card.getId(), source, game);
|
||||
targetFound = true;
|
||||
break; // return to next targetName
|
||||
|
|
@ -2662,7 +2651,7 @@ public class TestPlayer implements Player {
|
|||
for (String targetName : targetList) {
|
||||
for (Card card : game.getExile().getCards(filter, game)) {
|
||||
if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
|
||||
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
|
||||
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.contains(card.getId())) {
|
||||
target.addTarget(card.getId(), source, game);
|
||||
targetFound = true;
|
||||
break; // return to next targetName
|
||||
|
|
@ -2687,7 +2676,7 @@ public class TestPlayer implements Player {
|
|||
for (String targetName : targetList) {
|
||||
for (Card card : game.getBattlefield().getAllActivePermanents()) {
|
||||
if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
|
||||
if (targetFull.canTarget(abilityControllerId, card.getId(), source, game) && !targetFull.getTargets().contains(card.getId())) {
|
||||
if (targetFull.canTarget(abilityControllerId, card.getId(), source, game) && !targetFull.contains(card.getId())) {
|
||||
targetFull.add(card.getId(), game);
|
||||
targetFound = true;
|
||||
break; // return to next targetName
|
||||
|
|
@ -2739,7 +2728,7 @@ public class TestPlayer implements Player {
|
|||
Player player = game.getPlayer(playerId);
|
||||
for (Card card : player.getGraveyard().getCards(targetFull.getFilter(), game)) {
|
||||
if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
|
||||
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
|
||||
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.contains(card.getId())) {
|
||||
target.addTarget(card.getId(), source, game);
|
||||
targetFound = true;
|
||||
break IterateGraveyards; // return to next targetName
|
||||
|
|
@ -2754,7 +2743,6 @@ public class TestPlayer implements Player {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// stack
|
||||
|
|
@ -2769,7 +2757,7 @@ public class TestPlayer implements Player {
|
|||
for (String targetName : targetList) {
|
||||
for (StackObject stackObject : game.getStack()) {
|
||||
if (hasObjectTargetNameOrAlias(stackObject, targetName)) {
|
||||
if (target.canTarget(abilityControllerId, stackObject.getId(), source, game) && !target.getTargets().contains(stackObject.getId())) {
|
||||
if (target.canTarget(abilityControllerId, stackObject.getId(), source, game) && !target.contains(stackObject.getId())) {
|
||||
target.addTarget(stackObject.getId(), source, game);
|
||||
targetFound = true;
|
||||
break; // return to next targetName
|
||||
|
|
@ -2804,24 +2792,25 @@ public class TestPlayer implements Player {
|
|||
// how to fix: implement target class processing above (if it a permanent target then check "filter instanceof" code too)
|
||||
if (!targets.isEmpty()) {
|
||||
String message;
|
||||
Set<UUID> possibleTargets = target.possibleTargets(abilityControllerId, source, game);
|
||||
|
||||
if (source != null) {
|
||||
message = this.getName() + " - Targets list was setup by addTarget with " + targets + ", but not used"
|
||||
+ "\nCard: " + source.getSourceObject(game)
|
||||
+ "\nAbility: " + source.getClass().getSimpleName() + " (" + source.getRule() + ")"
|
||||
+ "\nTarget: " + target.getClass().getSimpleName() + " (" + target.getMessage(game) + ")"
|
||||
+ "\nTarget: selected " + target.getSize() + ", possible " + possibleTargets.size() + ", " + target.getClass().getSimpleName() + " (" + target.getMessage(game) + ")"
|
||||
+ "\nYou must implement target class support in TestPlayer, \"filter instanceof\", or setup good targets";
|
||||
} else {
|
||||
message = this.getName() + " - Targets list was setup by addTarget with " + targets + ", but not used"
|
||||
+ "\nCard: unknown source"
|
||||
+ "\nAbility: unknown source"
|
||||
+ "\nTarget: " + target.getClass().getSimpleName() + " (" + target.getMessage(game) + ")"
|
||||
+ "\nTarget: selected " + target.getSize() + ", possible " + possibleTargets.size() + ", " + target.getClass().getSimpleName() + " (" + target.getMessage(game) + ")"
|
||||
+ "\nYou must implement target class support in TestPlayer, \"filter instanceof\", or setup good targets";
|
||||
}
|
||||
Assert.fail(message);
|
||||
}
|
||||
|
||||
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, game));
|
||||
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, source, game));
|
||||
return computerPlayer.chooseTarget(outcome, target, source, game);
|
||||
}
|
||||
|
||||
|
|
@ -2840,7 +2829,7 @@ public class TestPlayer implements Player {
|
|||
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
||||
target.getTargets().size() >= target.getMinNumberOfTargets());
|
||||
targets.remove(0);
|
||||
return true;
|
||||
return false; // false - stop to choose
|
||||
}
|
||||
for (String targetDefinition : targets.stream().limit(takeMaxTargetsPerChoose).collect(Collectors.toList())) {
|
||||
String[] targetList = targetDefinition.split("\\^");
|
||||
|
|
@ -2848,7 +2837,7 @@ public class TestPlayer implements Player {
|
|||
for (String targetName : targetList) {
|
||||
for (Card card : cards.getCards(game)) {
|
||||
if (hasObjectTargetNameOrAlias(card, targetName)
|
||||
&& !target.getTargets().contains(card.getId())
|
||||
&& !target.contains(card.getId())
|
||||
&& target.canTarget(abilityControllerId, card.getId(), source, cards, game)) {
|
||||
target.addTarget(card.getId(), source, game);
|
||||
targetFound = true;
|
||||
|
|
@ -2867,7 +2856,7 @@ public class TestPlayer implements Player {
|
|||
LOGGER.warn("Wrong target");
|
||||
}
|
||||
|
||||
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, game));
|
||||
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, source, game));
|
||||
return computerPlayer.chooseTarget(outcome, cards, target, source, game);
|
||||
}
|
||||
|
||||
|
|
@ -2930,7 +2919,7 @@ public class TestPlayer implements Player {
|
|||
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
|
||||
assertAliasSupportInChoices(false);
|
||||
if (!choices.isEmpty()) {
|
||||
for (String choice : choices) {
|
||||
for (String choice : new ArrayList<>(choices)) {
|
||||
if (choice.startsWith("X=")) {
|
||||
int xValue = Integer.parseInt(choice.substring(2));
|
||||
assertXMinMaxValue(game, ability, xValue, min, max);
|
||||
|
|
@ -2941,7 +2930,7 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
|
||||
this.chooseStrictModeFailed("choice", game, getInfo(ability, game)
|
||||
+ "\nMessage: " + message + prepareXMaxInfo(min, max));
|
||||
+ "\nMessage: " + message + prepareXMaxInfo(min, max));
|
||||
return computerPlayer.announceXMana(min, max, message, game, ability);
|
||||
}
|
||||
|
||||
|
|
@ -4276,32 +4265,40 @@ public class TestPlayer implements Player {
|
|||
return choose(outcome, target, source, game, null);
|
||||
}
|
||||
|
||||
private boolean tryToSkipSelection(List<String> selections, String selectionMark) {
|
||||
if (!selections.isEmpty() && selections.get(0).equals(selectionMark)) {
|
||||
selections.remove(0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean choose(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
|
||||
assertAliasSupportInChoices(false);
|
||||
if (!choices.isEmpty()) {
|
||||
|
||||
// skip choices
|
||||
if (choices.get(0).equals(CHOICE_SKIP)) {
|
||||
choices.remove(0);
|
||||
if (tryToSkipSelection(choices, CHOICE_SKIP)) {
|
||||
if (cards.isEmpty()) {
|
||||
// cancel button forced in GUI on no possible choices
|
||||
// TODO: need research
|
||||
return false;
|
||||
} else {
|
||||
Assert.assertTrue("found skip choice, but it require more choices, needs "
|
||||
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
||||
target.getTargets().size() >= target.getMinNumberOfTargets());
|
||||
return true;
|
||||
return false; // stop dialog
|
||||
}
|
||||
}
|
||||
|
||||
for (String choose2 : choices) {
|
||||
for (String choose2 : new ArrayList<>(choices)) {
|
||||
// TODO: More targetting to fix
|
||||
String[] targetList = choose2.split("\\^");
|
||||
boolean targetFound = false;
|
||||
for (String targetName : targetList) {
|
||||
for (Card card : cards.getCards(game)) {
|
||||
if (target.getTargets().contains(card.getId())) {
|
||||
if (target.contains(card.getId())) {
|
||||
continue;
|
||||
}
|
||||
if (hasObjectTargetNameOrAlias(card, targetName)) {
|
||||
|
|
@ -4322,7 +4319,7 @@ public class TestPlayer implements Player {
|
|||
assertWrongChoiceUsage(choices.size() > 0 ? choices.get(0) : "empty list");
|
||||
}
|
||||
|
||||
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target, game));
|
||||
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target, source, game));
|
||||
return computerPlayer.choose(outcome, cards, target, source, game);
|
||||
}
|
||||
|
||||
|
|
@ -4344,7 +4341,7 @@ public class TestPlayer implements Player {
|
|||
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
||||
target.getTargets().size() >= target.getMinNumberOfTargets());
|
||||
targets.remove(0);
|
||||
return false; // false in chooseTargetAmount = stop to choose
|
||||
return false; // false - stop to choose
|
||||
}
|
||||
|
||||
// only target amount needs
|
||||
|
|
@ -4384,7 +4381,7 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
|
||||
if (foundTarget) {
|
||||
if (!target.getTargets().contains(possibleTarget) && target.canTarget(possibleTarget, source, game)) {
|
||||
if (!target.contains(possibleTarget) && target.canTarget(possibleTarget, source, game)) {
|
||||
// can select
|
||||
target.addTarget(possibleTarget, targetAmount, source, game);
|
||||
targets.remove(0);
|
||||
|
|
@ -4395,7 +4392,7 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
}
|
||||
|
||||
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, game));
|
||||
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, source, game));
|
||||
return computerPlayer.chooseTargetAmount(outcome, target, source, game);
|
||||
}
|
||||
|
||||
|
|
@ -4744,7 +4741,7 @@ public class TestPlayer implements Player {
|
|||
Assert.fail(String.format("Found wrong choice command (%s):\n%s\n%s\n%s",
|
||||
reason,
|
||||
lastChoice,
|
||||
getInfo(target, game),
|
||||
getInfo(target, source, game),
|
||||
getInfo(source, game)
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import mage.ObjectColor;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.ContinuousEffectsList;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.decks.Deck;
|
||||
import mage.cards.decks.DeckCardLists;
|
||||
|
|
@ -32,7 +31,6 @@ import mage.players.Player;
|
|||
import mage.server.game.GameSessionPlayer;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.ThreadUtils;
|
||||
import mage.utils.StreamUtils;
|
||||
import mage.utils.SystemUtil;
|
||||
import mage.view.GameView;
|
||||
import org.junit.Assert;
|
||||
|
|
@ -1677,7 +1675,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
|||
|
||||
public void assertChoicesCount(TestPlayer player, int count) throws AssertionError {
|
||||
String mes = String.format(
|
||||
"(Choices of %s) Count are not equal (found %s). Some inner choose dialogs can be set up only in strict mode.",
|
||||
"(Choices of %s) Count are not equal (found %s). Make sure you use target.chooseXXX instead player.choose. Also some inner choose dialogs can be set up only in strict mode.",
|
||||
player.getName(),
|
||||
player.getChoices()
|
||||
);
|
||||
|
|
@ -1686,7 +1684,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
|||
|
||||
public void assertTargetsCount(TestPlayer player, int count) throws AssertionError {
|
||||
String mes = String.format(
|
||||
"(Targets of %s) Count are not equal (found %s). Some inner choose dialogs can be set up only in strict mode.",
|
||||
"(Targets of %s) Count are not equal (found %s). Make sure you use target.chooseXXX instead player.choose. Also some inner choose dialogs can be set up only in strict mode.",
|
||||
player.getName(),
|
||||
player.getTargets()
|
||||
);
|
||||
|
|
@ -2271,11 +2269,18 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
|||
setChoice(player, choice ? "Yes" : "No", timesToChoose);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare non target choice. You can use multiple choices in one line like setChoice(name1^name2)
|
||||
* Also support "up to" choices, e.g. choose 2 of 3 cards by setChoice(card1^card2) + setChoice(TestPlayer.CHOICE_SKIP)
|
||||
*/
|
||||
public void setChoice(TestPlayer player, String choice) {
|
||||
setChoice(player, choice, 1);
|
||||
}
|
||||
|
||||
public void setChoice(TestPlayer player, String choice, int timesToChoose) {
|
||||
if (choice.equals(TestPlayer.TARGET_SKIP)) {
|
||||
Assert.fail("setChoice allow only TestPlayer.CHOICE_SKIP, but found " + choice);
|
||||
}
|
||||
for (int i = 0; i < timesToChoose; i++) {
|
||||
player.addChoice(choice);
|
||||
}
|
||||
|
|
@ -2340,13 +2345,18 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
|||
*
|
||||
* @param player
|
||||
* @param target you can add multiple targets by separating them by the "^"
|
||||
* character e.g. "creatureName1^creatureName2" you can
|
||||
* qualify the target additional by setcode e.g.
|
||||
* character e.g. "creatureName1^creatureName2"
|
||||
* -
|
||||
* you can qualify the target additional by setcode e.g.
|
||||
* "creatureName-M15" you can add [no copy] to the end of the
|
||||
* target name to prohibit targets that are copied you can add
|
||||
* [only copy] to the end of the target name to allow only
|
||||
* targets that are copies. For modal spells use a prefix with
|
||||
* the mode number: mode=1Lightning Bolt^mode=2Silvercoat Lion
|
||||
* -
|
||||
* it's also support multiple addTarget commands instead single line,
|
||||
* so you can declare not full "up to" targets list by addTarget(name)
|
||||
* and addTarget(TestPlayer.TARGET_SKIP)
|
||||
*/
|
||||
// TODO: mode options doesn't work here (see BrutalExpulsionTest)
|
||||
public void addTarget(TestPlayer player, String target) {
|
||||
|
|
@ -2354,6 +2364,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
|||
}
|
||||
|
||||
public void addTarget(TestPlayer player, String target, int timesToChoose) {
|
||||
if (target.equals(TestPlayer.CHOICE_SKIP)) {
|
||||
Assert.fail("addTarget allow only TestPlayer.TARGET_SKIP, but found " + target);
|
||||
}
|
||||
|
||||
for (int i = 0; i < timesToChoose; i++) {
|
||||
assertAliaseSupportInActivateCommand(target, true);
|
||||
player.addTarget(target);
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public class LoadCheatsTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
setChoice(playerA, "7"); // choose [group 3]: 7 = 4 default menus + 3 group
|
||||
setChoice(playerA, "8"); // choose [group 3]: 8 = 5 default menus + 3 group
|
||||
SystemUtil.executeCheatCommands(currentGame, commandsFile, playerA);
|
||||
|
||||
assertHandCount(playerA, "Razorclaw Bear", 1);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue