/** * Calculates gather rates. * * All available rates that have a value greater than 0 are summed and averaged */ function getGatherRates(templateName) { // TODO: It would be nice to use the gather rates present in the templates // instead of hard-coding the possible rates here. // We ignore ruins here, as those are not that common and would skew the results var types = { "food": ["food", "food.fish", "food.fruit", "food.grain", "food.meat", "food.milk"], "wood": ["wood", "wood.tree"], "stone": ["stone", "stone.rock"], "metal": ["metal", "metal.ore"] }; var rates = {}; for (let type in types) { let count, rate; [rate, count] = types[type].reduce(function(sum, t) { let r = +fetchValue(templateName, "ResourceGatherer/Rates/"+t); return [sum[0] + (r > 0 ? r : 0), sum[1] + (r > 0 ? 1 : 0)]; }, [0, 0]); if (rate > 0) rates[type] = Math.round(rate / count * 100) / 100; } if (!Object.keys(rates).length) return null; return rates; } function loadUnit(templateName) { if (!Engine.TemplateExists(templateName)) return null; var template = loadTemplate(templateName); var unit = GetTemplateDataHelper(template); unit.phase = false; if (unit.requiredTechnology) { if (unit.requiredTechnology.slice(0, 5) == "phase") unit.phase = unit.requiredTechnology; else if (unit.requiredTechnology.length) unit.required = unit.requiredTechnology; } unit.gather = getGatherRates(templateName); if (template.Heal) unit.healer = { "Range": +template.Heal.Range || 0, "HP": +template.Heal.HP || 0, "Rate": +template.Heal.Rate || 0 }; if (template.Builder && template.Builder.Entities._string) for (let build of template.Builder.Entities._string.split(" ")) { build = build.replace("{civ}", g_SelectedCiv); if (g_Lists.structures.indexOf(build) < 0) g_Lists.structures.push(build); } return unit; } function loadStructure(templateName) { var template = loadTemplate(templateName); var structure = GetTemplateDataHelper(template); structure.phase = false; if (structure.requiredTechnology) { if (structure.requiredTechnology.slice(0, 5) == "phase") structure.phase = structure.requiredTechnology; else if (structure.requiredTechnology.length) structure.required = structure.requiredTechnology; } structure.production = { "technology": [], "units": [] }; if (template.ProductionQueue) { if (template.ProductionQueue.Entities && template.ProductionQueue.Entities._string) for (let build of template.ProductionQueue.Entities._string.split(" ")) { build = build.replace("{civ}", g_SelectedCiv); structure.production.units.push(build); if (g_Lists.units.indexOf(build) < 0) g_Lists.units.push(build); } if (template.ProductionQueue.Technologies && template.ProductionQueue.Technologies._string) for (let research of template.ProductionQueue.Technologies._string.split(" ")) { structure.production.technology.push(research); if (g_Lists.techs.indexOf(research) < 0) g_Lists.techs.push(research); } } if (structure.wallSet) { structure.wallset = {}; // Note: Assume wall segments of all lengths have the same armor structure.armour = loadStructure(structure.wallSet.templates.long).armour; let health; for (let wSegm in structure.wallSet.templates) { let wPart = loadStructure(structure.wallSet.templates[wSegm]); structure.wallset[wSegm] = wPart; for (let research of wPart.production.technology) structure.production.technology.push(research); if (["gate", "tower"].indexOf(wSegm) != -1) continue; if (!health) { health = { "min": wPart.health, "max": wPart.health }; continue; } if (health.min > wPart.health) health.min = wPart.health; else if (health.max < wPart.health) health.max = wPart.health; } if (health.min == health.max) structure.health = health.min; else structure.health = sprintf(translate("%(val1)s to %(val2)s"), { val1: health.min, val2: health.max }); } return structure; } function loadTechnology(techName) { var template = loadTechData(techName); var tech = GetTechnologyDataHelper(template, g_SelectedCiv); tech.reqs = {}; if (template.pair !== undefined) tech.pair = template.pair; if (template.requirements !== undefined) { for (let op in template.requirements) { let val = template.requirements[op]; let req = calcReqs(op, val); switch (op) { case "tech": tech.reqs.generic = req; break; case "civ": tech.reqs[req] = []; break; case "any": if (req[0].length > 0) for (let r of req[0]) { let v = req[0][r]; if (typeof r == "number") tech.reqs[v] = []; else tech.reqs[r] = v; } if (req[1].length > 0) tech.reqs.generic = req[1]; break; case "all": if (req[0].length < 1) tech.reqs.generic = req[1]; else for (let r of req[0]) tech.reqs[r] = req[1]; break; } } } if (template.supersedes !== undefined) { if (tech.reqs.generic !== undefined) tech.reqs.generic.push(template.supersedes); else for (let ck of Object.keys(tech.reqs)) tech.reqs[ck].push(template.supersedes); } return tech; } function loadPhase(phaseCode) { var template = loadTechData(phaseCode); var phase = GetTechnologyDataHelper(template, g_SelectedCiv); phase.actualPhase = ""; return phase; } function loadTechnologyPair(pairCode) { var pairInfo = loadTechData(pairCode); return { "techs": [ pairInfo.top, pairInfo.bottom ], "req": (pairInfo.supersedes !== undefined) ? pairInfo.supersedes : "" }; } /** * Calculate the prerequisite requirements of a technology. * Works recursively if needed. * * @param op The base operation. Can be "civ", "tech", "all" or "any". * @param val The value associated with the above operation. * * @return Sorted requirments. */ function calcReqs(op, val) { switch (op) { case "civ": // nothing needs doing break; case "tech": if (depath(val).slice(0,4) === "pair") return loadTechnologyPair(val).techs; return [ val ]; case "all": case "any": let t = []; let c = []; for (let nv of val) { for (let o in nv) { let v = nv[o]; let r = calcReqs(o, v); switch (o) { case "civ": c.push(r); break; case "tech": t = t.concat(r); break; case "any": c = c.concat(r[0]); t = t.concat(r[1]); break; case "all": for (let ci in r[0]) c[ci] = r[1]; t = t; } } } return [ c, t ]; default: warn("Unknown reqs operator: "+op); } return val; } /** * Unravel phases * * @param techs The current available store of techs * * @return List of phases */ function unravelPhases(techs) { var phaseList = []; for (let techcode in techs) { let techdata = techs[techcode]; if (!("generic" in techdata.reqs) || techdata.reqs.generic.length < 2) continue; let reqTech = techs[techcode].reqs.generic[1]; // Tech that can't be researched anywhere if (!(reqTech in techs)) continue; if (!("generic" in techs[reqTech].reqs)) continue; let reqPhase = techs[reqTech].reqs.generic[0]; let myPhase = techs[techcode].reqs.generic[0]; if (reqPhase == myPhase || depath(reqPhase).slice(0,5) !== "phase" || depath(myPhase).slice(0,5) !== "phase") continue; let reqPhasePos = phaseList.indexOf(reqPhase); let myPhasePos = phaseList.indexOf(myPhase); if (phaseList.length === 0) phaseList = [reqPhase, myPhase]; else if (reqPhasePos < 0 && myPhasePos > -1) phaseList.splice(myPhasePos, 0, reqPhase); else if (myPhasePos < 0 && reqPhasePos > -1) phaseList.splice(reqPhasePos+1, 0, myPhase); else if (reqPhasePos > myPhasePos) { phaseList.splice(reqPhasePos+1, 0, myPhase); phaseList.splice(myPhasePos, 1); } } return phaseList; }