diff --git a/.gitattributes b/.gitattributes index 005bbf3fc4c..9a5086f0d68 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14251,10 +14251,12 @@ src/main/java/forge/game/event/DuelOutcomeEvent.java -text src/main/java/forge/game/event/EndOfTurnEvent.java -text src/main/java/forge/game/event/Event.java -text src/main/java/forge/game/event/FlipCoinEvent.java -text +src/main/java/forge/game/event/GameRestartedEvent.java -text src/main/java/forge/game/event/LandPlayedEvent.java -text src/main/java/forge/game/event/LifeLossEvent.java -text src/main/java/forge/game/event/ManaBurnEvent.java -text src/main/java/forge/game/event/MulliganEvent.java -text +src/main/java/forge/game/event/PhaseEvent.java -text src/main/java/forge/game/event/PlayerControlEvent.java -text src/main/java/forge/game/event/PoisonCounterEvent.java -text src/main/java/forge/game/event/SetTappedEvent.java -text diff --git a/src/main/java/forge/GameLogFormatter.java b/src/main/java/forge/GameLogFormatter.java index c7d62278383..a41f4b79056 100644 --- a/src/main/java/forge/GameLogFormatter.java +++ b/src/main/java/forge/GameLogFormatter.java @@ -7,8 +7,10 @@ import java.util.Map.Entry; import forge.game.GameOutcome; import forge.game.event.DuelOutcomeEvent; import forge.game.event.Event; +import forge.game.event.PhaseEvent; import forge.game.event.PlayerControlEvent; import forge.game.phase.Combat; +import forge.game.phase.PhaseType; import forge.game.player.LobbyPlayer; import forge.game.player.Player; import forge.game.player.PlayerStatistics; @@ -32,6 +34,10 @@ public class GameLogFormatter { message = String.format("%s is controlled by %s", p.getName(), newController.getName()); return new GameLogEntry(GameEventType.PLAYER_CONROL, message); + } else if ( ev instanceof PhaseEvent ) { + Player p = ((PhaseEvent) ev).playerTurn; + PhaseType ph = ((PhaseEvent) ev).phase; + return new GameLogEntry(GameEventType.PHASE, ((PhaseEvent) ev).phaseDesc + Lang.getPossesive(p.getName()) + " " + ph.NameForUi); } return null; } diff --git a/src/main/java/forge/card/ability/effects/RestartGameEffect.java b/src/main/java/forge/card/ability/effects/RestartGameEffect.java index 57b8325fcde..70b066f92c3 100644 --- a/src/main/java/forge/card/ability/effects/RestartGameEffect.java +++ b/src/main/java/forge/card/ability/effects/RestartGameEffect.java @@ -11,9 +11,15 @@ import forge.CardLists; import forge.CardPredicates; import forge.card.ability.SpellAbilityEffect; import forge.card.spellability.SpellAbility; +import forge.card.trigger.TriggerHandler; +import forge.card.trigger.TriggerType; +import forge.game.GameAction; +import forge.game.GameAge; import forge.game.GameNew; import forge.game.GameState; +import forge.game.RegisteredPlayer; import forge.game.player.Player; +import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; public class RestartGameEffect extends SpellAbilityEffect { @@ -47,10 +53,50 @@ public class RestartGameEffect extends SpellAbilityEffect { newLibrary.addAll(filteredCards); playerLibraries.put(p, newLibrary); } + + //Card.resetUniqueNumber(); + // need this code here, otherwise observables fail + forge.card.trigger.Trigger.resetIDs(); + TriggerHandler trigHandler = game.getTriggerHandler(); + trigHandler.clearDelayedTrigger(); + trigHandler.cleanUpTemporaryTriggers(); + trigHandler.suppressMode(TriggerType.ChangesZone); - GameNew.restartGame(game, sa.getActivatingPlayer(), playerLibraries); - - // because the caller method (invokeInNewThread) will try to unlock input. Restart has removed that input state. + game.getStack().reset(); + GameAction action = game.getAction(); + + List gamePlayers = game.getRegisteredPlayers(); + for( int i = 0; i < gamePlayers.size(); i++ ) { + + final Player player = gamePlayers.get(i); + if( player.hasLost()) continue; + + RegisteredPlayer psc = game.getMatch().getPlayers().get(i); + + player.setStartingLife(psc.getStartingLife()); + player.setNumLandsPlayed(0); + GameNew.putCardsOnBattlefield(player, psc.getCardsOnBattlefield(player)); + + PlayerZone library = player.getZone(ZoneType.Library); + List newLibrary = playerLibraries.get(player); + for (Card c : newLibrary) { + action.moveTo(library, c); + } + + player.shuffle(); + player.getZone(ZoneType.Battlefield).updateObservers(); + player.updateObservers(); + player.getZone(ZoneType.Hand).updateObservers(); + } + + trigHandler.clearSuppression(TriggerType.ChangesZone); + + game.setAge(GameAge.RestartedByKarn); + game.getPhaseHandler().setPlayerTurn(sa.getActivatingPlayer()); + + // Set turn number? + + // The rest is handled by phaseHandler } /* (non-Javadoc) diff --git a/src/main/java/forge/control/FControl.java b/src/main/java/forge/control/FControl.java index 9b1eaaae97e..db7da94ddba 100644 --- a/src/main/java/forge/control/FControl.java +++ b/src/main/java/forge/control/FControl.java @@ -46,7 +46,10 @@ import forge.game.ai.AiProfileUtil; import forge.game.event.CardsAntedEvent; import forge.game.event.DuelFinishedEvent; import forge.game.event.Event; +import forge.game.event.PhaseEvent; import forge.game.event.PlayerControlEvent; +import forge.game.phase.PhaseType; +import forge.game.phase.PhaseUtil; import forge.game.player.LobbyPlayer; import forge.game.player.Player; import forge.gui.GuiDialog; @@ -414,7 +417,7 @@ public enum FControl { } @Subscribe - public void receiveGameEvent(Event ev) { + public void receiveGameEvent(final Event ev) { if( ev instanceof DuelFinishedEvent ) { FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() { @@ -438,6 +441,12 @@ public enum FControl { h.getLayoutControl().updateHand(); } } }); + } else if ( ev instanceof PhaseEvent ) { + FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() { + Player p = ((PhaseEvent) ev).playerTurn; + PhaseType ph = ((PhaseEvent) ev).phase; + PhaseUtil.visuallyActivatePhase(p, ph); + } }); } } } diff --git a/src/main/java/forge/game/GameAction.java b/src/main/java/forge/game/GameAction.java index 602081551e4..429283185e2 100644 --- a/src/main/java/forge/game/GameAction.java +++ b/src/main/java/forge/game/GameAction.java @@ -1475,25 +1475,41 @@ public class GameAction { } } } - - game.getAction().checkStateEffects(); } public void startGame(final Player firstPlayer) { - performMulligans(firstPlayer, game.getType() == GameType.Commander); - game.setAge(GameAge.Play); - - // THIS CODE WILL WORK WITH PHASE = NULL { - if(game.getType() == GameType.Planechase) - firstPlayer.initPlane(); + Player first = firstPlayer; + do { + // Draw cards + for (final Player p1 : game.getPlayers()) { + p1.drawCards(p1.getMaxHandSize()); + } - handleLeylinesAndChancellors(); - // Run Trigger beginning of the game - final HashMap runParams = new HashMap(); - game.getTriggerHandler().runTrigger(TriggerType.NewGame, runParams, false); - // } + game.setAge(GameAge.Mulligan); + performMulligans(first, game.getType() == GameType.Commander); + + // should I restore everyting exiled by Karn here, or before Mulligans is fine? + + game.setAge(GameAge.Play); + + // THIS CODE WILL WORK WITH PHASE = NULL { + if(game.getType() == GameType.Planechase) + firstPlayer.initPlane(); - game.getPhaseHandler().startFirstTurn(firstPlayer); + handleLeylinesAndChancellors(); + checkStateEffects(); + + // Run Trigger beginning of the game + final HashMap runParams = new HashMap(); + game.getTriggerHandler().runTrigger(TriggerType.NewGame, runParams, false); + // } + + game.getPhaseHandler().startFirstTurn(first); + + first = game.getPhaseHandler().getPlayerTurn(); // needed only for restart + } while( game.getAge() == GameAge.RestartedByKarn ); + + System.out.println(FThreads.prependThreadId("Thread exited game loop due to ... " + ( game.isGameOver() ? "game over" : "interrupt" ))); } private void performMulligans(final Player firstPlayer, final boolean isCommander) { diff --git a/src/main/java/forge/game/GameAge.java b/src/main/java/forge/game/GameAge.java index c31770d935f..b6770e03760 100644 --- a/src/main/java/forge/game/GameAge.java +++ b/src/main/java/forge/game/GameAge.java @@ -4,5 +4,6 @@ public enum GameAge { BeforeMulligan, Mulligan, Play, + RestartedByKarn, GameOver } \ No newline at end of file diff --git a/src/main/java/forge/game/GameNew.java b/src/main/java/forge/game/GameNew.java index 02711b40b9e..64dd4cfef6f 100644 --- a/src/main/java/forge/game/GameNew.java +++ b/src/main/java/forge/game/GameNew.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Map.Entry; import java.util.Random; import java.util.Set; @@ -22,11 +21,9 @@ import forge.CardPredicates; import forge.GameEventType; import forge.card.trigger.Trigger; import forge.card.trigger.TriggerHandler; -import forge.card.trigger.TriggerType; import forge.deck.CardPool; import forge.deck.Deck; import forge.deck.DeckSection; -import forge.game.phase.PhaseHandler; import forge.game.player.Player; import forge.game.player.PlayerType; import forge.game.zone.PlayerZone; @@ -52,7 +49,7 @@ public class GameNew { public static final ForgePreferences preferences = forge.Singletons.getModel().getPreferences(); - private static void putCardsOnBattlefield(Player player, Iterable cards) { + public static void putCardsOnBattlefield(Player player, Iterable cards) { PlayerZone bf = player.getZone(ZoneType.Battlefield); if (cards != null) { for (final IPaperCard cp : cards) { @@ -331,61 +328,6 @@ public class GameNew { } } - // ultimate of Karn the Liberated - public static void restartGame(final GameState game, final Player startingTurn, Map> playerLibraries) { - - //Card.resetUniqueNumber(); - // need this code here, otherwise observables fail - forge.card.trigger.Trigger.resetIDs(); - TriggerHandler trigHandler = game.getTriggerHandler(); - trigHandler.clearDelayedTrigger(); - trigHandler.cleanUpTemporaryTriggers(); - trigHandler.suppressMode(TriggerType.ChangesZone); - - game.getStack().reset(); - GameAction action = game.getAction(); - - List gamePlayers = game.getRegisteredPlayers(); - for( int i = 0; i < gamePlayers.size(); i++ ) { - - final Player player = gamePlayers.get(i); - if( player.hasLost()) continue; - - RegisteredPlayer psc = game.getMatch().getPlayers().get(i); - - player.setStartingLife(psc.getStartingLife()); - player.setNumLandsPlayed(0); - putCardsOnBattlefield(player, psc.getCardsOnBattlefield(player)); - - PlayerZone library = player.getZone(ZoneType.Library); - List newLibrary = playerLibraries.get(player); - for (Card c : newLibrary) { - action.moveTo(library, c); - } - - player.shuffle(); - player.getZone(ZoneType.Battlefield).updateObservers(); - player.updateObservers(); - player.getZone(ZoneType.Hand).updateObservers(); - } - - trigHandler.clearSuppression(TriggerType.ChangesZone); - - PhaseHandler phaseHandler = game.getPhaseHandler(); - phaseHandler.setPlayerTurn(startingTurn); - - game.setAge(GameAge.Mulligan); - // Draw cards - for (final Player p : game.getPlayers()) { - p.drawCards(p.getMaxHandSize()); - } - - - game.getAction().startGame(startingTurn); - } - - - // this is where the computer cheats // changes AllZone.getComputerPlayer().getZone(Zone.Library) diff --git a/src/main/java/forge/game/GameState.java b/src/main/java/forge/game/GameState.java index 1ecc5d1f78a..c142ec5c4c1 100644 --- a/src/main/java/forge/game/GameState.java +++ b/src/main/java/forge/game/GameState.java @@ -302,6 +302,11 @@ public class GameState { */ public synchronized void setGameOver(GameEndReason reason) { this.age = GameAge.GameOver; + for (Player p : allPlayers ) { + if (p.isMindSlaved()) + p.releaseControl(); // for correct totals + } + for (Player p : roIngamePlayers) { p.onGameOver(); } @@ -617,7 +622,7 @@ public class GameState { return age; } - void setAge(GameAge value) { + public void setAge(GameAge value) { age = value; } } diff --git a/src/main/java/forge/game/MatchState.java b/src/main/java/forge/game/MatchState.java index 0561c7834d7..c1f4e0ec800 100644 --- a/src/main/java/forge/game/MatchState.java +++ b/src/main/java/forge/game/MatchState.java @@ -118,13 +118,6 @@ public class MatchState { @Override public void run() { final Player firstPlayer = determineFirstTurnPlayer(getLastGameOutcome(), currentGame); - - // Draw cards - for (final Player p1 : currentGame.getPlayers()) { - p1.drawCards(p1.getMaxHandSize()); - } - - currentGame.setAge(GameAge.Mulligan); currentGame.getAction().startGame(firstPlayer); } }); diff --git a/src/main/java/forge/game/event/GameRestartedEvent.java b/src/main/java/forge/game/event/GameRestartedEvent.java new file mode 100644 index 00000000000..889ad0e6359 --- /dev/null +++ b/src/main/java/forge/game/event/GameRestartedEvent.java @@ -0,0 +1,17 @@ +package forge.game.event; + +import forge.game.player.Player; + +/** + * TODO: Write javadoc for this type. + * + */ +public class GameRestartedEvent extends Event { + + public final Player whoRestarted; + + public GameRestartedEvent(Player playerTurn) { + whoRestarted = playerTurn; + } + +} diff --git a/src/main/java/forge/game/event/PhaseEvent.java b/src/main/java/forge/game/event/PhaseEvent.java new file mode 100644 index 00000000000..9c98ac968df --- /dev/null +++ b/src/main/java/forge/game/event/PhaseEvent.java @@ -0,0 +1,22 @@ +package forge.game.event; + +import forge.game.phase.PhaseType; +import forge.game.player.Player; + +/** + * TODO: Write javadoc for this type. + * + */ +public class PhaseEvent extends Event { + + public final Player playerTurn; + public final PhaseType phase; + public final String phaseDesc; + + public PhaseEvent(Player player, PhaseType ph, String desc) { + playerTurn = player; + phase = ph; + phaseDesc = desc; + } + +} diff --git a/src/main/java/forge/game/phase/PhaseHandler.java b/src/main/java/forge/game/phase/PhaseHandler.java index 692088445a3..28f9c79bef3 100644 --- a/src/main/java/forge/game/phase/PhaseHandler.java +++ b/src/main/java/forge/game/phase/PhaseHandler.java @@ -34,17 +34,19 @@ import forge.GameEventType; import forge.Singletons; import forge.CardPredicates.Presets; import forge.card.trigger.TriggerType; +import forge.game.GameAge; import forge.game.GameState; import forge.game.GameType; import forge.game.event.EndOfTurnEvent; +import forge.game.event.GameRestartedEvent; import forge.game.event.ManaBurnEvent; +import forge.game.event.PhaseEvent; import forge.game.player.Player; import forge.game.zone.ZoneType; import forge.gui.framework.SDisplayUtil; import forge.gui.match.CMatchUI; import forge.gui.match.nonsingleton.VField; import forge.properties.ForgePreferences.FPref; -import forge.util.Lang; import forge.util.MyObservable; @@ -300,9 +302,8 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable { this.turn++; game.getGameLog().add(GameEventType.TURN, "Turn " + this.turn + " (" + this.getPlayerTurn() + ")"); } - - game.getGameLog().add(GameEventType.PHASE, phaseType + Lang.getPossesive(this.getPlayerTurn().getName()) + " " + this.getPhase().NameForUi); - PhaseUtil.visuallyActivatePhase(this.getPlayerTurn(), this.getPhase()); + + game.getEvents().post(new PhaseEvent(this.getPlayerTurn(), this.getPhase(), phaseType)); } private final void handleBeginPhase() { @@ -499,12 +500,7 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable { // This line fixes Combat Damage triggers not going off when they should game.getStack().unfreezeStack(); - - // UNTAP - if (this.getPhase() != PhaseType.UNTAP) { - // during untap - this.resetPriority(); - } + } /** @@ -726,10 +722,9 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable { throw new IllegalStateException("Turns already started, call this only once per game"); setPlayerTurn(goesFirst); advancePhase(); - pPlayerPriority.getController().takePriority(); + // don't even offer priority, because it's untap of 1st turn now - // This is main game loop. It will hang waiting for player's input. - while (!game.isGameOver()) { // stop game if it's outcome is clear. + while (!game.isGameOver()) { // loop only while is playing final Player actingPlayer = this.getPriorityPlayer(); final Player firstAction = this.getFirstPriority(); @@ -754,6 +749,13 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable { updateObservers(); } + // If ever the karn's ultimate resolved + if( game.getAge() == GameAge.RestartedByKarn) { + phase = null; + game.getEvents().post(new GameRestartedEvent(playerTurn)); + return; + } + // Time to handle priority to next player. if ( phase == PhaseType.COMBAT_DECLARE_ATTACKERS || phase == PhaseType.COMBAT_DECLARE_BLOCKERS) @@ -786,8 +788,6 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable { System.out.print(" >>\n"); } } - - System.out.println(FThreads.prependThreadId("Thread exited game loop due to ... " + ( game.isGameOver() ? "game over" : "interrupt" ))); }