LCOV - code coverage report
Current view: top level - globalscripts - Templates.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 94 229 41.0 %
Date: 2023-04-02 12:52:40 Functions: 9 16 56.2 %

          Line data    Source code
       1             : /**
       2             :  * Loads history and gameplay data of all civs.
       3             :  *
       4             :  * @param selectableOnly {boolean} - Only load civs that can be selected
       5             :  *        in the gamesetup. Scenario maps might set non-selectable civs.
       6             :  */
       7             : function loadCivFiles(selectableOnly)
       8             : {
       9           6 :         let propertyNames = [
      10             :                 "Code", "Culture", "Music", "CivBonuses", "StartEntities",
      11             :                 "AINames", "SkirmishReplacements", "SelectableInGameSetup"];
      12             : 
      13           6 :         let civData = {};
      14             : 
      15           6 :         for (let filename of Engine.ListDirectoryFiles("simulation/data/civs/", "*.json", false))
      16             :         {
      17          84 :                 let data = Engine.ReadJSONFile(filename);
      18             : 
      19          84 :                 for (let prop of propertyNames)
      20         672 :                         if (data[prop] === undefined)
      21           0 :                                 throw new Error(filename + " doesn't contain " + prop);
      22             : 
      23          84 :                 if (selectableOnly && !data.SelectableInGameSetup)
      24           0 :                         continue;
      25             : 
      26          84 :                 const template = Engine.GetTemplate("special/players/" + data.Code);
      27          84 :                 data.Name = template.Identity.GenericName;
      28          84 :                 data.Emblem = "session/portraits/" + template.Identity.Icon;
      29          84 :                 data.History = template.Identity.History;
      30             : 
      31          84 :                 civData[data.Code] = data;
      32             :         }
      33             : 
      34           6 :         return civData;
      35             : }
      36             : 
      37             : /**
      38             :  * @return {string[]} - All the classes for this identity template.
      39             :  */
      40             : function GetIdentityClasses(template)
      41             : {
      42          20 :         let classString = "";
      43             : 
      44          20 :         if (template.Classes && template.Classes._string)
      45          13 :                 classString += " " + template.Classes._string;
      46             : 
      47          20 :         if (template.VisibleClasses && template.VisibleClasses._string)
      48           4 :                 classString += " " + template.VisibleClasses._string;
      49             : 
      50          20 :         if (template.Rank)
      51           2 :                 classString += " " + template.Rank;
      52             : 
      53          20 :         return classString.length > 1 ? classString.substring(1).split(" ") : [];
      54             : }
      55             : 
      56             : /**
      57             :  * Gets an array with all classes for this identity template
      58             :  * that should be shown in the GUI
      59             :  */
      60             : function GetVisibleIdentityClasses(template)
      61             : {
      62          14 :         return template.VisibleClasses && template.VisibleClasses._string ? template.VisibleClasses._string.split(" ") : [];
      63             : }
      64             : 
      65             : /**
      66             :  * Check if a given list of classes matches another list of classes.
      67             :  * Useful f.e. for checking identity classes.
      68             :  *
      69             :  * @param classes - List of the classes to check against.
      70             :  * @param match - Either a string in the form
      71             :  *     "Class1 Class2+Class3"
      72             :  * where spaces are handled as OR and '+'-signs as AND,
      73             :  * and ! is handled as NOT, thus Class1+!Class2 = Class1 AND NOT Class2.
      74             :  * Or a list in the form
      75             :  *     [["Class1"], ["Class2", "Class3"]]
      76             :  * where the outer list is combined as OR, and the inner lists are AND-ed.
      77             :  * Or a hybrid format containing a list of strings, where the list is
      78             :  * combined as OR, and the strings are split by space and '+' and AND-ed.
      79             :  *
      80             :  * @return undefined if there are no classes or no match object
      81             :  * true if the the logical combination in the match object matches the classes
      82             :  * false otherwise.
      83             :  */
      84             : function MatchesClassList(classes, match)
      85             : {
      86         262 :         if (!match || !classes)
      87           3 :                 return undefined;
      88             :         // Transform the string to an array
      89         259 :         if (typeof match == "string")
      90         121 :                 match = match.split(/\s+/);
      91             : 
      92         259 :         for (let sublist of match)
      93             :         {
      94             :                 // If the elements are still strings, split them by space or by '+'
      95         305 :                 if (typeof sublist == "string")
      96         292 :                         sublist = sublist.split(/[+\s]+/);
      97         342 :                 if (sublist.every(c => (c[0] == "!" && classes.indexOf(c.substr(1)) == -1) ||
      98             :                                        (c[0] != "!" && classes.indexOf(c) != -1)))
      99         146 :                         return true;
     100             :         }
     101             : 
     102         113 :         return false;
     103             : }
     104             : 
     105             : /**
     106             :  * Gets the value originating at the value_path as-is, with no modifiers applied.
     107             :  *
     108             :  * @param {Object} template - A valid template as returned from a template loader.
     109             :  * @param {string} value_path - Route to value within the xml template structure.
     110             :  * @param {number} default_value - A value to use if one is not specified in the template.
     111             :  * @return {number}
     112             :  */
     113             : function GetBaseTemplateDataValue(template, value_path, default_value)
     114             : {
     115          12 :         let current_value = template;
     116          12 :         for (let property of value_path.split("/"))
     117          44 :                 current_value = current_value[property] || default_value;
     118          12 :         return +current_value;
     119             : }
     120             : 
     121             : /**
     122             :  * Gets the value originating at the value_path with the modifiers dictated by the mod_key applied.
     123             :  *
     124             :  * @param {Object} template - A valid template as returned from a template loader.
     125             :  * @param {string} value_path - Route to value within the xml template structure.
     126             :  * @param {string} mod_key - Tech modification key, if different from value_path.
     127             :  * @param {number} player - Optional player id.
     128             :  * @param {Object} modifiers - Value modifiers from auto-researched techs, unit upgrades,
     129             :  *                             etc. Optional as only used if no player id provided.
     130             :  * @param {number} default_value - A value to use if one is not specified in the template.
     131             :  * @return {number} Modifier altered value.
     132             :  */
     133             : function GetModifiedTemplateDataValue(template, value_path, mod_key, player, modifiers={}, default_value)
     134             : {
     135          12 :         let current_value = GetBaseTemplateDataValue(template, value_path, default_value);
     136          12 :         mod_key = mod_key || value_path;
     137             : 
     138          12 :         if (player)
     139           6 :                 current_value = ApplyValueModificationsToTemplate(mod_key, current_value, player, template);
     140           6 :         else if (modifiers && modifiers[mod_key])
     141           0 :                 current_value = GetTechModifiedProperty(modifiers[mod_key], GetIdentityClasses(template.Identity), current_value);
     142             : 
     143             :         // Using .toFixed() to get around spidermonkey's treatment of numbers (3 * 1.1 = 3.3000000000000003 for instance).
     144          12 :         return +current_value.toFixed(8);
     145             : }
     146             : 
     147             : /**
     148             :  * Get information about a template with or without technology modifications.
     149             :  *
     150             :  * NOTICE: The data returned here should have the same structure as
     151             :  * the object returned by GetEntityState and GetExtendedEntityState!
     152             :  *
     153             :  * @param {Object} template - A valid template as returned by the template loader.
     154             :  * @param {number} player - An optional player id to get the technology modifications
     155             :  *                          of properties.
     156             :  * @param {Object} auraTemplates - In the form of { key: { "auraName": "", "auraDescription": "" } }.
     157             :  * @param {Object} resources - An instance of the Resources class.
     158             :  * @param {Object} modifiers - Modifications from auto-researched techs, unit upgrades
     159             :  *                             etc. Optional as only used if there's no player
     160             :  *                             id provided.
     161             :  */
     162             : function GetTemplateDataHelper(template, player, auraTemplates, resources, modifiers = {})
     163             : {
     164             :         // Return data either from template (in tech tree) or sim state (ingame).
     165             :         // @param {string} value_path - Route to the value within the template.
     166             :         // @param {string} mod_key - Modification key, if not the same as the value_path.
     167             :         // @param {number} default_value - A value to use if one is not specified in the template.
     168           4 :         const getEntityValue = function(value_path, mod_key, default_value = 0) {
     169          12 :                 return GetModifiedTemplateDataValue(template, value_path, mod_key, player, modifiers, default_value);
     170             :         };
     171             : 
     172           4 :         let ret = {};
     173             : 
     174           4 :         if (template.Resistance)
     175             :         {
     176             :                 // Don't show Foundation resistance.
     177           0 :                 ret.resistance = {};
     178           0 :                 if (template.Resistance.Entity)
     179             :                 {
     180           0 :                         if (template.Resistance.Entity.Damage)
     181             :                         {
     182           0 :                                 ret.resistance.Damage = {};
     183           0 :                                 for (let damageType in template.Resistance.Entity.Damage)
     184           0 :                                         ret.resistance.Damage[damageType] = getEntityValue("Resistance/Entity/Damage/" + damageType);
     185             :                         }
     186           0 :                         if (template.Resistance.Entity.Capture)
     187           0 :                                 ret.resistance.Capture = getEntityValue("Resistance/Entity/Capture");
     188           0 :                         if (template.Resistance.Entity.ApplyStatus)
     189             :                         {
     190           0 :                                 ret.resistance.ApplyStatus = {};
     191           0 :                                 for (let statusEffect in template.Resistance.Entity.ApplyStatus)
     192           0 :                                         ret.resistance.ApplyStatus[statusEffect] = {
     193             :                                                 "blockChance": getEntityValue("Resistance/Entity/ApplyStatus/" + statusEffect + "/BlockChance"),
     194             :                                                 "duration": getEntityValue("Resistance/Entity/ApplyStatus/" + statusEffect + "/Duration")
     195             :                                         };
     196             :                         }
     197             :                 }
     198             :         }
     199             : 
     200           4 :         let getAttackEffects = (temp, path) => {
     201           0 :                 let effects = {};
     202           0 :                 if (temp.Capture)
     203           0 :                         effects.Capture = getEntityValue(path + "/Capture");
     204             : 
     205           0 :                 if (temp.Damage)
     206             :                 {
     207           0 :                         effects.Damage = {};
     208           0 :                         for (let damageType in temp.Damage)
     209           0 :                                 effects.Damage[damageType] = getEntityValue(path + "/Damage/" + damageType);
     210             :                 }
     211             : 
     212           0 :                 if (temp.ApplyStatus)
     213           0 :                         effects.ApplyStatus = temp.ApplyStatus;
     214             : 
     215           0 :                 return effects;
     216             :         };
     217             : 
     218           4 :         if (template.Attack)
     219             :         {
     220           0 :                 ret.attack = {};
     221           0 :                 for (let type in template.Attack)
     222             :                 {
     223           0 :                         let getAttackStat = function(stat) {
     224           0 :                                 return getEntityValue("Attack/" + type + "/" + stat);
     225             :                         };
     226             : 
     227           0 :                         ret.attack[type] = {
     228             :                                 "attackName": {
     229             :                                         "name": template.Attack[type].AttackName._string || template.Attack[type].AttackName,
     230             :                                         "context": template.Attack[type].AttackName["@context"]
     231             :                                 },
     232             :                                 "minRange": getAttackStat("MinRange"),
     233             :                                 "maxRange": getAttackStat("MaxRange"),
     234             :                                 "yOrigin": getAttackStat("Origin/Y")
     235             :                         };
     236             : 
     237           0 :                         ret.attack[type].elevationAdaptedRange = Math.sqrt(ret.attack[type].maxRange *
     238             :                                 (2 * ret.attack[type].yOrigin + ret.attack[type].maxRange));
     239             : 
     240           0 :                         ret.attack[type].repeatTime = getAttackStat("RepeatTime");
     241           0 :                         if (template.Attack[type].Projectile)
     242           0 :                                 ret.attack[type].friendlyFire = template.Attack[type].Projectile.FriendlyFire == "true";
     243             : 
     244           0 :                         Object.assign(ret.attack[type], getAttackEffects(template.Attack[type], "Attack/" + type));
     245             : 
     246           0 :                         if (template.Attack[type].Splash)
     247             :                         {
     248           0 :                                 ret.attack[type].splash = {
     249             :                                         "friendlyFire": template.Attack[type].Splash.FriendlyFire != "false",
     250             :                                         "shape": template.Attack[type].Splash.Shape,
     251             :                                 };
     252           0 :                                 Object.assign(ret.attack[type].splash, getAttackEffects(template.Attack[type].Splash, "Attack/" + type + "/Splash"));
     253             :                         }
     254             :                 }
     255             :         }
     256             : 
     257           4 :         if (template.DeathDamage)
     258             :         {
     259           0 :                 ret.deathDamage = {
     260             :                         "friendlyFire": template.DeathDamage.FriendlyFire != "false",
     261             :                 };
     262             : 
     263           0 :                 Object.assign(ret.deathDamage, getAttackEffects(template.DeathDamage, "DeathDamage"));
     264             :         }
     265             : 
     266           4 :         if (template.Auras && auraTemplates)
     267             :         {
     268           0 :                 ret.auras = {};
     269           0 :                 for (let auraID of template.Auras._string.split(/\s+/))
     270           0 :                         ret.auras[auraID] = GetAuraDataHelper(auraTemplates[auraID]);
     271             :         }
     272             : 
     273           4 :         if (template.BuildingAI)
     274           0 :                 ret.buildingAI = {
     275             :                         "defaultArrowCount": Math.round(getEntityValue("BuildingAI/DefaultArrowCount")),
     276             :                         "garrisonArrowMultiplier": getEntityValue("BuildingAI/GarrisonArrowMultiplier"),
     277             :                         "maxArrowCount": Math.round(getEntityValue("BuildingAI/MaxArrowCount"))
     278             :                 };
     279             : 
     280           4 :         if (template.BuildRestrictions)
     281             :         {
     282             :                 // required properties
     283           0 :                 ret.buildRestrictions = {
     284             :                         "placementType": template.BuildRestrictions.PlacementType,
     285             :                         "territory": template.BuildRestrictions.Territory,
     286             :                         "category": template.BuildRestrictions.Category,
     287             :                 };
     288             : 
     289             :                 // optional properties
     290           0 :                 if (template.BuildRestrictions.Distance)
     291             :                 {
     292           0 :                         ret.buildRestrictions.distance = {
     293             :                                 "fromClass": template.BuildRestrictions.Distance.FromClass,
     294             :                         };
     295             : 
     296           0 :                         if (template.BuildRestrictions.Distance.MinDistance)
     297           0 :                                 ret.buildRestrictions.distance.min = getEntityValue("BuildRestrictions/Distance/MinDistance");
     298             : 
     299           0 :                         if (template.BuildRestrictions.Distance.MaxDistance)
     300           0 :                                 ret.buildRestrictions.distance.max = getEntityValue("BuildRestrictions/Distance/MaxDistance");
     301             :                 }
     302             :         }
     303             : 
     304           4 :         if (template.TrainingRestrictions)
     305             :         {
     306           0 :                 ret.trainingRestrictions = {
     307             :                         "category": template.TrainingRestrictions.Category
     308             :                 };
     309           0 :                 if (template.TrainingRestrictions.MatchLimit)
     310           0 :                         ret.trainingRestrictions.matchLimit = +template.TrainingRestrictions.MatchLimit;
     311             :         }
     312             : 
     313           4 :         if (template.Cost)
     314             :         {
     315           0 :                 ret.cost = {};
     316           0 :                 for (let resCode in template.Cost.Resources)
     317           0 :                         ret.cost[resCode] = getEntityValue("Cost/Resources/" + resCode);
     318             : 
     319           0 :                 if (template.Cost.Population)
     320           0 :                         ret.cost.population = getEntityValue("Cost/Population");
     321             : 
     322           0 :                 if (template.Cost.BuildTime)
     323           0 :                         ret.cost.time = getEntityValue("Cost/BuildTime");
     324             :         }
     325             : 
     326           4 :         if (template.Footprint)
     327             :         {
     328           0 :                 ret.footprint = { "height": template.Footprint.Height };
     329             : 
     330           0 :                 if (template.Footprint.Square)
     331           0 :                         ret.footprint.square = {
     332             :                                 "width": +template.Footprint.Square["@width"],
     333             :                                 "depth": +template.Footprint.Square["@depth"]
     334             :                         };
     335           0 :                 else if (template.Footprint.Circle)
     336           0 :                         ret.footprint.circle = { "radius": +template.Footprint.Circle["@radius"] };
     337             :                 else
     338           0 :                         warn("GetTemplateDataHelper(): Unrecognized Footprint type");
     339             :         }
     340             : 
     341           4 :         if (template.Garrisonable)
     342           0 :                 ret.garrisonable = {
     343             :                         "size": getEntityValue("Garrisonable/Size")
     344             :                 };
     345             : 
     346           4 :         if (template.GarrisonHolder)
     347             :         {
     348           0 :                 ret.garrisonHolder = {
     349             :                         "buffHeal": getEntityValue("GarrisonHolder/BuffHeal")
     350             :                 };
     351             : 
     352           0 :                 if (template.GarrisonHolder.Max)
     353           0 :                         ret.garrisonHolder.capacity = getEntityValue("GarrisonHolder/Max");
     354             :         }
     355             : 
     356           4 :         if (template.Heal)
     357           0 :                 ret.heal = {
     358             :                         "health": getEntityValue("Heal/Health"),
     359             :                         "range": getEntityValue("Heal/Range"),
     360             :                         "interval": getEntityValue("Heal/Interval")
     361             :                 };
     362             : 
     363           4 :         if (template.ResourceGatherer)
     364             :         {
     365           0 :                 ret.resourceGatherRates = {};
     366           0 :                 let baseSpeed = getEntityValue("ResourceGatherer/BaseSpeed");
     367           0 :                 for (let type in template.ResourceGatherer.Rates)
     368           0 :                         ret.resourceGatherRates[type] = getEntityValue("ResourceGatherer/Rates/"+ type) * baseSpeed;
     369             :         }
     370             : 
     371           4 :         if (template.ResourceDropsite)
     372           0 :                 ret.resourceDropsite = {
     373             :                         "types": template.ResourceDropsite.Types.split(" ")
     374             :                 };
     375             : 
     376           4 :         if (template.ResourceTrickle)
     377             :         {
     378           0 :                 ret.resourceTrickle = {
     379             :                         "interval": +template.ResourceTrickle.Interval,
     380             :                         "rates": {}
     381             :                 };
     382           0 :                 for (let type in template.ResourceTrickle.Rates)
     383           0 :                         ret.resourceTrickle.rates[type] = getEntityValue("ResourceTrickle/Rates/" + type);
     384             :         }
     385             : 
     386           4 :         if (template.Loot)
     387             :         {
     388           0 :                 ret.loot = {};
     389           0 :                 for (let type in template.Loot)
     390           0 :                         ret.loot[type] = getEntityValue("Loot/"+ type);
     391             :         }
     392             : 
     393           4 :         if (template.Obstruction)
     394             :         {
     395           0 :                 ret.obstruction = {
     396             :                         "active": ("" + template.Obstruction.Active == "true"),
     397             :                         "blockMovement": ("" + template.Obstruction.BlockMovement == "true"),
     398             :                         "blockPathfinding": ("" + template.Obstruction.BlockPathfinding == "true"),
     399             :                         "blockFoundation": ("" + template.Obstruction.BlockFoundation == "true"),
     400             :                         "blockConstruction": ("" + template.Obstruction.BlockConstruction == "true"),
     401             :                         "disableBlockMovement": ("" + template.Obstruction.DisableBlockMovement == "true"),
     402             :                         "disableBlockPathfinding": ("" + template.Obstruction.DisableBlockPathfinding == "true"),
     403             :                         "shape": {}
     404             :                 };
     405             : 
     406           0 :                 if (template.Obstruction.Static)
     407             :                 {
     408           0 :                         ret.obstruction.shape.type = "static";
     409           0 :                         ret.obstruction.shape.width = +template.Obstruction.Static["@width"];
     410           0 :                         ret.obstruction.shape.depth = +template.Obstruction.Static["@depth"];
     411             :                 }
     412           0 :                 else if (template.Obstruction.Unit)
     413             :                 {
     414           0 :                         ret.obstruction.shape.type = "unit";
     415           0 :                         ret.obstruction.shape.radius = +template.Obstruction.Unit["@radius"];
     416             :                 }
     417             :                 else
     418           0 :                         ret.obstruction.shape.type = "cluster";
     419             :         }
     420             : 
     421           4 :         if (template.Pack)
     422           0 :                 ret.pack = {
     423             :                         "state": template.Pack.State,
     424             :                         "time": getEntityValue("Pack/Time"),
     425             :                 };
     426             : 
     427           4 :         if (template.Population && template.Population.Bonus)
     428           0 :                 ret.population = {
     429             :                         "bonus": getEntityValue("Population/Bonus")
     430             :                 };
     431             : 
     432           4 :         if (template.Health)
     433           0 :                 ret.health = Math.round(getEntityValue("Health/Max"));
     434             : 
     435           4 :         if (template.Identity)
     436             :         {
     437           4 :                 ret.selectionGroupName = template.Identity.SelectionGroupName;
     438           4 :                 ret.name = {
     439             :                         "specific": (template.Identity.SpecificName || template.Identity.GenericName),
     440             :                         "generic": template.Identity.GenericName
     441             :                 };
     442           4 :                 ret.icon = template.Identity.Icon;
     443           4 :                 ret.tooltip = template.Identity.Tooltip;
     444           4 :                 ret.requirements = template.Identity.Requirements;
     445           4 :                 ret.visibleIdentityClasses = GetVisibleIdentityClasses(template.Identity);
     446           4 :                 ret.nativeCiv = template.Identity.Civ;
     447             :         }
     448             : 
     449           4 :         if (template.UnitMotion)
     450             :         {
     451           0 :                 const walkSpeed = getEntityValue("UnitMotion/WalkSpeed");
     452           0 :                 ret.speed = {
     453             :                         "walk": walkSpeed,
     454             :                         "run": walkSpeed,
     455             :                         "acceleration": getEntityValue("UnitMotion/Acceleration")
     456             :                 };
     457           0 :                 if (template.UnitMotion.RunMultiplier)
     458           0 :                         ret.speed.run *= getEntityValue("UnitMotion/RunMultiplier");
     459             :         }
     460             : 
     461           4 :         if (template.Upgrade)
     462             :         {
     463           4 :                 ret.upgrades = [];
     464           4 :                 for (let upgradeName in template.Upgrade)
     465             :                 {
     466           4 :                         let upgrade = template.Upgrade[upgradeName];
     467             : 
     468           4 :                         let cost = {};
     469           4 :                         if (upgrade.Cost)
     470           4 :                                 for (let res in upgrade.Cost)
     471           8 :                                         cost[res] = getEntityValue("Upgrade/" + upgradeName + "/Cost/" + res, "Upgrade/Cost/" + res);
     472           4 :                         if (upgrade.Time)
     473           4 :                                 cost.time = getEntityValue("Upgrade/" + upgradeName + "/Time", "Upgrade/Time");
     474             : 
     475           4 :                         ret.upgrades.push({
     476             :                                 "entity": upgrade.Entity,
     477             :                                 "tooltip": upgrade.Tooltip,
     478             :                                 "cost": cost,
     479             :                                 "icon": upgrade.Icon,
     480             :                                 "requirements": upgrade.Requirements
     481             :                         });
     482             :                 }
     483             :         }
     484             : 
     485           4 :         if (template.Researcher)
     486             :         {
     487           0 :                 ret.techCostMultiplier = {};
     488           0 :                 for (const res of resources.GetCodes().concat(["time"]))
     489           0 :                         ret.techCostMultiplier[res] = getEntityValue("Researcher/TechCostMultiplier/" + res, null, 1);
     490             :         }
     491             : 
     492           4 :         if (template.Trader)
     493           0 :                 ret.trader = {
     494             :                         "GainMultiplier": getEntityValue("Trader/GainMultiplier")
     495             :                 };
     496             : 
     497           4 :         if (template.Treasure)
     498             :         {
     499           0 :                 ret.treasure = {
     500             :                         "collectTime": getEntityValue("Treasure/CollectTime"),
     501             :                         "resources": {}
     502             :                 };
     503           0 :                 for (let resource in template.Treasure.Resources)
     504           0 :                         ret.treasure.resources[resource] = getEntityValue("Treasure/Resources/" + resource);
     505             :         }
     506             : 
     507           4 :         if (template.TurretHolder)
     508           0 :                 ret.turretHolder = {
     509             :                         "turretPoints": template.TurretHolder.TurretPoints
     510             :                 };
     511             : 
     512           4 :         if (template.Upkeep)
     513             :         {
     514           0 :                 ret.upkeep = {
     515             :                         "interval": +template.Upkeep.Interval,
     516             :                         "rates": {}
     517             :                 };
     518           0 :                 for (let type in template.Upkeep.Rates)
     519           0 :                         ret.upkeep.rates[type] = getEntityValue("Upkeep/Rates/" + type);
     520             :         }
     521             : 
     522           4 :         if (template.WallSet)
     523             :         {
     524           0 :                 ret.wallSet = {
     525             :                         "templates": {
     526             :                                 "tower": template.WallSet.Templates.Tower,
     527             :                                 "gate": template.WallSet.Templates.Gate,
     528             :                                 "fort": template.WallSet.Templates.Fort || "structures/" + template.Identity.Civ + "/fortress",
     529             :                                 "long": template.WallSet.Templates.WallLong,
     530             :                                 "medium": template.WallSet.Templates.WallMedium,
     531             :                                 "short": template.WallSet.Templates.WallShort
     532             :                         },
     533             :                         "maxTowerOverlap": +template.WallSet.MaxTowerOverlap,
     534             :                         "minTowerOverlap": +template.WallSet.MinTowerOverlap
     535             :                 };
     536           0 :                 if (template.WallSet.Templates.WallEnd)
     537           0 :                         ret.wallSet.templates.end = template.WallSet.Templates.WallEnd;
     538           0 :                 if (template.WallSet.Templates.WallCurves)
     539           0 :                         ret.wallSet.templates.curves = template.WallSet.Templates.WallCurves.split(/\s+/);
     540             :         }
     541             : 
     542           4 :         if (template.WallPiece)
     543           0 :                 ret.wallPiece = {
     544             :                         "length": +template.WallPiece.Length,
     545             :                         "angle": +(template.WallPiece.Orientation || 1) * Math.PI,
     546             :                         "indent": +(template.WallPiece.Indent || 0),
     547             :                         "bend": +(template.WallPiece.Bend || 0) * Math.PI
     548             :                 };
     549             : 
     550           4 :         return ret;
     551             : }
     552             : 
     553             : /**
     554             :  * Get basic information about a technology template.
     555             :  * @param {Object} template - A valid template as obtained by loading the tech JSON file.
     556             :  * @param {string} civ - Civilization for which the tech requirements should be calculated.
     557             :  */
     558             : function GetTechnologyBasicDataHelper(template, civ)
     559             : {
     560           0 :         return {
     561             :                 "name": {
     562             :                         "generic": template.genericName
     563             :                 },
     564             :                 "icon": template.icon ? "technologies/" + template.icon : undefined,
     565             :                 "description": template.description,
     566             :                 "reqs": DeriveTechnologyRequirements(template, civ),
     567             :                 "modifications": template.modifications,
     568             :                 "affects": template.affects,
     569             :                 "replaces": template.replaces
     570             :         };
     571             : }
     572             : 
     573             : /**
     574             :  * Get information about a technology template.
     575             :  * @param {Object} template - A valid template as obtained by loading the tech JSON file.
     576             :  * @param {string} civ - Civilization for which the specific name and tech requirements should be returned.
     577             :  * @param {Object} resources - An instance of the Resources class.
     578             :  */
     579             : function GetTechnologyDataHelper(template, civ, resources)
     580             : {
     581           0 :         let ret = GetTechnologyBasicDataHelper(template, civ);
     582             : 
     583           0 :         if (template.specificName)
     584           0 :                 ret.name.specific = template.specificName[civ] || template.specificName.generic;
     585             : 
     586           0 :         ret.cost = { "time": template.researchTime ? +template.researchTime : 0 };
     587           0 :         for (let type of resources.GetCodes())
     588           0 :                 ret.cost[type] = +(template.cost && template.cost[type] || 0);
     589             : 
     590           0 :         ret.tooltip = template.tooltip;
     591           0 :         ret.requirementsTooltip = template.requirementsTooltip || "";
     592             : 
     593           0 :         return ret;
     594             : }
     595             : 
     596             : /**
     597             :  * Get information about an aura template.
     598             :  * @param {object} template - A valid template as obtained by loading the aura JSON file.
     599             :  */
     600             : function GetAuraDataHelper(template)
     601             : {
     602           0 :         return {
     603             :                 "name": {
     604             :                         "generic": template.auraName,
     605             :                 },
     606             :                 "description": template.auraDescription || null,
     607             :                 "modifications": template.modifications,
     608             :                 "radius": template.radius || null,
     609             :         };
     610             : }
     611             : 
     612             : function calculateCarriedResources(carriedResources, tradingGoods)
     613             : {
     614           0 :         var resources = {};
     615             : 
     616           0 :         if (carriedResources)
     617           0 :                 for (let resource of carriedResources)
     618           0 :                         resources[resource.type] = (resources[resource.type] || 0) + resource.amount;
     619             : 
     620           0 :         if (tradingGoods && tradingGoods.amount)
     621           0 :                 resources[tradingGoods.type] =
     622             :                         (resources[tradingGoods.type] || 0) +
     623             :                         (tradingGoods.amount.traderGain || 0) +
     624             :                         (tradingGoods.amount.market1Gain || 0) +
     625             :                         (tradingGoods.amount.market2Gain || 0);
     626             : 
     627           0 :         return resources;
     628             : }
     629             : 
     630             : /**
     631             :  * Remove filter prefix (mirage, corpse, etc) from template name.
     632             :  *
     633             :  * ie. filter|dir/to/template -> dir/to/template
     634             :  */
     635             : function removeFiltersFromTemplateName(templateName)
     636             : {
     637           0 :         return templateName.split("|").pop();
     638             : }

Generated by: LCOV version 1.14