Index: ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js (revision 25083) +++ ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js (revision 25084) @@ -1,446 +1,446 @@ /** * Highlights the victory condition in the game-description. */ var g_DescriptionHighlight = "orange"; /** * The rating assigned to lobby players who didn't complete a ranked 1v1 yet. */ var g_DefaultLobbyRating = 1200; /** * XEP-0172 doesn't restrict nicknames, but our lobby policy does. * So use this human readable delimiter to separate buddy names in the config file. */ var g_BuddyListDelimiter = ","; /** * Returns the nickname without the lobby rating. */ function splitRatingFromNick(playerName) { let result = /^(\S+)\ \((\d+)\)$/g.exec(playerName); return { "nick": result ? result[1] : playerName, "rating": result ? +result[2] : "" }; } /** * Array of playernames that the current user has marked as buddies. */ var g_Buddies = Engine.ConfigDB_GetValue("user", "lobby.buddies").split(g_BuddyListDelimiter); /** * Denotes which players are a lobby buddy of the current user. */ var g_BuddySymbol = '•'; /** * Returns a formatted string describing the player assignments. * Needs g_CivData to translate! * * @param {Object} playerDataArray - As known from game setup and sim state. * @param {(string[]|false)} playerStates - One of "won", "defeated", "active" for each player. * @returns {string} */ function formatPlayerInfo(playerDataArray, playerStates) { let playerDescriptions = {}; let playerIdx = 0; for (let playerData of playerDataArray) { if (playerData == null || playerData.Civ && playerData.Civ == "gaia") continue; ++playerIdx; let teamIdx = playerData.Team; let isAI = playerData.AI && playerData.AI != ""; let playerState = playerStates && playerStates[playerIdx] || playerData.State; let isActive = !playerState || playerState == "active"; let playerDescription; if (isAI) { if (playerData.Civ) { if (isActive) // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu playerDescription = translate("%(playerName)s (%(civ)s, %(AIdescription)s)"); else // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu playerDescription = translate("%(playerName)s (%(civ)s, %(AIdescription)s, %(state)s)"); } else { if (isActive) // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu playerDescription = translate("%(playerName)s (%(AIdescription)s)"); else // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu playerDescription = translate("%(playerName)s (%(AIdescription)s, %(state)s)"); } } else { if (playerData.Offline) { // Can only occur in the lobby for now, so no strings with civ needed if (isActive) // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu playerDescription = translate("%(playerName)s (OFFLINE)"); else // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu playerDescription = translate("%(playerName)s (OFFLINE, %(state)s)"); } else { if (playerData.Civ) if (isActive) // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu playerDescription = translate("%(playerName)s (%(civ)s)"); else // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu playerDescription = translate("%(playerName)s (%(civ)s, %(state)s)"); else if (isActive) // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu playerDescription = translate("%(playerName)s"); else // Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu playerDescription = translate("%(playerName)s (%(state)s)"); } } // Sort player descriptions by team if (!playerDescriptions[teamIdx]) playerDescriptions[teamIdx] = []; let playerNick = splitRatingFromNick(playerData.Name).nick; playerDescriptions[teamIdx].push(sprintf(playerDescription, { "playerName": coloredText( (g_Buddies.indexOf(playerNick) != -1 ? g_BuddySymbol + " " : "") + escapeText(playerData.Name), (typeof getPlayerColor == 'function' ? (isAI ? "white" : getPlayerColor(playerNick)) : rgbToGuiColor(playerData.Color || g_Settings.PlayerDefaults[playerIdx].Color))), "civ": !playerData.Civ ? translate("Unknown Civilization") : g_CivData && g_CivData[playerData.Civ] && g_CivData[playerData.Civ].Name ? translate(g_CivData[playerData.Civ].Name) : playerData.Civ, "state": playerState == "defeated" ? translateWithContext("playerstate", "defeated") : translateWithContext("playerstate", "won"), "AIdescription": translateAISettings(playerData) })); } let teams = Object.keys(playerDescriptions); if (teams.indexOf("observer") > -1) teams.splice(teams.indexOf("observer"), 1); let teamDescription = []; // If there are no teams, merge all playersDescriptions if (teams.length == 1) teamDescription.push(playerDescriptions[teams[0]].join("\n")); // If there are teams, merge "Team N:" + playerDescriptions else teamDescription = teams.map(team => { let teamCaption = team == -1 ? translate("No Team") : sprintf(translate("Team %(team)s"), { "team": +team + 1 }); // Translation: Describe players of one team in a selected game, f.e. in the replay- or savegame menu or lobby return sprintf(translate("%(team)s:\n%(playerDescriptions)s"), { "team": '[font="sans-bold-14"]' + teamCaption + "[/font]", "playerDescriptions": playerDescriptions[team].join("\n") }); }); if (playerDescriptions.observer) teamDescription.push(sprintf(translate("%(team)s:\n%(playerDescriptions)s"), { "team": '[font="sans-bold-14"]' + translatePlural("Observer", "Observers", playerDescriptions.observer.length) + "[/font]", "playerDescriptions": playerDescriptions.observer.join("\n") })); return teamDescription.join("\n\n"); } /** * Sets an additional map label, map preview image and describes the chosen game settings more closely. * * Requires g_VictoryConditions. */ function getGameDescription(initAttributes, mapCache) { let titles = []; if (!initAttributes.settings.VictoryConditions.length) titles.push({ "label": translateWithContext("victory condition", "Endless Game"), "value": translate("No winner will be determined, even if everyone is defeated.") }); for (let victoryCondition of g_VictoryConditions) { if (initAttributes.settings.VictoryConditions.indexOf(victoryCondition.Name) == -1) continue; let title = translateVictoryCondition(victoryCondition.Name); if (victoryCondition.Name == "wonder") { let wonderDuration = Math.round(initAttributes.settings.WonderDuration); title = sprintf( translatePluralWithContext( "victory condition", "Wonder (%(min)s minute)", "Wonder (%(min)s minutes)", wonderDuration ), { "min": wonderDuration }); } let isCaptureTheRelic = victoryCondition.Name == "capture_the_relic"; if (isCaptureTheRelic) { let relicDuration = Math.round(initAttributes.settings.RelicDuration); title = sprintf( translatePluralWithContext( "victory condition", "Capture the Relic (%(min)s minute)", "Capture the Relic (%(min)s minutes)", relicDuration ), { "min": relicDuration }); } titles.push({ "label": title, "value": victoryCondition.Description }); if (isCaptureTheRelic) titles.push({ "label": translate("Relic Count"), "value": Math.round(initAttributes.settings.RelicCount) }); if (victoryCondition.Name == "regicide") if (initAttributes.settings.RegicideGarrison) titles.push({ "label": translate("Hero Garrison"), "value": translate("Heroes can be garrisoned.") }); else titles.push({ "label": translate("Exposed Heroes"), "value": translate("Heroes cannot be garrisoned and they are vulnerable to raids.") }); } if (initAttributes.settings.RatingEnabled && initAttributes.settings.PlayerData.length == 2) titles.push({ "label": translate("Rated game"), "value": translate("When the winner of this match is determined, the lobby score will be adapted.") }); if (initAttributes.settings.LockTeams) titles.push({ "label": translate("Locked Teams"), "value": translate("Players can't change the initial teams.") }); else titles.push({ "label": translate("Diplomacy"), "value": translate("Players can make alliances and declare war on allies.") }); if (initAttributes.settings.LastManStanding) titles.push({ "label": translate("Last Man Standing"), "value": translate("Only one player can win the game. If the remaining players are allies, the game continues until only one remains.") }); else titles.push({ "label": translate("Allied Victory"), "value": translate("If one player wins, his or her allies win too. If one group of allies remains, they win.") }); let ceasefire = Math.round(initAttributes.settings.Ceasefire); titles.push({ "label": translate("Ceasefire"), "value": !ceasefire ? translate("disabled") : sprintf(translatePlural( "For the first minute, other players will stay neutral.", "For the first %(min)s minutes, other players will stay neutral.", ceasefire), { "min": ceasefire }) }); if (initAttributes.map == "random") titles.push({ "label": translateWithContext("Map Selection", "Random Map"), "value": translate("Randomly select a map from the list.") }); else { titles.push({ "label": translate("Map Name"), "value": mapCache.translateMapName( mapCache.getTranslatableMapName(initAttributes.mapType, initAttributes.map, initAttributes)) }); titles.push({ "label": translate("Map Description"), "value": mapCache.getTranslatedMapDescription(initAttributes.mapType, initAttributes.map) }); } titles.push({ "label": translate("Map Type"), "value": g_MapTypes.Title[g_MapTypes.Name.indexOf(initAttributes.mapType)] }); if (initAttributes.mapType == "random") { let mapSize = g_MapSizes.Name[g_MapSizes.Tiles.indexOf(initAttributes.settings.Size)]; if (mapSize) titles.push({ "label": translate("Map Size"), "value": mapSize }); } if (initAttributes.settings.Biome) { let biome = g_Settings.Biomes.find(b => b.Id == initAttributes.settings.Biome); titles.push({ "label": biome ? biome.Title : translateWithContext("biome", "Random Biome"), "value": biome ? biome.Description : translate("Randomly select a biome from the list.") }); } if (initAttributes.settings.TriggerDifficulty !== undefined) { let triggerDifficulty = g_Settings.TriggerDifficulties.find(difficulty => difficulty.Difficulty == initAttributes.settings.TriggerDifficulty); titles.push({ "label": triggerDifficulty.Title, "value": triggerDifficulty.Tooltip }); } if (initAttributes.settings.Nomad !== undefined) titles.push({ "label": initAttributes.settings.Nomad ? translate("Nomad Mode") : translate("Civic Centers"), "value": initAttributes.settings.Nomad ? translate("Players start with only few units and have to find a suitable place to build their city.") : translate("Players start with a Civic Center.") }); if (initAttributes.settings.StartingResources !== undefined) titles.push({ "label": translate("Starting Resources"), "value": initAttributes.settings.PlayerData && initAttributes.settings.PlayerData.some(pData => pData && pData.Resources !== undefined) ? translateWithContext("starting resources", "Per Player") : sprintf(translate("%(startingResourcesTitle)s (%(amount)s)"), { "startingResourcesTitle": g_StartingResources.Title[ g_StartingResources.Resources.indexOf( initAttributes.settings.StartingResources)], "amount": initAttributes.settings.StartingResources }) }); if (initAttributes.settings.PopulationCap !== undefined) titles.push({ "label": translate("Population Limit"), "value": initAttributes.settings.PlayerData && initAttributes.settings.PlayerData.some(pData => pData && pData.PopulationLimit !== undefined) ? translateWithContext("population limit", "Per Player") : g_PopulationCapacities.Title[ g_PopulationCapacities.Population.indexOf( initAttributes.settings.PopulationCap)] }); if (initAttributes.settings.WorldPopulationCap !== undefined) titles.push({ "label": translate("World Population Cap"), "value": g_WorldPopulationCapacities.Title[ g_WorldPopulationCapacities.Population.indexOf( initAttributes.settings.WorldPopulationCap)] }); titles.push({ "label": translate("Treasures"), "value": initAttributes.settings.DisableTreasures ? translateWithContext("treasures", "Disabled") : translateWithContext("treasures", "As defined by the map.") }); titles.push({ - "label": translate("Revealed Map"), - "value": initAttributes.settings.RevealMap + "label": translate("Explored Map"), + "value": initAttributes.settings.ExploreMap }); titles.push({ - "label": translate("Explored Map"), - "value": initAttributes.settings.ExploreMap + "label": translate("Revealed Map"), + "value": initAttributes.settings.RevealMap }); titles.push({ "label": translate("Cheats"), "value": initAttributes.settings.CheatsEnabled }); return titles.map(title => sprintf(translate("%(label)s %(details)s"), { "label": coloredText(title.label, g_DescriptionHighlight), "details": title.value === true ? translateWithContext("game setup option", "enabled") : title.value || translateWithContext("game setup option", "disabled") })).join("\n"); } /** * Sets the win/defeat icon to indicate current player's state. */ function setOutcomeIcon(state, image) { if (state == "won") { image.sprite = "stretched:session/icons/victory.png"; image.tooltip = translate("Victorious"); } else if (state == "defeated") { image.sprite = "stretched:session/icons/defeat.png"; image.tooltip = translate("Defeated"); } } function translateAISettings(playerData) { if (!playerData.AI) return ""; return sprintf(translate("%(AIdifficulty)s %(AIbehavior)s %(AIname)s"), { "AIname": translateAIName(playerData.AI), "AIdifficulty": translateAIDifficulty(playerData.AIDiff), "AIbehavior": translateAIBehavior(playerData.AIBehavior), }); } Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerCiv.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerCiv.js (revision 25083) +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerCiv.js (revision 25084) @@ -1,133 +1,138 @@ /** * Stores civ settings for all players. */ GameSettings.prototype.Attributes.PlayerCiv = class PlayerCiv extends GameSetting { init() { // NB: watchers aren't auto-triggered when modifying array elements. this.values = []; this.locked = []; this.settings.playerCount.watch(() => this.maybeUpdate(), ["nbPlayers"]); this.settings.map.watch(() => this.onMapChange(), ["map"]); } toInitAttributes(attribs) { if (!attribs.settings.PlayerData) attribs.settings.PlayerData = []; while (attribs.settings.PlayerData.length < this.values.length) attribs.settings.PlayerData.push({}); for (let i in this.values) if (this.values[i]) attribs.settings.PlayerData[i].Civ = this.values[i]; } fromInitAttributes(attribs) { if (!this.getLegacySetting(attribs, "PlayerData")) return; let pData = this.getLegacySetting(attribs, "PlayerData"); if (this.values.length < pData.length) this._resize(pData.length); for (let i in pData) if (pData[i] && pData[i].Civ) this.setValue(i, pData[i].Civ); } _resize(nb) { while (this.values.length > nb) { this.values.pop(); this.locked.pop(); } while (this.values.length < nb) { this.values.push("random"); this.locked.push(false); } } onMapChange() { // Reset. if (this.settings.map.type == "scenario" || this.getMapSetting("PlayerData") && this.getMapSetting("PlayerData").some(data => data && data.Civ)) { this._resize(0); this.maybeUpdate(); } + else + { + this.locked = this.locked.map(x => false); + this.trigger("locked"); + } } maybeUpdate() { this._resize(this.settings.playerCount.nbPlayers); this.values.forEach((c, i) => this._set(i, c)); this.trigger("values"); } pickRandomItems() { // Get a unique array of selectable cultures let cultures = Object.keys(this.settings.civData).filter(civ => this.settings.civData[civ].SelectableInGameSetup).map(civ => this.settings.civData[civ].Culture); cultures = cultures.filter((culture, index) => cultures.indexOf(culture) === index); let picked = false; for (let i in this.values) { if (this.values[i] != "random") continue; picked = true; // Pick a random civ of a random culture let culture = pickRandom(cultures); this.values[i] = pickRandom(Object.keys(this.settings.civData).filter(civ => this.settings.civData[civ].Culture == culture && this.settings.civData[civ].SelectableInGameSetup)); } if (picked) this.trigger("values"); return picked; } _getMapData(i) { let data = this.settings.map.data; if (!data || !data.settings || !data.settings.PlayerData) return undefined; if (data.settings.PlayerData.length <= i) return undefined; return data.settings.PlayerData[i].Civ; } _set(playerIndex, value) { let map = this._getMapData(playerIndex); if (!!map) { this.values[playerIndex] = map; this.locked[playerIndex] = true; } else { this.values[playerIndex] = value; this.locked[playerIndex] = this.settings.map.type == "scenario"; } } setValue(playerIndex, val) { this._set(playerIndex, val); this.trigger("values"); } swap(sourceIndex, targetIndex) { [this.values[sourceIndex], this.values[targetIndex]] = [this.values[targetIndex], this.values[sourceIndex]]; [this.locked[sourceIndex], this.locked[targetIndex]] = [this.locked[targetIndex], this.locked[sourceIndex]]; this.trigger("values"); } }; Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerColor.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerColor.js (revision 25083) +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerColor.js (revision 25084) @@ -1,181 +1,189 @@ /** * Stores player color for all players. */ GameSettings.prototype.Attributes.PlayerColor = class PlayerColor extends GameSetting { init() { this.defaultColors = g_Settings.PlayerDefaults.slice(1).map(pData => pData.Color); this.watch(() => this.maybeUpdate(), ["available"]); this.settings.playerCount.watch(() => this.maybeUpdate(), ["nbPlayers"]); this.settings.map.watch(() => this.onMapChange(), ["map"]); // NB: watchers aren't auto-triggered when modifying array elements. this.values = []; this.locked = []; this._updateAvailable(); } toInitAttributes(attribs) { if (!attribs.settings.PlayerData) attribs.settings.PlayerData = []; while (attribs.settings.PlayerData.length < this.values.length) attribs.settings.PlayerData.push({}); for (let i in this.values) if (this.values[i]) attribs.settings.PlayerData[i].Color = this.values[i]; } fromInitAttributes(attribs) { if (!this.getLegacySetting(attribs, "PlayerData")) return; let pData = this.getLegacySetting(attribs, "PlayerData"); if (this.values.length < pData.length) this._resize(pData.length); for (let i in pData) if (pData[i] && pData[i].Color) this.setColor(i, pData[i].Color); } _resize(nb) { while (this.values.length > nb) { this.values.pop(); this.locked.pop(); } while (this.values.length < nb) { this.values.push(undefined); this.locked.push(false); } } onMapChange() { // Reset. - if (this.settings.map.type == "scenario" || - this.getMapSetting("PlayerData") && - this.getMapSetting("PlayerData").some(data => data && data.Color)) - { + this.locked = this.locked.map(x => this.settings.map.type == "scenario"); + this.trigger("locked"); + + if (this.settings.map.type === "scenario") this._resize(0); - this._updateAvailable(); - this.maybeUpdate(); - } + this._updateAvailable(); + this.maybeUpdate(); + this.maybeUpdate(); } maybeUpdate() { this._resize(this.settings.playerCount.nbPlayers); this.values.forEach((c, i) => this._set(i, c)); this.trigger("values"); } _set(playerIndex, color) { let inUse = this.values.findIndex((otherColor, i) => color && otherColor && sameColor(color, otherColor)); if (inUse !== -1 && inUse !== playerIndex) { - // Swap colors. - let col = this.values[playerIndex]; - this.values[playerIndex] = undefined; - this._set(inUse, col); + if (sameColor(this.values[playerIndex], this.values[inUse])) + { + this.values[playerIndex] = undefined; + color = undefined; + } + else + { + // Swap colors. + let col = this.values[playerIndex]; + this.values[playerIndex] = undefined; + this._set(inUse, col); + } } if (!color || this.available.indexOf(color) == -1) { this.values[playerIndex] = color ? this._findClosestColor(color, this.available) : this._getUnusedColor(); } else this.values[playerIndex] = color; } get(playerIndex) { if (playerIndex >= this.values.length) return undefined; return this.values[playerIndex]; } setColor(playerIndex, color) { this._set(playerIndex, color); this.trigger("values"); } swap(sourceIndex, targetIndex) { [this.values[sourceIndex], this.values[targetIndex]] = [this.values[targetIndex], this.values[sourceIndex]]; [this.locked[sourceIndex], this.locked[targetIndex]] = [this.locked[targetIndex], this.locked[sourceIndex]]; this.trigger("values"); } _getMapData(i) { let data = this.settings.map.data; if (!data || !data.settings || !data.settings.PlayerData) return undefined; if (data.settings.PlayerData.length <= i) return undefined; return data.settings.PlayerData[i].Color; } _updateAvailable() { // Pick colors that the map specifies, add most unsimilar default colors // Provide the access to g_MaxPlayers different colors, regardless of current playercount. let values = []; for (let i = 0; i < g_MaxPlayers; ++i) values.push(this._getMapData(i) || this.defaultColors[i] || this._findFarthestUnusedColor(values)); this.available = values; } _findClosestColor(targetColor, colors) { let colorDistances = colors.map(color => colorDistance(color, targetColor)); let smallestDistance = colorDistances.find( distance => colorDistances.every(distance2 => distance2 >= distance)); return colors.find(color => colorDistance(color, targetColor) == smallestDistance); } _findFarthestUnusedColor(values) { let farthestColor; let farthestDistance = 0; for (let defaultColor of this.defaultColors) { let smallestDistance = Infinity; for (let usedColor of values) { let distance = colorDistance(usedColor, defaultColor); if (distance < smallestDistance) smallestDistance = distance; } if (smallestDistance >= farthestDistance) { farthestColor = defaultColor; farthestDistance = smallestDistance; } } return farthestColor; } _getUnusedColor() { return this.available.find(color => { return this.values.every(otherColor => !otherColor || !sameColor(color, otherColor)); }); } }; Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerTeam.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerTeam.js (revision 25083) +++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerTeam.js (revision 25084) @@ -1,83 +1,84 @@ /** * Stores team settings for all players. */ GameSettings.prototype.Attributes.PlayerTeam = class PlayerTeam extends GameSetting { init() { // NB: watchers aren't auto-triggered when modifying array elements. this.values = []; this.locked = []; this.settings.playerCount.watch(() => this.maybeUpdate(), ["nbPlayers"]); this.settings.map.watch(() => this.onMapChange(), ["map"]); } toInitAttributes(attribs) { if (!attribs.settings.PlayerData) attribs.settings.PlayerData = []; while (attribs.settings.PlayerData.length < this.values.length) attribs.settings.PlayerData.push({}); for (let i in this.values) if (this.values[i] !== undefined) attribs.settings.PlayerData[i].Team = this.values[i]; } fromInitAttributes(attribs) { if (!this.getLegacySetting(attribs, "PlayerData")) return; let pData = this.getLegacySetting(attribs, "PlayerData"); if (this.values.length < pData.length) this._resize(pData.length); for (let i in pData) if (pData[i] && pData[i].Team !== undefined) this.setValue(i, pData[i].Team); } _resize(nb) { while (this.values.length > nb) { this.values.pop(); this.locked.pop(); } while (this.values.length < nb) { // -1 is None this.values.push(-1); this.locked.push(false); } } onMapChange() { + this.locked = this.locked.map(x => this.settings.map.type === "scenario"); + this.trigger("locked"); if (this.settings.map.type === "random") return; let pData = this.getMapSetting("PlayerData"); if (pData && pData.every(x => x.Team === undefined)) return; for (let p in pData) this._set(+p, pData[p].Team === undefined ? -1 : pData[p].Team); this.trigger("values"); } maybeUpdate() { this._resize(this.settings.playerCount.nbPlayers); this.values.forEach((c, i) => this._set(i, c)); this.trigger("values"); } _set(playerIndex, value) { this.values[playerIndex] = value; - this.locked[playerIndex] = this.settings.map.type == "scenario"; } setValue(playerIndex, val) { this._set(playerIndex, val); this.trigger("values"); } }; Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/AIGameSettingControl.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/AIGameSettingControl.js (revision 25083) +++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/AIGameSettingControl.js (revision 25084) @@ -1,31 +1,43 @@ class AIGameSettingControlDropdown extends GameSettingControlDropdown { onOpenPage(playerIndex) { + this.setEnabled(true); this.playerIndex = playerIndex; this.render(); } + /** + * Overloaded: no need to trigger a relayout, + * but updateVisibility must be called manually + * as the AI control manager does not subscribe to updateLayout. + */ + setHidden(hidden) + { + this.hidden = hidden; + this.updateVisibility(); + } + setControl(aiConfigPage) { aiConfigPage.registerOpenPageHandler(this.onOpenPage.bind(this)); let i = aiConfigPage.getRow(); this.frame = Engine.GetGUIObjectByName("aiSettingFrame[" + i + "]"); this.title = this.frame.children[0]; this.dropdown = this.frame.children[1]; this.label = this.frame.children[2]; let size = this.frame.size; size.top = i * (this.Height + this.Margin); size.bottom = size.top + this.Height; this.frame.size = size; this.setHidden(false); } } AIGameSettingControlDropdown.prototype.Height= 28; AIGameSettingControlDropdown.prototype.Margin= 7; Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/PopulationCap.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/PopulationCap.js (revision 25083) +++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/PopulationCap.js (revision 25084) @@ -1,73 +1,71 @@ GameSettingControls.PopulationCap = class PopulationCap extends GameSettingControlDropdown { constructor(...args) { super(...args); - this.perPlayer = false; - this.dropdown.list = g_PopulationCapacities.Title; this.dropdown.list_data = g_PopulationCapacities.Population; this.sprintfArgs = {}; g_GameSettings.population.watch(() => this.render(), ["useWorldPop", "cap", "perPlayer"]); g_GameSettings.map.watch(() => this.render(), ["type"]); this.render(); } render() { this.setHidden(g_GameSettings.population.useWorldPop); - this.setEnabled(!g_GameSettings.map.type == "scenario" && !g_GameSettings.population.perPlayer); + this.setEnabled(g_GameSettings.map.type != "scenario" && !g_GameSettings.population.perPlayer); if (g_GameSettings.population.perPlayer) this.label.caption = this.PerPlayerCaption; else this.setSelectedValue(g_GameSettings.population.cap); } onHoverChange() { let tooltip = this.Tooltip; if (this.dropdown.hovered != -1) { let popCap = g_PopulationCapacities.Population[this.dropdown.hovered]; let players = g_GameSettings.playerCount.nbPlayers; if (popCap * players >= this.PopulationCapacityRecommendation) { this.sprintfArgs.players = players; this.sprintfArgs.popCap = popCap; tooltip = setStringTags(sprintf(this.HoverTooltip, this.sprintfArgs), this.HoverTags); } } this.dropdown.tooltip = tooltip; } onSelectionChange(itemIdx) { g_GameSettings.population.setPopCap(false, g_PopulationCapacities.Population[itemIdx]); this.gameSettingsControl.setNetworkInitAttributes(); } }; GameSettingControls.PopulationCap.prototype.TitleCaption = translate("Population Cap"); GameSettingControls.PopulationCap.prototype.Tooltip = translate("Select population limit."); GameSettingControls.PopulationCap.prototype.PerPlayerCaption = translateWithContext("population limit", "Per Player"); GameSettingControls.PopulationCap.prototype.HoverTooltip = translate("Warning: There might be performance issues if all %(players)s players reach %(popCap)s population."); GameSettingControls.PopulationCap.prototype.HoverTags = { "color": "orange" }; /** * Total number of units that the engine can run with smoothly. * It means a 4v4 with 150 population can still run nicely, but more than that might "lag". */ GameSettingControls.PopulationCap.prototype.PopulationCapacityRecommendation = 1200;