Index: ps/trunk/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js (revision 25682) +++ ps/trunk/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js (revision 25683) @@ -1,214 +1,220 @@ /** * This is the main menu screen of the campaign. * It shows you the currently available scenarios, scenarios you've already completed, etc. * This particular variant is extremely simple and shows a list similar to Age 1's campaigns, * but conceptually nothing really prevents more complex systems. */ class CampaignMenu extends AutoWatcher { constructor(campaignRun) { super("render"); this.run = campaignRun; this.selectedLevel = -1; this.levelSelection = Engine.GetGUIObjectByName("levelSelection"); this.levelSelection.onSelectionChange = () => { this.selectedLevel = this.levelSelection.selected; }; this.levelSelection.onMouseLeftDoubleClickItem = () => this.startScenario(); Engine.GetGUIObjectByName('startButton').onPress = () => this.startScenario(); Engine.GetGUIObjectByName('backToMain').onPress = () => this.goBackToMainMenu(); Engine.GetGUIObjectByName('savedGamesButton').onPress = () => Engine.PushGuiPage('page_loadgame.xml', { 'campaignRun': this.run.filename }); this.mapCache = new MapCache(); this._ready = true; } goBackToMainMenu() { this.run.save(); Engine.SwitchGuiPage("page_pregame.xml", {}); } startScenario() { const level = this.getSelectedLevelData(); if (!meetsRequirements(this.run, level)) return; const settings = { "mapType": level.MapType, "map": "maps/" + level.Map, "settings": { "CheatsEnabled": true }, "campaignData": { "run": this.run.filename, "levelID": this.levelSelection.list_data[this.selectedLevel], "data": this.run.data } }; const assignments = { "local": { "player": 1, "name": Engine.ConfigDB_GetValue("user", "playername.singleplayer") || Engine.GetSystemUsername() } }; const gameSettings = new GameSettings().init(); gameSettings.fromInitAttributes(settings); if (level.Preview) gameSettings.mapPreview.setCustom("cropped:" + 400/512 + "," + 300/512 + ":" + level.Preview); gameSettings.mapName.set(this.getLevelName(level)); // TODO: level description should also be passed, ideally. if (level.useGameSetup) { // Setup some default AI on the non-human players. for (let i = 1; i < gameSettings.playerCount.nbPlayers; ++i) gameSettings.playerAI.set(i, { "bot": g_Settings.PlayerDefaults[i + 1].AI, "difficulty": +Engine.ConfigDB_GetValue("user", "gui.gamesetup.aidifficulty"), "behavior": Engine.ConfigDB_GetValue("user", "gui.gamesetup.aibehavior"), }); const attributes = gameSettings.toInitAttributes(); attributes.guiData = { "lockSettings": { "map": true, }, }; Engine.SwitchGuiPage("page_gamesetup.xml", { + "backPage": { + "page": this.run.getMenuPath(), + "data": { + "filename": this.run.filename + } + }, "gameSettings": attributes, }); return; } gameSettings.launchGame(assignments); Engine.SwitchGuiPage("page_loading.xml", { "attribs": gameSettings.toInitAttributes(), "playerAssignments": assignments }); } getSelectedLevelData() { if (this.selectedLevel === -1) return undefined; return this.run.template.Levels[this.levelSelection.list_data[this.selectedLevel]]; } shouldShowLevel(levelData) { if (this.run.template.ShowUnavailable) return true; return meetsRequirements(this.run, levelData); } getLevelName(levelData) { if (levelData.Name) return translateWithContext("Campaign Template", levelData.Name); return translate(this.mapCache.getTranslatableMapName(levelData.MapType, "maps/" + levelData.Map)); } getLevelDescription(levelData) { if (levelData.Description) return translateWithContext("Campaign Template", levelData.Description); return this.mapCache.getTranslatedMapDescription(levelData.MapType, "maps/" + levelData.Map); } getLevelPreview(levelData) { if (levelData.Preview) return "cropped:" + 400/512 + "," + 300/512 + ":" + levelData.Preview; return this.mapCache.getMapPreview(levelData.MapType, "maps/" + levelData.Map); } displayLevelsList() { let list = []; for (let key in this.run.template.Levels) { let level = this.run.template.Levels[key]; if (!this.shouldShowLevel(level)) continue; let status = ""; let name = this.getLevelName(level); if (isCompleted(this.run, key)) status = translateWithContext("campaign status", "Completed"); else if (meetsRequirements(this.run, level)) status = coloredText(translateWithContext("campaign status", "Available"), "green"); else name = coloredText(name, "gray"); list.push({ "ID": key, "name": name, "status": status }); } list.sort((a, b) => this.run.template.Order.indexOf(a.ID) - this.run.template.Order.indexOf(b.ID)); list = prepareForDropdown(list); this.levelSelection.list_name = list.name || []; this.levelSelection.list_status = list.status || []; // COList needs these changed last or crashes. this.levelSelection.list = list.ID || []; this.levelSelection.list_data = list.ID || []; } displayLevelDetails() { if (this.selectedLevel === -1) { Engine.GetGUIObjectByName("startButton").enabled = false; Engine.GetGUIObjectByName("startButton").hidden = false; return; } let level = this.getSelectedLevelData(); Engine.GetGUIObjectByName("scenarioName").caption = this.getLevelName(level); Engine.GetGUIObjectByName("scenarioDesc").caption = this.getLevelDescription(level); Engine.GetGUIObjectByName('levelPreviewBox').sprite = this.getLevelPreview(level); Engine.GetGUIObjectByName("startButton").enabled = meetsRequirements(this.run, level); Engine.GetGUIObjectByName("startButton").hidden = false; Engine.GetGUIObjectByName("loadSavedButton").hidden = true; } render() { Engine.GetGUIObjectByName("campaignTitle").caption = this.run.getLabel(); this.displayLevelDetails(); this.displayLevelsList(); } } var g_CampaignMenu; function init(initData) { let run = initData?.filename || CampaignRun.getCurrentRunFilename(); try { run = new CampaignRun(run).load(); if (!run.isCurrent()) run.setCurrent(); g_CampaignMenu = new CampaignMenu(run); } catch (err) { error(sprintf(translate("Error loading campaign run %s: %s."), CampaignRun.getCurrentRunFilename(), err)); Engine.SwitchGuiPage("page_pregame.xml", {}); } } Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/SetupWindow.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesetup/SetupWindow.js (revision 25682) +++ ps/trunk/binaries/data/mods/public/gui/gamesetup/SetupWindow.js (revision 25683) @@ -1,120 +1,124 @@ /** * This class stores the GameSetupPage and every subpage that is shown in the game setup. */ class SetupWindowPages { } /** * The SetupWindow is the root class owning all other class instances. * The class shall be ineligible to perform any GUI object logic and shall defer that task to owned classes. */ class SetupWindow { constructor(initData, hotloadData) { if (!g_Settings) return; Engine.ProfileStart("SetupWindow"); this.loadHandlers = new Set(); this.closePageHandlers = new Set(); this.getHotloadDataHandlers = new Set(); + if (initData?.backPage) + this.backPage = initData.backPage; - let mapCache = new MapCache(); + const mapCache = new MapCache(); g_GameSettings = new GameSettings().init(mapCache); let netMessages = new NetMessages(); let mapFilters = new MapFilters(mapCache); let playerAssignmentsController = new PlayerAssignmentsController(this, netMessages); let gameSettingsController = new GameSettingsController(this, netMessages, playerAssignmentsController, mapCache); let readyController = new ReadyController(netMessages, gameSettingsController, playerAssignmentsController); let lobbyGameRegistrationController = Engine.HasXmppClient() && new LobbyGameRegistrationController(initData, this, netMessages, mapCache, playerAssignmentsController); // These class instances control central data and do not manage any GUI Object. this.controls = { "gameSettingsController": gameSettingsController, "playerAssignmentsController": playerAssignmentsController, "mapCache": mapCache, "mapFilters": mapFilters, "readyController": readyController, "netMessages": netMessages, "lobbyGameRegistrationController": lobbyGameRegistrationController }; // These are the pages within the setup window that may use the controls defined above this.pages = {}; for (let name in SetupWindowPages) this.pages[name] = new SetupWindowPages[name](this); netMessages.registerNetMessageHandler("netwarn", addNetworkWarning); setTimeout(displayGamestateNotifications, 1000); Engine.GetGUIObjectByName("setupWindow").onTick = () => this.onTick(); // This event is triggered after all classes have been instantiated and subscribed to each others events for (let handler of this.loadHandlers) handler(initData, hotloadData); Engine.ProfileStop(); } registerLoadHandler(handler) { this.loadHandlers.add(handler); } unregisterLoadHandler(handler) { this.loadHandlers.delete(handler); } registerClosePageHandler(handler) { this.closePageHandlers.add(handler); } unregisterClosePageHandler(handler) { this.closePageHandlers.delete(handler); } registerGetHotloadDataHandler(handler) { this.getHotloadDataHandlers.add(handler); } unregisterGetHotloadDataHandler(handler) { this.getHotloadDataHandlers.delete(handler); } getHotloadData() { let object = {}; for (let handler of this.getHotloadDataHandlers) handler(object); return object; } onTick() { this.controls.netMessages.pollPendingMessages(); updateTimers(); } closePage() { for (let handler of this.closePageHandlers) handler(); Engine.DisconnectNetworkGame(); - if (Engine.HasXmppClient()) + if (this.backPage) + Engine.SwitchGuiPage(this.backPage.page, this.backPage?.data); + else if (Engine.HasXmppClient()) Engine.SwitchGuiPage("page_lobby.xml", { "dialog": false }); else Engine.SwitchGuiPage("page_pregame.xml"); } }