implement [MH3] Nethergoyf, refactor targets usages by game param (#12267)

This commit is contained in:
Susucre 2024-05-21 13:34:38 +02:00 committed by GitHub
parent 88b6f4036f
commit 754b382e78
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
62 changed files with 592 additions and 285 deletions

View file

@ -0,0 +1,193 @@
package org.mage.test.cards.single.mh3;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
/**
* @author Susucr
*/
public class NethergoyfTest extends CardTestPlayerBaseWithAIHelps {
/**
* {@link mage.cards.n.Nethergoyf Nethergoyf} {B}
* Creature Lhurgoyf
* Nethergoyfs power is equal to the number of card types among cards in your graveyard and its toughness is equal to that number plus 1.
* Escape{2}{B}, Exile any number of other cards from your graveyard with four or more card types among them. (You may cast this card from your graveyard for its escape cost.)
* * / 1+*
*/
private static final String nethergoyf = "Nethergoyf";
@Test
public void test_Escape_Two_DualTypes() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.GRAVEYARD, playerA, "Memnite"); // Creature Artifact
addCard(Zone.GRAVEYARD, playerA, "Bitterblossom"); // Tribal Enchantment
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^Bitterblossom"); // cards exiled for escape cost
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, nethergoyf, 1);
assertPowerToughness(playerA, nethergoyf, 0, 1);
assertExileCount(playerA, 2);
}
@Test
public void test_AI_Escape_Two_DualTypes() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.GRAVEYARD, playerA, "Memnite"); // Creature Artifact
addCard(Zone.GRAVEYARD, playerA, "Bitterblossom"); // Tribal Enchantment
checkPlayableAbility("can escape", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", true);
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, nethergoyf, 1);
assertPowerToughness(playerA, nethergoyf, 0, 1);
assertExileCount(playerA, 2);
}
@Test
public void test_Escape_MoreCardsThanNeeded() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.GRAVEYARD, playerA, "Memnite", 5); // Creature Artifact
addCard(Zone.GRAVEYARD, playerA, "Bitterblossom"); // Tribal Enchantment
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.
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, nethergoyf, 1);
assertPowerToughness(playerA, nethergoyf, 2, 3); // 1 Memnite in graveyard
assertExileCount(playerA, 5);
assertGraveyardCount(playerA, 1);
}
@Test
public void test_AI_Escape_MoreCardsThanNeeded() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.GRAVEYARD, playerA, "Bitterblossom"); // Tribal Enchantment
addCard(Zone.GRAVEYARD, playerA, "Memnite", 5); // Creature Artifact
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, nethergoyf, 1);
assertPowerToughness(playerA, nethergoyf, 0, 1);
assertExileCount(playerA, 6); // It is weird, but AI likes to choose all to be exiled, even though the Outcome is Exile (so detriment)
assertGraveyardCount(playerA, 0);
}
@Test
public void test_CantEscape_Without4TypesInGraveyard() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.GRAVEYARD, playerA, "Taiga"); // Land
addCard(Zone.GRAVEYARD, playerA, "Bitterblossom"); // Tribal Enchantment
checkPlayableAbility("can't escape", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", false);
// 3 types from other cards in graveyard, Nethergoyf can't escape
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, 3);
}
@Test
public void test_AI_CantEscape_Without4TypesInGraveyard() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.GRAVEYARD, playerA, "Taiga"); // Land
addCard(Zone.GRAVEYARD, playerA, "Bitterblossom"); // Tribal Enchantment
checkPlayableAbility("can't escape", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", false);
// 3 types from other cards in graveyard, Nethergoyf can't escape
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, 3);
}
@Test
public void test_DynamicGameType() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 7);
addCard(Zone.GRAVEYARD, playerA, "Taiga"); // Land
addCard(Zone.GRAVEYARD, playerA, "Grist, the Hunger Tide"); // Planeswalker, is a Creature if not in play
// Nonland permanents you control are artifacts in addition to their other types.
// The same is true for permanent spells you control and nonland permanent cards you own that arent on the battlefield.
addCard(Zone.HAND, playerA, "Encroaching Mycosynth");
checkPlayableAbility("1: can't escape", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", false);
// 3 types from other cards in graveyard, Nethergoyf can't escape
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Encroaching Mycosynth", true);
// After Mycosynth in play, Grist is now an Artifact in addition to its other types
checkPlayableAbility("2: can", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, nethergoyf + " with Escape");
setChoice(playerA, "Taiga^Grist, the Hunger Tide"); // cards exiled for escape cost
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, nethergoyf, 1);
assertPowerToughness(playerA, nethergoyf, 0, 1);
assertExileCount(playerA, 2);
}
@Test
public void test_AI_DynamicGameType() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 7);
addCard(Zone.GRAVEYARD, playerA, "Taiga"); // Land
addCard(Zone.GRAVEYARD, playerA, "Grist, the Hunger Tide"); // Planeswalker, is a Creature if not in play
// Nonland permanents you control are artifacts in addition to their other types.
// The same is true for permanent spells you control and nonland permanent cards you own that arent on the battlefield.
addCard(Zone.HAND, playerA, "Encroaching Mycosynth");
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, nethergoyf, 1);
assertPowerToughness(playerA, nethergoyf, 0, 1);
assertExileCount(playerA, 2);
}
}

View file

@ -2001,8 +2001,8 @@ public class TestPlayer implements Player {
return "Ability: null";
}
private String getInfo(Target o) {
return "Target: " + (o != null ? o.getClass().getSimpleName() + ": " + o.getMessage() : "null");
private String getInfo(Target o, Game game) {
return "Target: " + (o != null ? o.getClass().getSimpleName() + ": " + o.getMessage(game) : "null");
}
private void assertAliasSupportInChoices(boolean methodSupportAliases) {
@ -2167,7 +2167,7 @@ public class TestPlayer implements Player {
}
// ignore player select
if (target.getMessage().equals("Select a starting player")) {
if (target.getMessage(game).equals("Select a starting player")) {
return computerPlayer.choose(outcome, target, source, game, options);
}
@ -2317,7 +2317,7 @@ public class TestPlayer implements Player {
// apply only on ALL targets or revert
if (usedChoices.size() > 0) {
if (target.isChosen()) {
if (target.isChosen(game)) {
// remove all used choices
for (int i = choices.size(); i >= 0; i--) {
if (usedChoices.contains(i)) {
@ -2369,7 +2369,7 @@ public class TestPlayer implements Player {
}
}
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target));
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target, game));
return computerPlayer.choose(outcome, target, source, game, options);
}
@ -2690,19 +2690,19 @@ public class TestPlayer implements Player {
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() + ")"
+ "\nTarget: " + 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() + ")"
+ "\nTarget: " + 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));
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, game));
return computerPlayer.chooseTarget(outcome, target, source, game);
}
@ -2748,7 +2748,7 @@ public class TestPlayer implements Player {
LOGGER.warn("Wrong target");
}
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target));
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, game));
return computerPlayer.chooseTarget(outcome, cards, target, source, game);
}
@ -4134,7 +4134,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));
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target, game));
return computerPlayer.choose(outcome, cards, target, source, game);
}
@ -4207,7 +4207,7 @@ public class TestPlayer implements Player {
}
}
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target));
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, game));
return computerPlayer.chooseTargetAmount(outcome, target, source, game);
}