mirror of
https://github.com/magefree/mage.git
synced 2025-12-22 11:32:00 -08:00
* AI: fixed game freezes with Karn Liberated in the game (#7922);
This commit is contained in:
parent
d9e414db34
commit
b929b28e43
7 changed files with 192 additions and 59 deletions
|
|
@ -15,6 +15,8 @@ import mage.choices.Choice;
|
||||||
import mage.constants.AbilityType;
|
import mage.constants.AbilityType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.constants.RangeOfInfluence;
|
import mage.constants.RangeOfInfluence;
|
||||||
|
import mage.counters.CounterType;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.combat.Combat;
|
import mage.game.combat.Combat;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
|
|
@ -40,8 +42,6 @@ import java.io.File;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import mage.counters.CounterType;
|
|
||||||
import mage.filter.StaticFilters;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author nantuko
|
* @author nantuko
|
||||||
|
|
@ -215,7 +215,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
||||||
logger.trace("Add Action [" + depth + "] " + node.getAbilities().toString() + " a: " + alpha + " b: " + beta);
|
logger.trace("Add Action [" + depth + "] " + node.getAbilities().toString() + " a: " + alpha + " b: " + beta);
|
||||||
}
|
}
|
||||||
Game game = node.getGame();
|
Game game = node.getGame();
|
||||||
if (COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS
|
if (!COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS
|
||||||
&& Thread.interrupted()) {
|
&& Thread.interrupted()) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
logger.debug("interrupted");
|
logger.debug("interrupted");
|
||||||
|
|
@ -435,7 +435,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
||||||
pool.execute(task);
|
pool.execute(task);
|
||||||
try {
|
try {
|
||||||
int maxSeconds = maxThink;
|
int maxSeconds = maxThink;
|
||||||
if (!COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS) {
|
if (COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS) {
|
||||||
maxSeconds = 3600;
|
maxSeconds = 3600;
|
||||||
}
|
}
|
||||||
logger.debug("maxThink: " + maxSeconds + " seconds ");
|
logger.debug("maxThink: " + maxSeconds + " seconds ");
|
||||||
|
|
@ -460,7 +460,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int simulatePriority(SimulationNode2 node, Game game, int depth, int alpha, int beta) {
|
protected int simulatePriority(SimulationNode2 node, Game game, int depth, int alpha, int beta) {
|
||||||
if (COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS
|
if (!COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS
|
||||||
&& Thread.interrupted()) {
|
&& Thread.interrupted()) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
logger.info("interrupted");
|
logger.info("interrupted");
|
||||||
|
|
@ -480,7 +480,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
||||||
int bestValSubNodes = Integer.MIN_VALUE;
|
int bestValSubNodes = Integer.MIN_VALUE;
|
||||||
for (Ability action : allActions) {
|
for (Ability action : allActions) {
|
||||||
counter++;
|
counter++;
|
||||||
if (COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS
|
if (!COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS
|
||||||
&& Thread.interrupted()) {
|
&& Thread.interrupted()) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
logger.info("Sim Prio [" + depth + "] -- interrupted");
|
logger.info("Sim Prio [" + depth + "] -- interrupted");
|
||||||
|
|
@ -492,7 +492,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
||||||
&& sim.getPlayer(currentPlayer.getId()).activateAbility((ActivatedAbility) action.copy(), sim)) {
|
&& sim.getPlayer(currentPlayer.getId()).activateAbility((ActivatedAbility) action.copy(), sim)) {
|
||||||
sim.applyEffects();
|
sim.applyEffects();
|
||||||
if (checkForRepeatedAction(sim, node, action, currentPlayer.getId())) {
|
if (checkForRepeatedAction(sim, node, action, currentPlayer.getId())) {
|
||||||
logger.debug("Sim Prio [" + depth + "] -- repeated action: " + action.toString());
|
logger.debug("Sim Prio [" + depth + "] -- repeated action: " + action);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!sim.checkIfGameIsOver()
|
if (!sim.checkIfGameIsOver()
|
||||||
|
|
@ -513,7 +513,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
||||||
} else {
|
} else {
|
||||||
val = addActions(newNode, depth - 1, alpha, beta);
|
val = addActions(newNode, depth - 1, alpha, beta);
|
||||||
}
|
}
|
||||||
logger.debug("Sim Prio " + BLANKS.substring(0, 2 + (maxDepth - depth) * 3) + '[' + depth + "]#" + counter + " <" + val + "> - (" + action.toString() + ") ");
|
logger.debug("Sim Prio " + BLANKS.substring(0, 2 + (maxDepth - depth) * 3) + '[' + depth + "]#" + counter + " <" + val + "> - (" + action + ") ");
|
||||||
if (logger.isInfoEnabled()
|
if (logger.isInfoEnabled()
|
||||||
&& depth >= maxDepth) {
|
&& depth >= maxDepth) {
|
||||||
StringBuilder sb = new StringBuilder("Sim Prio [").append(depth).append("] #").append(counter)
|
StringBuilder sb = new StringBuilder("Sim Prio [").append(depth).append("] #").append(counter)
|
||||||
|
|
@ -979,14 +979,15 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
||||||
protected Game createSimulation(Game game) {
|
protected Game createSimulation(Game game) {
|
||||||
Game sim = game.copy();
|
Game sim = game.copy();
|
||||||
sim.setSimulation(true);
|
sim.setSimulation(true);
|
||||||
for (Player copyPlayer : sim.getState().getPlayers().values()) {
|
for (Player oldPlayer : sim.getState().getPlayers().values()) {
|
||||||
Player origPlayer = game.getState().getPlayers().get(copyPlayer.getId()).copy();
|
// replace original player by simulated player and find result (execute/resolve current action)
|
||||||
|
Player origPlayer = game.getState().getPlayers().get(oldPlayer.getId()).copy();
|
||||||
if (!suggestedActions.isEmpty()) {
|
if (!suggestedActions.isEmpty()) {
|
||||||
logger.debug(origPlayer.getName() + " suggested: " + suggestedActions);
|
logger.debug(origPlayer.getName() + " suggested: " + suggestedActions);
|
||||||
}
|
}
|
||||||
SimulatedPlayer2 newPlayer = new SimulatedPlayer2(copyPlayer.getId(), copyPlayer.getId().equals(playerId), suggestedActions);
|
SimulatedPlayer2 simPlayer = new SimulatedPlayer2(oldPlayer, oldPlayer.getId().equals(playerId), suggestedActions);
|
||||||
newPlayer.restore(origPlayer);
|
simPlayer.restore(origPlayer);
|
||||||
sim.getState().getPlayers().put(copyPlayer.getId(), newPlayer);
|
sim.getState().getPlayers().put(oldPlayer.getId(), simPlayer);
|
||||||
}
|
}
|
||||||
return sim;
|
return sim;
|
||||||
}
|
}
|
||||||
|
|
@ -1069,7 +1070,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
||||||
*
|
*
|
||||||
* @param game
|
* @param game
|
||||||
* @param targets
|
* @param targets
|
||||||
* @param format example: my %s in data
|
* @param format example: my %s in data
|
||||||
* @param emptyText default text for empty targets list
|
* @param emptyText default text for empty targets list
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -31,14 +31,14 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
public class SimulatedPlayer2 extends ComputerPlayer {
|
public class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(SimulatedPlayer2.class);
|
private static final Logger logger = Logger.getLogger(SimulatedPlayer2.class);
|
||||||
private static PassAbility pass = new PassAbility();
|
private static final PassAbility pass = new PassAbility();
|
||||||
private final boolean isSimulatedPlayer;
|
private final boolean isSimulatedPlayer;
|
||||||
private final List<String> suggested;
|
private final List<String> suggested;
|
||||||
private transient ConcurrentLinkedQueue<Ability> allActions;
|
private transient ConcurrentLinkedQueue<Ability> allActions;
|
||||||
private boolean forced;
|
private boolean forced;
|
||||||
|
|
||||||
public SimulatedPlayer2(UUID id, boolean isSimulatedPlayer, List<String> suggested) {
|
public SimulatedPlayer2(Player originalPlayer, boolean isSimulatedPlayer, List<String> suggested) {
|
||||||
super(id);
|
super(originalPlayer.getId());
|
||||||
pass.setControllerId(playerId);
|
pass.setControllerId(playerId);
|
||||||
this.isSimulatedPlayer = isSimulatedPlayer;
|
this.isSimulatedPlayer = isSimulatedPlayer;
|
||||||
this.suggested = suggested;
|
this.suggested = suggested;
|
||||||
|
|
@ -435,8 +435,15 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean priority(Game game) {
|
public boolean priority(Game game) {
|
||||||
//should never get here
|
// simulated player do nothing - it must pass until stack resolve to see final game score after action apply
|
||||||
|
|
||||||
|
// it's a workaround for Karn Liberated restart ability (see CommandersGameRestartTest)
|
||||||
|
// reason: restarted game is broken (miss clear code of some game/player data?) and ai can't simulate it
|
||||||
|
// so game is freezes on non empty stack (last part of karn's restart ability)
|
||||||
|
if (game.getStack().isEmpty()) {
|
||||||
|
game.pause();
|
||||||
|
}
|
||||||
|
pass(game);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,11 @@ class KarnLiberatedEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
game.getState().clear();
|
|
||||||
|
// dirty hack for game restart, can cause bugs due strange clear code (some data like ZCC keeping on new game)
|
||||||
|
// see testCommanderRestoredToBattlefieldAfterKarnUltimate for more details
|
||||||
|
|
||||||
|
game.getState().clearOnGameRestart();
|
||||||
// default watchers init, TODO: remove all restart/init code to game
|
// default watchers init, TODO: remove all restart/init code to game
|
||||||
((GameImpl) game).initGameDefaultWatchers();
|
((GameImpl) game).initGameDefaultWatchers();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -209,5 +209,4 @@ public class CastCreaturesTest extends CardTestPlayerBaseAI {
|
||||||
|
|
||||||
assertPowerToughness(playerB, "Ammit Eternal", 4, 4);
|
assertPowerToughness(playerB, "Ammit Eternal", 4, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
package org.mage.test.cards.continuous;
|
||||||
|
|
||||||
|
import mage.constants.CommanderCardType;
|
||||||
|
import mage.constants.PhaseStep;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import mage.counters.CounterType;
|
||||||
|
import mage.watchers.common.CommanderPlaysCountWatcher;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.serverside.base.CardTestCommander4PlayersWithAIHelps;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class CommandersGameRestartTest extends CardTestCommander4PlayersWithAIHelps {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_KarnLiberated_Manual() {
|
||||||
|
// Player order: A -> D -> C -> B
|
||||||
|
|
||||||
|
addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}, 2/2, commander
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
|
||||||
|
//
|
||||||
|
// -14: Restart the game, leaving in exile all non-Aura permanent cards exiled with Karn Liberated.
|
||||||
|
// Then put those cards onto the battlefield under your control.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Karn Liberated", 1);
|
||||||
|
|
||||||
|
// prepare commander
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||||
|
checkPermanentCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1);
|
||||||
|
|
||||||
|
// prepare karn
|
||||||
|
addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Karn Liberated", CounterType.LOYALTY, 20);
|
||||||
|
|
||||||
|
// check watcher before restart
|
||||||
|
runCode("before restart", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||||
|
UUID commanderId = game.getCommandersIds(player, CommanderCardType.ANY, false).stream().findFirst().orElse(null);
|
||||||
|
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
||||||
|
Assert.assertEquals("commander tax must be x1", 1, watcher.getPlaysCount(commanderId));
|
||||||
|
});
|
||||||
|
|
||||||
|
// game restart
|
||||||
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-14: ");
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
// check watcher after restart
|
||||||
|
UUID commanderId = currentGame.getCommandersIds(playerA, CommanderCardType.ANY, false).stream().findFirst().orElse(null);
|
||||||
|
CommanderPlaysCountWatcher watcher = currentGame.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
||||||
|
Assert.assertEquals("commander tax must be x0", 0, watcher.getPlaysCount(commanderId));
|
||||||
|
|
||||||
|
assertPermanentCount(playerA, 0); // no cards on battle after game restart
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_KarnLiberated_AI() {
|
||||||
|
// Player order: A -> D -> C -> B
|
||||||
|
|
||||||
|
addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G}, 2/2, commander
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
|
||||||
|
//
|
||||||
|
// -14: Restart the game, leaving in exile all non-Aura permanent cards exiled with Karn Liberated.
|
||||||
|
// Then put those cards onto the battlefield under your control.
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Karn Liberated", 1);
|
||||||
|
//
|
||||||
|
addCard(Zone.HAND, playerB, "Balduvian Bears", 5);
|
||||||
|
addCard(Zone.HAND, playerC, "Balduvian Bears", 5);
|
||||||
|
addCard(Zone.HAND, playerD, "Balduvian Bears", 5);
|
||||||
|
|
||||||
|
// prepare commander
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears");
|
||||||
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||||
|
checkPermanentCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1);
|
||||||
|
|
||||||
|
// prepare karn
|
||||||
|
addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Karn Liberated", CounterType.LOYALTY, 50);
|
||||||
|
|
||||||
|
// check watcher before restart
|
||||||
|
runCode("before restart", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||||
|
UUID commanderId = game.getCommandersIds(player, CommanderCardType.ANY, false).stream().findFirst().orElse(null);
|
||||||
|
CommanderPlaysCountWatcher watcher = game.getState().getWatcher(CommanderPlaysCountWatcher.class);
|
||||||
|
Assert.assertEquals("commander tax must be x1", 1, watcher.getPlaysCount(commanderId));
|
||||||
|
});
|
||||||
|
|
||||||
|
// possible bug: ai can use restart in one of the simulations, so it can freeze the game (if bugged)
|
||||||
|
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||||
|
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerB);
|
||||||
|
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerC);
|
||||||
|
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerD);
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package org.mage.test.commander.duel;
|
package org.mage.test.commander.duel;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
|
@ -10,8 +9,9 @@ import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestCommanderDuelBase;
|
import org.mage.test.serverside.base.CardTestCommanderDuelBase;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public class CastBRGCommanderTest extends CardTestCommanderDuelBase {
|
public class CastBRGCommanderTest extends CardTestCommanderDuelBase {
|
||||||
|
|
@ -97,22 +97,36 @@ public class CastBRGCommanderTest extends CardTestCommanderDuelBase {
|
||||||
addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
|
addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
|
||||||
addCard(Zone.BATTLEFIELD, playerB, "Island", 1);
|
addCard(Zone.BATTLEFIELD, playerB, "Island", 1);
|
||||||
|
|
||||||
|
// exile from hand 1
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+4: Target player", playerA);
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+4: Target player", playerA);
|
||||||
addTarget(playerA, "Silvercoat Lion");
|
addTarget(playerA, "Silvercoat Lion");
|
||||||
|
|
||||||
|
// prepare commander
|
||||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Daxos of Meletis");
|
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Daxos of Meletis");
|
||||||
|
|
||||||
|
// exile from hand 2
|
||||||
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "+4: Target player", playerA);
|
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "+4: Target player", playerA);
|
||||||
addTarget(playerA, "Silvercoat Lion");
|
addTarget(playerA, "Silvercoat Lion");
|
||||||
|
|
||||||
|
// attack and get commander damage
|
||||||
attack(4, playerB, "Daxos of Meletis");
|
attack(4, playerB, "Daxos of Meletis");
|
||||||
|
|
||||||
|
// exile commander
|
||||||
activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "-3: Exile target permanent", "Daxos of Meletis");
|
activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "-3: Exile target permanent", "Daxos of Meletis");
|
||||||
setChoice(playerB, "No"); // Move commander NOT to command zone
|
setChoice(playerB, "No"); // Move commander NOT to command zone
|
||||||
|
|
||||||
|
// exile from hand 3
|
||||||
activateAbility(7, PhaseStep.PRECOMBAT_MAIN, playerA, "+4: Target player", playerA);
|
activateAbility(7, PhaseStep.PRECOMBAT_MAIN, playerA, "+4: Target player", playerA);
|
||||||
addTarget(playerA, "Silvercoat Lion");
|
addTarget(playerA, "Silvercoat Lion");
|
||||||
|
|
||||||
|
// restart game and return to battlefield 1x commander and 3x lions
|
||||||
activateAbility(9, PhaseStep.PRECOMBAT_MAIN, playerA, "-14: Restart");
|
activateAbility(9, PhaseStep.PRECOMBAT_MAIN, playerA, "-14: Restart");
|
||||||
|
// warning:
|
||||||
|
// - karn restart code can clear some game data
|
||||||
|
// - current version ignores a card's ZCC
|
||||||
|
// - so ZCC are same after game restart and SBA can't react on commander new move
|
||||||
|
// - logic can be changed in the future, so game can ask commander move again here
|
||||||
|
//setChoice(playerB, "No"); // Move commander NOT to command zone
|
||||||
|
|
||||||
setStopAt(9, PhaseStep.BEGIN_COMBAT);
|
setStopAt(9, PhaseStep.BEGIN_COMBAT);
|
||||||
|
|
||||||
|
|
@ -127,8 +141,7 @@ public class CastBRGCommanderTest extends CardTestCommanderDuelBase {
|
||||||
assertPermanentCount(playerA, "Daxos of Meletis", 1); // Karn brings back the cards under the control of Karn's controller
|
assertPermanentCount(playerA, "Daxos of Meletis", 1); // Karn brings back the cards under the control of Karn's controller
|
||||||
|
|
||||||
CommanderInfoWatcher watcher = currentGame.getState().getWatcher(CommanderInfoWatcher.class, playerB.getCommandersIds().iterator().next());
|
CommanderInfoWatcher watcher = currentGame.getState().getWatcher(CommanderInfoWatcher.class, playerB.getCommandersIds().iterator().next());
|
||||||
Assert.assertEquals("Watcher is reset to 0 commander damage", 0, (int) watcher.getDamageToPlayer().size());
|
Assert.assertEquals("Watcher is reset to 0 commander damage", 0, watcher.getDamageToPlayer().size());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,45 @@ public class GameState implements Serializable, Copyable<GameState> {
|
||||||
this.commandersToStay.addAll(state.commandersToStay);
|
this.commandersToStay.addAll(state.commandersToStay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clearOnGameRestart() {
|
||||||
|
// special code for Karn Liberated
|
||||||
|
// must clear game data on restart, but also must keep some info (wtf, why?)
|
||||||
|
// if you catch freezes or bugs with Karn then research here
|
||||||
|
// test example: testCommanderRestoredToBattlefieldAfterKarnUltimate
|
||||||
|
// TODO: must be implemented as full data clear?
|
||||||
|
|
||||||
|
battlefield.clear();
|
||||||
|
effects.clear();
|
||||||
|
triggers.clear();
|
||||||
|
delayed.clear();
|
||||||
|
triggered.clear();
|
||||||
|
stack.clear();
|
||||||
|
exile.clear();
|
||||||
|
command.clear();
|
||||||
|
designations.clear();
|
||||||
|
seenPlanes.clear();
|
||||||
|
isPlaneChase = false;
|
||||||
|
revealed.clear();
|
||||||
|
lookedAt.clear();
|
||||||
|
companion.clear();
|
||||||
|
turnNum = 1;
|
||||||
|
stepNum = 0;
|
||||||
|
extraTurn = false;
|
||||||
|
legendaryRuleActive = true;
|
||||||
|
gameOver = false;
|
||||||
|
specialActions.clear();
|
||||||
|
cardState.clear();
|
||||||
|
combat.clear();
|
||||||
|
turnMods.clear();
|
||||||
|
watchers.clear();
|
||||||
|
values.clear();
|
||||||
|
zones.clear();
|
||||||
|
simultaneousEvents.clear();
|
||||||
|
copiedCards.clear();
|
||||||
|
usePowerInsteadOfToughnessForDamageLethalityFilters.clear();
|
||||||
|
permanentOrderNumber = 0;
|
||||||
|
}
|
||||||
|
|
||||||
public void restoreForRollBack(GameState state) {
|
public void restoreForRollBack(GameState state) {
|
||||||
restore(state);
|
restore(state);
|
||||||
this.turn = state.turn;
|
this.turn = state.turn;
|
||||||
|
|
@ -1124,7 +1163,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
||||||
if (attachedTo instanceof PermanentCard) {
|
if (attachedTo instanceof PermanentCard) {
|
||||||
throw new IllegalArgumentException("Error, wrong code usage. If you want to add new ability to the "
|
throw new IllegalArgumentException("Error, wrong code usage. If you want to add new ability to the "
|
||||||
+ "permanent then use a permanent.addAbility(a, source, game): "
|
+ "permanent then use a permanent.addAbility(a, source, game): "
|
||||||
+ ability.getClass().getCanonicalName() + " - " + ability.toString());
|
+ ability.getClass().getCanonicalName() + " - " + ability);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1153,39 +1192,6 @@ public class GameState implements Serializable, Copyable<GameState> {
|
||||||
this.setManaBurn(false);
|
this.setManaBurn(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
battlefield.clear();
|
|
||||||
effects.clear();
|
|
||||||
triggers.clear();
|
|
||||||
delayed.clear();
|
|
||||||
triggered.clear();
|
|
||||||
stack.clear();
|
|
||||||
exile.clear();
|
|
||||||
command.clear();
|
|
||||||
designations.clear();
|
|
||||||
seenPlanes.clear();
|
|
||||||
isPlaneChase = false;
|
|
||||||
revealed.clear();
|
|
||||||
lookedAt.clear();
|
|
||||||
companion.clear();
|
|
||||||
turnNum = 0;
|
|
||||||
stepNum = 0;
|
|
||||||
extraTurn = false;
|
|
||||||
legendaryRuleActive = true;
|
|
||||||
gameOver = false;
|
|
||||||
specialActions.clear();
|
|
||||||
cardState.clear();
|
|
||||||
combat.clear();
|
|
||||||
turnMods.clear();
|
|
||||||
watchers.clear();
|
|
||||||
values.clear();
|
|
||||||
zones.clear();
|
|
||||||
simultaneousEvents.clear();
|
|
||||||
copiedCards.clear();
|
|
||||||
usePowerInsteadOfToughnessForDamageLethalityFilters.clear();
|
|
||||||
permanentOrderNumber = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void pause() {
|
public void pause() {
|
||||||
this.paused = true;
|
this.paused = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue