Index: ps/trunk/binaries/data/mods/public/gui/autostart/autostart.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/autostart/autostart.js +++ ps/trunk/binaries/data/mods/public/gui/autostart/autostart.js @@ -11,7 +11,7 @@ settings.launchGame(assignments); Engine.SwitchGuiPage("page_loading.xml", { - "attribs": settings.toInitAttributes(), + "attribs": settings.finalizedAttributes, "playerAssignments": assignments }); } 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 +++ ps/trunk/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js @@ -40,10 +40,14 @@ if (!meetsRequirements(this.run, level)) return; + // TODO: level description should also be passed, ideally. const settings = { "mapType": level.MapType, "map": "maps/" + level.Map, "settings": { + // TODO: don't translate this here. + "mapName": this.getLevelName(level), + "mapPreview": level.Preview && "cropped:" + 400/512 + "," + 300/512 + ":" + level.Preview, "CheatsEnabled": true }, "campaignData": { @@ -62,11 +66,6 @@ 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. @@ -98,7 +97,7 @@ gameSettings.launchGame(assignments); Engine.SwitchGuiPage("page_loading.xml", { - "attribs": gameSettings.toInitAttributes(), + "attribs": gameSettings.finalizedAttributes, "playerAssignments": assignments }); } Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/GameSettings.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/GameSettings.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/GameSettings.js @@ -37,7 +37,8 @@ this[name] = new GameSettings.prototype.Attributes[comp](this); } for (let comp in this) - this[comp].init(); + if (this[comp].init) + this[comp].init(); return this; } @@ -114,18 +115,22 @@ { this.pickRandomItems(); - Engine.SetRankedGame(this.rating.enabled); + // Let the settings finalize themselves. Let them do anything they need to do before the + // game starts and set any value in the attributes which mustn't be persisted. + const attribs = this.toInitAttributes(); + for (const comp in this) + if (this[comp].onFinalizeAttributes) + this[comp].onFinalizeAttributes(attribs, playerAssignments); - // Replace player names with the real players. - for (let guid in playerAssignments) - if (playerAssignments[guid].player !== -1) - this.playerName.values[playerAssignments[guid].player -1] = playerAssignments[guid].name; + Object.defineProperty(this, "finalizedAttributes", { + "value": deepfreeze(attribs) + }); // NB: for multiplayer support, the clients must be listening to "start" net messages. if (this.isNetworked) - Engine.StartNetworkGame(this.toInitAttributes()); + Engine.StartNetworkGame(this.finalizedAttributes); else - Engine.StartGame(this.toInitAttributes(), playerAssignments.local.player); + Engine.StartGame(this.finalizedAttributes, playerAssignments.local.player); } } Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/CampaignData.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/CampaignData.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/CampaignData.js @@ -4,10 +4,6 @@ */ GameSettings.prototype.Attributes.CampaignData = class CampaignData extends GameSetting { - init() - { - } - toInitAttributes(attribs) { if (this.value) Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Ceasefire.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Ceasefire.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Ceasefire.js @@ -8,8 +8,6 @@ toInitAttributes(attribs) { - if (!this.value) - return; attribs.settings.Ceasefire = this.value; } Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/CircularMap.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/CircularMap.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/CircularMap.js @@ -5,19 +5,19 @@ { init() { - this.value = undefined; + this.value = false; this.settings.map.watch(() => this.onMapChange(), ["map"]); } toInitAttributes(attribs) { - if (this.value) - attribs.settings.CircularMap = this.value; + attribs.settings.CircularMap = this.value; } - /** - * Exceptionally, this setting has no Deserialize: it's entirely determined by the map - */ + fromInitAttributes(attribs) + { + this.value = !!this.getLegacySetting(attribs, "CircularMap"); + } onMapChange() { Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/GameSpeed.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/GameSpeed.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/GameSpeed.js @@ -13,9 +13,8 @@ fromInitAttributes(attribs) { - if (!attribs.gameSpeed) - return; - this.gameSpeed = +attribs.gameSpeed; + if (attribs.gameSpeed) + this.gameSpeed = +attribs.gameSpeed; } onMapChange() Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapName.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapName.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapName.js @@ -6,31 +6,23 @@ { init() { + this.value = undefined; + this.settings.map.watch(() => this.updateName(), ["map"]); } toInitAttributes(attribs) { - if (this.value) - attribs.settings.Name = this.value; - else - { - // Copy from the map data by default - this helps make InitAttributes self-sufficient, - // which is nice for replays / saved games. - // Fallback to the map name to avoid 'undefined' errors. - attribs.settings.Name = this.settings.map?.data?.settings?.Name || this.settings.map.map; - } + attribs.settings.mapName = this.value; } fromInitAttributes(attribs) { - // Ser/Deser from a different attribute name as a poor man's not-persisted-setting. - // TODO: split this off more properly. - if (attribs.mapName) - this.value = attribs.mapName; + if (attribs?.settings?.mapName) + this.value = attribs.settings.mapName; } - set(name) + updateName() { - this.value = name; + this.value = this.settings.map?.data?.settings?.Name || this.settings.map.map; } }; Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapPreview.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapPreview.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapPreview.js @@ -6,7 +6,7 @@ { init() { - this.isDefault = true; + this.value = undefined; this.settings.map.watch(() => this.updatePreview(), ["map"]); this.settings.biome.watch(() => this.updatePreview(), ["biome"]); this.settings.landscape.watch(() => this.updatePreview(), ["value"]); @@ -15,14 +15,14 @@ toInitAttributes(attribs) { - // TODO: this shouldn't be persisted, only serialised for the game proper. - if (this.value) - attribs.mapPreview = this.value; + if (this.value !== undefined) + attribs.settings.mapPreview = this.value; } fromInitAttributes(attribs) { - // For now - this won't be deserialized or persisted match settings will be problematic. + if (!!this.getLegacySetting(attribs, "mapPreview")) + this.value = this.getLegacySetting(attribs, "mapPreview"); } getPreviewForSubtype(basepath, subtype) @@ -48,10 +48,6 @@ updatePreview() { - // Don't overwrite the preview if it's been manually set. - if (!this.isDefault) - return; - if (!this.settings.map.map) { this.value = undefined; @@ -65,10 +61,4 @@ this.getPreviewForSubtype(mapPath, this.settings.daytime.value) || this.settings.mapCache.getMapPreview(this.settings.map.type, this.settings.map.map); } - - setCustom(preview) - { - this.isDefault = false; - this.value = preview; - } }; Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MatchID.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MatchID.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MatchID.js @@ -1,23 +1,7 @@ GameSettings.prototype.Attributes.MatchID = class MatchID extends GameSetting { - init() + onFinalizeAttributes(attribs) { - this.matchID = 0; - } - - toInitAttributes(attribs) - { - attribs.matchID = this.matchID; - } - - fromInitAttributes(attribs) - { - if (attribs.matchID !== undefined) - this.matchID = attribs.matchID; - } - - pickRandomItems() - { - this.matchID = Engine.GetMatchID(); + attribs.matchID = Engine.GetMatchID(); } }; Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerName.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerName.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerName.js @@ -2,9 +2,8 @@ * Stores in-game names for all players. * * NB: the regular gamesetup has a particular handling of this setting. - * The names are loaded from the map, but the GUI also show playernames - * and forces them when starting the game. - * This is therefore just handling map-defined names & AI random bot names. + * The names are loaded from the map, but the GUI also show playernames. + * Force these at the start of the match. */ GameSettings.prototype.Attributes.PlayerName = class PlayerName extends GameSetting { @@ -27,6 +26,21 @@ attribs.settings.PlayerData[i].Name = this.values[i]; } + fromInitAttributes(attribs) + { + if (!this.getLegacySetting(attribs, "PlayerData")) + return; + const pData = this.getLegacySetting(attribs, "PlayerData"); + if (this.values.length < pData.length) + this._resize(pData.length); + for (const i in pData) + if (pData[i] && pData[i].Name !== undefined) + { + this.values[i] = pData[i].Name; + this.trigger("values"); + } + } + _resize(nb) { while (this.values.length > nb) @@ -92,6 +106,14 @@ return picked; } + onFinalizeAttributes(attribs, playerAssignments) + { + // Replace client player names with the real players. + for (const guid in playerAssignments) + if (playerAssignments[guid].player !== -1) + attribs.settings.PlayerData[playerAssignments[guid].player -1].Name = playerAssignments[guid].name; + } + _getMapData(i) { let data = this.settings.map.data; Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Rating.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Rating.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Rating.js @@ -36,4 +36,9 @@ !this.settings.cheats.enabled; this.enabled = this.available; } + + onFinalizeAttributes() + { + Engine.SetRankedGame(this.enabled); + } }; Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/RegicideGarrison.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/RegicideGarrison.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/RegicideGarrison.js @@ -9,7 +9,8 @@ toInitAttributes(attribs) { - attribs.settings.RegicideGarrison = this.enabled; + if (this.available) + attribs.settings.RegicideGarrison = this.enabled; } fromInitAttributes(attribs) Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/SeaLevelRise.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/SeaLevelRise.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/SeaLevelRise.js @@ -10,7 +10,7 @@ toInitAttributes(attribs) { - if (this.value) + if (this.value !== undefined) attribs.settings.SeaLevelRiseTime = this.value; } Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Seeds.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Seeds.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Seeds.js @@ -2,20 +2,18 @@ { init() { - this.seed = 0; - this.AIseed = 0; + this.seed = "random"; + this.AIseed = "random"; } toInitAttributes(attribs) { - // Seed is used for map generation and simulation. - attribs.settings.Seed = this.seed; - attribs.settings.AISeed = this.AIseed; + attribs.settings.Seed = this.seed == "random" ? this.seed : +this.seed; + attribs.settings.AISeed = this.AIseed == "random" ? this.AIseed : +this.AIseed; } fromInitAttributes(attribs) { - // Seed is used for map generation and simulation. if (this.getLegacySetting(attribs, "Seed") !== undefined) this.seed = this.getLegacySetting(attribs, "Seed"); if (this.getLegacySetting(attribs, "AISeed") !== undefined) @@ -24,7 +22,18 @@ pickRandomItems() { - this.seed = randIntExclusive(0, Math.pow(2, 32)); - this.AIseed = randIntExclusive(0, Math.pow(2, 32)); + let picked = false; + if (this.seed === "random") + { + this.seed = randIntExclusive(0, Math.pow(2, 32)); + picked = true; + } + + if (this.AIseed === "random") + { + this.AIseed = randIntExclusive(0, Math.pow(2, 32)); + picked = true; + } + return picked; } }; Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingCamera.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingCamera.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingCamera.js @@ -17,14 +17,25 @@ attribs.settings.PlayerData = []; while (attribs.settings.PlayerData.length < this.values.length) attribs.settings.PlayerData.push({}); - for (let i in this.values) + for (const i in this.values) if (this.values[i]) attribs.settings.PlayerData[i].StartingCamera = this.values[i]; } - /** - * Exceptionally, this setting has no Deserialize: it's entirely determined by the map - */ + fromInitAttributes(attribs) + { + if (!this.getLegacySetting(attribs, "PlayerData")) + return; + const pData = this.getLegacySetting(attribs, "PlayerData"); + if (this.values.length < pData.length) + this._resize(pData.length); + for (const i in pData) + if (pData[i] && pData[i].StartingCamera !== undefined) + { + this.values[i] = pData[i].StartingCamera; + this.trigger("values"); + } + } _resize(nb) { Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TeamPlacement.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TeamPlacement.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TeamPlacement.js @@ -9,7 +9,7 @@ toInitAttributes(attribs) { - if (this.value) + if (this.value !== undefined) attribs.settings.TeamPlacement = this.value; } Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TriggerScripts.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TriggerScripts.js +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TriggerScripts.js @@ -2,23 +2,23 @@ { init() { - this.victory = new Set(); - this.map = new Set(); + this.customScripts = new Set(); + this.victoryScripts = new Set(); + this.mapScripts = new Set(); this.settings.map.watch(() => this.updateMapScripts(), ["map"]); this.settings.victoryConditions.watch(() => this.updateVictoryScripts(), ["active"]); } toInitAttributes(attribs) { - let scripts = new Set(this.victory); - for (let elem of this.map) - scripts.add(elem); - attribs.settings.TriggerScripts = Array.from(scripts); + attribs.settings.TriggerScripts = Array.from(this.customScripts); } - /** - * Exceptionally, this setting has no Deserialize: it's entirely determined from other settings. - */ + fromInitAttributes(attribs) + { + if (!!this.getLegacySetting(attribs, "TriggerScripts")) + this.customScripts = new Set(this.getLegacySetting(attribs, "TriggerScripts")); + } updateVictoryScripts() { @@ -26,7 +26,7 @@ let scripts = new Set(); for (let cond of setting.active) setting.conditions[cond].Scripts.forEach(script => scripts.add(script)); - this.victory = scripts; + this.victoryScripts = scripts; } updateMapScripts() @@ -34,9 +34,19 @@ if (!this.settings.map.data || !this.settings.map.data.settings || !this.settings.map.data.settings.TriggerScripts) { - this.map = new Set(); + this.mapScripts = new Set(); return; } - this.map = new Set(this.settings.map.data.settings.TriggerScripts); + this.mapScripts = new Set(this.settings.map.data.settings.TriggerScripts); + } + + onFinalizeAttributes(attribs) + { + const scripts = this.customScripts; + for (const elem of this.victoryScripts) + scripts.add(elem); + for (const elem of this.mapScripts) + scripts.add(elem); + attribs.settings.TriggerScripts = Array.from(scripts); } }; Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controllers/GameSettingsController.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesetup/Controllers/GameSettingsController.js +++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Controllers/GameSettingsController.js @@ -280,7 +280,7 @@ switchToLoadingPage(attributes) { Engine.SwitchGuiPage("page_loading.xml", { - "attribs": attributes?.initAttributes || g_GameSettings.toInitAttributes(), + "attribs": attributes?.initAttributes || g_GameSettings.finalizedAttributes, "playerAssignments": g_PlayerAssignments }); } Index: ps/trunk/binaries/data/mods/public/gui/loadgame/SavegameList.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/loadgame/SavegameList.js +++ ps/trunk/binaries/data/mods/public/gui/loadgame/SavegameList.js @@ -105,8 +105,8 @@ cmpB = +b.time; break; case 'mapName': - cmpA = translate(a.initAttributes.settings.Name); - cmpB = translate(b.initAttributes.settings.Name); + cmpA = translate(a.initAttributes.settings.mapName); + cmpB = translate(b.initAttributes.settings.mapName); break; case 'mapType': cmpA = translateMapType(a.initAttributes.mapType); @@ -131,7 +131,7 @@ this.campaignFilter(metadata, this.campaignRun); return { "date": this.generateSavegameDateString(metadata, engineInfo), - "mapName": compatibilityColor(translate(metadata.initAttributes.settings.Name), isCompatible), + "mapName": compatibilityColor(translate(metadata.initAttributes.settings.mapName), isCompatible), "mapType": compatibilityColor(translateMapType(metadata.initAttributes.mapType), isCompatible), "description": compatibilityColor(metadata.description, isCompatible) }; Index: ps/trunk/binaries/data/mods/public/gui/loading/TitleDisplay.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/loading/TitleDisplay.js +++ ps/trunk/binaries/data/mods/public/gui/loading/TitleDisplay.js @@ -8,7 +8,7 @@ let loadingMapName = Engine.GetGUIObjectByName("loadingMapName"); loadingMapName.caption = sprintf( data.attribs.mapType == "random" ? this.Generating : this.Loading, - { "map": translate(data.attribs.settings.Name) }); + { "map": translate(data.attribs.settings.mapName) }); } } Index: ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_filters.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_filters.js +++ ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_filters.js @@ -280,7 +280,7 @@ // Filter by map name let mapNameFilter = Engine.GetGUIObjectByName("mapNameFilter"); - if (mapNameFilter.selected > 0 && replay.attribs.settings.Name != mapNameFilter.list_data[mapNameFilter.selected]) + if (mapNameFilter.selected > 0 && replay.attribs.settings.mapName != mapNameFilter.list_data[mapNameFilter.selected]) return false; // Filter by map size Index: ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js +++ ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js @@ -106,8 +106,8 @@ sanitizeInitAttributes(replay.attribs); // Extract map names - if (g_MapNames.indexOf(replay.attribs.settings.Name) == -1 && replay.attribs.settings.Name != "") - g_MapNames.push(replay.attribs.settings.Name); + if (g_MapNames.indexOf(replay.attribs.settings.mapName) == -1 && replay.attribs.settings.mapName != "") + g_MapNames.push(replay.attribs.settings.mapName); // Extract playernames for (let playerData of replay.attribs.settings.PlayerData) @@ -168,8 +168,8 @@ if (!attribs.settings.Size) attribs.settings.Size = -1; - if (!attribs.settings.Name) - attribs.settings.Name = ""; + if (!attribs.settings.mapName) + attribs.settings.mapName = ""; if (!attribs.settings.PlayerData) attribs.settings.PlayerData = []; @@ -277,7 +277,7 @@ let replay = g_ReplaysFiltered[selected]; - Engine.GetGUIObjectByName("sgMapName").caption = translate(replay.attribs.settings.Name); + Engine.GetGUIObjectByName("sgMapName").caption = translate(replay.attribs.settings.mapName); Engine.GetGUIObjectByName("sgMapSize").caption = translateMapSize(replay.attribs.settings.Size); Engine.GetGUIObjectByName("sgMapType").caption = translateMapType(replay.attribs.mapType); Engine.GetGUIObjectByName("sgVictory").caption = replay.attribs.settings.VictoryConditions.map(victoryConditionName => @@ -326,7 +326,7 @@ */ function getReplayMapName(replay) { - return translate(replay.attribs.settings.Name); + return translate(replay.attribs.settings.mapName); } /** Index: ps/trunk/binaries/data/mods/public/gui/session/lobby/LobbyRatingReporter.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/lobby/LobbyRatingReporter.js +++ ps/trunk/binaries/data/mods/public/gui/session/lobby/LobbyRatingReporter.js @@ -30,7 +30,7 @@ let report = { "playerID": Engine.GetPlayerID(), "matchID": g_InitAttributes.matchID, - "mapName": g_InitAttributes.settings.Name, + "mapName": g_InitAttributes.settings.mapName, "timeElapsed": extendedSimState.timeElapsed, }; Index: ps/trunk/binaries/data/mods/public/gui/summary/summary.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/summary/summary.js +++ ps/trunk/binaries/data/mods/public/gui/summary/summary.js @@ -530,7 +530,7 @@ Engine.GetGUIObjectByName("mapName").caption = sprintf( translate("%(mapName)s - %(mapType)s"), { - "mapName": translate(g_GameData.sim.mapSettings.Name), + "mapName": translate(g_GameData.sim.mapSettings.mapName), "mapType": mapSize ? mapSize.Name : (mapType ? mapType.Title : "") }); }