Index: ps/trunk/binaries/data/mods/public/globalscripts/Technologies.js =================================================================== --- ps/trunk/binaries/data/mods/public/globalscripts/Technologies.js +++ ps/trunk/binaries/data/mods/public/globalscripts/Technologies.js @@ -44,6 +44,74 @@ } /** + * Derives modifications (to be applied to entities) from a given technology. + * + * @param {Object} techTemplate - The technology template to derive the modifications from. + * @return {Object} containing the relevant modifications. + */ +function DeriveModificationsFromTech(techTemplate) +{ + if (!techTemplate.modifications) + return {}; + + let techMods = {}; + let techAffects = []; + if (techTemplate.affects && techTemplate.affects.length) + for (let affected of techTemplate.affects) + techAffects.push(affected.split(/\s+/)); + else + techAffects.push([]); + + for (let mod of techTemplate.modifications) + { + let affects = techAffects.slice(); + if (mod.affects) + { + let specAffects = mod.affects.split(/\s+/); + for (let a in affects) + affects[a] = affects[a].concat(specAffects); + } + + let newModifier = { "affects": affects }; + for (let idx in mod) + if (idx !== "value" && idx !== "affects") + newModifier[idx] = mod[idx]; + + if (!techMods[mod.value]) + techMods[mod.value] = []; + techMods[mod.value].push(newModifier); + } + return techMods; +} + +/** + * Derives modifications (to be applied to entities) from a provided array + * of technology template data. + * + * @param {array} techsDataArray + * @return {object} containing the combined relevant modifications of all + * the technologies. + */ +function DeriveModificationsFromTechnologies(techsDataArray) +{ + let derivedModifiers = {}; + for (let technology of techsDataArray) + { + if (!technology.reqs) + continue; + + let modifiers = DeriveModificationsFromTech(technology); + for (let modPath in modifiers) + { + if (!derivedModifiers[modPath]) + derivedModifiers[modPath] = []; + derivedModifiers[modPath] = derivedModifiers[modPath].concat(modifiers[modPath]); + } + } + return derivedModifiers; +} + +/** * Returns whether the given modification applies to the entity containing the given class list */ function DoesModificationApply(modification, classes) Index: ps/trunk/binaries/data/mods/public/globalscripts/Templates.js =================================================================== --- ps/trunk/binaries/data/mods/public/globalscripts/Templates.js +++ ps/trunk/binaries/data/mods/public/globalscripts/Templates.js @@ -66,31 +66,67 @@ } /** - * Get information about a template with or without technology modifications. + * Gets the value originating at the value_path as-is, with no modifiers applied. * - * NOTICE: The data returned here should have the same structure as - * the object returned by GetEntityState and GetExtendedEntityState! + * @param {object} template - A valid template as returned from a template loader. + * @param {string} value_path - Route to value within the xml template structure. + * @return {number} + */ +function GetBaseTemplateDataValue(template, value_path) +{ + let current_value = template; + for (let property of value_path.split("/")) + current_value = current_value[property] || 0; + return +current_value; +} + +/** + * Gets the value originating at the value_path with the modifiers dictated by the mod_key applied. * - * @param template A valid template as returned by the template loader. - * @param player An optional player id to get the technology modifications - * of properties. - * @param auraTemplates An object in the form of {key: {auraName: "", auraDescription: ""}} - * @param resources An instance of the Resources prototype + * @param {object} template - A valid template as returned from a template loader. + * @param {string} value_path - Route to value within the xml template structure. + * @param {string} mod_key - Tech modification key, if different from value_path. + * @param {number} player - Optional player id. + * @param {object} modifiers - Value modifiers from auto-researched techs, unit upgrades, + * etc. Optional as only used if no player id provided. + * @return {number} Modifier altered value. */ -function GetTemplateDataHelper(template, player, auraTemplates, resources) +function GetModifiedTemplateDataValue(template, value_path, mod_key, player, modifiers={}) { - // Return data either from template (in tech tree) or sim state (ingame) - let getEntityValue = function(tech_type) { + let current_value = GetBaseTemplateDataValue(template, value_path); + mod_key = mod_key || value_path; - let current_value = template; - for (let property of tech_type.split("/")) - current_value = current_value[property] || 0; - current_value = +current_value; + if (player) + current_value = ApplyValueModificationsToTemplate(mod_key, current_value, player, template); + else if (modifiers) + current_value = GetTechModifiedProperty(modifiers, GetIdentityClasses(template.Identity), mod_key, current_value); - if (!player) - return current_value; + // Using .toFixed() to get around spidermonkey's treatment of numbers (3 * 1.1 = 3.3000000000000003 for instance). + return +current_value.toFixed(8); +} - return ApplyValueModificationsToTemplate(tech_type, current_value, player, template); +/** + * Get information about a template with or without technology modifications. + * + * NOTICE: The data returned here should have the same structure as + * the object returned by GetEntityState and GetExtendedEntityState! + * + * @param {object} template - A valid template as returned by the template loader. + * @param {number} player - An optional player id to get the technology modifications + * of properties. + * @param {object} auraTemplates - In the form of { key: { "auraName": "", "auraDescription": "" } }. + * @param {object} resources - An instance of the Resources prototype. + * @param {object} modifiers - Modifications from auto-researched techs, unit upgrades + * etc. Optional as only used if there's no player + * id provided. + */ +function GetTemplateDataHelper(template, player, auraTemplates, resources, modifiers={}) +{ + // Return data either from template (in tech tree) or sim state (ingame). + // @param {string} value_path - Route to the value within the template. + // @param {string} mod_key - Modification key, if not the same as the value_path. + let getEntityValue = function(value_path, mod_key) { + return GetModifiedTemplateDataValue(template, value_path, mod_key, player, modifiers); }; let ret = {}; @@ -243,8 +279,9 @@ if (template.ResourceGatherer) { ret.resourceGatherRates = {}; + let baseSpeed = getEntityValue("ResourceGatherer/BaseSpeed"); for (let type in template.ResourceGatherer.Rates) - ret.resourceGatherRates[type] = getEntityValue("ResourceGatherer/Rates/"+ type); + ret.resourceGatherRates[type] = getEntityValue("ResourceGatherer/Rates/"+ type) * baseSpeed; } if (template.ResourceTrickle) @@ -309,7 +346,7 @@ "generic": template.Identity.GenericName }; ret.icon = template.Identity.Icon; - ret.tooltip = template.Identity.Tooltip; + ret.tooltip = template.Identity.Tooltip; ret.requiredTechnology = template.Identity.RequiredTechnology; ret.visibleIdentityClasses = GetVisibleIdentityClasses(template.Identity); } @@ -355,26 +392,35 @@ } /** + * 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 + }; +} + +/** * Get information about a technology template. - * @param template A valid template as obtained by loading the tech JSON file. - * @param civ Civilization for which the specific name should be returned. - * @param resources An instance of the Resources prototype. + * @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. */ function GetTechnologyDataHelper(template, civ, resources) { - let ret = {}; + let ret = GetTechnologyBasicDataHelper(template, civ); - // Get specific name for this civ or else the generic specific name - let specific; if (template.specificName) - specific = template.specificName[civ] || template.specificName.generic; - - ret.name = { - "specific": specific, - "generic": template.genericName, - }; - - ret.icon = template.icon ? "technologies/" + template.icon : null; + ret.name.specific = template.specificName[civ] || template.specificName.generic; ret.cost = { "time": template.researchTime ? +template.researchTime : 0 }; for (let type of resources.GetCodes()) @@ -383,10 +429,6 @@ ret.tooltip = template.tooltip; ret.requirementsTooltip = template.requirementsTooltip || ""; - ret.reqs = DeriveTechnologyRequirements(template, civ); - - ret.description = template.description; - return ret; } Index: ps/trunk/binaries/data/mods/public/gui/structree/helper.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/structree/helper.js +++ ps/trunk/binaries/data/mods/public/gui/structree/helper.js @@ -1,7 +1,13 @@ +const g_TechnologyPath = "simulation/data/technologies/"; +const g_AuraPath = "simulation/data/auras/"; + var g_TemplateData = {}; var g_TechnologyData = {}; var g_AuraData = {}; +// Must be defined after g_TechnologyData object is declared. +const g_AutoResearchTechList = findAllAutoResearchedTechs(); + function loadTemplate(templateName) { if (!(templateName in g_TemplateData)) @@ -24,9 +30,8 @@ { if (!(templateName in g_TechnologyData)) { - var filename = "simulation/data/technologies/" + templateName + ".json"; - var data = Engine.ReadJSONFile(filename); - translateObjectKeys(data, ["genericName", "tooltip"]); + let data = Engine.ReadJSONFile(g_TechnologyPath + templateName + ".json"); + translateObjectKeys(data, ["genericName", "tooltip", "description"]); g_TechnologyData[templateName] = data; } @@ -38,8 +43,7 @@ { if (!(templateName in g_AuraData)) { - let filename = "simulation/data/auras/" + templateName + ".json"; - let data = Engine.ReadJSONFile(filename); + let data = Engine.ReadJSONFile(g_AuraPath + templateName + ".json"); translateObjectKeys(data, ["auraName", "auraDescription"]); g_AuraData[templateName] = data; @@ -48,6 +52,32 @@ return g_AuraData[templateName]; } +function findAllAutoResearchedTechs() +{ + let techList = []; + + for (let filename of Engine.BuildDirEntList(g_TechnologyPath, "*.json", true)) + { + // -5 to strip off the file extension + let templateName = filename.slice(g_TechnologyPath.length, -5); + let data = loadTechData(templateName); + + if (data && data.autoResearch) + techList.push(templateName); + } + + return techList; +} + +function deriveModifications(techList) +{ + let techData = []; + for (let techName of techList) + techData.push(GetTechnologyBasicDataHelper(loadTechData(techName), g_SelectedCiv)); + + return DeriveModificationsFromTechnologies(techData); +} + /** * This is needed because getEntityCostTooltip in tooltip.js needs to get * the template data of the different wallSet pieces. In the session this @@ -56,7 +86,7 @@ function GetTemplateData(templateName) { var template = loadTemplate(templateName); - return GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData); + return GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData, g_CurrentModifiers); } /** Index: ps/trunk/binaries/data/mods/public/gui/structree/load.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/structree/load.js +++ ps/trunk/binaries/data/mods/public/gui/structree/load.js @@ -4,7 +4,7 @@ return null; let template = loadTemplate(templateName); - let unit = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData); + let unit = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData, g_CurrentModifiers); if (template.ProductionQueue) { @@ -46,7 +46,7 @@ function loadStructure(templateName) { let template = loadTemplate(templateName); - let structure = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData); + let structure = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData, g_CurrentModifiers); structure.production = { "technology": [], Index: ps/trunk/binaries/data/mods/public/gui/structree/structree.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/structree/structree.js +++ ps/trunk/binaries/data/mods/public/gui/structree/structree.js @@ -8,6 +8,7 @@ var g_Lists = {}; var g_CivData = {}; var g_SelectedCiv = ""; +var g_CurrentModifiers = {}; var g_CallbackSet = false; var g_ResourceData = new Resources(); @@ -49,6 +50,8 @@ g_SelectedCiv = civCode; + g_CurrentModifiers = deriveModifications(g_AutoResearchTechList); + // If a buildList already exists, then this civ has already been parsed if (g_CivData[g_SelectedCiv].buildList) { Index: ps/trunk/binaries/data/mods/public/simulation/components/TechnologyManager.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/TechnologyManager.js +++ ps/trunk/binaries/data/mods/public/simulation/components/TechnologyManager.js @@ -270,48 +270,18 @@ // store the modifications in an easy to access structure if (template.modifications) { - var affects = []; - if (template.affects && template.affects.length > 0) + let derivedModifiers = DeriveModificationsFromTech(template); + for (let modifierPath in derivedModifiers) { - for (let affect of template.affects) - { - // Put the list of classes into an array for convenient access - affects.push(affect.split(/\s+/)); - } - } - else - { - affects.push([]); - } - - // We add an item to this.modifications for every modification in the template.modifications array - for (var i in template.modifications) - { - var modification = template.modifications[i]; - if (!this.modifications[modification.value]) - this.modifications[modification.value] = []; - - var modAffects = affects.slice(); - if (modification.affects) - { - var extraAffects = modification.affects.split(/\s+/); - for (var a in modAffects) - modAffects[a] = modAffects[a].concat(extraAffects); - } - - var mod = {"affects": modAffects}; - - // copy the modification data into our new data structure - for (var j in modification) - if (j !== "value" && j !== "affects") - mod[j] = modification[j]; + if (!this.modifications[modifierPath]) + this.modifications[modifierPath] = []; + this.modifications[modifierPath] = this.modifications[modifierPath].concat(derivedModifiers[modifierPath]); - this.modifications[modification.value].push(mod); - var component = modification.value.split("/")[0]; + let component = modifierPath.split("/")[0]; if (!modifiedComponents[component]) modifiedComponents[component] = []; - modifiedComponents[component].push(modification.value); - this.modificationCache[modification.value] = {}; + modifiedComponents[component].push(modifierPath); + this.modificationCache[modifierPath] = {}; } }