diff --git a/binaries/data/mods/public/gui/session/MenuButtons.js b/binaries/data/mods/public/gui/session/MenuButtons.js index 9b977ecb27..22849a6327 100644 --- a/binaries/data/mods/public/gui/session/MenuButtons.js +++ b/binaries/data/mods/public/gui/session/MenuButtons.js @@ -115,8 +115,7 @@ MenuButtons.prototype.Summary = class }, "selectedData": this.selectedData }, - data => - { + data => { this.selectedData = data.summarySelectedData; this.pauseControl.implicitResume(); }); @@ -225,23 +224,7 @@ MenuButtons.prototype.Resign = class onPress() { - closeOpenDialogs(); - this.pauseControl.implicitPause(); - - messageBox( - 400, 200, - translate("Are you sure you want to resign?"), - translate("Confirmation"), - [translate("No"), translate("Yes")], - [ - resumeGame, - () => { - Engine.PostNetworkCommand({ - "type": "resign" - }); - resumeGame(); - } - ]); + (new ResignConfirmation()).display(); } }; @@ -257,50 +240,11 @@ MenuButtons.prototype.Exit = class onPress() { - closeOpenDialogs(); - this.pauseControl.implicitPause(); - - let messageType = g_IsNetworked && g_IsController ? "host" : - (g_IsNetworked && !g_IsObserver ? "client" : "singleplayer"); - - messageBox( - 400, 200, - this.Confirmation[messageType].caption(), - translate("Confirmation"), - [translate("No"), translate("Yes")], - this.Confirmation[messageType].buttons()); - } -}; - -MenuButtons.prototype.Exit.prototype.Confirmation = { - "host": { - "caption": () => translate("Are you sure you want to quit? Leaving will disconnect all other players."), - "buttons": () => [resumeGame, endGame] - }, - "client": { - "caption": () => translate("Are you sure you want to quit?"), - "buttons": () => [ - resumeGame, - () => { - messageBox( - 400, 200, - translate("Do you want to resign or will you return soon?"), - translate("Confirmation"), - [translate("I will return"), translate("I resign")], - [ - endGame, - () => { - Engine.PostNetworkCommand({ - "type": "resign" - }); - resumeGame(); - } - ]); - } - ] - }, - "singleplayer": { - "caption": () => translate("Are you sure you want to quit?"), - "buttons": () => [resumeGame, endGame] + for (let name in QuitConfirmationMenu.prototype) + { + let quitConfirmation = new QuitConfirmationMenu.prototype[name](); + if (quitConfirmation.enabled()) + quitConfirmation.display(); + } } }; diff --git a/binaries/data/mods/public/gui/session/SessionMessageBox.js b/binaries/data/mods/public/gui/session/SessionMessageBox.js new file mode 100644 index 0000000000..ae78cd896b --- /dev/null +++ b/binaries/data/mods/public/gui/session/SessionMessageBox.js @@ -0,0 +1,40 @@ +/** + * This is the same as a regular MessageBox, but it pauses if it is + * a singleplayer game, until the player answered the dialog. + */ +class SessionMessageBox +{ + display() + { + this.onPageOpening(); + + Engine.PushGuiPage( + "page_msgbox.xml", + { + "width": this.Width, + "height": this.Height, + "title": this.Title, + "message": this.Caption, + "buttonCaptions": this.Buttons ? this.Buttons.map(button => button.caption) : undefined, + }, + this.onPageClosed.bind(this)); + } + + onPageOpening() + { + closeOpenDialogs(); + g_PauseControl.implicitPause(); + } + + onPageClosed(buttonId) + { + if (this.Buttons && this.Buttons[buttonId].onPress) + this.Buttons[buttonId].onPress.call(this); + + if (Engine.IsGameStarted()) + resumeGame(); + } +} + +SessionMessageBox.prototype.Width = 400; +SessionMessageBox.prototype.Height = 200; diff --git a/binaries/data/mods/public/gui/session/developer_overlay/TimeWarp.js b/binaries/data/mods/public/gui/session/developer_overlay/TimeWarp.js index 231a98c664..c33250142a 100644 --- a/binaries/data/mods/public/gui/session/developer_overlay/TimeWarp.js +++ b/binaries/data/mods/public/gui/session/developer_overlay/TimeWarp.js @@ -23,10 +23,7 @@ class TimeWarp this.enabled = enabled; if (enabled) - messageBox( - 500, 250, - translate(this.Description), - translate(this.Title)); + (new TimeWarpMessageBox()).display(); Engine.EnableTimeWarpRecording(enabled ? this.NumberTurns : 0); } @@ -60,7 +57,11 @@ TimeWarp.prototype.NumberTurns = 10; */ TimeWarp.prototype.FastForwardSpeed = 20; -TimeWarp.prototype.Title = markForTranslation("Time warp mode"); - -TimeWarp.prototype.Description = markForTranslation( +class TimeWarpMessageBox extends SessionMessageBox +{ +} +TimeWarpMessageBox.prototype.Width = 500; +TimeWarpMessageBox.prototype.Height = 250; +TimeWarpMessageBox.prototype.Title = translate("Time warp mode"); +TimeWarpMessageBox.prototype.Caption = translate( "Note: time warp mode is a developer option, and not intended for use over long periods of time. Using it incorrectly may cause the game to run out of memory or crash."); diff --git a/binaries/data/mods/public/gui/session/message_box/DeleteSelectionConfirmation.js b/binaries/data/mods/public/gui/session/message_box/DeleteSelectionConfirmation.js new file mode 100644 index 0000000000..25de1e13ee --- /dev/null +++ b/binaries/data/mods/public/gui/session/message_box/DeleteSelectionConfirmation.js @@ -0,0 +1,20 @@ +class DeleteSelectionConfirmation extends SessionMessageBox +{ + constructor(deleteSelection) + { + super(); + this.deleteSelection = deleteSelection; + } +} + +DeleteSelectionConfirmation.prototype.Title = translate("Delete"); +DeleteSelectionConfirmation.prototype.Caption = translate("Destroy everything currently selected?"); +DeleteSelectionConfirmation.prototype.Buttons = [ + { + "caption": translate("No") + }, + { + "caption": translate("Yes"), + "onPress": function() { this.deleteSelection(); } + } +]; diff --git a/binaries/data/mods/public/gui/session/message_box/OutOfSyncNetwork.js b/binaries/data/mods/public/gui/session/message_box/OutOfSyncNetwork.js new file mode 100644 index 0000000000..2e46ea3ca5 --- /dev/null +++ b/binaries/data/mods/public/gui/session/message_box/OutOfSyncNetwork.js @@ -0,0 +1,51 @@ +class OutOfSyncNetwork extends SessionMessageBox +{ + constructor() + { + super(); + registerNetworkOutOfSyncHandler(this.onNetworkOutOfSync.bind(this)); + } + + /** + * The message object is constructed in CNetClientTurnManager::OnSyncError. + */ + onNetworkOutOfSync(msg) + { + let txt = [ + sprintf(translate("Out-Of-Sync error on turn %(turn)s."), { + "turn": msg.turn + }), + + sprintf(translateWithContext("Out-Of-Sync", "Players: %(players)s"), { + "players": msg.players.join(translateWithContext("Separator for a list of players", ", ")) + }), + + msg.hash == msg.expectedHash ? + translateWithContext("Out-Of-Sync", "Your game state is identical to the hosts game state.") : + translateWithContext("Out-Of-Sync", "Your game state differs from the hosts game state."), + + "" + ]; + + if (msg.turn > 1 && g_GameAttributes.settings.PlayerData.some(pData => pData && pData.AI)) + txt.push(translateWithContext("Out-Of-Sync", "Rejoining Multiplayer games with AIs is not supported yet!")); + else + txt.push( + translateWithContext("Out-Of-Sync", "Ensure all players use the same mods."), + translateWithContext("Out-Of-Sync", 'Click on "Report a Bug" in the main menu to help fix this.'), + sprintf(translateWithContext("Out-Of-Sync", "Replay saved to %(filepath)s"), { + "filepath": escapeText(msg.path_replay) + }), + sprintf(translateWithContext("Out-Of-Sync", "Dumping current state to %(filepath)s"), { + "filepath": escapeText(msg.path_oos_dump) + })); + + this.Caption = txt.join("\n"); + this.display(); + } +} + +OutOfSyncNetwork.prototype.Width = 600; +OutOfSyncNetwork.prototype.Height = 280; + +OutOfSyncNetwork.prototype.Title = translate("Out of Sync"); diff --git a/binaries/data/mods/public/gui/session/message_box/OutOfSyncReplay.js b/binaries/data/mods/public/gui/session/message_box/OutOfSyncReplay.js new file mode 100644 index 0000000000..a9bd44e9f3 --- /dev/null +++ b/binaries/data/mods/public/gui/session/message_box/OutOfSyncReplay.js @@ -0,0 +1,29 @@ +class OutOfSyncReplay extends SessionMessageBox +{ + constructor() + { + super(); + Engine.GetGUIObjectByName("session").onReplayOutOfSync = this.onReplayOutOfSync.bind(this); + } + + onReplayOutOfSync(turn, hash, expectedHash) + { + this.Caption = sprintf(this.Captions.join("\n"), { + "turn": turn, + "hash": hash, + "expectedHash": expectedHash + }); + this.display(); + } +} + +OutOfSyncReplay.prototype.Width = 500; +OutOfSyncReplay.prototype.Height = 140; + +OutOfSyncReplay.prototype.Title = translate("Out of Sync"); + +OutOfSyncReplay.prototype.Captions = [ + translate("Out-Of-Sync error on turn %(turn)s."), + // Translation: This is shown if replay is out of sync + translate("Out-Of-Sync", "The current game state is different from the original game state.") +]; diff --git a/binaries/data/mods/public/gui/session/message_box/QuitConfirmation.js b/binaries/data/mods/public/gui/session/message_box/QuitConfirmation.js new file mode 100644 index 0000000000..dd9993934b --- /dev/null +++ b/binaries/data/mods/public/gui/session/message_box/QuitConfirmation.js @@ -0,0 +1,20 @@ +class QuitConfirmation extends SessionMessageBox +{ +} + +QuitConfirmation.prototype.Title = + translate("Confirmation"); + +QuitConfirmation.prototype.Caption = + translate("Are you sure you want to quit?"); + +QuitConfirmation.prototype.Buttons = +[ + { + "caption": translate("No") + }, + { + "caption": translate("Yes"), + "onPress": endGame + } +]; diff --git a/binaries/data/mods/public/gui/session/message_box/QuitConfirmationDefeat.js b/binaries/data/mods/public/gui/session/message_box/QuitConfirmationDefeat.js new file mode 100644 index 0000000000..6451566b8f --- /dev/null +++ b/binaries/data/mods/public/gui/session/message_box/QuitConfirmationDefeat.js @@ -0,0 +1,57 @@ +/** + * This class will spawn a dialog asking to exit the game in case the user was an assigned player who has been defeated. + */ +class QuitConfirmationDefeat extends QuitConfirmation +{ + constructor() + { + super(); + + if (Engine.IsAtlasRunning()) + return; + + this.confirmHandler = undefined; + registerPlayersFinishedHandler(this.onPlayersFinished.bind(this)); + } + + onPlayersFinished(players, won) + { + if (players.indexOf(Engine.GetPlayerID()) == -1) + return; + + // Defer simulation result until + // 1. the loading screen finished for all networked clients (g_IsNetworkedActive) + // 2. all messages modifying g_Players victory state were processed (next turn) + this.confirmHandler = this.confirmExit.bind(this, won); + registerSimulationUpdateHandler(this.confirmHandler); + } + + confirmExit(won) + { + if (g_IsNetworked && !g_IsNetworkedActive) + return; + + unregisterSimulationUpdateHandler(this.confirmHandler); + + // Don't ask for exit if other humans are still playing. + let askExit = !Engine.HasNetServer() || g_Players.every((player, i) => + i == 0 || + player.state != "active" || + g_GameAttributes.settings.PlayerData[i].AI != ""); + + this.Title = won ? this.TitleVictory : this.TitleDefeated; + + this.Caption = + g_PlayerStateMessages[won ? "won" : "defeated"] + + (askExit ? "\n" + this.Question : ""); + + this.Buttons = askExit ? super.Buttons : undefined; + + this.display(); + } +} + +QuitConfirmationDefeat.prototype.TitleVictory = translate("VICTORIOUS!"); +QuitConfirmationDefeat.prototype.TitleDefeated = translate("DEFEATED!"); + +QuitConfirmationDefeat.prototype.Question = translate("Do you want to quit?"); diff --git a/binaries/data/mods/public/gui/session/message_box/QuitConfirmationMenu.js b/binaries/data/mods/public/gui/session/message_box/QuitConfirmationMenu.js new file mode 100644 index 0000000000..65c2236c92 --- /dev/null +++ b/binaries/data/mods/public/gui/session/message_box/QuitConfirmationMenu.js @@ -0,0 +1,78 @@ +/** + * The class that is enabled() will be triggered when the user clicks on the Exit menu button. + */ +class QuitConfirmationMenu +{ +} + +/** + * In singleplayer mode, replaymode and for observers in multiplayermatches that + * aren't the host, exit the match instantly. + */ +QuitConfirmationMenu.prototype.Singleplayer = class extends QuitConfirmation +{ + enabled() + { + return !g_IsNetworked || (!g_IsController && g_IsObserver); + } +}; + +/** + * If the current player is the host of a networked match, have the player + * confirm intent to end the game for the remote players as well. + */ +QuitConfirmationMenu.prototype.MultiplayerHost = class extends QuitConfirmation +{ + enabled() + { + return g_IsNetworked && g_IsController; + } +}; + +QuitConfirmationMenu.prototype.MultiplayerHost.prototype.Caption = + translate("Are you sure you want to quit? Leaving will disconnect all other players."); + +/** + * Active players that aren't the host will be asked if they want to resign before leaving. + */ +QuitConfirmationMenu.prototype.MultiplayerClient = class extends QuitConfirmation +{ + enabled() + { + return g_IsNetworked && !g_IsController && !g_IsObserver; + } +}; + +QuitConfirmationMenu.prototype.MultiplayerClient.prototype.Buttons = +[ + { + "caption": translate("No") + }, + { + "caption": translate("Yes"), + "onPress": () => { + (new ReturnQuestion()).display(); + } + } +]; + +class ReturnQuestion extends SessionMessageBox +{ +} + +ReturnQuestion.prototype.Title = translate("Confirmation"); +ReturnQuestion.prototype.Caption = translate("Do you want to resign or will you return soon?"); +ReturnQuestion.prototype.Buttons = [ + { + "caption": translate("I will return"), + "onPress": endGame + }, + { + "caption": translate("I resign"), + "onPress": () => { + Engine.PostNetworkCommand({ + "type": "resign" + }); + } + } +]; diff --git a/binaries/data/mods/public/gui/session/message_box/QuitConfirmationReplay.js b/binaries/data/mods/public/gui/session/message_box/QuitConfirmationReplay.js new file mode 100644 index 0000000000..befa1fcb83 --- /dev/null +++ b/binaries/data/mods/public/gui/session/message_box/QuitConfirmationReplay.js @@ -0,0 +1,28 @@ +/** + * This class is concerned with opening a message box if the game is in replaymode and that replay ended. + */ +class QuitConfirmationReplay extends SessionMessageBox +{ + constructor() + { + super(); + Engine.GetGUIObjectByName("session").onReplayFinished = this.display.bind(this); + } +} + +QuitConfirmationReplay.prototype.Title = + translateWithContext("replayFinished", "Confirmation"); + +QuitConfirmationReplay.prototype.Caption = + translateWithContext("replayFinished", "The replay has finished. Do you want to quit?"); + +QuitConfirmationReplay.prototype.Buttons = +[ + { + "caption": translateWithContext("replayFinished", "No") + }, + { + "caption": translateWithContext("replayFinished", "Yes"), + "onPress": endGame + } +]; diff --git a/binaries/data/mods/public/gui/session/message_box/ResignConfirmation.js b/binaries/data/mods/public/gui/session/message_box/ResignConfirmation.js new file mode 100644 index 0000000000..157c8094dd --- /dev/null +++ b/binaries/data/mods/public/gui/session/message_box/ResignConfirmation.js @@ -0,0 +1,19 @@ +class ResignConfirmation extends SessionMessageBox +{ +} + +ResignConfirmation.prototype.Title = translate("Confirmation"); +ResignConfirmation.prototype.Caption = translate("Are you sure you want to resign?"); +ResignConfirmation.prototype.Buttons = [ + { + "caption": translate("No") + }, + { + "caption": translate("Yes"), + "onPress": () => { + Engine.PostNetworkCommand({ + "type": "resign" + }); + } + } +]; diff --git a/binaries/data/mods/public/gui/session/messages.js b/binaries/data/mods/public/gui/session/messages.js index 59d36bac40..f2eb040fae 100644 --- a/binaries/data/mods/public/gui/session/messages.js +++ b/binaries/data/mods/public/gui/session/messages.js @@ -23,6 +23,11 @@ var g_PlayerAssignmentsChangeHandlers = new Set(); */ var g_CeasefireEndedHandlers = new Set(); +/** + * These handlers are fired if the server informed the players that the networked game is out of sync. + */ +var g_NetworkOutOfSyncHandlers = new Set(); + /** * Handle all netmessage types that can occur. */ @@ -34,7 +39,8 @@ var g_NetMessageTypes = { addNetworkWarning(msg); }, "out-of-sync": msg => { - onNetworkOutOfSync(msg); + for (let handler of g_NetworkOutOfSyncHandlers) + handler(msg); }, "players": msg => { handlePlayerAssignmentsMessage(msg); @@ -302,6 +308,11 @@ function registerCeasefireEndedHandler(handler) g_CeasefireEndedHandlers.add(handler); } +function registerNetworkOutOfSyncHandler(handler) +{ + g_NetworkOutOfSyncHandlers.add(handler); +} + /** * Loads all known cheat commands. */ @@ -516,58 +527,6 @@ function handleClientsLoadingMessage(guids) loadingClientsText.caption = guids.map(guid => colorizePlayernameByGUID(guid)).join(translateWithContext("Separator for a list of client loading messages", ", ")); } -function onNetworkOutOfSync(msg) -{ - let txt = [ - sprintf(translate("Out-Of-Sync error on turn %(turn)s."), { - "turn": msg.turn - }), - - sprintf(translateWithContext("Out-Of-Sync", "Players: %(players)s"), { - "players": msg.players.join(translateWithContext("Separator for a list of players", ", ")) - }), - - msg.hash == msg.expectedHash ? - translateWithContext("Out-Of-Sync", "Your game state is identical to the hosts game state.") : - translateWithContext("Out-Of-Sync", "Your game state differs from the hosts game state."), - - "" - ]; - - if (msg.turn > 1 && g_GameAttributes.settings.PlayerData.some(pData => pData && pData.AI)) - txt.push(translateWithContext("Out-Of-Sync", "Rejoining Multiplayer games with AIs is not supported yet!")); - else - txt.push( - translateWithContext("Out-Of-Sync", "Ensure all players use the same mods."), - translateWithContext("Out-Of-Sync", 'Click on "Report a Bug" in the main menu to help fix this.'), - sprintf(translateWithContext("Out-Of-Sync", "Replay saved to %(filepath)s"), { - "filepath": escapeText(msg.path_replay) - }), - sprintf(translateWithContext("Out-Of-Sync", "Dumping current state to %(filepath)s"), { - "filepath": escapeText(msg.path_oos_dump) - }) - ); - - messageBox( - 600, 280, - txt.join("\n"), - translate("Out of Sync") - ); -} - -function onReplayOutOfSync(turn, hash, expectedHash) -{ - messageBox( - 500, 140, - sprintf(translate("Out-Of-Sync error on turn %(turn)s."), { - "turn": turn - }) + "\n" + - // Translation: This is shown if replay is out of sync - translateWithContext("Out-Of-Sync", "The current game state is different from the original game state."), - translate("Out of Sync") - ); -} - function handlePlayerAssignmentsMessage(message) { for (let guid in g_PlayerAssignments) diff --git a/binaries/data/mods/public/gui/session/session.js b/binaries/data/mods/public/gui/session/session.js index b9d0bba8c1..46d3ca28ae 100644 --- a/binaries/data/mods/public/gui/session/session.js +++ b/binaries/data/mods/public/gui/session/session.js @@ -18,10 +18,14 @@ var g_GameSpeedControl; var g_Menu; var g_MiniMapPanel; var g_ObjectivesDialog; +var g_OutOfSyncNetwork; +var g_OutOfSyncReplay; var g_PanelEntityManager; var g_PauseControl; var g_PauseOverlay; var g_PlayerViewControl; +var g_QuitConfirmationDefeat; +var g_QuitConfirmationReplay; var g_RangeOverlayManager; var g_ResearchProgress; var g_TradeDialog; @@ -68,11 +72,6 @@ var g_IsObserver = false; */ var g_HasRejoined = false; -/** - * Shows a message box asking the user to leave if "won" or "defeated". - */ -var g_ConfirmExit = false; - /** * The playerID selected in the change perspective tool. */ @@ -263,11 +262,11 @@ function init(initData, hotloadData) } g_DiplomacyColors = new DiplomacyColors(); - g_PlayerViewControl = new PlayerViewControl(); g_PlayerViewControl.registerViewedPlayerChangeHandler(g_DiplomacyColors.updateDisplayedPlayerColors.bind(g_DiplomacyColors)); g_DiplomacyColors.registerDiplomacyColorsChangeHandler(g_PlayerViewControl.rebuild.bind(g_PlayerViewControl)); g_DiplomacyColors.registerDiplomacyColorsChangeHandler(updateGUIObjects); + g_PauseControl = new PauseControl(); g_PlayerViewControl.registerPreViewedPlayerChangeHandler(removeStatusBarDisplay); g_PlayerViewControl.registerViewedPlayerChangeHandler(resetTemplates); @@ -275,12 +274,15 @@ function init(initData, hotloadData) g_DeveloperOverlay = new DeveloperOverlay(g_PlayerViewControl, g_Selection); g_DiplomacyDialog = new DiplomacyDialog(g_PlayerViewControl, g_DiplomacyColors); g_GameSpeedControl = new GameSpeedControl(g_PlayerViewControl); + g_Menu = new Menu(g_PauseControl, g_PlayerViewControl, g_Chat); g_MiniMapPanel = new MiniMapPanel(g_PlayerViewControl, g_DiplomacyColors, g_WorkerTypes); g_ObjectivesDialog = new ObjectivesDialog(g_PlayerViewControl); + g_OutOfSyncNetwork = new OutOfSyncNetwork(); + g_OutOfSyncReplay = new OutOfSyncReplay(); g_PanelEntityManager = new PanelEntityManager(g_PlayerViewControl, g_Selection, g_PanelEntityOrder); - g_PauseControl = new PauseControl(); g_PauseOverlay = new PauseOverlay(g_PauseControl); - g_Menu = new Menu(g_PauseControl, g_PlayerViewControl, g_Chat); + g_QuitConfirmationDefeat = new QuitConfirmationDefeat(); + g_QuitConfirmationReplay = new QuitConfirmationReplay(); g_RangeOverlayManager = new RangeOverlayManager(g_Selection); g_ResearchProgress = new ResearchProgress(g_PlayerViewControl, g_Selection); g_TradeDialog = new TradeDialog(g_PlayerViewControl); @@ -479,8 +481,6 @@ function playersFinished(players, victoryString, won) global.music.states.VICTORY : global.music.states.DEFEAT ); - - g_ConfirmExit = won ? "won" : "defeated"; } function resumeGame() @@ -625,42 +625,6 @@ function onSimulationUpdate() updateCinemaPath(); handleNotifications(); updateGUIObjects(); - - if (g_ConfirmExit) - confirmExit(); -} - -/** - * Don't show the message box before all playerstate changes are processed. - */ -function confirmExit() -{ - if (g_IsNetworked && !g_IsNetworkedActive) - return; - - closeOpenDialogs(); - g_PauseControl.implicitPause(); - - // Don't ask for exit if other humans are still playing - let askExit = !Engine.HasNetServer() || g_Players.every((player, i) => - i == 0 || - player.state != "active" || - g_GameAttributes.settings.PlayerData[i].AI != ""); - - let subject = g_PlayerStateMessages[g_ConfirmExit]; - if (askExit) - subject += "\n" + translate("Do you want to quit?"); - - messageBox( - 400, 200, - subject, - g_ConfirmExit == "won" ? - translate("VICTORIOUS!") : - translate("DEFEATED!"), - askExit ? [translate("No"), translate("Yes")] : [translate("OK")], - askExit ? [resumeGame, endGame] : [resumeGame]); - - g_ConfirmExit = false; } function toggleGUI() @@ -702,18 +666,6 @@ function updateGUIObjects() } } -function onReplayFinished() -{ - closeOpenDialogs(); - g_PauseControl.implicitPause(); - - messageBox(400, 200, - translateWithContext("replayFinished", "The replay has finished. Do you want to quit?"), - translateWithContext("replayFinished", "Confirmation"), - [translateWithContext("replayFinished", "No"), translateWithContext("replayFinished", "Yes")], - [resumeGame, endGame]); -} - function updateGroups() { g_Groups.update(); diff --git a/binaries/data/mods/public/gui/session/session.xml b/binaries/data/mods/public/gui/session/session.xml index dd8f1a1ad8..76d7499d2f 100644 --- a/binaries/data/mods/public/gui/session/session.xml +++ b/binaries/data/mods/public/gui/session/session.xml @@ -10,6 +10,7 @@