mirror of
https://github.com/magefree/mage.git
synced 2025-12-21 19:11:59 -08:00
- refactor: migrated AI's target amount code to shared selection logic; - ai: fixed game freezes on some use cases; - tests: added AI's testable dialogs for target amount; - tests: improved load tests result table, added game cycles stats; - Dwarven Catapult - fixed game error on usage;
This commit is contained in:
parent
8e7a7e9fc6
commit
f3e18e245f
23 changed files with 502 additions and 390 deletions
|
|
@ -21,52 +21,81 @@ import org.mage.test.serverside.base.CardTestPlayerBaseAI;
|
|||
public class TargetPriorityTest extends CardTestPlayerBaseAI {
|
||||
|
||||
// TODO: enable _target_ tests after computerPlayer.chooseTarget will be reworks like chooseTargetAmount
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void test_target_PriorityKillByBigPT() {
|
||||
public void test_Target_PriorityDamageToGoodOpponent() {
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 3); // 1/1
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 3); // 2/2
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Ashcoat Bear", 3); // 2/2 with ability
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Golden Bear", 3); // 4/3
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Battering Sliver", 3); // 4/4 with ability
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Ashcoat Bear", 3); // 2/2 with ability
|
||||
|
||||
// AI must make damage to opponent
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt");
|
||||
|
||||
setStrictChooseMode(false);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20 - 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Target_PriorityDamageToBadLowestCreature() {
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
//
|
||||
// You have shroud.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Ivory Mask", 1);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 3); // 2/2
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Memnite", 3); // 1/1
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Ashcoat Bear", 3); // 2/2 with ability
|
||||
|
||||
// AI can't target opponent so target own lowest creature
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt");
|
||||
|
||||
setStrictChooseMode(false);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
assertPermanentCount(playerA, "Memnite", 3 - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Target_PriorityDamageToBiggestCreature() {
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
//
|
||||
// You have shroud.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Ivory Mask", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Ivory Mask", 1);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 3); // 1/1
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 3); // 2/2
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Ashcoat Bear", 3); // 2/2 with ability
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Golden Bear", 3); // 4/3
|
||||
//addCard(Zone.BATTLEFIELD, playerB, "Battering Sliver", 3); // 4/4 with ability TODO: add after AI will simulation simple choices too
|
||||
|
||||
// AI must choose biggest creature to kill from opponent - 4/3
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
showBattlefield("as", 1, PhaseStep.PRECOMBAT_MAIN, playerB);
|
||||
|
||||
setStrictChooseMode(false);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
assertPermanentCount(playerB, "Memnite", 3);
|
||||
assertPermanentCount(playerB, "Balduvian Bears", 3);
|
||||
assertPermanentCount(playerB, "Ashcoat Bear", 3);
|
||||
assertPermanentCount(playerB, "Golden Bear", 3 - 1);
|
||||
assertPermanentCount(playerB, "Battering Sliver", 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void test_target_PriorityByKillByLowPT() {
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 3); // 1/1
|
||||
//addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 3); // 2/2
|
||||
//addCard(Zone.BATTLEFIELD, playerB, "Ashcoat Bear", 3); // 2/2 with ability
|
||||
//addCard(Zone.BATTLEFIELD, playerB, "Golden Bear", 3); // 4/3
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Battering Sliver", 3); // 4/4 with ability
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerB, "Memnite", 3 - 1);
|
||||
//assertPermanentCount(playerB, "Balduvian Bears", 3);
|
||||
//assertPermanentCount(playerB, "Ashcoat Bear", 3);
|
||||
//assertPermanentCount(playerB, "Golden Bear", 3);
|
||||
assertPermanentCount(playerB, "Battering Sliver", 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -153,7 +182,7 @@ public class TargetPriorityTest extends CardTestPlayerBaseAI {
|
|||
|
||||
// TARGET AMOUNT
|
||||
@Test
|
||||
public void test_targetAmount_PriorityKillByBigPT() {
|
||||
public void test_TargetAmount_PriorityKillByBigPT() {
|
||||
addCard(Zone.HAND, playerA, "Flames of the Firebrand"); // damage 3
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
//
|
||||
|
|
@ -176,7 +205,7 @@ public class TargetPriorityTest extends CardTestPlayerBaseAI {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void test_targetAmount_PriorityByKillByLowPT() {
|
||||
public void test_TargetAmount_PriorityByKillByLowPT() {
|
||||
addCard(Zone.HAND, playerA, "Flames of the Firebrand"); // damage 3
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
//
|
||||
|
|
@ -199,7 +228,7 @@ public class TargetPriorityTest extends CardTestPlayerBaseAI {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void test_targetAmount_PriorityKillByExtraPoints() {
|
||||
public void test_TargetAmount_PriorityKillByExtraPoints() {
|
||||
addCard(Zone.HAND, playerA, "Flames of the Firebrand"); // damage 3
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
//
|
||||
|
|
@ -222,7 +251,7 @@ public class TargetPriorityTest extends CardTestPlayerBaseAI {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void test_targetAmount_NormalCase() {
|
||||
public void test_TargetAmount_NormalCase() {
|
||||
Ability ability = new SimpleActivatedAbility(Zone.ALL, new DamageMultiEffect(), new ManaCostsImpl<>("{R}"));
|
||||
ability.addTarget(new TargetCreaturePermanentAmount(3, 0, 3));
|
||||
addCustomCardWithAbility("damage 3", playerA, ability);
|
||||
|
|
@ -247,7 +276,7 @@ public class TargetPriorityTest extends CardTestPlayerBaseAI {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void test_targetAmount_BadCase() {
|
||||
public void test_TargetAmount_BadCase() {
|
||||
// choose targets as enters battlefield (e.g. can't be canceled)
|
||||
SpellAbility spell = new SpellAbility(new ManaCostsImpl<>("{R}"), "damage 3", Zone.HAND);
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new DamageMultiEffect());
|
||||
|
|
@ -263,14 +292,12 @@ public class TargetPriorityTest extends CardTestPlayerBaseAI {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "damage 3");
|
||||
|
||||
// must damage x3 Balduvian Bears by -1 to keep alive
|
||||
checkDamage("pt after", 1, PhaseStep.BEGIN_COMBAT, playerA, "Balduvian Bears", 1);
|
||||
// showBattlefield("after", 1, PhaseStep.BEGIN_COMBAT, playerA);
|
||||
// up to target is optional, so AI must choose nothing due only bad targets
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "damage 3", 1);
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
assertPermanentCount(playerA, "Memnite", 3);
|
||||
assertPermanentCount(playerA, "Balduvian Bears", 3);
|
||||
assertPermanentCount(playerA, "Ashcoat Bear", 3);
|
||||
|
|
@ -280,7 +307,7 @@ public class TargetPriorityTest extends CardTestPlayerBaseAI {
|
|||
|
||||
@Test
|
||||
@Ignore // do not enable it in production, only for devs
|
||||
public void test_targetAmount_Performance() {
|
||||
public void test_TargetAmount_Performance() {
|
||||
int cardsMultiplier = 3;
|
||||
|
||||
Ability ability = new SimpleActivatedAbility(Zone.ALL, new DamageMultiEffect(), new ManaCostsImpl<>("{R}"));
|
||||
|
|
|
|||
|
|
@ -14,19 +14,22 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
|
|||
public class VivienTest extends CardTestPlayerBase {
|
||||
|
||||
@Test
|
||||
public void testVivienArkbowRangerAbility1NoTargets() {
|
||||
setStrictChooseMode(true);
|
||||
public void test_Distribute_NoTargets() {
|
||||
// +1: Distribute two +1/+1 counters among up to two target creatures. They gain trample until end of turn.
|
||||
// −3: Target creature you control deals damage equal to its power to target creature or planeswalker.
|
||||
// −5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand.
|
||||
addCard(Zone.HAND, playerA, "Vivien, Arkbow Ranger"); // Planeswalker {1}{G}{G}{G} - starts with 4 Loyality counters
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
|
||||
|
||||
// You can activate Vivien’s first ability without choosing any target creatures. The counters won’t be
|
||||
// put on anything. This is a change from previous rules regarding distributing counters.
|
||||
// (2019-07-12)
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vivien, Arkbow Ranger", true);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Distribute");
|
||||
addTargetAmount(playerA, TestPlayer.TARGET_SKIP); // stop choosing (not targets)
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Distribute");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
|
|
@ -36,8 +39,7 @@ public class VivienTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testVivienArkbowRangerAbilityOnePossibleTargetWithOne() {
|
||||
setStrictChooseMode(true);
|
||||
public void test_Distribute_OneTarget() {
|
||||
// +1: Distribute two +1/+1 counters among up to two target creatures. They gain trample until end of turn.
|
||||
// −3: Target creature you control deals damage equal to its power to target creature or planeswalker.
|
||||
// −5: You may choose a creature card you own from outside the game, reveal it, and put it into your hand.
|
||||
|
|
@ -49,16 +51,16 @@ public class VivienTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vivien, Arkbow Ranger", true);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Distribute");
|
||||
addTargetAmount(playerA, "Silvercoat Lion", 1);
|
||||
addTargetAmount(playerA, TestPlayer.TARGET_SKIP); // stop choosing (one target)
|
||||
addTargetAmount(playerA, "Silvercoat Lion", 2);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Vivien, Arkbow Ranger", 1);
|
||||
assertCounterCount("Vivien, Arkbow Ranger", CounterType.LOYALTY, 5);
|
||||
|
||||
assertPowerToughness(playerB, "Silvercoat Lion", 2 + 1, 2 + 1);
|
||||
assertPowerToughness(playerB, "Silvercoat Lion", 2 + 2, 2 + 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -75,7 +75,6 @@ public class TestableDialogsTest extends CardTestPlayerBaseWithAIHelps {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore // TODO: enable and fix all failed dialogs
|
||||
public void test_RunAll_AI() {
|
||||
// it's impossible to setup 700+ dialogs, so all choices made by AI
|
||||
// current AI uses only simple choices in dialogs, not simulations
|
||||
|
|
@ -108,7 +107,7 @@ public class TestableDialogsTest extends CardTestPlayerBaseWithAIHelps {
|
|||
@Test
|
||||
@Ignore // debug only - run single dialog by reg number
|
||||
public void test_RunSingle_Debugging() {
|
||||
int needRegNumber = 93;
|
||||
int needRegNumber = 557;
|
||||
|
||||
prepareCards();
|
||||
|
||||
|
|
|
|||
|
|
@ -918,12 +918,17 @@ public class LoadTest {
|
|||
public int getTotalEffectsCount() {
|
||||
return finalGameView == null ? 0 : this.finalGameView.getTotalEffectsCount();
|
||||
}
|
||||
|
||||
public int getGameCycle() {
|
||||
return finalGameView == null ? 0 : this.finalGameView.getGameCycle();
|
||||
}
|
||||
}
|
||||
|
||||
private static class LoadTestGameResultsList extends HashMap<Integer, LoadTestGameResult> {
|
||||
|
||||
private static final String tableFormatHeader = "|%-10s|%-15s|%-20s|%-10s|%-10s|%-10s|%-10s|%-10s|%-15s|%-15s|%-10s|%n";
|
||||
private static final String tableFormatData = "|%-10s|%15s|%20s|%10s|%10s|%10s|%10s|%10s|%15s|%15s|%10s|%n";
|
||||
// index, name, random sid, game cycle, errors, effects, turn, life p1, life p2, creatures p1, creatures p2, =time, sec, ~time, sec
|
||||
private static final String tableFormatHeader = "|%-10s|%-15s|%-20s|%-10s|%-10s|%-10s|%-10s|%-10s|%-10s|%-15s|%-15s|%-15s|%-15s|%n";
|
||||
private static final String tableFormatData = "|%-10s|%15s|%20s|%10s|%10s|%10s|%10s|%10s|%10s|%15s|%15s|%15s|%15s|%n";
|
||||
|
||||
public LoadTestGameResult createGame(int index, String name, long randomSeed) {
|
||||
if (this.containsKey(index)) {
|
||||
|
|
@ -939,6 +944,7 @@ public class LoadTest {
|
|||
"index",
|
||||
"name",
|
||||
"random sid",
|
||||
"game cycles",
|
||||
"errors",
|
||||
"effects",
|
||||
"turn",
|
||||
|
|
@ -946,8 +952,8 @@ public class LoadTest {
|
|||
"life p2",
|
||||
"creatures p1",
|
||||
"creatures p2",
|
||||
"time, sec",
|
||||
"time per turn, sec"
|
||||
"=time, sec",
|
||||
"~time, sec"
|
||||
);
|
||||
System.out.printf(tableFormatHeader, data.toArray());
|
||||
}
|
||||
|
|
@ -961,6 +967,7 @@ public class LoadTest {
|
|||
String.valueOf(gameResult.index), //"index",
|
||||
gameResult.name, //"name",
|
||||
String.valueOf(gameResult.randomSeed), // "random sid",
|
||||
String.valueOf(gameResult.getGameCycle()), // "game cycles",
|
||||
String.valueOf(gameResult.getTotalErrorsCount()), // "errors",
|
||||
String.valueOf(gameResult.getTotalEffectsCount()), // "effects",
|
||||
gameResult.getTurnInfo(), //"turn",
|
||||
|
|
@ -979,15 +986,16 @@ public class LoadTest {
|
|||
"TOTAL/AVG", //"index",
|
||||
String.valueOf(this.size()), //"name",
|
||||
"total, secs: " + String.format("%.3f", (float) this.getTotalDurationMs() / 1000), // "random sid",
|
||||
String.valueOf(this.getTotalErrorsCount()), // errors
|
||||
String.valueOf(this.getAvgEffectsCount()), // effects
|
||||
String.valueOf(this.getAvgTurn()), // turn
|
||||
String.valueOf(this.getAvgLife1()), // life p1
|
||||
String.valueOf(this.getAvgLife2()), // life p2
|
||||
String.valueOf(this.getAvgCreaturesCount1()), // creatures p1
|
||||
String.valueOf(this.getAvgCreaturesCount2()), // creatures p2
|
||||
String.valueOf(String.format("%.3f", (float) this.getAvgDurationMs() / 1000)), // time, sec
|
||||
String.valueOf(String.format("%.3f", (float) this.getAvgDurationPerTurnMs() / 1000)) // time per turn, sec
|
||||
"~" + this.getAvgGameCycle(), // game cycles
|
||||
"=" + this.getTotalErrorsCount(), // errors
|
||||
"~" + this.getAvgEffectsCount(), // effects
|
||||
"~" + this.getAvgTurn(), // turn
|
||||
"~" + this.getAvgLife1(), // life p1
|
||||
"~" + this.getAvgLife2(), // life p2
|
||||
"~" + this.getAvgCreaturesCount1(), // creatures p1
|
||||
"~" + this.getAvgCreaturesCount2(), // creatures p2
|
||||
"~" + String.format("%.3f", (float) this.getAvgDurationMs() / 1000), // time, sec
|
||||
"~" + String.format("%.3f", (float) this.getAvgDurationPerTurnMs() / 1000) // time per turn, sec
|
||||
);
|
||||
System.out.printf(tableFormatData, data.toArray());
|
||||
}
|
||||
|
|
@ -996,6 +1004,10 @@ public class LoadTest {
|
|||
return this.values().stream().mapToInt(LoadTestGameResult::getTotalErrorsCount).sum();
|
||||
}
|
||||
|
||||
private int getAvgGameCycle() {
|
||||
return this.size() == 0 ? 0 : this.values().stream().mapToInt(LoadTestGameResult::getGameCycle).sum() / this.size();
|
||||
}
|
||||
|
||||
private int getAvgEffectsCount() {
|
||||
return this.size() == 0 ? 0 : this.values().stream().mapToInt(LoadTestGameResult::getTotalEffectsCount).sum() / this.size();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -543,7 +543,7 @@ public class TestPlayer implements Player {
|
|||
if (currentTarget.getOriginalTarget() instanceof TargetCreaturePermanentAmount) {
|
||||
// supports only to set the complete amount to one target
|
||||
TargetCreaturePermanentAmount targetAmount = (TargetCreaturePermanentAmount) currentTarget.getOriginalTarget();
|
||||
targetAmount.setAmount(ability, game);
|
||||
targetAmount.prepareAmount(ability, game);
|
||||
int amount = targetAmount.getAmountRemaining();
|
||||
targetAmount.addTarget(id, amount, ability, game);
|
||||
targetsSet++;
|
||||
|
|
@ -2101,10 +2101,7 @@ public class TestPlayer implements Player {
|
|||
if (target == null) {
|
||||
return "Target: null";
|
||||
}
|
||||
UUID abilityControllerId = getId();
|
||||
if (target.getTargetController() != null && target.getAbilityController() != null) {
|
||||
abilityControllerId = target.getAbilityController();
|
||||
}
|
||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(this.getId());
|
||||
Set<UUID> possibleTargets = target.possibleTargets(abilityControllerId, source, game);
|
||||
|
||||
return "Target: selected " + target.getSize() + ", possible " + possibleTargets.size()
|
||||
|
|
@ -2274,10 +2271,7 @@ public class TestPlayer implements Player {
|
|||
return true;
|
||||
}
|
||||
|
||||
UUID abilityControllerId = this.getId();
|
||||
if (target.getTargetController() != null && target.getAbilityController() != null) {
|
||||
abilityControllerId = target.getAbilityController();
|
||||
}
|
||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(this.getId());
|
||||
|
||||
// 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
|
||||
|
|
@ -2509,11 +2503,7 @@ public class TestPlayer implements Player {
|
|||
|
||||
@Override
|
||||
public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) {
|
||||
UUID abilityControllerId = this.getId();
|
||||
if (target.getTargetController() != null && target.getAbilityController() != null) {
|
||||
abilityControllerId = target.getAbilityController();
|
||||
}
|
||||
UUID sourceId = source != null ? source.getSourceId() : null;
|
||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(this.getId());
|
||||
|
||||
assertAliasSupportInTargets(true);
|
||||
if (!targets.isEmpty()) {
|
||||
|
|
@ -2817,10 +2807,7 @@ public class TestPlayer implements Player {
|
|||
|
||||
@Override
|
||||
public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
|
||||
UUID abilityControllerId = this.getId();
|
||||
if (target.getTargetController() != null && target.getAbilityController() != null) {
|
||||
abilityControllerId = target.getAbilityController();
|
||||
}
|
||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(this.getId());
|
||||
|
||||
assertAliasSupportInTargets(false);
|
||||
if (!targets.isEmpty()) {
|
||||
|
|
@ -4329,12 +4316,20 @@ public class TestPlayer implements Player {
|
|||
// chooseTargetAmount calls for EACH target cycle (e.g. one target per click, see TargetAmount)
|
||||
// if use want to stop choosing then chooseTargetAmount must return false (example: up to xxx)
|
||||
|
||||
// nothing to choose
|
||||
target.prepareAmount(source, game);
|
||||
if (target.getAmountRemaining() <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (target.getMaxNumberOfTargets() == 0 && target.getMinNumberOfTargets() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(this.getId());
|
||||
|
||||
assertAliasSupportInTargets(true);
|
||||
if (!targets.isEmpty()) {
|
||||
|
||||
while (!targets.isEmpty()) {
|
||||
|
||||
// skip targets
|
||||
if (targets.get(0).equals(TARGET_SKIP)) {
|
||||
|
|
@ -4386,7 +4381,11 @@ public class TestPlayer implements Player {
|
|||
// can select
|
||||
target.addTarget(possibleTarget, targetAmount, source, game);
|
||||
targets.remove(0);
|
||||
return true; // one target per choose call
|
||||
// allow test player to choose as much as possible until skip command
|
||||
if (target.getAmountRemaining() <= 0) {
|
||||
return true;
|
||||
}
|
||||
break; // try next target
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue