mirror of
https://github.com/magefree/mage.git
synced 2026-01-10 12:52:06 -08:00
Reworking effects which allow casting spells from a selection of cards (ready for review) (#8136)
* added function for casting spells with specific attributes from a selection of cards * updated cascade to use new method * refactored various cards to use new methods * added TestPlayer method * fixed a small error * text fix * broke out some repeated code * added missing notTarget setting * add additional retain zone check * some more cards refactored * more refactoring * added interface for split/modal cards * reworked spell casting methods * reworked multiple cast to prevent unnecessary dialogs * fixed test failures due to change in functionality * add AI code * small nonfunctional change * reworked Kaya, the Inexorable * added currently failing test * added more tests * updated Geode Golem implementation * fixed adventure/cascade interaction, added/updated tests * some nonfunctional refactoring * added interface for subcards * [AFC] Implemented Fevered Suspicion * [AFC] Implemented Extract Brain * [AFC] updated Arcane Endeavor implementation * [C17] reworked implementation of Izzet Chemister * [ZEN] reworked implemented of Chandra Ablaze * additional merge fix * [SLD] updated Eleven, the Mage * [NEO] Implemented Discover the Impossible * [NEO] Implemented The Dragon-Kami Reborn / Dragon-Kami's Egg * [NEO] Implemented Invoke Calamity * [AFR] Implemented Rod of Absorption * [VOC] Implemented Spectral Arcanist * [VOC] added additional printings * [NEO] added all variants * [SLD] updated implementation of Ken, Burning Brawler
This commit is contained in:
parent
7fb089db48
commit
bbb9382150
83 changed files with 2551 additions and 2059 deletions
|
|
@ -143,7 +143,7 @@ public class BuybackTest extends CardTestPlayerBase {
|
|||
// bolt 2 - cast (R) and copy as free cast (R), return reiterate with buyback (RRR)
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}:");
|
||||
setChoice(playerA, "Reiterate"); // free cast
|
||||
setChoice(playerA, true); // cast for free
|
||||
setChoice(playerA, true); // use buyback
|
||||
addTarget(playerA, "Lightning Bolt"); // copy target
|
||||
setChoice(playerA, false); // same bolt's target
|
||||
|
|
|
|||
|
|
@ -670,7 +670,6 @@ public class KickerTest extends CardTestPlayerBase {
|
|||
// attack and prepare free cast, use kicker
|
||||
attack(1, playerA, "Etali, Primal Storm", playerB);
|
||||
setChoice(playerA, true); // cast for free
|
||||
setChoice(playerA, "Ardent Soldier"); // cast for free
|
||||
setChoice(playerA, true); // use kicker
|
||||
|
||||
setStrictChooseMode(true);
|
||||
|
|
@ -703,7 +702,6 @@ public class KickerTest extends CardTestPlayerBase {
|
|||
// attack and prepare free cast
|
||||
attack(1, playerA, "Etali, Primal Storm", playerB);
|
||||
setChoice(playerA, true); // cast for free
|
||||
setChoice(playerA, "Thieving Skydiver"); // cast for free
|
||||
setChoice(playerA, true); // use kicker
|
||||
setChoiceAmount(playerA, 2); // X=2 for Kicker X
|
||||
addTarget(playerA, "Brain in a Jar"); // kicker's target (take control of artifact)
|
||||
|
|
|
|||
|
|
@ -766,4 +766,122 @@ public class AdventureCardsTest extends CardTestPlayerBase {
|
|||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Cascade_CuriousPair() {
|
||||
// If a player cascades into Curious Pair with Bloodbraid Elf they can cast either spell
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
skipInitShuffling();
|
||||
|
||||
// Cascade
|
||||
addCard(Zone.HAND, playerA, "Bloodbraid Elf"); // {2}{R}{G}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||
//
|
||||
addCard(Zone.LIBRARY, playerA, "Swamp", 2);
|
||||
addCard(Zone.LIBRARY, playerA, "Curious Pair", 1);
|
||||
addCard(Zone.LIBRARY, playerA, "Island", 2);
|
||||
|
||||
// play elf with cascade
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodbraid Elf");
|
||||
setChoice(playerA, true); // use free cast
|
||||
setChoice(playerA, "Cast Treats to Share"); // can cast either
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Curious Pair", 0);
|
||||
assertPermanentCount(playerA, "Food", 1);
|
||||
assertExileCount(playerA, "Curious Pair", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Cascade_FlaxenIntruder() {
|
||||
// If a player cascades into Flaxen Intruder with Bloodbraid Elf they shouldn't be able to cast Welcome Home
|
||||
removeAllCardsFromLibrary(playerA);
|
||||
skipInitShuffling();
|
||||
|
||||
// Cascade
|
||||
addCard(Zone.HAND, playerA, "Bloodbraid Elf"); // {2}{R}{G}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
|
||||
//
|
||||
addCard(Zone.LIBRARY, playerA, "Swamp", 2);
|
||||
addCard(Zone.LIBRARY, playerA, "Flaxen Intruder", 1);
|
||||
addCard(Zone.LIBRARY, playerA, "Island", 2);
|
||||
|
||||
// play elf with cascade
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodbraid Elf");
|
||||
setChoice(playerA, true); // use free cast
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Flaxen Intruder", 1);
|
||||
assertPermanentCount(playerA, "Bear", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SramsExpertise_CuriousPair() {
|
||||
addCard(Zone.HAND, playerA, "Sram's Expertise");
|
||||
addCard(Zone.HAND, playerA, "Curious Pair");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sram's Expertise");
|
||||
setChoice(playerA, true); // use free cast
|
||||
setChoice(playerA, "Cast Treats to Share");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Curious Pair", 0);
|
||||
assertPermanentCount(playerA, "Food", 1);
|
||||
assertPermanentCount(playerA, "Servo", 3);
|
||||
assertExileCount(playerA, "Curious Pair", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SramsExpertise_FlaxenIntruder() {
|
||||
addCard(Zone.HAND, playerA, "Sram's Expertise");
|
||||
addCard(Zone.HAND, playerA, "Flaxen Intruder");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sram's Expertise");
|
||||
setChoice(playerA, true); // use free cast
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Flaxen Intruder", 1);
|
||||
assertPermanentCount(playerA, "Bear", 0);
|
||||
assertPermanentCount(playerA, "Servo", 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SramsExpertise_LonesomeUnicorn() {
|
||||
addCard(Zone.HAND, playerA, "Sram's Expertise");
|
||||
addCard(Zone.HAND, playerA, "Lonesome Unicorn");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sram's Expertise");
|
||||
setChoice(playerA, true); // use free cast
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Lonesome Unicorn", 0);
|
||||
assertPermanentCount(playerA, "Knight", 1);
|
||||
assertPermanentCount(playerA, "Servo", 3);
|
||||
assertExileCount(playerA, "Lonesome Unicorn", 1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -866,6 +866,44 @@ public class ModalDoubleFacesCardsTest extends CardTestPlayerBase {
|
|||
assertPermanentCount(playerA, "The Omenkeel", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SramsExpertise_ValkiGodOfLies() {
|
||||
addCard(Zone.HAND, playerA, "Sram's Expertise");
|
||||
addCard(Zone.HAND, playerA, "Valki, God of Lies");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sram's Expertise");
|
||||
setChoice(playerA, true); // use free cast
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP); // no choices for valki's etb exile
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Valki, God of Lies", 1);
|
||||
assertPermanentCount(playerA, "Servo", 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SramsExpertise_CosimaGodOfTheVoyage() {
|
||||
addCard(Zone.HAND, playerA, "Sram's Expertise");
|
||||
addCard(Zone.HAND, playerA, "Cosima, God of the Voyage");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sram's Expertise");
|
||||
setChoice(playerA, true); // use free cast
|
||||
setChoice(playerA, "Cast The Omenkeel"); // can cast any side here
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "The Omenkeel", 1);
|
||||
assertPermanentCount(playerA, "Servo", 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Copy_AsSpell() {
|
||||
addCard(Zone.HAND, playerA, "Akoum Warrior", 1); // {5}{R}
|
||||
|
|
@ -1000,4 +1038,4 @@ public class ModalDoubleFacesCardsTest extends CardTestPlayerBase {
|
|||
execute();
|
||||
assertAllCommandsUsed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ public class CastSplitCardsFromOtherZonesTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mindclaw Shaman");
|
||||
addTarget(playerA, playerB);
|
||||
setChoice(playerA, "Wear // Tear"); // select card
|
||||
setChoice(playerA, true); // confirm to cast
|
||||
setChoice(playerA, "Cast Tear"); // select tear side
|
||||
addTarget(playerA, "Sanguine Bond"); // target for tear
|
||||
|
|
@ -67,7 +66,6 @@ public class CastSplitCardsFromOtherZonesTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mindclaw Shaman");
|
||||
addTarget(playerA, playerB);
|
||||
setChoice(playerA, "Wear // Tear"); // select card
|
||||
setChoice(playerA, true); // confirm to cast
|
||||
setChoice(playerA, "Cast Wear"); // select wear side
|
||||
addTarget(playerA, "Icy Manipulator"); // target for wear
|
||||
|
|
@ -100,7 +98,6 @@ public class CastSplitCardsFromOtherZonesTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mindclaw Shaman");
|
||||
addTarget(playerA, playerB);
|
||||
setChoice(playerA, "Wear // Tear"); // select card
|
||||
setChoice(playerA, true); // confirm to cast
|
||||
setChoice(playerA, "Cast fused Wear // Tear"); // select fused
|
||||
addTarget(playerA, "Icy Manipulator"); // target for wear
|
||||
|
|
@ -137,7 +134,6 @@ public class CastSplitCardsFromOtherZonesTest extends CardTestPlayerBase {
|
|||
|
||||
attack(2, playerB, "Etali, Primal Storm");
|
||||
setChoice(playerB, true); // free cast
|
||||
setChoice(playerB, "Fire // Ice"); // card to cast
|
||||
setChoice(playerB, "Cast Fire"); // ability to cast
|
||||
addTargetAmount(playerB, "Silvercoat Lion", 2);
|
||||
|
||||
|
|
|
|||
|
|
@ -4364,19 +4364,17 @@ public class TestPlayer implements Player {
|
|||
@Override
|
||||
public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) {
|
||||
assertAliasSupportInChoices(false);
|
||||
|
||||
MageObject object = game.getObject(card.getId()); // must be object to find real abilities (example: commander)
|
||||
Map<UUID, ActivatedAbility> useable = PlayerImpl.getCastableSpellAbilities(game, this.getId(), object, game.getState().getZone(object.getId()), noMana);
|
||||
String allInfo = useable.values().stream().map(Object::toString).collect(Collectors.joining("\n"));
|
||||
Map<UUID, SpellAbility> useable = PlayerImpl.getCastableSpellAbilities(game, this.getId(), object, game.getState().getZone(object.getId()), noMana);
|
||||
if (useable.size() == 1) {
|
||||
return (SpellAbility) useable.values().iterator().next();
|
||||
return useable.values().iterator().next();
|
||||
}
|
||||
|
||||
if (!choices.isEmpty()) {
|
||||
for (ActivatedAbility ability : useable.values()) {
|
||||
for (SpellAbility ability : useable.values()) {
|
||||
if (ability.toString().startsWith(choices.get(0))) {
|
||||
choices.remove(0);
|
||||
return (SpellAbility) ability;
|
||||
return ability;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4384,6 +4382,7 @@ public class TestPlayer implements Player {
|
|||
//Assert.fail("Wrong choice");
|
||||
}
|
||||
|
||||
String allInfo = useable.values().stream().map(Object::toString).collect(Collectors.joining("\n"));
|
||||
this.chooseStrictModeFailed("choice", game, getInfo(card) + " - can't select ability to cast.\n" + "Card's abilities:\n" + allInfo);
|
||||
return computerPlayer.chooseAbilityForCast(card, game, noMana);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue