Index: binaries/data/mods/public/gui/common/gamedescription.js =================================================================== --- binaries/data/mods/public/gui/common/gamedescription.js +++ binaries/data/mods/public/gui/common/gamedescription.js @@ -410,13 +410,27 @@ }); titles.push({ - "label": translate("Population Limit"), - "value": - g_PopulationCapacities.Title[ - g_PopulationCapacities.Population.indexOf( - g_GameAttributes.settings.PopulationCap)] + "label": translate("World Population"), + "value": g_GameAttributes.settings.WorldPopulation }); + if (g_GameAttributes.settings.WorldPopulation) + titles.push({ + "label": translate("World Population Limit"), + "value": + g_PopulationCapacitiesWorld.Title[ + g_PopulationCapacitiesWorld.Population.indexOf( + g_GameAttributes.settings.PopulationCapWorld)] + }); + else + titles.push({ + "label": translate("Individual Population Limit"), + "value": + g_PopulationCapacities.Title[ + g_PopulationCapacities.Population.indexOf( + g_GameAttributes.settings.PopulationCap)] + }); + titles.push({ "label": translate("Treasures"), "value": g_GameAttributes.settings.DisableTreasures ? Index: binaries/data/mods/public/gui/common/settings.js =================================================================== --- binaries/data/mods/public/gui/common/settings.js +++ binaries/data/mods/public/gui/common/settings.js @@ -292,7 +292,7 @@ */ function loadPopulationCapacities() { - var json = Engine.ReadJSONFile(g_SettingsDirectory + "population_capacities.json"); + let json = Engine.ReadJSONFile(g_SettingsDirectory + "population_capacities.json"); if (!json || json.Default === undefined || !json.PopulationCapacities || !Array.isArray(json.PopulationCapacities)) { @@ -300,11 +300,19 @@ return undefined; } - return json.PopulationCapacities.map(population => ({ + let result = {}; + result.individual = json.PopulationCapacities.map(population => ({ "Population": population, "Default": population == json.Default, "Title": population < 10000 ? population : translate("Unlimited") })); + result.world = json.WorldPopulationCapacities.map(population => ({ + "Population": population, + "Default": population == json.Default, + "Title": population < 10000 ? population : translate("Unlimited") + })); + + return result; } /** @@ -406,9 +414,13 @@ * @param {Number} population - for example 300 * @returns {string} */ -function translatePopulationCapacity(population) +function translatePopulationCapacity(population, world) { - let popCap = g_Settings.PopulationCapacities.find(p => p.Population == population); + let type = world ? "world" : "individual"; + let popCap = g_Settings.PopulationCapacities[type].find(p => p.Population == population); + + if (world) + return popCap ? popCap.Title + " " + translateWithContext("population capacity", "(world)") : translateWithContext("population capacity", "Unknown"); return popCap ? popCap.Title : translateWithContext("population capacity", "Unknown"); } Index: binaries/data/mods/public/gui/gamesetup/gamesetup.js =================================================================== --- binaries/data/mods/public/gui/gamesetup/gamesetup.js +++ binaries/data/mods/public/gui/gamesetup/gamesetup.js @@ -5,7 +5,8 @@ const g_MapSizes = prepareForDropdown(g_Settings && g_Settings.MapSizes); const g_MapTypes = prepareForDropdown(g_Settings && g_Settings.MapTypes); const g_TriggerDifficulties = prepareForDropdown(g_Settings && g_Settings.TriggerDifficulties); -const g_PopulationCapacities = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities); +const g_PopulationCapacities = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities.individual); +const g_PopulationCapacitiesWorld = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities.world); const g_StartingResources = prepareForDropdown(g_Settings && g_Settings.StartingResources); const g_VictoryDurations = prepareForDropdown(g_Settings && g_Settings.VictoryDurations); const g_VictoryConditions = g_Settings && g_Settings.VictoryConditions; @@ -419,7 +420,9 @@ "label": translateWithContext("Match settings tab name", "Player"), "settings": [ "numPlayers", + "worldPopulation", "populationCap", + "populationCapWorld", "startingResources", "disableSpies", "enableCheats" @@ -597,7 +600,35 @@ "select": (itemIdx) => { g_GameAttributes.settings.PopulationCap = g_PopulationCapacities.Population[itemIdx]; }, - "enabled": () => g_GameAttributes.mapType != "scenario", + "hidden": () => g_GameAttributes.settings.WorldPopulation, + "enabled": () => g_GameAttributes.mapType != "scenario" && !g_GameAttributes.settings.WorldPopulation, + "initOrder": 1000 + }, + "populationCapWorld": { + "title": () => translate("Population Cap"), + "tooltip": (hoverIdx) => { + + let popCap = g_PopulationCapacitiesWorld.Population[hoverIdx]; + + if (hoverIdx == -1 || popCap <= g_PopulationCapacityRecommendation) + return translate("Select world population limit."); + + return coloredText( + sprintf(translate("Warning: There might be performance issues if a total of %(popCap)s population is reached."), { + "popCap": popCap + }), + "orange"); + }, + "labels": () => g_PopulationCapacitiesWorld.Title, + "ids": () => g_PopulationCapacitiesWorld.Population, + "default": () => g_PopulationCapacitiesWorld.Default, + "defined": () => g_GameAttributes.settings.PopulationCapWorld !== undefined, + "get": () => g_GameAttributes.settings.PopulationCapWorld, + "select": (itemIdx) => { + g_GameAttributes.settings.PopulationCapWorld = g_PopulationCapacitiesWorld.Population[itemIdx]; + }, + "hidden": () => !g_GameAttributes.settings.WorldPopulation, + "enabled": () => g_GameAttributes.mapType != "scenario" && g_GameAttributes.settings.WorldPopulation, "initOrder": 1000 }, "startingResources": { @@ -857,6 +888,18 @@ "hidden": () => g_GameAttributes.mapType != "random", "initOrder": 1000 }, + "worldPopulation": { + "title": () => translate("World population"), + "tooltip": () => translate("When checked the Population Cap will be evenly distributed over all living players."), + "default": () => false, + "defined": () => g_GameAttributes.settings.WorldPopulation !== undefined, + "get": () => g_GameAttributes.settings.WorldPopulation, + "set": checked => { + g_GameAttributes.settings.WorldPopulation = checked; + }, + "enabled": () => g_GameAttributes.mapType != "scenario", + "initOrder": 1000 + }, "revealMap": { "title": () => // Translation: Make sure to differentiate between the revealed map and explored map settings! Index: binaries/data/mods/public/gui/replaymenu/replay_filters.js =================================================================== --- binaries/data/mods/public/gui/replaymenu/replay_filters.js +++ binaries/data/mods/public/gui/replaymenu/replay_filters.js @@ -15,7 +15,8 @@ /** * Allow to filter by population capacity. */ -const g_PopulationCapacities = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities); +const g_PopulationCapacities = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities.individual); +const g_PopulationCapacitiesWorld = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities.world); /** * Reloads the selectable values in the filters. The filters depend on g_Settings and g_Replays Index: binaries/data/mods/public/gui/replaymenu/replay_menu.js =================================================================== --- binaries/data/mods/public/gui/replaymenu/replay_menu.js +++ binaries/data/mods/public/gui/replaymenu/replay_menu.js @@ -225,7 +225,7 @@ return { "directories": replay.directory, "months": compatibilityColor(getReplayDateTime(replay), works), - "popCaps": compatibilityColor(translatePopulationCapacity(replay.attribs.settings.PopulationCap), works), + "popCaps": compatibilityColor(translatePopulationCapacity(replay.attribs.settings.PopulationCap, !!replay.attribs.settings.WorldPopulation), works), "mapNames": compatibilityColor(getReplayMapName(replay), works), "mapSizes": compatibilityColor(translateMapSize(replay.attribs.settings.Size), works), "durations": compatibilityColor(getReplayDuration(replay), works), Index: binaries/data/mods/public/gui/session/session.js =================================================================== --- binaries/data/mods/public/gui/session/session.js +++ binaries/data/mods/public/gui/session/session.js @@ -5,7 +5,8 @@ const g_Ceasefire = prepareForDropdown(g_Settings && g_Settings.Ceasefire); const g_MapSizes = prepareForDropdown(g_Settings && g_Settings.MapSizes); const g_MapTypes = prepareForDropdown(g_Settings && g_Settings.MapTypes); -const g_PopulationCapacities = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities); +const g_PopulationCapacities = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities.individual); +const g_PopulationCapacitiesWorld = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities.world); const g_StartingResources = prepareForDropdown(g_Settings && g_Settings.StartingResources); const g_VictoryDurations = prepareForDropdown(g_Settings && g_Settings.VictoryDurations); const g_VictoryConditions = g_Settings && g_Settings.VictoryConditions; Index: binaries/data/mods/public/simulation/components/GuiInterface.js =================================================================== --- binaries/data/mods/public/simulation/components/GuiInterface.js +++ binaries/data/mods/public/simulation/components/GuiInterface.js @@ -56,7 +56,8 @@ "players": [] }; - let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers(); + let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + let numPlayers = cmpPlayerManager.GetNumPlayers(); for (let i = 0; i < numPlayers; ++i) { let cmpPlayer = QueryPlayerIDInterface(i); @@ -157,6 +158,9 @@ ret.victoryConditions = cmpEndGameManager.GetVictoryConditions(); ret.alliedVictory = cmpEndGameManager.GetAlliedVictory(); + // Add the max world population. + ret.maxWorldPopulation = cmpPlayerManager.GetMaxWorldPopulation(); + // Add basic statistics to each player for (let i = 0; i < numPlayers; ++i) { Index: binaries/data/mods/public/simulation/components/Player.js =================================================================== --- binaries/data/mods/public/simulation/components/Player.js +++ binaries/data/mods/public/simulation/components/Player.js @@ -739,6 +739,16 @@ this.disabledTemplates[template.replace(/\{civ\}/g, this.civ)] = true; }; +Player.prototype.OnGlobalPlayerDefeated = function(msg) +{ + let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + let worldPopulation = cmpPlayerManager.GetMaxWorldPopulation(); + if (worldPopulation) + // We need to default to "1" here because when a game is won all players + // become "inactive" letting an observer see a infinite max pop cap. + this.SetMaxPopulation(worldPopulation / (cmpPlayerManager.GetActivePlayers().length || 1)); +}; + /** * Keep track of population effects of all entities that * become owned or unowned by this player Index: binaries/data/mods/public/simulation/components/PlayerManager.js =================================================================== --- binaries/data/mods/public/simulation/components/PlayerManager.js +++ binaries/data/mods/public/simulation/components/PlayerManager.js @@ -5,7 +5,10 @@ PlayerManager.prototype.Init = function() { - this.playerEntities = []; // list of player entity IDs + // List of player entity IDs. + this.playerEntities = []; + // Maximum world population (if applicable will be distributed amongst living players). + this.maxWorldPopulation = undefined; }; PlayerManager.prototype.AddPlayer = function(ent) @@ -165,4 +168,14 @@ Engine.DestroyEntity(lastId); }; +PlayerManager.prototype.SetMaxWorldPopulation = function(max) +{ + this.maxWorldPopulation = max; +}; + +PlayerManager.prototype.GetMaxWorldPopulation = function() +{ + return this.maxWorldPopulation; +}; + Engine.RegisterSystemComponentType(IID_PlayerManager, "PlayerManager", PlayerManager); Index: binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js +++ binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js @@ -72,7 +72,8 @@ AddMock(SYSTEM_ENTITY, IID_PlayerManager, { GetNumPlayers: function() { return 2; }, - GetPlayerByID: function(id) { TS_ASSERT(id === 0 || id === 1); return 100+id; } + GetPlayerByID: function(id) { TS_ASSERT(id === 0 || id === 1); return 100+id; }, + GetMaxWorldPopulation: function() { } }); AddMock(SYSTEM_ENTITY, IID_RangeManager, { @@ -367,7 +368,8 @@ circularMap: false, timeElapsed: 0, "victoryConditions": ["conquest", "wonder"], - alliedVictory: false + alliedVictory: false, + "maxWorldPopulation": undefined }); TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), { @@ -520,7 +522,8 @@ "circularMap": false, "timeElapsed": 0, "victoryConditions": ["conquest", "wonder"], - "alliedVictory": false + "alliedVictory": false, + "maxWorldPopulation": undefined }); Index: binaries/data/mods/public/simulation/data/settings/population_capacities.json =================================================================== --- binaries/data/mods/public/simulation/data/settings/population_capacities.json +++ binaries/data/mods/public/simulation/data/settings/population_capacities.json @@ -1,4 +1,5 @@ { "PopulationCapacities": [50, 100, 150, 200, 250, 300, 10000], + "WorldPopulationCapacities": [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 10000], "Default": 300 } Index: binaries/data/mods/public/simulation/helpers/InitGame.js =================================================================== --- binaries/data/mods/public/simulation/helpers/InitGame.js +++ binaries/data/mods/public/simulation/helpers/InitGame.js @@ -64,8 +64,18 @@ "Cost/BuildTime": [{ "affects": ["Unit", "Structure"], "multiply": time[AIDiff] }], }, cmpPlayer.entity); } - if (settings.PopulationCap) + + if (settings.PopulationCap && !settings.WorldPopulation) cmpPlayer.SetMaxPopulation(settings.PopulationCap); + if (settings.PopulationCapWorld && settings.WorldPopulation) + { + let popCap = settings.PopulationCapWorld; + let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + cmpPlayerManager.SetMaxWorldPopulation(popCap); + // Exclude GAIA. + popCap /= settings.PlayerData.length - 1; + cmpPlayer.SetMaxPopulation(popCap); + } if (settings.mapType !== "scenario" && settings.StartingResources) {