0ad/binaries/data/mods/public/globalscripts/Templates.js

853 lines
26 KiB
JavaScript
Raw Normal View History

/**
* Loads history and gameplay data of all civs.
*
* @param selectableOnly {boolean} - Only load civs that can be selected
* in the gamesetup. Scenario maps might set non-selectable civs.
*/
function loadCivFiles(selectableOnly)
{
const propertyNames = [
"Code", "Culture", "Music", "CivBonuses", "StartEntities",
"AINames", "SkirmishReplacements", "SelectableInGameSetup"];
const civData = {};
for (const filename of Engine.ListDirectoryFiles("simulation/data/civs/", "*.json", false))
{
const data = Engine.ReadJSONFile(filename);
for (const prop of propertyNames)
if (data[prop] === undefined)
throw new Error(filename + " doesn't contain " + prop);
if (selectableOnly && !data.SelectableInGameSetup)
continue;
const template = Engine.GetTemplate("special/players/" + data.Code);
data.Name = template.Identity.GenericName;
data.Emblem = "session/portraits/" + template.Identity.Icon;
data.History = template.Identity.History;
civData[data.Code] = data;
}
return civData;
}
/**
* @return {string[]} - All the classes for this identity template.
*/
function GetIdentityClasses(template)
{
let classString = "";
if (template.Classes && template.Classes._string)
classString += " " + template.Classes._string;
if (template.VisibleClasses && template.VisibleClasses._string)
classString += " " + template.VisibleClasses._string;
if (template.Rank)
classString += " " + template.Rank;
return classString.length > 1 ? classString.substring(1).split(" ") : [];
}
/**
* Gets an array with all classes for this identity template
* that should be shown in the GUI
*/
function GetVisibleIdentityClasses(template)
{
return template.VisibleClasses && template.VisibleClasses._string ? template.VisibleClasses._string.split(" ") : [];
}
/**
* Check if a given list of classes matches another list of classes.
* Useful f.e. for checking identity classes.
*
* @param classes - List of the classes to check against.
* @param match - Either a string in the form
* "Class1 Class2+Class3"
* where spaces are handled as OR and '+'-signs as AND,
* and ! is handled as NOT, thus Class1+!Class2 = Class1 AND NOT Class2.
* Or a list in the form
* [["Class1"], ["Class2", "Class3"]]
* where the outer list is combined as OR, and the inner lists are AND-ed.
* Or a hybrid format containing a list of strings, where the list is
* combined as OR, and the strings are split by space and '+' and AND-ed.
*
* @return undefined if there are no classes or no match object
* true if the the logical combination in the match object matches the classes
* false otherwise.
*/
function MatchesClassList(classes, match)
{
if (!match || !classes)
return undefined;
// Transform the string to an array
if (typeof match === "string")
match = match.split(/\s+/);
for (let sublist of match)
{
// If the elements are still strings, split them by space or by '+'
if (typeof sublist === "string")
sublist = sublist.split(/[+\s]+/);
if (sublist.every(c => (c[0] === "!" && classes.indexOf(c.substr(1)) === -1) ||
(c[0] !== "!" && classes.indexOf(c) !== -1)))
return true;
}
return false;
}
/**
* Get basic information about a technology template.
* @param {Object} template - A valid template as obtained by loading the tech JSON file.
* @param {string} civ - Civilization for which the tech requirements should be calculated.
*/
function GetTechnologyBasicDataHelper(template, civ)
{
return {
"name": {
"generic": template.genericName
},
"icon": template.icon ? "technologies/" + template.icon : undefined,
"description": template.description,
"reqs": DeriveTechnologyRequirements(template, civ),
"modifications": template.modifications,
"affects": template.affects,
"replaces": template.replaces
};
}
/**
* Get information about a technology template.
* @param {Object} template - A valid template as obtained by loading the tech JSON file.
* @param {string} civ - Civilization for which the specific name and tech requirements should be returned.
* @param {Object} resources - An instance of the Resources class.
*/
function GetTechnologyDataHelper(template, civ, resources)
{
const ret = GetTechnologyBasicDataHelper(template, civ);
if (template.specificName)
ret.name.specific = template.specificName[civ] || template.specificName.generic;
ret.cost = { "time": template.researchTime ? +template.researchTime : 0 };
for (const type of resources.GetCodes())
ret.cost[type] = +(template.cost && template.cost[type] || 0);
ret.tooltip = template.tooltip;
ret.requirementsTooltip = template.requirementsTooltip || "";
More versatile placement of research buttons This patch allows techs to optionally define a property "placeBelow" specifying a combination of class requirements. The selection panel then tries to place their research button below the training button whose unit matches these classes -- if there is only and exactly one. This is useful for techs only affecting a single type of unit: it e.g. allows always placing the fishing nets research button below the fishing boats training button. To prevent duplicating information in "placeBelow" (e.g. from "affects"), it can also be set to two magic values: "{AffectedUnit}" and "{UnlockedUnit}". In practice, many techs use those, but class combination remain useful for granular control in very specific cases. Regarding tech pairs, as explained in a comment, their two research buttons can now be placed in three different arrangements (with descending preference): 1. Vertically below a single training button. 2. Horizontally below two adjacent training buttons. 3. Horizontally adjacent in the bottom row (below no training buttons). Vertically in the third and fourth rows (below no training button) -- how it was previously -- is no longer done as it'd conflict with and doesn't fit the new layout and the clear separation between "generic" and "specific" techs. Whenever a research button can't be placed in their preferred location because it is already occupied, the code simply falls back to the default position in the bottom row. This patch also adds a new game option to hide the new small arrows above the research buttons in order to give more experienced players who already know the techs well the possibility to reduce visual clutter. By default, the arrows are shown.
2025-09-24 07:52:59 -07:00
if (template.placeBelow)
ret.placeBelow = template.placeBelow;
return ret;
}
/**
* Get information about an aura template.
* @param {object} template - A valid template as obtained by loading the aura JSON file.
*/
function GetAuraDataHelper(template)
{
return {
"name": {
"generic": template.auraName,
},
"description": template.auraDescription || null,
"modifications": template.modifications,
"radius": template.radius || null,
};
}
function calculateCarriedResources(carriedResources, tradingGoods)
{
var resources = {};
if (carriedResources)
for (const resource of carriedResources)
resources[resource.type] = (resources[resource.type] || 0) + resource.amount;
if (tradingGoods && tradingGoods.amount)
resources[tradingGoods.type] =
(resources[tradingGoods.type] || 0) +
(tradingGoods.amount.traderGain || 0) +
(tradingGoods.amount.market1Gain || 0) +
(tradingGoods.amount.market2Gain || 0);
return resources;
}
/**
* Remove filter prefix (mirage, corpse, etc) from template name.
*
* ie. filter|dir/to/template -> dir/to/template
*/
function removeFiltersFromTemplateName(templateName)
{
return templateName.split("|").pop();
}
/**
* Helper providing several options to compute an already loaded template's "data", i.e. interesting values with
* dynamic modifications (from e.g. auras, techs, or civ bonuses) applied.
*/
class TemplateHelper
{
/**
* Stores some temporary data from the current query to avoid having to pass it around all the time
* and to keep the computations clearer and cleaner.
*/
_context = Object.seal({
"template": {},
"player": null, // null or a player id
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
"civ": "",
"entity": null, // null or an entity id
"auraTemplates": {},
"resources": {},
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
"customModifiers": null, // null or an object defining modifications
"applyValueModifications": () => 0
});
/**
* Pull basic information from a template without applying any modifiers whatsoever.
*
* @param {Object} template - A valid template as returned by the template loader.
* @param {Object} auraTemplates - In the form of { key: { "auraName": "", "auraDescription": "" } }.
* @param {Object} resources - An instance of the Resources class.
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
* @param {string} civ - The civ of which the template is part of. Does not have to match the template's identity's own civ.
* @param {string[]} [components] - An array of components to process, if undefined all components are processed.
*/
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
getBasicData(template, auraTemplates, resources, civ, components)
{
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
return this.computeDataFromModifiers(template, auraTemplates, resources, {}, civ, components);
}
/**
* Pull basic information from a template and apply a given set of modifiers to it.
*
* @param {Object} template - A valid template as returned by the template loader.
* @param {Object} auraTemplates - In the form of { key: { "auraName": "", "auraDescription": "" } }.
* @param {Object} resources - An instance of the Resources class.
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
* @param {Object} modifiers - Modifications to apply to the template, e.g. from auto-researched techs or civ bonuses.
* @param {string} civ - The civ of which the template is part of. Does not have to match the template's identity's own civ.
* @param {string[]} [components] - An array of components to process, if undefined all components are processed.
*/
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
computeDataFromModifiers(template, auraTemplates, resources, modifiers, civ, components)
{
this._context.template = template;
this._context.player = null;
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
this._context.civ = civ;
this._context.entity = null;
this._context.auraTemplates = auraTemplates;
this._context.resources = resources;
this._context.customModifiers = modifiers;
this._context.applyValueModifications = this._applyCustomModifications.bind(this);
return this._computeModifiedTemplateData(template, components);
}
/**
* Pull basic information from a template and to it apply all modifiers registered (in the simulation) to a given player.
* Must only be called in simulation context.
*
* @param {Object} template - A valid template as returned by the template loader.
* @param {Object} auraTemplates - In the form of { key: { "auraName": "", "auraDescription": "" } }.
* @param {Object} resources - An instance of the Resources class.
* @param {number} player - ID of the target player.
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
* @param {string} playerCiv - Civ of the target player.
* @param {string[]} [components] - An array of components to process, if undefined all components are processed.
*/
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
computeDataFromPlayer(template, auraTemplates, resources, player, playerCiv, components)
{
this._context.template = template;
this._context.player = player;
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
this._context.civ = playerCiv;
this._context.entity = null;
this._context.auraTemplates = auraTemplates;
this._context.resources = resources;
this._context.customModifiers = null;
this._context.applyValueModifications = this._applyPlayerModifications.bind(this);
return this._computeModifiedTemplateData(template, components);
}
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
/**
* Pull basic information from a template and to it apply all modifiers registered (in the simulation) to a given entity or
* its owning player.
* Must only be called in simulation context.
*
* @param {Object} template - A valid template as returned by the template loader.
* @param {Object} auraTemplates - In the form of { key: { "auraName": "", "auraDescription": "" } }.
* @param {Object} resources - An instance of the Resources class.
* @param {number} entity - ID of the target entity.
* @param {string} ownerCiv - Civ of the target entity's owner.
* @param {undefined|string[]} components - An array of components to process, if undefined all components are processed.
*/
computeDataFromEntity(template, auraTemplates, resources, entity, ownerCiv, components)
{
this._context.template = template;
this._context.player = null;
this._context.civ = ownerCiv;
this._context.entity = entity;
this._context.auraTemplates = auraTemplates;
this._context.resources = resources;
this._context.customModifiers = null;
this._context.applyValueModifications = this._applyEntityModifications.bind(this);
return this._computeModifiedTemplateData(template, components);
}
/**
* Apply the manually-passed ("custom") modifiers to a given value.
*/
_applyCustomModifications(currentValue, modKey)
{
if (this._context.modifiers?.[modKey])
return GetTechModifiedProperty(
this._context.modifiers[modKey], GetIdentityClasses(this._context.template.Identity), currentValue
);
return currentValue;
}
/**
* Apply the modifiers of the current player to a given value.
*/
_applyPlayerModifications(currentValue, modKey)
{
return ApplyValueModificationsToTemplate(modKey, currentValue, this._context.player, this._context.template);
}
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
/**
* Apply all modifiers of the current entity and of its owner to a given value.
*/
_applyEntityModifications(currentValue, modKey)
{
return ApplyValueModificationsToEntity(modKey, currentValue, this._context.entity);
}
/**
* Read a given value from the current template, and apply the current modifications to it.
* Only call this for values that can theoretically be modified (i.e. the simulation supports it), like attack damge or max HP.
* Static, unmodifiable values (like turret points or footprint size) are better read from the template directly.
*/
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
_getModifiedValue(valuePath, modKey, isNumeric = true, defaultValue = 0)
{
let currentValue = this._context.template;
for (const property of valuePath.split("/"))
currentValue = currentValue[property] || defaultValue;
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
return isNumeric ?
// Using .toFixed() to get around spidermonkey's treatment of numbers (3 * 1.1 = 3.3000000000000003 for instance).
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
+this._context.applyValueModifications(+currentValue, modKey || valuePath).toFixed(8) :
this._context.applyValueModifications(currentValue, modKey || valuePath);
}
/**
* Pull the desired information from a template while accounting for modifications of the current context.
*/
_computeModifiedTemplateData(template, components)
{
const ret = {};
for (const component of (components || Object.keys(template)))
{
const handler = this._componentHandlers[component];
if (!handler)
// There doesn't need to be a handler for each component, so just skip it silently.
continue;
handler(template[component], ret);
}
return ret;
}
/**
* Simple helper for the component handlers.
*/
_getAttackEffects(template, path)
{
const ret = {};
if (template.Capture)
ret.Capture = this._getModifiedValue(path + "/Capture");
if (template.Damage)
{
ret.Damage = {};
for (const damageType in template.Damage)
ret.Damage[damageType] = this._getModifiedValue(path + "/Damage/" + damageType);
}
if (template.ApplyStatus)
ret.ApplyStatus = template.ApplyStatus;
return ret;
}
/**
* The methods performing the core logic, pulling and processing the values of the template's individual
* components.
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
* The structure of each component's data has to be coordinated with the one in EntityStateRetriever, so that they
* can be merged into one.
*/
_componentHandlers = {
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
"AlertRaiser": (alertRaiser, ret) =>
{
ret.alertRaiser = { "classes": alertRaiser.List._string };
},
"Attack": (attack, ret) =>
{
ret.attack = {};
for (const type in attack)
{
const attackType = attack[type];
const data = this._getAttackEffects(attackType, "Attack/" + type);
const getAttackStat = stat =>
this._getModifiedValue("Attack/" + type + "/" + stat);
data.attackName = {
"name": attackType.AttackName._string || attackType.AttackName
};
const context = attackType.AttackName["@context"];
if (context)
data.attackName.context = context;
data.minRange = getAttackStat("MinRange");
data.maxRange = getAttackStat("MaxRange");
data.yOrigin = getAttackStat("Origin/Y");
data.elevationAdaptedRange =
Math.sqrt(data.maxRange *
(2 * data.yOrigin + data.maxRange));
data.repeatTime = getAttackStat("RepeatTime");
if (attackType.Projectile)
data.projectileCount = +attackType.Projectile.Count || 1;
if (attackType.Projectile)
data.friendlyFire = attackType.Projectile.FriendlyFire === "true";
if (attackType.Splash)
{
data.splash = this._getAttackEffects(
attackType.Splash,
"Attack/" + type + "/Splash"
);
data.splash.friendlyFire = attackType.Splash.FriendlyFire !== "false";
data.splash.shape = attackType.Splash.Shape;
}
ret.attack[type] = data;
}
},
"Auras": (auras, ret) =>
{
ret.auras = {};
if (this._context.auraTemplates)
for (const auraID of auras._string.split(/\s+/))
ret.auras[auraID] =
GetAuraDataHelper(this._context.auraTemplates[auraID]);
},
"BuildRestrictions": (buildRestrictions, ret) =>
{
ret.buildRestrictions = {
"placementType": buildRestrictions.PlacementType,
"territory": buildRestrictions.Territory,
"category": buildRestrictions.Category
};
if (buildRestrictions.Distance)
{
ret.buildRestrictions.distance = {
"fromClass": buildRestrictions.Distance.FromClass
};
if (buildRestrictions.Distance.MinDistance)
ret.buildRestrictions.distance.min =
this._getModifiedValue("BuildRestrictions/Distance/MinDistance");
if (buildRestrictions.Distance.MaxDistance)
ret.buildRestrictions.distance.max =
this._getModifiedValue("BuildRestrictions/Distance/MaxDistance");
}
},
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
"Builder": (builder, ret) =>
{
ret.builder = true;
},
"BuildingAI": (buildingAI, ret) =>
{
ret.buildingAI = {
"defaultArrowCount": Math.round(this._getModifiedValue("BuildingAI/DefaultArrowCount")),
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
"maxArrowCount": Math.round(this._getModifiedValue("BuildingAI/MaxArrowCount")),
"garrisonArrowMultiplier": this._getModifiedValue("BuildingAI/GarrisonArrowMultiplier"),
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
"garrisonArrowClasses": buildingAI.GarrisonArrowClasses?.split(/\s+/)
};
},
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
"Capturable": (capturable, ret) =>
{
ret.maxCapturePoints = Math.round(this._getModifiedValue("Capturable/CapturePoints"));
},
"Cost": (cost, ret) =>
{
ret.cost = {};
for (const resCode in cost.Resources)
ret.cost[resCode] =
this._getModifiedValue("Cost/Resources/" + resCode);
if (cost.Population)
ret.cost.population = this._getModifiedValue("Cost/Population");
if (cost.BuildTime)
ret.cost.time = this._getModifiedValue("Cost/BuildTime");
},
"DeathDamage": (deathDamage, ret) =>
{
ret.deathDamage = this._getAttackEffects(deathDamage, "DeathDamage");
ret.deathDamage.friendlyFire = deathDamage.FriendlyFire != "false";
},
"Footprint": (footprint, ret) =>
{
ret.footprint = { "height": footprint.Height };
if (footprint.Square)
ret.footprint.square = {
"width": +footprint.Square["@width"],
"depth": +footprint.Square["@depth"]
};
else if (footprint.Circle)
ret.footprint.circle = {
"radius": +footprint.Circle["@radius"]
};
else
warn("TemplateHelper: Unrecognized Footprint type");
},
"Garrisonable": (garrisonable, ret) =>
{
ret.garrisonable = {
"size": this._getModifiedValue("Garrisonable/Size")
};
},
"GarrisonHolder": (garrisonHolder, ret) =>
{
ret.garrisonHolder = {
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
"allowedClasses": this._getModifiedValue("GarrisonHolder/List/_string", null, false, ""),
"buffHeal": this._getModifiedValue("GarrisonHolder/BuffHeal"),
"capacity": this._getModifiedValue("GarrisonHolder/Max")
};
},
"Heal": (heal, ret) =>
{
ret.heal = {
"health": this._getModifiedValue("Heal/Health"),
"range": this._getModifiedValue("Heal/Range"),
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
"interval": this._getModifiedValue("Heal/Interval"),
"unhealableClasses": heal.UnhealableClasses._string || "",
"healableClasses": heal.HealableClasses._string || ""
};
},
"Health": (health, ret) =>
{
ret.maxHitpoints = Math.round(this._getModifiedValue("Health/Max"));
},
"Identity": (identity, ret) =>
{
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
const allClasses = GetIdentityClasses(identity);
ret.selectionGroupName = identity.SelectionGroupName;
ret.name = {
"specific": (identity.SpecificName || identity.GenericName),
"generic": identity.GenericName
};
ret.icon = identity.Icon;
ret.tooltip = identity.Tooltip;
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
ret.identityClasses = allClasses;
ret.visibleIdentityClasses = GetVisibleIdentityClasses(identity);
ret.nativeCiv = identity.Civ;
ret.requirements = identity.Requirements;
ret.rank = identity.Rank;
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
ret.undeletable = identity.Undeletable;
ret.enablesBartering = allClasses.includes("Barter") && !this._context.template.Foundation;
},
"Loot": (loot, ret) =>
{
ret.loot = {};
for (const type in loot)
ret.loot[type] =
this._getModifiedValue("Loot/" + type);
},
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
"Market": (market, ret) =>
{
const tradeTypes = market.TradeType.split(/\s+/);
ret.market = {
"land": tradeTypes.includes("land"),
"naval": tradeTypes.includes("naval")
};
},
"Mirage": (mirage, ret) =>
{
ret.mirage = true;
},
"Obstruction": (obstruction, ret) =>
{
ret.obstruction = {
"active": ("" + obstruction.Active === "true"),
"blockMovement": ("" + obstruction.BlockMovement === "true"),
"blockPathfinding": ("" + obstruction.BlockPathfinding === "true"),
"blockFoundation": ("" + obstruction.BlockFoundation === "true"),
"blockConstruction": ("" + obstruction.BlockConstruction === "true"),
"disableBlockMovement": ("" + obstruction.DisableBlockMovement === "true"),
"disableBlockPathfinding": ("" + obstruction.DisableBlockPathfinding === "true"),
"shape": {}
};
if (obstruction.Static)
{
ret.obstruction.shape.type = "static";
ret.obstruction.shape.width = +obstruction.Static["@width"];
ret.obstruction.shape.depth = +obstruction.Static["@depth"];
}
else if (obstruction.Unit)
{
ret.obstruction.shape.type = "unit";
ret.obstruction.shape.radius = +obstruction.Unit["@radius"];
}
else
{
ret.obstruction.shape.type = "cluster";
}
},
"Pack": (pack, ret) =>
{
ret.pack = {
"state": pack.State,
"time": this._getModifiedValue("Pack/Time")
};
},
"Population": (population, ret) =>
{
if (!population.Bonus)
return;
ret.population = {
"bonus": this._getModifiedValue("Population/Bonus")
};
},
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
"Promotion": (promotion, ret) =>
{
ret.promotion = {
"req": this._getModifiedValue("Promotion/RequiredXp")
};
},
"Researcher": (researcher, ret) =>
{
ret.researcher = {
"techCostMultiplier": {}
};
for (const res of this._context.resources.GetCodes().concat(["time"]))
ret.researcher.techCostMultiplier[res] =
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
this._getModifiedValue("Researcher/TechCostMultiplier/" + res, null, true, 1);
},
"Resistance": (resistance, ret) =>
{
const form = this._context.template.Foundation ? "Foundation" : "Entity";
const resistanceForm = resistance?.[form];
if (!resistanceForm)
return;
ret.resistance = {};
if (resistanceForm.Damage)
{
ret.resistance.Damage = {};
for (const damageType in resistanceForm.Damage)
ret.resistance.Damage[damageType] =
this._getModifiedValue("Resistance/Entity/Damage/" + damageType);
}
if (resistanceForm.Capture)
ret.resistance.Capture = this._getModifiedValue("Resistance/Entity/Capture");
if (resistanceForm.ApplyStatus)
{
ret.resistance.ApplyStatus = {};
for (const statusEffect in resistanceForm.ApplyStatus)
ret.resistance.ApplyStatus[statusEffect] = {
"blockChance": this._getModifiedValue("Resistance/Entity/ApplyStatus/" + statusEffect + "/BlockChance"),
"duration": this._getModifiedValue("Resistance/Entity/ApplyStatus/" + statusEffect + "/Duration")
};
}
},
"ResourceDropsite": (resourceDropsite, ret) =>
{
ret.resourceDropsite = {
"types": resourceDropsite.Types.split(" ")
};
},
"ResourceGatherer": (resourceGatherer, ret) =>
{
ret.resourceGatherRates = {};
const baseSpeed = this._getModifiedValue("ResourceGatherer/BaseSpeed");
for (const type in resourceGatherer.Rates)
ret.resourceGatherRates[type] =
this._getModifiedValue("ResourceGatherer/Rates/" + type) * baseSpeed;
},
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
"ResourceSupply": (resourceSupply, ret) =>
{
const i = resourceSupply.Type.indexOf('.');
ret.resourceSupply = {
"isInfinite": !isFinite(+resourceSupply.Max),
"max": this._getModifiedValue("ResourceSupply/Max"),
"type": {
"generic": resourceSupply.Type.slice(0, i),
"specific": resourceSupply.Type.substring(i + 1)
},
"killBeforeGather": resourceSupply.KillBeforeGather === "true",
"maxGatherers": +resourceSupply.MaxGatherers
};
},
"ResourceTrickle": (resourceTrickle, ret) =>
{
ret.resourceTrickle = {
"interval": +resourceTrickle.Interval,
"rates": {}
};
for (const type in resourceTrickle.Rates)
ret.resourceTrickle.rates[type] =
this._getModifiedValue("ResourceTrickle/Rates/" + type);
},
"Trader": (trader, ret) =>
{
ret.trader = {
"GainMultiplier": this._getModifiedValue("Trader/GainMultiplier")
};
},
"Treasure": (treasure, ret) =>
{
ret.treasure = {
"collectTime": this._getModifiedValue("Treasure/CollectTime"),
"resources": {}
};
for (const resource in treasure.Resources)
ret.treasure.resources[resource] =
this._getModifiedValue("Treasure/Resources/" + resource);
},
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
"TreasureCollector": (treasureCollector, ret) =>
{
ret.treasureCollector = true;
},
"TurretHolder": (turretHolder, ret) =>
{
ret.turretHolder = {
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
"numTurretPoints": Object.keys(turretHolder.TurretPoints).length
};
},
"UnitAI": (unitAI, ret) =>
{
ret.unitAI = {
"formations": unitAI.Formations?._string?.split(/\s+/) || []
};
},
"UnitMotion": (unitMotion, ret) =>
{
const walkSpeed = this._getModifiedValue("UnitMotion/WalkSpeed");
ret.unitMotion = {
"walk": walkSpeed,
"run": walkSpeed,
"acceleration": this._getModifiedValue("UnitMotion/Acceleration")
};
if (unitMotion.RunMultiplier)
ret.unitMotion.run *= this._getModifiedValue("UnitMotion/RunMultiplier");
},
"Upkeep": (upkeep, ret) =>
{
ret.upkeep = {
"interval": +upkeep.Interval,
"rates": {}
};
for (const type in upkeep.Rates)
ret.upkeep.rates[type] =
this._getModifiedValue("Upkeep/Rates/" + type);
},
"Upgrade": (upgrade, ret) =>
{
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
ret.upgrade = { "options": [] };
for (const upgradeName in upgrade)
{
const upgr = upgrade[upgradeName];
const cost = {};
if (upgr.Cost)
for (const res in upgr.Cost)
cost[res] =
this._getModifiedValue("Upgrade/" + upgradeName + "/Cost/" + res, "Upgrade/Cost/" + res);
if (upgr.Time)
cost.time =
this._getModifiedValue("Upgrade/" + upgradeName + "/Time", "Upgrade/Time");
Optimise GetEntityState GetEntityState is a performance-critical function in the GUI when a large number of units are selected. The goal of this patch is to increase the efficiency of it without modifying the returned states visible to rest of the GUI in any way (it renames a few properties of entities states or moves them around, but the information they contain and the way to access it remain the complete same) As explained the comments, certain parts (the template data) of an entity state are "predictable" and can be reused between entities with the same template. What one has to account for, however, is that values of the template data can be modified. This happens on two different levels: Firstly, player-wide modifications, which apply to all entities owned by that player. This includes stuff like bonuses from researched techs, civs bonuses, team bonuses. And secondly, entity-local modifications, which apply to individual entities. This includes buffs or debuffs from status effects or auras (of other entities). So we can construct a whole entity state by first just computing the dynamic state (stuff like current hitpoints, which differs from entity to entity) and then adding the (potentially already cached) template data to it, which can be reused between entities with the same template and owning player. And then overwrite the values affected by entity-local modifications, which are usually just a few and even they can be cached and reused for entities with the same owning player, template, and entity-local modifications (identified by the "modifications ID"). This saves the effort of retrieving/computing a ton of data each turn and also saves the time it takes the engine to clone the data from the simulation to the GUI (as the return value of the GUI interface call), which previously took just as long as retrieving the entity states themselves. Since only the dynamic state is read from the components, a number of small getter methods have become unused; they are kept (for now at least) since they might be useful again in the future or for mods.
2026-02-12 06:52:21 -08:00
ret.upgrade.options.push({
"entity": upgr.Entity
.replace(/\{civ\}/g, this._context.civ)
.replace(/\{native\}/g, this._context.template.Identity.Civ),
"tooltip": upgr.Tooltip,
cost,
"icon": upgr.Icon,
"requirements": upgr.Requirements
});
}
},
"WallPiece": (wallPiece, ret) =>
{
ret.wallPiece = {
"length": +wallPiece.Length,
"angle": +(wallPiece.Orientation || 1) * Math.PI,
"indent": +(wallPiece.Indent || 0),
"bend": +(wallPiece.Bend || 0) * Math.PI
};
},
"WallSet": (wallSet, ret) =>
{
ret.wallSet = {
"templates": {
"tower": wallSet.Templates.Tower,
"gate": wallSet.Templates.Gate,
"fort": wallSet.Templates.Fort || "structures/" + this._context.template.Identity.Civ + "/fortress",
"long": wallSet.Templates.WallLong,
"medium": wallSet.Templates.WallMedium,
"short": wallSet.Templates.WallShort
},
"maxTowerOverlap": +wallSet.MaxTowerOverlap,
"minTowerOverlap": +wallSet.MinTowerOverlap
};
if (wallSet.Templates.WallEnd)
ret.wallSet.templates.end = wallSet.Templates.WallEnd;
if (wallSet.Templates.WallCurves)
ret.wallSet.templates.curves = wallSet.Templates.WallCurves.split(/\s+/);
}
};
}
var g_TemplateHelper = new TemplateHelper();