Index: ps/trunk/binaries/data/mods/public/gui/session/Menu.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/Menu.js +++ ps/trunk/binaries/data/mods/public/gui/session/Menu.js @@ -0,0 +1,120 @@ +/** + * This class constructs and positions the menu buttons and assigns the handlers defined in MenuButtons. + */ +class Menu +{ + constructor(pauseControl, playerViewControl, chat) + { + this.menuButton = Engine.GetGUIObjectByName("menuButton"); + this.menuButton.onPress = this.toggle.bind(this); + registerHotkeyChangeHandler(this.rebuild.bind(this)); + + this.isOpen = false; + this.lastTick = undefined; + + this.menuButtonPanel = Engine.GetGUIObjectByName("menuButtonPanel"); + let menuButtons = this.menuButtonPanel.children; + this.margin = menuButtons[0].size.top; + this.buttonHeight = menuButtons[0].size.bottom; + + let handlerNames = this.getHandlerNames(); + if (handlerNames.length > menuButtons.length) + throw new Error( + "There are " + handlerNames.length + " menu buttons defined, " + + "but only " + menuButtons.length + " objects!"); + + this.buttons = handlerNames.map((handlerName, i) => { + let handler = new MenuButtons.prototype[handlerName](menuButtons[i], pauseControl, playerViewControl, chat); + this.initButton(handler, menuButtons[i], i); + return handler; + }); + + this.endPosition = this.margin + this.buttonHeight * (1 + handlerNames.length); + let size = this.menuButtonPanel.size; + size.top = -this.endPosition; + size.bottom = 0; + this.menuButtonPanel.size = size; + } + + rebuild() + { + this.menuButton.tooltip = sprintf(translate("Press %(hotkey)s to toggle this menu."), { + "hotkey": colorizeHotkey("%(hotkey)s", this.menuButton.hotkey), + }); + } + + /** + * This function may be overwritten to change the button order. + */ + getHandlerNames() + { + return Object.keys(MenuButtons.prototype); + } + + toggle() + { + this.isOpen = !this.isOpen; + this.startAnimation(); + } + + close() + { + this.isOpen = false; + this.startAnimation(); + } + + initButton(handler, button, i) + { + button.onPress = () => { + this.close(); + handler.onPress(); + }; + + let size = button.size; + size.top = this.buttonHeight * (i + 1) + this.margin; + size.bottom = this.buttonHeight * (i + 2); + button.size = size; + + button.hidden = false; + } + + startAnimation() + { + this.lastTick = Date.now(); + this.menuButtonPanel.onTick = this.onTick.bind(this); + } + + /** + * Animate menu panel. + */ + onTick() + { + let tickLength = Date.now() - this.lastTick; + this.lastTick = Date.now(); + + let maxOffset = + this.endPosition + ( + this.isOpen ? + -this.menuButtonPanel.size.bottom : + +this.menuButtonPanel.size.top); + + + if (maxOffset <= 0) + { + // TODO: support actually deleting the handler + this.menuButtonPanel.onTick = () => {}; + return; + } + + let offset = Math.min(this.Speed * tickLength, maxOffset) * (this.isOpen ? +1 : -1); + let size = this.menuButtonPanel.size; + size.top += offset; + size.bottom += offset; + this.menuButtonPanel.size = size; + } +} + +/** + * Number of pixels per millisecond to move. + */ +Menu.prototype.Speed = 1.2; Index: ps/trunk/binaries/data/mods/public/gui/session/Menu.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/Menu.xml +++ ps/trunk/binaries/data/mods/public/gui/session/Menu.xml @@ -0,0 +1,17 @@ + + + + Index: ps/trunk/binaries/data/mods/public/gui/session/MenuButtons.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/MenuButtons.js +++ ps/trunk/binaries/data/mods/public/gui/session/MenuButtons.js @@ -0,0 +1,309 @@ +/** + * This class is extended in subclasses. + * Each subclass represents one button in the session menu. + * All subclasses store the button member so that mods can change it easily. + */ +class MenuButtons +{ +} + +MenuButtons.prototype.Manual = class +{ + constructor(button, pauseControl) + { + this.button = button; + this.button.caption = translate(translate("Manual")); + this.pauseControl = pauseControl; + } + + onPress() + { + closeOpenDialogs(); + this.pauseControl.implicitPause(); + Engine.PushGuiPage("page_manual.xml", {}, resumeGame); + } +}; + +MenuButtons.prototype.Chat = class +{ + constructor(button, pauseControl, playerViewControl, chat) + { + this.button = button; + this.button.caption = translate("Chat"); + this.chat = chat; + registerHotkeyChangeHandler(this.rebuild.bind(this)); + } + + rebuild() + { + this.button.tooltip = this.chat.getOpenHotkeyTooltip().trim(); + } + + onPress() + { + this.chat.openPage(); + } +}; + +MenuButtons.prototype.Save = class +{ + constructor(button, pauseControl) + { + this.button = button; + this.button.caption = translate("Save"); + this.pauseControl = pauseControl; + } + + onPress() + { + closeOpenDialogs(); + this.pauseControl.implicitPause(); + + Engine.PushGuiPage( + "page_loadgame.xml", + { "savedGameData": getSavedGameData() }, + resumeGame); + } +}; + +MenuButtons.prototype.Summary = class +{ + constructor(button, pauseControl) + { + this.button = button; + this.button.caption = translate("Summary"); + this.button.hotkey = "summary"; + // TODO: Atlas should pass g_GameAttributes.settings + this.button.enabled = !Engine.IsAtlasRunning(); + + this.pauseControl = pauseControl; + this.selectedData = undefined; + + registerHotkeyChangeHandler(this.rebuild.bind(this)); + } + + rebuild() + { + this.button.tooltip = sprintf(translate("Press %(hotkey)s to open the summary screen."), { + "hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey), + }); + } + + onPress() + { + if (Engine.IsAtlasRunning()) + return; + + closeOpenDialogs(); + this.pauseControl.implicitPause(); + // Allows players to see their own summary. + // If they have shared ally vision researched, they are able to see the summary of there allies too. + let simState = Engine.GuiInterfaceCall("GetExtendedSimulationState"); + Engine.PushGuiPage( + "page_summary.xml", + { + "sim": { + "mapSettings": g_GameAttributes.settings, + "playerStates": simState.players.filter((state, player) => + g_IsObserver || player == 0 || player == g_ViewedPlayer || + simState.players[g_ViewedPlayer].hasSharedLos && g_Players[player].isMutualAlly[g_ViewedPlayer]), + "timeElapsed": simState.timeElapsed + }, + "gui": { + "dialog": true, + "isInGame": true + }, + "selectedData": this.selectedData + }, + data => + { + this.selectedData = data.summarySelectedData; + this.pauseControl.implicitResume(); + }); + } +}; + +MenuButtons.prototype.Lobby = class +{ + constructor(button) + { + this.button = button; + this.button.caption = translate("Lobby"); + this.button.hotkey = "lobby"; + this.button.enabled = Engine.HasXmppClient(); + + registerHotkeyChangeHandler(this.rebuild.bind(this)); + } + + rebuild() + { + this.button.tooltip = sprintf(translate("Press %(hotkey)s to open the multiplayer lobby page without leaving the game."), { + "hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey), + }); + } + + onPress() + { + if (!Engine.HasXmppClient()) + return; + closeOpenDialogs(); + Engine.PushGuiPage("page_lobby.xml", { "dialog": true }); + } +}; + +MenuButtons.prototype.Options = class +{ + constructor(button, pauseControl) + { + this.button = button; + this.button.caption = translate("Options"); + this.pauseControl = pauseControl; + } + + onPress() + { + closeOpenDialogs(); + this.pauseControl.implicitPause(); + + Engine.PushGuiPage( + "page_options.xml", + {}, + callbackFunctionNames => { + for (let functionName of callbackFunctionNames) + if (global[functionName]) + global[functionName](); + + resumeGame(); + }); + } +}; + +MenuButtons.prototype.Pause = class +{ + constructor(button, pauseControl, playerViewControl) + { + this.button = button; + this.button.hotkey = "pause"; + this.pauseControl = pauseControl; + + registerPlayersInitHandler(this.rebuild.bind(this)); + registerPlayersFinishedHandler(this.rebuild.bind(this)); + playerViewControl.registerPlayerIDChangeHandler(this.rebuild.bind(this)); + pauseControl.registerPauseHandler(this.rebuild.bind(this)); + registerHotkeyChangeHandler(this.rebuild.bind(this)); + } + + rebuild() + { + this.button.enabled = !g_IsObserver || !g_IsNetworked || g_IsController; + this.button.caption = this.pauseControl.explicitPause ? translate("Resume") : translate("Pause"); + this.button.tooltip = sprintf(translate("Press %(hotkey)s to pause or resume the game."), { + "hotkey": colorizeHotkey("%(hotkey)s", this.button.hotkey), + }); + } + + onPress() + { + this.pauseControl.setPaused(!g_PauseControl.explicitPause, true); + } +}; + +MenuButtons.prototype.Resign = class +{ + constructor(button, pauseControl, playerViewControl) + { + this.button = button; + this.button.caption = translate("Resign"); + this.pauseControl = pauseControl; + + registerPlayersInitHandler(this.rebuild.bind(this)); + registerPlayersFinishedHandler(this.rebuild.bind(this)); + playerViewControl.registerPlayerIDChangeHandler(this.rebuild.bind(this)); + } + + rebuild() + { + this.button.enabled = !g_IsObserver; + } + + 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(); + } + ]); + } +}; + +MenuButtons.prototype.Exit = class +{ + constructor(button, pauseControl) + { + this.button = button; + this.button.caption = translate("Exit"); + this.button.enabled = !Engine.IsAtlasRunning(); + this.pauseControl = pauseControl; + } + + 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] + } +}; Index: ps/trunk/binaries/data/mods/public/gui/session/chat/Chat.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/chat/Chat.js +++ ps/trunk/binaries/data/mods/public/gui/session/chat/Chat.js @@ -68,6 +68,11 @@ this.ChatWindow.closePage(); } + getOpenHotkeyTooltip() + { + return this.ChatInput.getOpenHotkeyTooltip(); + } + /** * Send the given chat message. */ Index: ps/trunk/binaries/data/mods/public/gui/session/chat/ChatInput.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/chat/ChatInput.js +++ ps/trunk/binaries/data/mods/public/gui/session/chat/ChatInput.js @@ -12,18 +12,29 @@ this.chatInput = Engine.GetGUIObjectByName("chatInput"); this.chatInput.onPress = this.submitChatInput.bind(this); this.chatInput.onTab = this.autoComplete.bind(this); - this.chatInput.tooltip = this.getHotkeyTooltip(); this.sendChat = Engine.GetGUIObjectByName("sendChat"); this.sendChat.onPress = this.submitChatInput.bind(this); - this.sendChat.tooltip = this.getHotkeyTooltip(); + + registerHotkeyChangeHandler(this.onHotkeyChange.bind(this)); + } + + onHotkeyChange() + { + let tooltip = this.getInputHotkeyTooltip() + this.getOpenHotkeyTooltip(); + this.chatInput.tooltip = tooltip; + this.sendChat.tooltip = tooltip; + } + + getInputHotkeyTooltip() + { + return translateWithContext("chat input", "Type the message to send.") + "\n" + + colorizeAutocompleteHotkey(); } - getHotkeyTooltip() + getOpenHotkeyTooltip() { - return translateWithContext("chat input", "Type the message to send.") + "\n" + - colorizeAutocompleteHotkey() + - colorizeHotkey("\n" + translate("Press %(hotkey)s to open the public chat."), "chat") + + return colorizeHotkey("\n" + translate("Press %(hotkey)s to open the public chat."), "chat") + colorizeHotkey( "\n" + (g_IsObserver ? translate("Press %(hotkey)s to open the observer chat.") : Index: ps/trunk/binaries/data/mods/public/gui/session/hotkeys/misc.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/hotkeys/misc.xml +++ ps/trunk/binaries/data/mods/public/gui/session/hotkeys/misc.xml @@ -8,26 +8,10 @@ toggleGUI(); - - toggleMenu(); - - toggleTutorial(); - - openGameSummary(); - - - - openStrucTree("page_civinfo.xml"); - - - - openStrucTree("page_structree.xml"); - - toggleConfigBool("silhouettes"); Index: ps/trunk/binaries/data/mods/public/gui/session/input.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/input.js +++ ps/trunk/binaries/data/mods/public/gui/session/input.js @@ -478,7 +478,7 @@ // Close the menu when interacting with the game world if (!mouseIsOverObject && (ev.type =="mousebuttonup" || ev.type == "mousebuttondown") && (ev.button == SDL_BUTTON_LEFT || ev.button == SDL_BUTTON_RIGHT)) - closeMenu(); + g_Menu.close(); // State-machine processing: // Index: ps/trunk/binaries/data/mods/public/gui/session/menu.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/menu.js +++ ps/trunk/binaries/data/mods/public/gui/session/menu.js @@ -1,311 +0,0 @@ -// Menu / panel border size -var MARGIN = 4; - -// Includes the main menu button -const NUM_BUTTONS = 10; - -// Regular menu buttons -var BUTTON_HEIGHT = 32; - -// The position where the bottom of the menu will end up (currently 228) -const END_MENU_POSITION = (BUTTON_HEIGHT * NUM_BUTTONS) + MARGIN; - -// Menu starting position: bottom -const MENU_BOTTOM = 0; - -// Menu starting position: top -const MENU_TOP = MENU_BOTTOM - END_MENU_POSITION; - -// Number of pixels per millisecond to move -var MENU_SPEED = 1.2; - -/** - * Store civilization code and page (structree or history) opened in civilization info. - */ -var g_CivInfo = { - "civ": "", - "page": "page_structree.xml" -}; - -var g_IsMenuOpen = false; - -/** - * Remember last viewed summary panel and charts. - */ -var g_SummarySelectedData; - -function initMenu(playerViewControl, pauseControl) -{ - Engine.GetGUIObjectByName("menu").size = "100%-164 " + MENU_TOP + " 100% " + MENU_BOTTOM; - Engine.GetGUIObjectByName("lobbyButton").enabled = Engine.HasXmppClient(); - playerViewControl.registerViewedPlayerChangeHandler(updateResignButton); - updatePauseButton(); - pauseControl.registerPauseHandler(updatePauseButton); - - // TODO: Atlas should pass g_GameAttributes.settings - for (let button of ["menuExitButton", "summaryButton"]) - Engine.GetGUIObjectByName(button).enabled = !Engine.IsAtlasRunning(); -} - -function updateMenuPosition(dt) -{ - let menu = Engine.GetGUIObjectByName("menu"); - - let maxOffset = g_IsMenuOpen ? - END_MENU_POSITION - menu.size.bottom : - menu.size.top - MENU_TOP; - - if (maxOffset <= 0) - return; - - let offset = Math.min(MENU_SPEED * dt, maxOffset) * (g_IsMenuOpen ? +1 : -1); - - let size = menu.size; - size.top += offset; - size.bottom += offset; - menu.size = size; -} - -// Opens the menu by revealing the screen which contains the menu -function openMenu() -{ - g_IsMenuOpen = true; -} - -// Closes the menu and resets position -function closeMenu() -{ - g_IsMenuOpen = false; -} - -function toggleMenu() -{ - g_IsMenuOpen = !g_IsMenuOpen; -} - -function updatePauseButton() -{ - let pauseButton = Engine.GetGUIObjectByName("pauseButton"); - pauseButton.caption = g_PauseControl.explicitPause ? translate("Resume") : translate("Pause"); - pauseButton.enabled = !g_IsObserver || !g_IsNetworked || g_IsController; - pauseButton.onPress = () => { - closeMenu(); - g_PauseControl.setPaused(!g_PauseControl.explicitPause, true); - }; -} - -function updateResignButton() -{ - Engine.GetGUIObjectByName("menuResignButton").enabled = !g_IsObserver; -} - -function optionsMenuButton() -{ - closeOpenDialogs(); - openOptions(); -} - -function lobbyDialogButton() -{ - if (!Engine.HasXmppClient()) - return; - - closeOpenDialogs(); - Engine.PushGuiPage("page_lobby.xml", { "dialog": true }); -} - -function chatMenuButton() -{ - g_Chat.openPage(); -} - -function resignMenuButton() -{ - closeOpenDialogs(); - g_PauseControl.implicitPause(); - - messageBox( - 400, 200, - translate("Are you sure you want to resign?"), - translate("Confirmation"), - [translate("No"), translate("Yes")], - [resumeGame, resignGame]); -} - -function exitMenuButton() -{ - closeOpenDialogs(); - g_PauseControl.implicitPause(); - - let messageTypes = { - "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, resignQuestion] - }, - "singleplayer": { - "caption": translate("Are you sure you want to quit?"), - "buttons": [resumeGame, endGame] - } - }; - - let messageType = g_IsNetworked && g_IsController ? "host" : - (g_IsNetworked && !g_IsObserver ? "client" : "singleplayer"); - - messageBox( - 400, 200, - messageTypes[messageType].caption, - translate("Confirmation"), - [translate("No"), translate("Yes")], - messageTypes[messageType].buttons - ); -} - -function resignQuestion() -{ - messageBox( - 400, 200, - translate("Do you want to resign or will you return soon?"), - translate("Confirmation"), - [translate("I will return"), translate("I resign")], - [endGame, resignGame]); -} - -function openDeleteDialog(selection) -{ - closeOpenDialogs(); - g_PauseControl.implicitPause(); - - let deleteSelectedEntities = function(selectionArg) - { - Engine.PostNetworkCommand({ - "type": "delete-entities", - "entities": selectionArg - }); - resumeGame(); - }; - - messageBox( - 400, 200, - translate("Destroy everything currently selected?"), - translate("Delete"), - [translate("No"), translate("Yes")], - [resumeGame, deleteSelectedEntities], - [null, selection] - ); -} - -function openSave() -{ - closeOpenDialogs(); - g_PauseControl.implicitPause(); - - Engine.PushGuiPage( - "page_loadgame.xml", - { "savedGameData": getSavedGameData() }, - resumeGame); -} - -function openOptions() -{ - closeOpenDialogs(); - g_PauseControl.implicitPause(); - - Engine.PushGuiPage( - "page_options.xml", - {}, - callbackFunctionNames => { - for (let functionName of callbackFunctionNames) - if (global[functionName]) - global[functionName](); - - resumeGame(); - }); -} - -function toggleTutorial() -{ - let tutorialPanel = Engine.GetGUIObjectByName("tutorialPanel"); - tutorialPanel.hidden = !tutorialPanel.hidden || - !Engine.GetGUIObjectByName("tutorialText").caption; -} - -/** - * Allows players to see their own summary. - * If they have shared ally vision researched, they are able to see the summary of there allies too. - */ -function openGameSummary() -{ - closeOpenDialogs(); - g_PauseControl.implicitPause(); - - let extendedSimState = Engine.GuiInterfaceCall("GetExtendedSimulationState"); - Engine.PushGuiPage( - "page_summary.xml", - { - "sim": { - "mapSettings": g_GameAttributes.settings, - "playerStates": extendedSimState.players.filter((state, player) => - g_IsObserver || player == 0 || player == g_ViewedPlayer || - extendedSimState.players[g_ViewedPlayer].hasSharedLos && g_Players[player].isMutualAlly[g_ViewedPlayer]), - "timeElapsed": extendedSimState.timeElapsed - }, - "gui": { - "dialog": true, - "isInGame": true - }, - "selectedData": g_SummarySelectedData - }, - data => - { - g_SummarySelectedData = data.summarySelectedData; - g_PauseControl.implicitResume(); - }); -} - -function openStrucTree(page) -{ - closeOpenDialogs(); - g_PauseControl.implicitPause(); - - Engine.PushGuiPage( - page, - { - "civ": g_CivInfo.civ || g_Players[g_ViewedPlayer].civ - // TODO add info about researched techs and unlocked entities - }, - storeCivInfoPage); -} - -function storeCivInfoPage(data) -{ - if (data.nextPage) - Engine.PushGuiPage( - data.nextPage, - { "civ": data.civ }, - storeCivInfoPage); - else - { - g_CivInfo = data; - resumeGame(); - } -} - -function openManual() -{ - closeOpenDialogs(); - g_PauseControl.implicitPause(); - Engine.PushGuiPage("page_manual.xml", {}, resumeGame); -} - -function closeOpenDialogs() -{ - closeMenu(); - g_Chat.closePage(); - g_DiplomacyDialog.close(); - g_ObjectivesDialog.close(); - g_TradeDialog.close(); -} Index: ps/trunk/binaries/data/mods/public/gui/session/menu.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/menu.xml +++ ps/trunk/binaries/data/mods/public/gui/session/menu.xml @@ -1,112 +0,0 @@ - - - - - - - Manual - openManual(); - - - - - Chat - chatMenuButton(); - - - - - Save - - openSave(); - - - - - - Summary - openGameSummary(); - - - - - Lobby - Show the multiplayer lobby in a dialog window. - lobbyDialogButton(); - - - - - Options - optionsMenuButton(); - - - - - - - - Resign - resignMenuButton(); - - - - - Exit - exitMenuButton(); - - - - - Index: ps/trunk/binaries/data/mods/public/gui/session/messages.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/messages.js +++ ps/trunk/binaries/data/mods/public/gui/session/messages.js @@ -359,6 +359,12 @@ } } +function toggleTutorial() +{ + let tutorialPanel = Engine.GetGUIObjectByName("tutorialPanel"); + tutorialPanel.hidden = !tutorialPanel.hidden || !Engine.GetGUIObjectByName("tutorialText").caption; +} + /** * Updates the tutorial panel when a new goal. */ Index: ps/trunk/binaries/data/mods/public/gui/session/session.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/session.js +++ ps/trunk/binaries/data/mods/public/gui/session/session.js @@ -14,6 +14,7 @@ var g_DiplomacyColors; var g_DiplomacyDialog; var g_GameSpeedControl; +var g_Menu; var g_MiniMapPanel; var g_ObjectivesDialog; var g_PauseControl; @@ -287,6 +288,7 @@ g_ObjectivesDialog = new ObjectivesDialog(g_PlayerViewControl); g_PauseControl = new PauseControl(); g_PauseOverlay = new PauseOverlay(g_PauseControl); + g_Menu = new Menu(g_PauseControl, g_PlayerViewControl, g_Chat); g_TradeDialog = new TradeDialog(g_PlayerViewControl); g_TopPanel = new TopPanel(g_PlayerViewControl, g_DiplomacyDialog, g_TradeDialog, g_ObjectivesDialog, g_GameSpeedControl); @@ -294,7 +296,6 @@ LoadModificationTemplates(); updatePlayerData(); initializeMusic(); // before changing the perspective - initMenu(g_PlayerViewControl, g_PauseControl); initPanelEntities(); Engine.SetBoundingBoxDebugOverlay(false); updateEnabledRangeOverlayTypes(); @@ -504,13 +505,7 @@ // TODO: The other calls in this function should move too for (let handler of g_PlayerFinishedHandlers) - handler(); - - if (players.indexOf(g_ViewedPlayer) == -1) - return; - - // Select "observer" item on loss. On win enable observermode without changing perspective - g_PlayerViewControl.selectViewPlayer(won ? g_ViewedPlayer + 1 : 0); + handler(players, won); if (players.indexOf(Engine.GetPlayerID()) == -1 || Engine.IsAtlasRunning()) return; @@ -529,12 +524,13 @@ g_PauseControl.implicitResume(); } -function resignGame() +function closeOpenDialogs() { - Engine.PostNetworkCommand({ - "type": "resign" - }); - g_PauseControl.implicitResume(); + g_Menu.close(); + g_Chat.closePage(); + g_DiplomacyDialog.close(); + g_ObjectivesDialog.close(); + g_TradeDialog.close(); } function endGame() @@ -638,7 +634,6 @@ recalculateStatusBarDisplay(); updateTimers(); - updateMenuPosition(tickLength); Engine.GuiInterfaceCall("ClearRenamedEntities"); } Index: ps/trunk/binaries/data/mods/public/gui/session/session.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/session.xml +++ ps/trunk/binaries/data/mods/public/gui/session/session.xml @@ -96,7 +96,7 @@ - + Index: ps/trunk/binaries/data/mods/public/gui/session/top_panel/CivIcon.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/top_panel/CivIcon.js +++ ps/trunk/binaries/data/mods/public/gui/session/top_panel/CivIcon.js @@ -6,16 +6,53 @@ { constructor(playerViewControl) { + this.dialogSelection = { + "civ": "", + "page": "page_structree.xml" + }; + this.civIcon = Engine.GetGUIObjectByName("civIcon"); this.civIconOverlay = Engine.GetGUIObjectByName("civIconOverlay"); this.civIconOverlay.onPress = this.onPress.bind(this); + playerViewControl.registerViewedPlayerChangeHandler(this.rebuild.bind(this)); registerHotkeyChangeHandler(this.rebuild.bind(this)); + + Engine.SetGlobalHotkey("civinfo", () => this.openPage("page_civinfo.xml")); + Engine.SetGlobalHotkey("structree", () => this.openPage("page_structree.xml")); } onPress() { - openStrucTree(g_CivInfo.page); + this.openPage(this.dialogSelection.page); + } + + openPage(page) + { + closeOpenDialogs(); + g_PauseControl.implicitPause(); + + Engine.PushGuiPage( + page, + { + "civ": this.dialogSelection.civ || g_Players[g_ViewedPlayer].civ + // TODO add info about researched techs and unlocked entities + }, + this.storePageSelection.bind(this)); + } + + storePageSelection(data) + { + if (data.nextPage) + Engine.PushGuiPage( + data.nextPage, + { "civ": data.civ }, + this.storePageSelection.bind(this)); + else + { + this.dialogSelection = data; + resumeGame(); + } } rebuild() @@ -28,7 +65,7 @@ let civData = g_CivData[g_Players[g_ViewedPlayer].civ]; this.civIcon.sprite = "stretched:" + civData.Emblem; - this.civIconOverlay.tooltip = sprintf(translate(this.OverlayTooltip), { + this.civIconOverlay.tooltip = sprintf(translate(this.Tooltip), { "civ": setStringTags(civData.Name, this.CivTags), "hotkey_civinfo": colorizeHotkey("%(hotkey)s", "civinfo"), "hotkey_structree": colorizeHotkey("%(hotkey)s", "structree") @@ -36,7 +73,7 @@ } } -CivIcon.prototype.OverlayTooltip = +CivIcon.prototype.Tooltip = markForTranslation("%(civ)s\n%(hotkey_civinfo)s / %(hotkey_structree)s: View History / Structure Tree\nLast opened will be reopened on click."); CivIcon.prototype.CivTags = { "font": "sans-bold-stroke-14" }; Index: ps/trunk/binaries/data/mods/public/gui/session/top_panel/MenuButton.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/top_panel/MenuButton.xml +++ ps/trunk/binaries/data/mods/public/gui/session/top_panel/MenuButton.xml @@ -1,6 +1,7 @@ Menu - - toggleMenu(); - Index: ps/trunk/binaries/data/mods/public/gui/session/top_panel/PlayerViewControl.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/top_panel/PlayerViewControl.js +++ ps/trunk/binaries/data/mods/public/gui/session/top_panel/PlayerViewControl.js @@ -20,6 +20,7 @@ // Events this.viewPlayer.onSelectionChange = this.onSelectionChange.bind(this); registerPlayersInitHandler(this.onPlayersInit.bind(this)); + registerPlayersFinishedHandler(this.onPlayersFinished.bind(this)); this.registerViewedPlayerChangeHandler(this.rebuild.bind(this)); } @@ -48,13 +49,24 @@ this.observerText.hidden = g_ViewedPlayer > 0; } + /** + * Select "observer" in the view player dropdown when rejoining as a defeated player. + */ onPlayersInit() { this.rebuild(); - // Select "observer" in the view player dropdown when rejoining as a defeated player let playerState = g_Players[Engine.GetPlayerID()]; - this.viewPlayer.selected = playerState && playerState.state == "defeated" ? 0 : Engine.GetPlayerID() + 1; + this.selectViewPlayer(playerState && playerState.state == "defeated" ? 0 : Engine.GetPlayerID() + 1); + } + + /** + * Select "observer" item on loss. On win enable observermode without changing perspective. + */ + onPlayersFinished(playerIDs, won) + { + if (playerIDs.indexOf(g_ViewedPlayer) != -1) + this.selectViewPlayer(won ? g_ViewedPlayer + 1 : 0); } setChangePerspective(enabled) Index: ps/trunk/binaries/data/mods/public/gui/session/unit_actions.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/unit_actions.js +++ ps/trunk/binaries/data/mods/public/gui/session/unit_actions.js @@ -1063,16 +1063,41 @@ }, "execute": function(entStates) { - if (!entStates.length || entStates.every(entState => isUndeletable(entState))) + let entityIDs = entStates.reduce( + (ids, entState) => { + if (!isUndeletable(entState)) + ids.push(entState.id); + return ids; + }, + []); + + if (!entityIDs.length) return; + let deleteSelection = () => Engine.PostNetworkCommand({ + "type": "delete-entities", + "entities": entityIDs + }); + if (Engine.HotkeyIsPressed("session.noconfirmation")) - Engine.PostNetworkCommand({ - "type": "delete-entities", - "entities": entStates.map(entState => entState.id) - }); + deleteSelection(); else - openDeleteDialog(entStates.map(entState => entState.id)); + { + closeOpenDialogs(); + g_PauseControl.implicitPause(); + messageBox( + 400, 200, + translate("Destroy everything currently selected?"), + translate("Delete"), + [translate("No"), translate("Yes")], + [ + resumeGame, + () => { + deleteSelection(); + resumeGame(); + } + ]); + }; }, },