diff --git a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java index 578c9de20fe..de244cd5794 100644 --- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java +++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java @@ -1030,136 +1030,139 @@ public class PhaseHandler implements java.io.Serializable { } public void mainGameLoop() { - StopWatch sw = new StopWatch(); - // MAIN GAME LOOP - while (!game.isGameOver()) { - if (givePriorityToPlayer) { - if (DEBUG_PHASES) { - sw.start(); - } + while (!game.isGameOver() && !(game.getAge() == GameStage.RestartedByKarn)) { + mainLoopStep(); + } + } - game.fireEvent(new GameEventPlayerPriority(playerTurn, phase, getPriorityPlayer())); - List chosenSa = null; - - int loopCount = 0; - do { - if (checkStateBasedEffects()) { - // state-based effects check could lead to game over - // TODO: handle game over return - return; - } - game.stashGameState(); - - chosenSa = pPlayerPriority.getController().chooseSpellAbilityToPlay(); - - // this needs to come after chosenSa so it sees you conceding on own turn - if (playerTurn.hasLost() && pPlayerPriority.equals(playerTurn) && pFirstPriority.equals(playerTurn)) { - // If the active player has lost, and they have priority, set the next player to have priority - System.out.println("Active player is no longer in the game..."); - pPlayerPriority = game.getNextPlayerAfter(getPriorityPlayer()); - pFirstPriority = pPlayerPriority; - } - - if (chosenSa == null) { - break; // that means 'I pass' - } - if (DEBUG_PHASES) { - System.out.print("... " + pPlayerPriority + " plays " + chosenSa); - } - - boolean rollback = false; - for (SpellAbility sa : chosenSa) { - Card saHost = sa.getHostCard(); - final Zone originZone = saHost.getZone(); - final CardZoneTable triggerList = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard()); - - if (pPlayerPriority.getController().playChosenSpellAbility(sa)) { - // 117.3c If a player has priority when they cast a spell, activate an ability, [play a land] - // that player receives priority afterward. - pFirstPriority = pPlayerPriority; // all opponents have to pass before stack is allowed to resolve - } else if (game.EXPERIMENTAL_RESTORE_SNAPSHOT) { - rollback = true; - } - - saHost = game.getCardState(saHost); - final Zone currentZone = saHost.getZone(); - - // Need to check if Zone did change - if (currentZone != null && originZone != null && !currentZone.equals(originZone) && (sa.isSpell() || sa.isLandAbility())) { - // currently there can be only one Spell put on the Stack at once, or Land Abilities be played - triggerList.put(originZone.getZoneType(), currentZone.getZoneType(), saHost); - triggerList.triggerChangesZoneAll(game, sa); - } - } - // Don't copy last state if we're in the middle of rolling back a spell... - if (!rollback) { - game.copyLastState(); - } - loopCount++; - } while (loopCount < 999 || !pPlayerPriority.getController().isAI()); - - if (loopCount >= 999 && pPlayerPriority.getController().isAI()) { - System.out.print("AI looped too much with: " + chosenSa); - } - - if (DEBUG_PHASES) { - sw.stop(); - System.out.print("... passed in " + sw.getTime()/1000f + " s\n"); - System.out.println("\t\tStack: " + game.getStack()); - sw.reset(); - } - } - else if (DEBUG_PHASES) { - System.out.print(" >> (no priority given to " + getPriorityPlayer() + ")\n"); + public void mainLoopStep() { + StopWatch sw = new StopWatch(); + if (givePriorityToPlayer) { + if (DEBUG_PHASES) { + sw.start(); } - // actingPlayer is the player who may act - // the firstAction is the player who gained Priority First in this segment - // of Priority - Player nextPlayer = game.getNextPlayerAfter(getPriorityPlayer()); + game.fireEvent(new GameEventPlayerPriority(playerTurn, phase, getPriorityPlayer())); + List chosenSa = null; - // TODO handle concession return - if (game.isGameOver() || nextPlayer == null) { return; } // conceded? + int loopCount = 0; + do { + if (checkStateBasedEffects()) { + // state-based effects check could lead to game over + // TODO: handle game over return + return; + } + game.stashGameState(); + + chosenSa = pPlayerPriority.getController().chooseSpellAbilityToPlay(); + + // this needs to come after chosenSa so it sees you conceding on own turn + if (playerTurn.hasLost() && pPlayerPriority.equals(playerTurn) && pFirstPriority.equals(playerTurn)) { + // If the active player has lost, and they have priority, set the next player to have priority + System.out.println("Active player is no longer in the game..."); + pPlayerPriority = game.getNextPlayerAfter(getPriorityPlayer()); + pFirstPriority = pPlayerPriority; + } + + if (chosenSa == null) { + break; // that means 'I pass' + } + if (DEBUG_PHASES) { + System.out.print("... " + pPlayerPriority + " plays " + chosenSa); + } + + boolean rollback = false; + for (SpellAbility sa : chosenSa) { + Card saHost = sa.getHostCard(); + final Zone originZone = saHost.getZone(); + final CardZoneTable triggerList = new CardZoneTable(game.getLastStateBattlefield(), game.getLastStateGraveyard()); + + if (pPlayerPriority.getController().playChosenSpellAbility(sa)) { + // 117.3c If a player has priority when they cast a spell, activate an ability, [play a land] + // that player receives priority afterward. + pFirstPriority = pPlayerPriority; // all opponents have to pass before stack is allowed to resolve + } else if (game.EXPERIMENTAL_RESTORE_SNAPSHOT) { + rollback = true; + } + + saHost = game.getCardState(saHost); + final Zone currentZone = saHost.getZone(); + + // Need to check if Zone did change + if (currentZone != null && originZone != null && !currentZone.equals(originZone) && (sa.isSpell() || sa.isLandAbility())) { + // currently there can be only one Spell put on the Stack at once, or Land Abilities be played + triggerList.put(originZone.getZoneType(), currentZone.getZoneType(), saHost); + triggerList.triggerChangesZoneAll(game, sa); + } + } + // Don't copy last state if we're in the middle of rolling back a spell... + if (!rollback) { + game.copyLastState(); + } + loopCount++; + } while (loopCount < 999 || !pPlayerPriority.getController().isAI()); + + if (loopCount >= 999 && pPlayerPriority.getController().isAI()) { + System.out.print("AI looped too much with: " + chosenSa); + } if (DEBUG_PHASES) { - System.out.println(TextUtil.concatWithSpace(playerTurn.toString(),TextUtil.addSuffix(phase.toString(),":"), pPlayerPriority.toString(),"is active, previous was", nextPlayer.toString())); + sw.stop(); + System.out.print("... passed in " + sw.getTime()/1000f + " s\n"); + System.out.println("\t\tStack: " + game.getStack()); + sw.reset(); } - if (pFirstPriority == nextPlayer) { - if (game.getStack().isEmpty()) { - if (playerTurn.hasLost()) { - setPriority(game.getNextPlayerAfter(playerTurn)); - } else { - setPriority(playerTurn); - } + } + else if (DEBUG_PHASES) { + System.out.print(" >> (no priority given to " + getPriorityPlayer() + ")\n"); + } - // end phase - givePriorityToPlayer = true; - onPhaseEnd(); - advanceToNextPhase(); - onPhaseBegin(); + // actingPlayer is the player who may act + // the firstAction is the player who gained Priority First in this segment + // of Priority + Player nextPlayer = game.getNextPlayerAfter(getPriorityPlayer()); + + // TODO handle concession return + if (game.isGameOver() || nextPlayer == null) { return; } // conceded? + + if (DEBUG_PHASES) { + System.out.println(TextUtil.concatWithSpace(playerTurn.toString(),TextUtil.addSuffix(phase.toString(),":"), pPlayerPriority.toString(),"is active, previous was", nextPlayer.toString())); + } + if (pFirstPriority == nextPlayer) { + if (game.getStack().isEmpty()) { + if (playerTurn.hasLost()) { + setPriority(game.getNextPlayerAfter(playerTurn)); + } else { + setPriority(playerTurn); } - else if (!game.getStack().hasSimultaneousStackEntries()) { - game.getStack().resolveStack(); - } - } else { - // pass the priority to other player - pPlayerPriority = nextPlayer; - } - // If ever the karn's ultimate resolved - if (game.getAge() == GameStage.RestartedByKarn) { - setPhase(null); - game.updatePhaseForView(); - game.fireEvent(new GameEventGameRestarted(playerTurn)); - // TODO: handle karn restart return - return; + // end phase + givePriorityToPlayer = true; + onPhaseEnd(); + advanceToNextPhase(); + onPhaseBegin(); } + else if (!game.getStack().hasSimultaneousStackEntries()) { + game.getStack().resolveStack(); + } + } else { + // pass the priority to other player + pPlayerPriority = nextPlayer; + } - // update Priority for all players - for (final Player p : game.getPlayers()) { - p.setHasPriority(getPriorityPlayer() == p); - } + // If ever the karn's ultimate resolved + if (game.getAge() == GameStage.RestartedByKarn) { + setPhase(null); + game.updatePhaseForView(); + game.fireEvent(new GameEventGameRestarted(playerTurn)); + // TODO: handle karn restart return + return; + } + + // update Priority for all players + for (final Player p : game.getPlayers()) { + p.setHasPriority(getPriorityPlayer() == p); } }