1
0
forked from mirrors/0ad

Allow world population capacity.

This allows to specify a world population capacity that is divided
evenly amongst living players.

Differential Revision: D2426
Reviewed by: @Angen
This was SVN commit r23873.
This commit is contained in:
Freagarach
2020-07-23 09:00:34 +00:00
parent 69ff754148
commit edb956424e
16 changed files with 267 additions and 14 deletions
@@ -377,6 +377,15 @@ function getGameDescription(mapCache)
g_GameAttributes.settings.PopulationCap)]
});
if (g_GameAttributes.settings.WorldPopulationCap !== undefined)
titles.push({
"label": translate("World Population Cap"),
"value":
g_WorldPopulationCapacities.Title[
g_WorldPopulationCapacities.Population.indexOf(
g_GameAttributes.settings.WorldPopulationCap)]
});
titles.push({
"label": translate("Treasures"),
"value": g_GameAttributes.settings.DisableTreasures ?
@@ -44,6 +44,7 @@ function loadSettingsValues()
"Biomes": loadBiomes(),
"PlayerDefaults": loadPlayerDefaults(),
"PopulationCapacities": loadPopulationCapacities(),
"WorldPopulationCapacities": loadWorldPopulationCapacities(),
"StartingResources": loadSettingValuesFile("starting_resources.json"),
"VictoryConditions": loadVictoryConditions(),
"TriggerDifficulties": loadSettingValuesFile("trigger_difficulties.json")
@@ -268,6 +269,29 @@ function loadPopulationCapacities()
}));
}
/**
* Loads available world population capacities.
*
* @returns {Object[]|undefined} - An array of the world population capacities in the form:
* { "Population": number, "Default": number, "Title": number|String }.
*/
function loadWorldPopulationCapacities()
{
let json = Engine.ReadJSONFile(g_SettingsDirectory + "world_population_capacities.json");
if (!json || json.Default === undefined || !json.WorldPopulationCapacities || !Array.isArray(json.WorldPopulationCapacities))
{
error("Could not load population_capacities.json");
return undefined;
}
return json.WorldPopulationCapacities.map(population => ({
"Population": population,
"Default": population == json.Default,
"Title": population < 10000 ? population : translate("Unlimited")
}));
}
/**
* Creates an object with all values of that property of the given setting and
* finds the index of the default value.
@@ -364,11 +388,19 @@ function translateMapSize(tiles)
/**
* Returns title or placeholder.
*
* @param {Number} population - for example 300
* @param {Number} population
* @param {boolean} world - Whether the entry has world population enabled.
* @returns {string}
*/
function translatePopulationCapacity(population)
function translatePopulationCapacity(population, world)
{
if (world)
{
let popCap = g_Settings.WorldPopulationCapacities.find(p => p.Population == population);
return popCap ? popCap.Title + " " + translateWithContext("population capacity addendum", "(world)") :
translateWithContext("population capacity", "Unknown");
}
let popCap = g_Settings.PopulationCapacities.find(p => p.Population == population);
return popCap ? popCap.Title : translateWithContext("population capacity", "Unknown");
}
@@ -26,7 +26,9 @@ var g_GameSettingsLayout = [
"label": translateWithContext("Match settings tab name", "Player"),
"settings": [
"PlayerCount",
"WorldPopulation",
"PopulationCap",
"WorldPopulationCap",
"StartingResources",
"Spies",
"Cheats"
@@ -0,0 +1,49 @@
GameSettingControls.WorldPopulation = class extends GameSettingControlCheckbox
{
onMapChange(mapData)
{
let mapValue =
mapData &&
mapData.settings &&
mapData.settings.WorldPopulation || undefined;
if (mapValue !== undefined && mapValue != g_GameAttributes.settings.WorldPopulation)
{
g_GameAttributes.settings.WorldPopulation = mapValue;
this.gameSettingsControl.updateGameAttributes();
}
}
onGameAttributesChange()
{
if (!g_GameAttributes.mapType)
return;
if (g_GameAttributes.settings.WorldPopulation !== undefined)
return;
g_GameAttributes.settings.WorldPopulation = false;
this.gameSettingsControl.updateGameAttributes();
}
onGameAttributesBatchChange()
{
if (!g_GameAttributes.mapType)
return;
this.setChecked(g_GameAttributes.settings.WorldPopulation);
}
onPress(checked)
{
g_GameAttributes.settings.WorldPopulation = checked;
this.gameSettingsControl.updateGameAttributes();
this.gameSettingsControl.setNetworkGameAttributes();
}
};
GameSettingControls.WorldPopulation.prototype.TitleCaption =
translate("World population");
GameSettingControls.WorldPopulation.prototype.Tooltip =
translate("When checked the Population Cap will be evenly distributed over all living players.");
@@ -40,10 +40,19 @@ GameSettingControls.PopulationCap = class extends GameSettingControlDropdown
onGameAttributesChange()
{
if (g_GameAttributes.settings.PopulationCap === undefined)
if (g_GameAttributes.settings.WorldPopulation)
{
g_GameAttributes.settings.PopulationCap = g_PopulationCapacities.Population[g_PopulationCapacities.Default];
this.gameSettingsControl.updateGameAttributes();
this.setHidden(true);
g_GameAttributes.settings.PopulationCap = undefined;
}
else
{
this.setHidden(false);
if (g_GameAttributes.settings.PopulationCap === undefined)
{
g_GameAttributes.settings.PopulationCap = g_PopulationCapacities.Population[g_PopulationCapacities.Default];
this.gameSettingsControl.updateGameAttributes();
}
}
}
@@ -0,0 +1,91 @@
GameSettingControls.WorldPopulationCap = class extends GameSettingControlDropdown
{
constructor(...args)
{
super(...args);
this.dropdown.list = g_WorldPopulationCapacities.Title;
this.dropdown.list_data = g_WorldPopulationCapacities.Population;
this.sprintfArgs = {};
}
onMapChange(mapData)
{
let mapValue =
mapData &&
mapData.settings &&
mapData.settings.WorldPopulationCap || undefined;
if (mapValue !== undefined && mapValue != g_GameAttributes.settings.WorldPopulationCap)
{
g_GameAttributes.settings.WorldPopulationCap = mapValue;
this.gameSettingsControl.updateGameAttributes();
}
this.setEnabled(g_GameAttributes.mapType != "scenario");
}
onGameAttributesChange()
{
if (g_GameAttributes.settings.WorldPopulation)
{
this.setHidden(false);
if (g_GameAttributes.settings.WorldPopulationCap === undefined)
{
g_GameAttributes.settings.WorldPopulationCap = g_WorldPopulationCapacities.Population[g_WorldPopulationCapacities.Default];
this.gameSettingsControl.updateGameAttributes();
}
}
else
{
this.setHidden(true);
g_GameAttributes.settings.WorldPopulationCap = undefined;
}
}
onGameAttributesBatchChange()
{
this.setSelectedValue(g_GameAttributes.settings.WorldPopulationCap);
}
onHoverChange()
{
let tooltip = this.Tooltip;
if (this.dropdown.hovered != -1)
{
let popCap = g_WorldPopulationCapacities.Population[this.dropdown.hovered];
if (popCap >= this.WorldPopulationCapacityRecommendation)
{
this.sprintfArgs.popCap = popCap;
tooltip = setStringTags(sprintf(this.HoverTooltip, this.sprintfArgs), this.HoverTags);
}
}
this.dropdown.tooltip = tooltip;
}
onSelectionChange(itemIdx)
{
g_GameAttributes.settings.WorldPopulationCap = g_WorldPopulationCapacities.Population[itemIdx];
this.gameSettingsControl.updateGameAttributes();
this.gameSettingsControl.setNetworkGameAttributes();
}
};
GameSettingControls.WorldPopulationCap.prototype.TitleCaption =
translate("World Population Cap");
GameSettingControls.WorldPopulationCap.prototype.Tooltip =
translate("Select world population limit.");
GameSettingControls.WorldPopulationCap.prototype.HoverTooltip =
translate("Warning: There might be performance issues if %(popCap)s population is reached.");
GameSettingControls.WorldPopulationCap.prototype.HoverTags = {
"color": "orange"
};
/**
* Total number of units that the engine can run with smoothly.
*/
GameSettingControls.WorldPopulationCap.prototype.WorldPopulationCapacityRecommendation = 1200;
@@ -2,6 +2,7 @@
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_WorldPopulationCapacities = prepareForDropdown(g_Settings && g_Settings.WorldPopulationCapacities);
const g_StartingResources = prepareForDropdown(g_Settings && g_Settings.StartingResources);
const g_VictoryConditions = g_Settings && g_Settings.VictoryConditions;
@@ -16,6 +16,7 @@ var g_DurationFilterIntervals = [
* Allow to filter by population capacity.
*/
const g_PopulationCapacities = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities);
const g_WorldPopulationCapacities = prepareForDropdown(g_Settings && g_Settings.WorldPopulationCapacities);
/**
* Reloads the selectable values in the filters. The filters depend on g_Settings and g_Replays
@@ -226,7 +226,7 @@ function displayReplayList()
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),
@@ -5,6 +5,7 @@ const g_CivData = loadCivData(false, true);
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_WorldPopulationCapacities = prepareForDropdown(g_Settings && g_Settings.WorldPopulationCapacities);
const g_StartingResources = prepareForDropdown(g_Settings && g_Settings.StartingResources);
const g_VictoryConditions = g_Settings && g_Settings.VictoryConditions;
@@ -58,7 +58,8 @@ GuiInterface.prototype.GetSimulationState = function()
"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);
@@ -154,6 +155,8 @@ GuiInterface.prototype.GetSimulationState = function()
ret.victoryConditions = cmpEndGameManager.GetVictoryConditions();
ret.alliedVictory = cmpEndGameManager.GetAlliedVictory();
ret.maxWorldPopulation = cmpPlayerManager.GetMaxWorldPopulation();
for (let i = 0; i < numPlayers; ++i)
{
let cmpPlayerStatisticsTracker = QueryPlayerIDInterface(i, IID_StatisticsTracker);
@@ -5,7 +5,11 @@ PlayerManager.prototype.Schema =
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 +169,35 @@ PlayerManager.prototype.RemoveLastPlayer = function()
Engine.DestroyEntity(lastId);
};
PlayerManager.prototype.SetMaxWorldPopulation = function(max)
{
this.maxWorldPopulation = max;
this.RedistributeWorldPopulation();
};
PlayerManager.prototype.GetMaxWorldPopulation = function()
{
return this.maxWorldPopulation;
};
PlayerManager.prototype.RedistributeWorldPopulation = function()
{
let worldPopulation = this.GetMaxWorldPopulation();
if (!worldPopulation)
return;
let activePlayers = this.GetActivePlayers();
if (!activePlayers.length)
return;
let newMaxPopulation = worldPopulation / activePlayers.length;
for (let playerID of activePlayers)
QueryPlayerIDInterface(playerID).SetMaxPopulation(newMaxPopulation);
};
PlayerManager.prototype.OnGlobalPlayerDefeated = function(msg)
{
this.RedistributeWorldPopulation();
};
Engine.RegisterSystemComponentType(IID_PlayerManager, "PlayerManager", PlayerManager);
@@ -74,7 +74,8 @@ AddMock(SYSTEM_ENTITY, IID_EndGameManager, {
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, {
@@ -369,7 +370,8 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {
"circularMap": false,
"timeElapsed": 0,
"victoryConditions": ["conquest", "wonder"],
"alliedVictory": false
"alliedVictory": false,
"maxWorldPopulation": undefined
});
TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {
@@ -522,7 +524,8 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {
"circularMap": false,
"timeElapsed": 0,
"victoryConditions": ["conquest", "wonder"],
"alliedVictory": false
"alliedVictory": false,
"maxWorldPopulation": undefined
});
@@ -0,0 +1,4 @@
{
"WorldPopulationCapacities": [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 63000],
"Default": 600
}
@@ -24,11 +24,16 @@ function Cheat(input)
cmpRangeManager.SetLosRevealAll(-1, true);
return;
case "maxpopulation":
cmpPlayer.SetPopulationBonuses(500);
cmpPlayer.SetPopulationBonuses((cmpPlayerManager.GetMaxWorldPopulation() || cmpPlayer.GetMaxPopulation()) + 500);
return;
case "changemaxpopulation":
cmpPlayer.SetMaxPopulation(500);
{
let cmpModifiersManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ModifiersManager);
cmpModifiersManager.AddModifiers("cheat/maxpopulation", {
"Player/MaxPopulation": [{ "affects": ["Player"], "add": 500 }],
}, playerEnt);
return;
}
case "convertunit":
for (let ent of input.selected)
{
@@ -69,6 +74,7 @@ function Cheat(input)
cmpProductionQueue.SpawnUnits(input.templates[i % input.templates.length], 1, null);
return;
case "fastactions":
{
let cmpModifiersManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ModifiersManager);
if (cmpModifiersManager.HasAnyModifier("cheat/fastactions", playerEnt))
cmpModifiersManager.RemoveAllModifiers("cheat/fastactions", playerEnt);
@@ -81,6 +87,7 @@ function Cheat(input)
"ProductionQueue/TechCostMultiplier/time": [{ "affects": [["Structure"], ["Unit"]], "multiply": 0.01 }]
}, playerEnt);
return;
}
case "changephase":
var cmpTechnologyManager = Engine.QueryInterface(playerEnt, IID_TechnologyManager);
if (!cmpTechnologyManager)
@@ -65,8 +65,14 @@ function InitGame(settings)
"Cost/BuildTime": [{ "affects": ["Unit", "Structure"], "multiply": time[AIDiff] }],
}, cmpPlayer.entity);
}
if (settings.PopulationCap)
cmpPlayer.SetMaxPopulation(settings.PopulationCap);
}
// Map or player data (handicap...) dependent initialisations of components (i.e. garrisoned units)
if (settings.WorldPopulationCap)
Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).SetMaxWorldPopulation(settings.WorldPopulationCap);
// Map or player data (handicap...) dependent initialisations of components (i.e. garrisoned units).
Engine.BroadcastMessage(MT_InitGame, {});
cmpAIManager.TryLoadSharedComponent();