Ticket #4263: 4263_techreqs_v1-4.patch
File 4263_techreqs_v1-4.patch, 73.3 KB (added by , 7 years ago) |
---|
-
binaries/data/mods/public/globalscripts/Technologies.js
diff --git a/binaries/data/mods/public/globalscripts/Technologies.js b/binaries/data/mods/public/globalscripts/Technologies.js index 1af45a4dc..5340e611d 100644
a b function DoesModificationApply(modification, classes) 50 50 { 51 51 return MatchesClassList(classes, modification.affects); 52 52 } 53 54 /** 55 * Derives the technology requirements from a given technology template. 56 * Takes into account the `supersedes` attribute. 57 * 58 * @param {object} template - The template object. Loading of the template must have already occured. 59 * 60 * @return Derived technology requirements. See `InterpretTechRequirements` for object's syntax. 61 */ 62 function DeriveTechnologyRequirements(template, civ) 63 { 64 let requirements = []; 65 66 if (template.requirements) 67 { 68 let op = Object.keys(template.requirements)[0]; 69 let val = template.requirements[op]; 70 requirements = InterpretTechRequirements(civ, op, val); 71 } 72 73 if (template.supersedes && requirements) 74 { 75 if (!requirements.length) 76 requirements.push({}); 77 78 for (let req of requirements) 79 { 80 if (!req.techs) 81 req.techs = []; 82 req.techs.push(template.supersedes); 83 } 84 } 85 86 return requirements; 87 } 88 89 /** 90 * Interprets the prerequisite requirements of a technology. 91 * 92 * Takes the initial { key: value } from the short-form requirements object in entity templates, 93 * and parses it into an object that can be more easily checked by simulation and gui. 94 * 95 * Works recursively if needed. 96 * 97 * The returned object is in the form: 98 * ``` 99 * { "techs": ["tech1", "tech2"] }, 100 * { "techs": ["tech3"] } 101 * ``` 102 * or 103 * ``` 104 * { "entities": [[{ 105 * "class": "human", 106 * "number": 2, 107 * "check": "count" 108 * } 109 * or 110 * ``` 111 * false; 112 * ``` 113 * (Or, to translate: 114 * 1. need either both `tech1` and `tech2`, or `tech3` 115 * 2. need 2 entities with the `human` class 116 * 3. cannot research this tech at all) 117 * 118 * @param {string} civ - The civ code 119 * @param {string} operator - The base operation. Can be "civ", "notciv", "tech", "entity", "all" or "any". 120 * @param {mixed} value - The value associated with the above operation. 121 * 122 * @return Object containing the requirements for the given civ, or false if the civ cannot research the tech. 123 */ 124 function InterpretTechRequirements(civ, operator, value) 125 { 126 let requirements = []; 127 128 switch (operator) 129 { 130 case "civ": 131 return !civ || civ == value ? [] : false; 132 133 case "notciv": 134 return civ == value ? false : []; 135 136 case "entity": 137 { 138 let number = value.number || value.numberOfTypes || 0; 139 if (number > 0) 140 requirements.push({ 141 "entities": [{ 142 "class": value.class, 143 "number": number, 144 "check": value.number ? "count" : "variants" 145 }] 146 }); 147 } 148 break; 149 150 case "tech": 151 requirements.push({ 152 "techs": [value] 153 }); 154 break; 155 156 case "all": 157 { 158 let civPermitted = -1; // tri-state boolean 159 for (let subvalue of value) 160 { 161 let newOper = Object.keys(subvalue)[0]; 162 let newValue = subvalue[newOper]; 163 let result = InterpretTechRequirements(civ, newOper, newValue); 164 165 switch (newOper) 166 { 167 case "civ": 168 if (result) 169 civPermitted = true; 170 else if (civPermitted < true) 171 civPermitted = false; 172 break; 173 174 case "notciv": 175 if (!result) 176 return false; 177 break; 178 179 case "any": 180 if (!result) 181 return false; 182 // else, fall through 183 184 case "all": 185 if (!result) 186 { 187 let nullcivreqs = InterpretTechRequirements(null, newOper, newValue); 188 if (!nullcivreqs || !nullcivreqs.length) 189 civPermitted = false; 190 continue; 191 } 192 // else, fall through 193 194 case "tech": 195 case "entity": 196 { 197 if (result.length) 198 { 199 if (!requirements.length) 200 requirements.push({}); 201 202 let newRequirements = []; 203 for (let currReq of requirements) 204 for (let res of result) 205 { 206 let newReq = {} 207 for (let subtype in currReq) 208 newReq[subtype] = currReq[subtype]; 209 210 for (let subtype in res) 211 { 212 if (!newReq[subtype]) 213 newReq[subtype] = []; 214 newReq[subtype] = newReq[subtype].concat(res[subtype]); 215 } 216 newRequirements.push(newReq); 217 } 218 requirements = newRequirements; 219 } 220 } 221 break; 222 223 } 224 } 225 if (!civPermitted) 226 return false; 227 } 228 break; 229 230 case "any": 231 { 232 let civPermitted = false; 233 for (let subvalue of value) 234 { 235 let newOper = Object.keys(subvalue)[0]; 236 let newValue = subvalue[newOper]; 237 let result = InterpretTechRequirements(civ, newOper, newValue); 238 239 switch (newOper) 240 { 241 242 case "civ": 243 if (result) 244 return []; 245 break; 246 247 case "notciv": 248 if (!result) 249 return false; 250 civPermitted = true; 251 break; 252 253 case "any": 254 if (!result) 255 { 256 let nullcivreqs = InterpretTechRequirements(null, newOper, newValue); 257 if (!nullcivreqs || !nullcivreqs.length) 258 continue; 259 return false; 260 } 261 // else, fall through 262 263 case "all": 264 if (!result) 265 continue; 266 civPermitted = true; 267 // else, fall through 268 269 case "tech": 270 case "entity": 271 for (let res of result) 272 requirements.push(res); 273 break; 274 275 } 276 } 277 if (!civPermitted && !requirements.length) 278 return false; 279 } 280 break; 281 282 default: 283 warn("Unknown requirement operator: "+operator); 284 } 285 286 return requirements; 287 } -
binaries/data/mods/public/globalscripts/Templates.js
diff --git a/binaries/data/mods/public/globalscripts/Templates.js b/binaries/data/mods/public/globalscripts/Templates.js index 66d445c91..db54ca469 100644
a b function GetTemplateDataHelper(template, player, auraTemplates, resources) 351 351 */ 352 352 function GetTechnologyDataHelper(template, civ, resources) 353 353 { 354 varret = {};354 let ret = {}; 355 355 356 356 // Get specific name for this civ or else the generic specific name 357 varspecific;357 let specific; 358 358 if (template.specificName) 359 { 360 if (template.specificName[civ]) 361 specific = template.specificName[civ]; 362 else 363 specific = template.specificName['generic']; 364 } 359 specific = template.specificName[civ] || template.specificName.generic; 365 360 366 361 ret.name = { 367 362 "specific": specific, … … function GetTechnologyDataHelper(template, civ, resources) 377 372 ret.tooltip = template.tooltip; 378 373 ret.requirementsTooltip = template.requirementsTooltip || ""; 379 374 380 // TODO: This doesn't handle all types of requirements 381 if (template.requirements && template.requirements.class) 382 ret.classRequirements = { 383 "class": template.requirements.class, 384 "number": template.requirements.number 385 }; 386 else if (template.requirements && template.requirements.all) 387 for (let req of template.requirements.all) 388 if (req.class) 389 ret.classRequirements = { 390 "class": req.class, 391 "number": req.number 392 }; 375 ret.reqs = DeriveTechnologyRequirements(template, civ); 393 376 394 377 ret.description = template.description; 395 378 -
binaries/data/mods/public/globalscripts/utility.js
diff --git a/binaries/data/mods/public/globalscripts/utility.js b/binaries/data/mods/public/globalscripts/utility.js index 6146b4634..9fdc1b71d 100644
a b function shuffleArray(source) 34 34 } 35 35 return result; 36 36 } 37 38 /** 39 * Removes prefixing path from a path or filename, leaving just the file's name (with extension) 40 * 41 * ie. a/b/c/file.ext -> file.ext 42 */ 43 function basename(path) 44 { 45 return path.slice(path.lastIndexOf("/") + 1); 46 } -
binaries/data/mods/public/gui/common/tooltips.js
diff --git a/binaries/data/mods/public/gui/common/tooltips.js b/binaries/data/mods/public/gui/common/tooltips.js index adae9432b..460c52061 100644
a b function getEntityCostTooltip(template, trainNum, entity) 459 459 return ""; 460 460 } 461 461 462 function getRequiredTechnologyTooltip(technologyEnabled, requiredTechnology )462 function getRequiredTechnologyTooltip(technologyEnabled, requiredTechnology, civ) 463 463 { 464 464 if (technologyEnabled) 465 465 return ""; 466 466 467 467 return sprintf(translate("Requires %(technology)s"), { 468 "technology": getEntityNames(GetTechnologyData(requiredTechnology ))468 "technology": getEntityNames(GetTechnologyData(requiredTechnology, civ)) 469 469 }); 470 470 } 471 471 -
binaries/data/mods/public/gui/session/selection_panels.js
diff --git a/binaries/data/mods/public/gui/session/selection_panels.js b/binaries/data/mods/public/gui/session/selection_panels.js index 6bc6e12be..96ec3e0c1 100644
a b g_SelectionPanels.Construction = { 350 350 let limits = getEntityLimitAndCount(data.playerState, data.item); 351 351 tooltips.push( 352 352 formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers), 353 getRequiredTechnologyTooltip(technologyEnabled, template.requiredTechnology ),353 getRequiredTechnologyTooltip(technologyEnabled, template.requiredTechnology, GetSimState().players[data.player].civ), 354 354 getNeededResourcesTooltip(neededResources)); 355 355 356 356 data.button.tooltip = tooltips.filter(tip => tip).join("\n"); … … g_SelectionPanels.Research = { 794 794 setPanelObjectPosition(pair, data.i, data.rowLength); 795 795 796 796 // Handle one or two techs (tech pair) 797 let player = data.player; 797 798 for (let i in techs) 798 799 { 799 800 let tech = techs[i]; 800 801 801 802 // Don't change the object returned by GetTechnologyData 802 let template = clone(GetTechnologyData(tech ));803 let template = clone(GetTechnologyData(tech, GetSimState().players[player].civ)); 803 804 if (!template) 804 805 return false; 805 806 … … g_SelectionPanels.Research = { 808 809 809 810 let neededResources = Engine.GuiInterfaceCall("GetNeededResources", { 810 811 "cost": template.cost, 811 "player": data.player812 "player": player 812 813 }); 813 814 814 815 let requirementsPassed = Engine.GuiInterfaceCall("CheckTechnologyRequirements", { 815 816 "tech": tech, 816 "player": data.player817 "player": player 817 818 }); 818 819 819 820 let button = Engine.GetGUIObjectByName("unitResearchButton[" + position + "]"); … … g_SelectionPanels.Research = { 828 829 if (!requirementsPassed) 829 830 { 830 831 let tip = template.requirementsTooltip; 831 if (template.classRequirements) 832 let reqs = template.reqs; 833 for (let req of reqs) 832 834 { 833 let player = data.player; 834 let current = GetSimState().players[player].classCounts[template.classRequirements.class] || 0; 835 let remaining = template.classRequirements.number - current; 836 tip += " " + sprintf(translatePlural("Remaining: %(number)s to build.", "Remaining: %(number)s to build.", remaining), { 837 "number": remaining 835 if (!req.entities) 836 continue; 837 838 let entityCounts = []; 839 for (let entity of req.entities) 840 { 841 let current = 0; 842 switch (entity.check) 843 { 844 case "count": 845 current = GetSimState().players[player].classCounts[entity.class] || 0; 846 break; 847 848 case "variants": 849 current = GetSimState().players[player].typeCountsByClass[entity.class] ? Object.keys(GetSimState().players[player].typeCountsByClass[entity.class]).length : 0; 850 break; 851 } 852 853 let remaining = entity.number - current; 854 if (remaining < 1) 855 continue; 856 857 entityCounts.push(sprintf(translatePlural("%(number)s entity of class %(class)s", "%(number)s entities of class %(class)s", remaining), { 858 "number": remaining, 859 "class": entity.class 860 })); 861 } 862 863 tip += " " + sprintf(translate("Remaining: %(entityCounts)s"), { 864 "entityCounts": entityCounts.join(translate(", ")) 838 865 }); 839 866 } 840 867 tooltips.push(tip); … … g_SelectionPanels.Training = { 1064 1091 "[color=\"" + g_HotkeyColor + "\"]" + 1065 1092 formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch) + 1066 1093 "[/color]", 1067 getRequiredTechnologyTooltip(technologyEnabled, template.requiredTechnology ),1094 getRequiredTechnologyTooltip(technologyEnabled, template.requiredTechnology, GetSimState().players[data.player].civ), 1068 1095 getNeededResourcesTooltip(neededResources)); 1069 1096 1070 1097 data.button.tooltip = tooltips.filter(tip => tip).join("\n"); … … g_SelectionPanels.Upgrade = { 1154 1181 tooltips.push( 1155 1182 getEntityCostTooltip(data.item), 1156 1183 formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers), 1157 getRequiredTechnologyTooltip(technologyEnabled, data.item.requiredTechnology ),1184 getRequiredTechnologyTooltip(technologyEnabled, data.item.requiredTechnology, GetSimState().players[data.player].civ), 1158 1185 getNeededResourcesTooltip(neededResources)); 1159 1186 1160 1187 tooltip = tooltips.filter(tip => tip).join("\n"); -
binaries/data/mods/public/gui/session/session.js
diff --git a/binaries/data/mods/public/gui/session/session.js b/binaries/data/mods/public/gui/session/session.js index e2b43aae5..d0b479581 100644
a b function GetTemplateDataWithoutLocalization(templateName) 222 222 return g_TemplateDataWithoutLocalization[templateName]; 223 223 } 224 224 225 function GetTechnologyData(technologyName )225 function GetTechnologyData(technologyName, civ) 226 226 { 227 if (!(technologyName in g_TechnologyData)) 227 if (!g_TechnologyData[civ]) 228 g_TechnologyData[civ] = {}; 229 230 if (!(technologyName in g_TechnologyData[civ])) 228 231 { 229 let template = Engine.GuiInterfaceCall("GetTechnologyData", technologyName);232 let template = Engine.GuiInterfaceCall("GetTechnologyData", { "name": technologyName, "civ": civ }); 230 233 translateObjectKeys(template, ["specific", "generic", "description", "tooltip", "requirementsTooltip"]); 231 g_TechnologyData[ technologyName] = template;234 g_TechnologyData[civ][technologyName] = template; 232 235 } 233 236 234 return g_TechnologyData[ technologyName];237 return g_TechnologyData[civ][technologyName]; 235 238 } 236 239 237 240 function init(initData, hotloadData) -
binaries/data/mods/public/gui/structree/draw.js
diff --git a/binaries/data/mods/public/gui/structree/draw.js b/binaries/data/mods/public/gui/structree/draw.js index bb437cc8b..44be50783 100644
a b function draw() 97 97 if (stru.production.technology[prod_pha]) 98 98 for (let prod of stru.production.technology[prod_pha]) 99 99 { 100 prod = clone( depath(prod).slice(0,5) == "phase" ?100 prod = clone(basename(prod).slice(0,5) == "phase" ? 101 101 g_ParsedData.phases[prod] : 102 g_ParsedData.techs[ prod]);102 g_ParsedData.techs[g_SelectedCiv][prod]); 103 103 104 104 for (let res in stru.techCostMultiplier) 105 105 if (prod.cost[res]) … … function draw() 183 183 prod = g_ParsedData.units[prod]; 184 184 break; 185 185 case "techs": 186 prod = clone(g_ParsedData.techs[ prod]);186 prod = clone(g_ParsedData.techs[civCode][prod]); 187 187 for (let res in trainer.techCostMultiplier) 188 188 if (prod.cost[res]) 189 189 prod.cost[res] *= trainer.techCostMultiplier[res]; -
binaries/data/mods/public/gui/structree/helper.js
diff --git a/binaries/data/mods/public/gui/structree/helper.js b/binaries/data/mods/public/gui/structree/helper.js index b0c8577ac..53e4fa036 100644
a b function loadAuraData(templateName) 48 48 return g_AuraData[templateName]; 49 49 } 50 50 51 function depath(path)52 {53 return path.slice(path.lastIndexOf("/") + 1);54 }55 56 51 /** 57 52 * This is needed because getEntityCostTooltip in tooltip.js needs to get 58 53 * the template data of the different wallSet pieces. In the session this … … function GetTemplateData(templateName) 63 58 var template = loadTemplate(templateName); 64 59 return GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData); 65 60 } 61 62 /** 63 * Determines and returns the phase in which a given technology can be 64 * first researched. Works recursively through the given tech's 65 * pre-requisite and superseded techs if necessary. 66 * 67 * @param {string} techName - The Technology's name 68 * @return The name of the phase the technology belongs to, or false if 69 * the current civ can't research this tech 70 */ 71 function GetPhaseOfTechnology(techName) 72 { 73 let phaseIdx = -1; 74 75 if (basename(techName).slice(0, 5) === "phase") 76 { 77 phaseIdx = g_ParsedData.phaseList.indexOf(GetActualPhase(techName)); 78 if (phaseIdx > 0) 79 return g_ParsedData.phaseList[phaseIdx - 1]; 80 } 81 82 if (!g_ParsedData.techs[g_SelectedCiv][techName]) 83 warn(g_SelectedCiv + " : " + techName); 84 let techReqs = g_ParsedData.techs[g_SelectedCiv][techName].reqs; 85 if (!techReqs) 86 return false; 87 88 for (let option of techReqs) 89 if (option.techs) 90 for (let tech of option.techs) 91 { 92 if (basename(tech).slice(0, 5) === "phase") 93 return tech; 94 phaseIdx = Math.max(phaseIdx, g_ParsedData.phaseList.indexOf(GetPhaseOfTechnology(tech))); 95 } 96 return g_ParsedData.phaseList[phaseIdx] || false; 97 } 98 99 function GetActualPhase(phaseName) 100 { 101 if (g_ParsedData.phases[phaseName]) 102 return g_ParsedData.phases[phaseName].actualPhase; 103 104 warn("Unrecognised phase (" + techName + ")"); 105 return g_ParsedData.phaseList[0]; 106 } 107 108 function GetPhaseOfTemplate(template) 109 { 110 if (!template.requiredTechnology) 111 return g_ParsedData.phaseList[0]; 112 113 if (basename(template.requiredTechnology).slice(0, 5) == "phase") 114 return GetActualPhase(template.requiredTechnology); 115 116 return GetPhaseOfTechnology(template.requiredTechnology); 117 } -
binaries/data/mods/public/gui/structree/load.js
diff --git a/binaries/data/mods/public/gui/structree/load.js b/binaries/data/mods/public/gui/structree/load.js index 07a55cb1c..2d6b58b77 100644
a b function loadUnit(templateName) 2 2 { 3 3 if (!Engine.TemplateExists(templateName)) 4 4 return null; 5 var template = loadTemplate(templateName);6 5 7 var unit = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData); 8 unit.phase = false; 9 10 if (unit.requiredTechnology) 11 { 12 if (depath(unit.requiredTechnology).slice(0, 5) == "phase") 13 unit.phase = unit.requiredTechnology; 14 else if (unit.requiredTechnology.length) 15 unit.required = unit.requiredTechnology; 16 } 6 let template = loadTemplate(templateName); 7 let unit = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData); 17 8 18 9 if (template.ProductionQueue) 19 10 { … … function loadUnit(templateName) 54 45 55 46 function loadStructure(templateName) 56 47 { 57 var template = loadTemplate(templateName); 58 var structure = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData); 59 structure.phase = false; 60 61 if (structure.requiredTechnology) 62 { 63 if (depath(structure.requiredTechnology).slice(0, 5) == "phase") 64 structure.phase = structure.requiredTechnology; 65 else if (structure.requiredTechnology.length) 66 structure.required = structure.requiredTechnology; 67 } 48 let template = loadTemplate(templateName); 49 let structure = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData); 68 50 69 51 structure.production = { 70 52 "technology": [], … … function loadStructure(templateName) 140 122 141 123 function loadTechnology(techName) 142 124 { 143 var template = loadTechData(techName); 144 var tech = GetTechnologyDataHelper(template, g_SelectedCiv, g_ResourceData); 145 tech.reqs = {}; 125 let template = loadTechData(techName); 126 let tech = GetTechnologyDataHelper(template, g_SelectedCiv, g_ResourceData); 146 127 147 128 if (template.pair !== undefined) 148 129 tech.pair = template.pair; 149 130 150 if (template.requirements !== undefined)151 {152 for (let op in template.requirements)153 {154 let val = template.requirements[op];155 let req = calcReqs(op, val);156 157 switch (op)158 {159 case "tech":160 tech.reqs.generic = req;161 break;162 163 case "civ":164 tech.reqs[req] = [];165 break;166 167 case "any":168 if (req[0].length > 0)169 for (let r of req[0])170 {171 let v = req[0][r];172 if (typeof r == "number")173 tech.reqs[v] = [];174 else175 tech.reqs[r] = v;176 }177 if (req[1].length > 0)178 tech.reqs.generic = req[1];179 break;180 181 case "all":182 if (!req[0].length)183 tech.reqs.generic = req[1];184 else185 for (let r of req[0])186 tech.reqs[r] = req[1];187 break;188 }189 }190 }191 192 if (template.supersedes !== undefined)193 {194 if (tech.reqs.generic !== undefined)195 tech.reqs.generic.push(template.supersedes);196 else197 for (let ck of Object.keys(tech.reqs))198 tech.reqs[ck].push(template.supersedes);199 }200 201 131 return tech; 202 132 } 203 133 … … function loadTechnologyPair(pairCode) 219 149 220 150 return { 221 151 "techs": [ pairInfo.top, pairInfo.bottom ], 222 "req ": pairInfo.supersedes || ""152 "reqs": DeriveTechnologyRequirements(pairInfo, g_SelectedCiv) 223 153 }; 224 154 } 225 155 226 156 /** 227 * Calculate the prerequisite requirements of a technology.228 * Works recursively if needed.229 *230 * @param op The base operation. Can be "civ", "tech", "all" or "any".231 * @param val The value associated with the above operation.232 *233 * @return Sorted requirments.234 */235 function calcReqs(op, val)236 {237 switch (op)238 {239 case "civ":240 case "class":241 case "notciv":242 case "number":243 // nothing needs doing244 break;245 246 case "tech":247 if (depath(val).slice(0,4) === "pair")248 return loadTechnologyPair(val).techs;249 return [ val ];250 251 case "all":252 case "any":253 let t = [];254 let c = [];255 for (let nv of val)256 {257 for (let o in nv)258 {259 let v = nv[o];260 let r = calcReqs(o, v);261 switch (o)262 {263 case "civ":264 case "notciv":265 c.push(r);266 break;267 268 case "tech":269 t = t.concat(r);270 break;271 272 case "any":273 c = c.concat(r[0]);274 t = t.concat(r[1]);275 break;276 277 case "all":278 for (let ci in r[0])279 c[ci] = r[1];280 t = t;281 }282 }283 }284 return [ c, t ];285 286 default:287 warn("Unknown reqs operator: "+op);288 }289 return val;290 }291 292 /**293 157 * Unravel phases 294 158 * 295 159 * @param techs The current available store of techs … … function unravelPhases(techs) 304 168 { 305 169 let techdata = techs[techcode]; 306 170 307 if (! ("generic" in techdata.reqs) || techdata.reqs.generic.length < 2)171 if (!techdata.reqs || !techdata.reqs.length || !techdata.reqs[0].techs || techdata.reqs[0].techs.length < 2) 308 172 continue; 309 173 310 let reqTech = techs[techcode].reqs.generic[1]; 311 312 // Tech that can't be researched anywhere 313 if (!(reqTech in techs)) 314 continue; 174 let reqTech = techs[techcode].reqs[0].techs[1]; 315 175 316 if (! ("generic" in techs[reqTech].reqs))176 if (!techs[reqTech] || !techs[reqTech].reqs.length) 317 177 continue; 318 178 319 let reqPhase = techs[reqTech].reqs .generic[0];320 let myPhase = techs[techcode].reqs .generic[0];179 let reqPhase = techs[reqTech].reqs[0].techs[0]; 180 let myPhase = techs[techcode].reqs[0].techs[0]; 321 181 322 if (reqPhase == myPhase || depath(reqPhase).slice(0,5) !== "phase" || depath(myPhase).slice(0,5) !== "phase")182 if (reqPhase == myPhase || basename(reqPhase).slice(0,5) !== "phase" || basename(myPhase).slice(0,5) !== "phase") 323 183 continue; 324 184 325 185 let reqPhasePos = phaseList.indexOf(reqPhase); 326 186 let myPhasePos = phaseList.indexOf(myPhase); 327 187 328 if ( phaseList.length === 0)188 if (!phaseList.length) 329 189 phaseList = [reqPhase, myPhase]; 330 190 else if (reqPhasePos < 0 && myPhasePos > -1) 331 191 phaseList.splice(myPhasePos, 0, reqPhase); -
binaries/data/mods/public/gui/structree/structree.js
diff --git a/binaries/data/mods/public/gui/structree/structree.js b/binaries/data/mods/public/gui/structree/structree.js index fcb6a9c25..20f20fdcb 100644
a b function selectCiv(civCode) 61 61 "structures": [], 62 62 "techs": [] 63 63 }; 64 g_ParsedData.techs[civCode] = {}; 64 65 65 66 // get initial units 66 var startStructs = [];67 67 for (let entity of g_CivData[civCode].StartEntities) 68 68 { 69 69 if (entity.Template.slice(0, 5) == "units") 70 70 g_Lists.units.push(entity.Template); 71 71 else if (entity.Template.slice(0, 6) == "struct") 72 {73 72 g_Lists.structures.push(entity.Template); 74 startStructs.push(entity.Template);75 }76 73 } 77 74 78 75 // Load units and structures … … function selectCiv(civCode) 95 92 var techPairs = {}; 96 93 for (let techcode of g_Lists.techs) 97 94 { 98 let realcode = depath(techcode);95 let realcode = basename(techcode); 99 96 100 97 if (realcode.slice(0,4) == "pair" || realcode.indexOf("_pair") > -1) 101 98 techPairs[techcode] = loadTechnologyPair(techcode); 102 99 else if (realcode.slice(0,5) == "phase") 103 100 g_ParsedData.phases[techcode] = loadPhase(techcode); 104 101 else 105 g_ParsedData.techs[ techcode] = loadTechnology(techcode);102 g_ParsedData.techs[civCode][techcode] = loadTechnology(techcode); 106 103 } 107 104 108 105 // Expand tech pairs 109 106 for (let paircode in techPairs) 110 107 { 111 108 let pair = techPairs[paircode]; 109 if (pair.reqs === false) 110 continue; 111 112 112 for (let techcode of pair.techs) 113 113 { 114 if ( depath(techcode).slice(0, 5) === "phase")114 if (basename(techcode).slice(0, 5) === "phase") 115 115 g_ParsedData.phases[techcode] = loadPhase(techcode); 116 116 else 117 117 { 118 118 let newTech = loadTechnology(techcode); 119 if (pair.req !== "") 120 { 121 if ("generic" in newTech.reqs) 122 newTech.reqs.generic.concat(techPairs[pair.req].techs); 123 else 124 { 125 for (let civkey of Object.keys(newTech.reqs)) 126 newTech.reqs[civkey].concat(techPairs[pair.req].techs); 127 } 128 } 129 g_ParsedData.techs[techcode] = newTech; 119 120 if (!newTech.reqs) 121 newTech.reqs = {}; 122 else if (newTech.reqs === false) 123 continue; 124 125 for (let option of pair.reqs) 126 for (let type in option) 127 for (let opt in newTech.reqs) 128 { 129 if (!newTech.reqs[opt][type]) 130 newTech.reqs[opt][type] = []; 131 newTech.reqs[opt][type] = newTech.reqs[opt][type].concat(option[type]); 132 } 133 134 g_ParsedData.techs[civCode][techcode] = newTech; 130 135 } 131 136 } 132 137 } 133 138 134 139 // Establish phase order 135 g_ParsedData.phaseList = unravelPhases(g_ParsedData.techs );140 g_ParsedData.phaseList = unravelPhases(g_ParsedData.techs[civCode]); 136 141 for (let phasecode of g_ParsedData.phaseList) 137 142 { 138 143 let phaseInfo = loadTechData(phasecode); … … function selectCiv(civCode) 170 175 for (let structCode of g_Lists.structures) 171 176 { 172 177 let structInfo = g_ParsedData.structures[structCode]; 178 structInfo.phase = GetPhaseOfTemplate(structInfo); 173 179 let structPhaseIdx = g_ParsedData.phaseList.indexOf(structInfo.phase); 174 180 175 181 // If this building is shared with another civ, … … function selectCiv(civCode) 181 187 for (let prod of structInfo.production.technology) 182 188 if (prod in techPairs) 183 189 structInfo.production.technology.splice( 184 structInfo.production.technology.indexOf(prod), 1,185 techPairs[prod].techs[0], techPairs[prod].techs[1]190 structInfo.production.technology.indexOf(prod), 191 1, ...techPairs[prod].techs 186 192 ); 187 193 188 194 // Sort techs by phase 189 195 let newProdTech = {}; 190 196 for (let prod of structInfo.production.technology) 191 197 { 192 let phase = ""; 193 194 if (depath(prod).slice(0,5) === "phase") 195 { 196 phase = g_ParsedData.phaseList.indexOf(g_ParsedData.phases[prod].actualPhase); 197 if (phase > 0) 198 phase = g_ParsedData.phaseList[phase - 1]; 199 } 200 else if (g_SelectedCiv in g_ParsedData.techs[prod].reqs) 201 { 202 for (let req of g_ParsedData.techs[prod].reqs[g_SelectedCiv]) 203 if (depath(req).slice(0,5) === "phase") 204 phase = req; 205 } 206 else if ("generic" in g_ParsedData.techs[prod].reqs) 207 { 208 for (let req of g_ParsedData.techs[prod].reqs.generic) 209 if (depath(req).slice(0,5) === "phase") 210 phase = req; 211 } 198 let phase = GetPhaseOfTechnology(prod); 199 if (phase === false) 200 continue; 212 201 213 if (depath(phase).slice(0,5) !== "phase" || 214 g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx) 215 { 216 if (structInfo.phase !== false) 217 phase = structInfo.phase; 218 else 219 phase = g_ParsedData.phaseList[0]; 220 } 202 if (g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx) 203 phase = structInfo.phase; 221 204 222 205 if (!(phase in newProdTech)) 223 206 newProdTech[phase] = []; … … function selectCiv(civCode) 225 208 newProdTech[phase].push(prod); 226 209 } 227 210 228 // Determine phase for units211 // Sort units by phase 229 212 let newProdUnits = {}; 230 213 for (let prod of structInfo.production.units) 231 214 { 232 215 if (!g_ParsedData.units[prod]) 233 216 continue; 234 217 235 let unit = g_ParsedData.units[prod]; 236 let phase = ""; 237 238 if (unit.phase !== false) 239 { 240 if (g_ParsedData.phaseList.indexOf(unit.phase) < 0) 241 phase = g_ParsedData.phases[unit.phase].actualPhase; 242 else 243 phase = unit.phase; 244 } 245 else if (unit.required !== undefined) 246 { 247 if (g_ParsedData.phases[unit.required]) 248 phase = g_ParsedData.phases[unit.required].actualPhase; 249 else if (g_ParsedData.techs[unit.required]) 250 { 251 let reqs = g_ParsedData.techs[unit.required].reqs; 252 if (reqs[g_SelectedCiv]) 253 phase = reqs[g_SelectedCiv][0]; 254 else if (reqs.generic) 255 phase = reqs.generic[0]; 256 else 257 warn("Empty requirements found on technology " + unit.required); 258 } 259 else 260 warn("Technology " + unit.required + " for " + prod + " not found."); 261 } 218 let phase = GetPhaseOfTemplate(g_ParsedData.units[prod]); 219 if (phase === false) 220 continue; 262 221 263 if (depath(phase).slice(0,5) !== "phase" || g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx) 264 if (structInfo.phase !== false) 265 phase = structInfo.phase; 266 else 267 phase = g_ParsedData.phaseList[0]; 222 if (g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx) 223 phase = structInfo.phase; 268 224 269 225 if (!(phase in newProdUnits)) 270 226 newProdUnits[phase] = []; … … function selectCiv(civCode) 279 235 } 280 236 281 237 // Determine the buildList for the civ (grouped by phase) 282 varbuildList = {};283 vartrainerList = [];238 let buildList = {}; 239 let trainerList = []; 284 240 for (let pha of g_ParsedData.phaseList) 285 241 buildList[pha] = []; 286 242 for (let structCode of g_Lists.structures) 287 243 { 288 if (!g_ParsedData.structures[structCode].phase || startStructs.indexOf(structCode) > -1) 289 g_ParsedData.structures[structCode].phase = g_ParsedData.phaseList[0]; 290 291 let myPhase = g_ParsedData.structures[structCode].phase; 292 if (g_ParsedData.phaseList.indexOf(myPhase) === -1) 293 myPhase = g_ParsedData.phases[myPhase].actualPhase; 294 295 buildList[myPhase].push(structCode); 244 let phase = g_ParsedData.structures[structCode].phase; 245 buildList[phase].push(structCode); 296 246 } 297 247 for (let unitCode of g_Lists.units) 298 248 if (g_ParsedData.units[unitCode] && g_ParsedData.units[unitCode].production) -
binaries/data/mods/public/simulation/ai/common-api/gamestate.js
diff --git a/binaries/data/mods/public/simulation/ai/common-api/gamestate.js b/binaries/data/mods/public/simulation/ai/common-api/gamestate.js index 32669ef33..b3ddef78a 100644
a b m.GameState.prototype.init = function(SharedScript, state, player) { 36 36 let k = techs.indexOf(this.phases[i].name); 37 37 if (k !== -1) 38 38 { 39 this.phases[i].requirements = (this.getTemplate(techs[k]))._template.requirements;39 this.phases[i].requirements = DeriveTechnologyRequirements(this.getTemplate(techs[k])._template, this.civ()); 40 40 continue; 41 41 } 42 42 for (let tech of techs) … … m.GameState.prototype.init = function(SharedScript, state, player) { 45 45 if (template.replaces && template.replaces.indexOf(this.phases[i].name) != -1) 46 46 { 47 47 this.phases[i].name = tech; 48 this.phases[i].requirements = template.requirements;48 this.phases[i].requirements = DeriveTechnologyRequirements(template, this.civ()); 49 49 break; 50 50 } 51 51 } … … m.GameState.prototype.cityPhase = function() 169 169 return this.phases[2].name; 170 170 }; 171 171 172 m.GameState.prototype.getPhase Requirements = function(i)172 m.GameState.prototype.getPhaseEntityRequirements = function(i) 173 173 { 174 if (!this.phases[i-1].requirements) 175 return undefined; 176 let requirements = this.phases[i-1].requirements; 177 if (requirements.number) 178 return requirements; 179 else if (requirements.all) 174 let entityReqs = []; 175 176 for (let requirement of this.phases[i-1].requirements) 180 177 { 181 for (let req of requirements.all) 182 if (req.number) 183 return req; 178 if (!requirement.entities) 179 continue; 180 for (let entity of requirement.entities) 181 if (entity.check == "count") 182 entityReqs.push({ 183 "class": entity.class, 184 "count": entity.number 185 }); 184 186 } 185 return undefined; 186 }; 187 188 return entityReqs; 189 } 187 190 188 191 m.GameState.prototype.isResearched = function(template) 189 192 { … … m.GameState.prototype.canResearch = function(techTemplateName, noRequirementChec 213 216 if (noRequirementCheck === true) 214 217 return true; 215 218 216 // not already researched, check if we can.217 // basically a copy of the function in technologyManager since we can't use it.218 // Checks the requirements for a technology to see if it can be researched at the current time219 220 // The technology which this technology supersedes is required221 if (template.supersedes() && !this.playerData.researchedTechs[template.supersedes()])222 return false;223 224 219 // if this is a pair, we must check that the pair tech is not being researched 225 220 if (template.pair()) 226 221 { … … m.GameState.prototype.canResearch = function(techTemplateName, noRequirementChec 231 226 return false; 232 227 } 233 228 234 return this.checkTechRequirements(template.requirements( ));229 return this.checkTechRequirements(template.requirements(this.playerData.civ)); 235 230 }; 236 231 237 232 /** 238 * Private function for checking a set of requirements is met 239 * basically copies TechnologyManager 233 * Private function for checking a set of requirements is met. 234 * Basically copies TechnologyManager, but compares against 235 * variables only available within the AI 240 236 */ 241 237 m.GameState.prototype.checkTechRequirements = function(reqs) 242 238 { 243 // If there are no requirements then all requirements are met244 239 if (!reqs) 240 return false; 241 242 if (!reqs.length) 245 243 return true; 246 244 247 if (reqs.all) 248 return reqs.all.every(r => this.checkTechRequirements(r)); 249 if (reqs.any) 250 return reqs.any.some(r => this.checkTechRequirements(r)); 251 if (reqs.civ) 252 return this.playerData.civ == reqs.civ; 253 if (reqs.notciv) 254 return this.playerData.civ != reqs.notciv; 255 if (reqs.tech) 256 return this.playerData.researchedTechs[reqs.tech] !== undefined && this.playerData.researchedTechs[reqs.tech]; 257 if (reqs.class && reqs.numberOfTypes) 258 return this.playerData.typeCountsByClass[reqs.class] && 259 Object.keys(this.playerData.typeCountsByClass[reqs.class]).length >= reqs.numberOfTypes; 260 if (reqs.class && reqs.number) 261 return this.playerData.classCounts[reqs.class] && 262 this.playerData.classCounts[reqs.class] >= reqs.number; 263 264 // The technologies requirements are not a recognised format 265 error("Bad requirements " + uneval(reqs)); 266 return false; 245 function doesEntitySpecPass(entity) 246 { 247 switch (entity.check) 248 { 249 case "count": 250 if (!this.playerData.classCounts[entity.class] || this.playerData.classCounts[entity.class] < entity.number) 251 return false; 252 break; 253 254 case "variants": 255 if (!this.playerData.typeCountsByClass[entity.class] || Object.keys(this.playerData.typeCountsByClass[entity.class]).length < entity.number) 256 return false; 257 break; 258 } 259 return true; 260 }; 261 262 return reqs.some(req => { 263 return Object.keys(req).every(type => { 264 switch (type) 265 { 266 case "techs": 267 return req[type].every(tech => !!this.playerData.researchedTechs[tech]); 268 269 case "entities": 270 return req[type].every(doesEntitySpecPass, this); 271 } 272 return false; 273 }); 274 }); 267 275 }; 268 276 269 277 m.GameState.prototype.getMap = function() -
binaries/data/mods/public/simulation/ai/common-api/technology.js
diff --git a/binaries/data/mods/public/simulation/ai/common-api/technology.js b/binaries/data/mods/public/simulation/ai/common-api/technology.js index 14788a768..3f9a9189b 100644
a b m.Technology.prototype.researchTime = function() 97 97 return this._template.researchTime; 98 98 }; 99 99 100 m.Technology.prototype.requirements = function( )100 m.Technology.prototype.requirements = function(civ) 101 101 { 102 if (!this._template.requirements) 103 return undefined; 104 return this._template.requirements; 102 return DeriveTechnologyRequirements(this._template, civ); 105 103 }; 106 104 107 105 m.Technology.prototype.autoResearch = function() -
binaries/data/mods/public/simulation/ai/petra/headquarters.js
diff --git a/binaries/data/mods/public/simulation/ai/petra/headquarters.js b/binaries/data/mods/public/simulation/ai/petra/headquarters.js index 255a6fbb5..f5c1c40a5 100644
a b m.HQ.prototype.buildTemple = function(gameState, queues) 1285 1285 { 1286 1286 if (gameState.currentPhase() < 2 || this.econState !== "cityPhasing") 1287 1287 return; 1288 let requirements = gameState.getPhaseRequirements(3); 1289 if (!requirements || !requirements.number) 1290 return; 1291 if (gameState.getOwnStructures().filter(API3.Filters.byClass(requirements["class"])).length >= requirements.number) 1288 let requirements = gameState.getPhaseEntityRequirements(3); 1289 if (!requirements.length) 1292 1290 return; 1291 for (let entityReq of requirements) 1292 if (gameState.getOwnStructures().filter(API3.Filters.byClass(entityReq.class)).length >= entityReq.count) 1293 return; 1293 1294 } 1294 1295 if (!this.canBuild(gameState, "structures/{civ}_temple")) 1295 1296 return; … … m.HQ.prototype.manageCorral = function(gameState, queues) 1399 1400 * build more houses if needed. 1400 1401 * kinda ugly, lots of special cases to both build enough houses but not tooo many… 1401 1402 */ 1402 m.HQ.prototype.buildMoreHouses = function(gameState, queues)1403 m.HQ.prototype.buildMoreHouses = function(gameState, queues) 1403 1404 { 1404 1405 if (gameState.getPopulationMax() <= gameState.getPopulationLimit()) 1405 1406 return; … … m.HQ.prototype.buildMoreHouses = function(gameState,queues) 1429 1430 queues.house.addPlan(plan); 1430 1431 } 1431 1432 1432 if (numPlanned > 0 && this.econState == "townPhasing" && gameState.getPhase Requirements(2))1433 if (numPlanned > 0 && this.econState == "townPhasing" && gameState.getPhaseEntityRequirements(2).length) 1433 1434 { 1434 let requirements = gameState.getPhaseRequirements(2); 1435 let count = gameState.getOwnStructures().filter(API3.Filters.byClass(requirements["class"])).length; 1436 if (requirements && count < requirements.number && this.stopBuilding.has(gameState.applyCiv("structures/{civ}_house"))) 1435 let houseTemplateName = gameState.applyCiv("structures/{civ}_house"); 1436 let houseTemplate = gameState.getTemplate(houseTemplateName); 1437 1438 let needed = 0; 1439 for (let entityReq of gameState.getPhaseEntityRequirements(2)) 1437 1440 { 1438 if (this.Config.debug > 1) 1439 API3.warn("no room to place a house ... try to be less restrictive"); 1440 this.stopBuilding.delete(gameState.applyCiv("structures/{civ}_house")); 1441 this.requireHouses = true; 1441 if (!houseTemplate.hasClass(entityReq.class)) 1442 continue; 1443 1444 let count = gameState.getOwnStructures().filter(API3.Filters.byClass(entityReq.class)).length; 1445 if (count < entityReq.count && this.stopBuilding.has(houseTemplateName)) 1446 { 1447 if (this.Config.debug > 1) 1448 API3.warn("no room to place a house ... try to be less restrictive"); 1449 this.stopBuilding.delete(houseTemplateName); 1450 this.requireHouses = true; 1451 } 1452 needed = Math.max(needed, entityReq.count - count); 1442 1453 } 1454 1443 1455 let houseQueue = queues.house.plans; 1444 1456 for (let i = 0; i < numPlanned; ++i) 1445 {1446 1457 if (houseQueue[i].isGo(gameState)) 1447 ++count;1448 else if ( count < requirements.number)1458 --needed; 1459 else if (needed > 0) 1449 1460 { 1450 1461 houseQueue[i].isGo = function () { return true; }; 1451 ++count;1462 --needed; 1452 1463 } 1453 }1454 1464 } 1455 1465 1456 1466 if (this.requireHouses) 1457 1467 { 1458 let requirements = gameState.getPhaseRequirements(2); 1459 if (gameState.getOwnStructures().filter(API3.Filters.byClass(requirements["class"])).length >= requirements.number) 1460 this.requireHouses = undefined; 1468 let houseTemplate = gameState.getTemplate(gameState.applyCiv("structures/{civ}_house")); 1469 if (gameState.getPhaseEntityRequirements(2).every(req => 1470 !houseTemplate.hasClass(req.class) || gameState.getOwnStructures().filter(API3.Filters.byClass(req.class)).length >= req.count)) 1471 this.requireHouses = undefined; 1461 1472 } 1462 1473 1463 1474 // When population limit too tight -
binaries/data/mods/public/simulation/components/GuiInterface.js
diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js index 932c8cd3d..04ced0a3e 100644
a b GuiInterface.prototype.GetTemplateData = function(player, extendedName) 658 658 return GetTemplateDataHelper(template, player, aurasTemplate, Resources); 659 659 }; 660 660 661 GuiInterface.prototype.GetTechnologyData = function(player, name)661 GuiInterface.prototype.GetTechnologyData = function(player, data) 662 662 { 663 663 let cmpDataTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_DataTemplateManager); 664 let template = cmpDataTemplateManager.GetTechnologyTemplate( name);664 let template = cmpDataTemplateManager.GetTechnologyTemplate(data.name); 665 665 666 666 if (!template) 667 667 { 668 warn("Tried to get data for invalid technology: " + name);668 warn("Tried to get data for invalid technology: " + data.name); 669 669 return null; 670 670 } 671 671 672 672 let cmpPlayer = QueryPlayerIDInterface(player, IID_Player); 673 return GetTechnologyDataHelper(template, cmpPlayer.GetCiv(), Resources);673 return GetTechnologyDataHelper(template, data.civ || cmpPlayer.GetCiv(), Resources); 674 674 }; 675 675 676 676 GuiInterface.prototype.IsTechnologyResearched = function(player, data) -
binaries/data/mods/public/simulation/components/ProductionQueue.js
diff --git a/binaries/data/mods/public/simulation/components/ProductionQueue.js b/binaries/data/mods/public/simulation/components/ProductionQueue.js index c9c90a51e..720cafdfe 100644
a b ProductionQueue.prototype.GetTechnologiesList = function() 162 162 163 163 // Remove any technologies that can't be researched by this civ 164 164 techs = techs.filter(tech => { 165 let reqs = cmpTechnologyManager.GetTechnologyTemplate(tech).requirements || null;165 let reqs = DeriveTechnologyRequirements(cmpTechnologyManager.GetTechnologyTemplate(tech), cmpPlayer.GetCiv()); 166 166 return cmpTechnologyManager.CheckTechnologyRequirements(reqs, true); 167 167 }); 168 168 -
binaries/data/mods/public/simulation/components/TechnologyManager.js
diff --git a/binaries/data/mods/public/simulation/components/TechnologyManager.js b/binaries/data/mods/public/simulation/components/TechnologyManager.js index f5a17a78d..33a372df6 100644
a b TechnologyManager.prototype.IsTechnologyResearched = function(tech) 97 97 // Checks the requirements for a technology to see if it can be researched at the current time 98 98 TechnologyManager.prototype.CanResearch = function(tech) 99 99 { 100 var template = this.GetTechnologyTemplate(tech); 100 let template = this.GetTechnologyTemplate(tech); 101 101 102 if (!template) 102 103 { 103 104 warn("Technology \"" + tech + "\" does not exist"); 104 105 return false; 105 106 } 106 107 107 // The technology which this technology supersedes is required108 if (template.supersedes && !this.IsTechnologyResearched(template.supersedes))109 return false;110 111 108 if (template.top && this.IsInProgress(template.top) || 112 109 template.bottom && this.IsInProgress(template.bottom)) 113 110 return false; … … TechnologyManager.prototype.CanResearch = function(tech) 121 118 if (this.IsTechnologyResearched(tech)) 122 119 return false; 123 120 124 return this.CheckTechnologyRequirements( template.requirements || null);121 return this.CheckTechnologyRequirements(DeriveTechnologyRequirements(template, Engine.QueryInterface(this.entity, IID_Player).GetCiv())); 125 122 }; 126 123 127 124 /** 128 125 * Private function for checking a set of requirements is met 129 * @param reqs Object of technology requirements as given by the technology template130 * @param civonly A boolean set to true if only the civ requirement ischecked126 * @param {object} reqs - Technology requirements as derived from the technology template by globalscripts 127 * @param {boolean} civonly - True if only the civ requirement is to be checked 131 128 * 132 * @return true if the requirements are checked 133 * false otherwise 129 * @return true if the requirements pass, false otherwise 134 130 */ 135 TechnologyManager.prototype.CheckTechnologyRequirements = function(reqs, civonly )131 TechnologyManager.prototype.CheckTechnologyRequirements = function(reqs, civonly = false) 136 132 { 137 // If there are no requirements then all requirements are met 133 let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 134 138 135 if (!reqs) 136 return false; 137 138 if (civonly || !reqs.length) 139 139 return true; 140 140 141 if (reqs.all) 142 return reqs.all.every(r => this.CheckTechnologyRequirements(r, civonly)); 143 if (reqs.any) 144 return reqs.any.some(r => this.CheckTechnologyRequirements(r, civonly)); 145 if (reqs.civ) 146 { 147 let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 148 return cmpPlayer && cmpPlayer.GetCiv() == reqs.civ; 149 } 150 if (reqs.notciv) 141 return reqs.some(req => { 142 return Object.keys(req).every(type => { 143 switch (type) 144 { 145 case "techs": 146 return req[type].every(this.IsTechnologyResearched, this); 147 148 case "entities": 149 return req[type].every(this.DoesEntitySpecPass, this); 150 } 151 return false; 152 }); 153 }); 154 } 155 156 TechnologyManager.prototype.DoesEntitySpecPass = function(entity) 157 { 158 switch (entity.check) 151 159 { 152 let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 153 return cmpPlayer && cmpPlayer.GetCiv() != reqs.notciv; 160 case "count": 161 if (!this.classCounts[entity.class] || this.classCounts[entity.class] < entity.number) 162 return false; 163 break; 164 165 case "variants": 166 if (!this.typeCountsByClass[entity.class] || Object.keys(this.typeCountsByClass[entity.class]).length < entity.number) 167 return false; 168 break; 154 169 } 155 if (civonly) 156 return true; 157 if (reqs.tech) 158 return this.IsTechnologyResearched(reqs.tech); 159 if (reqs.class && reqs.numberOfTypes) 160 return this.typeCountsByClass[reqs.class] && 161 Object.keys(this.typeCountsByClass[reqs.class]).length >= reqs.numberOfTypes; 162 if (reqs.class && reqs.number) 163 return this.classCounts[reqs.class] && 164 this.classCounts[reqs.class] >= reqs.number; 165 // The technologies requirements are not a recognised format 166 error("Bad requirements " + uneval(reqs)); 167 return false; 170 return true; 168 171 }; 169 172 170 173 TechnologyManager.prototype.OnGlobalOwnershipChanged = function(msg) -
new file inaries/data/mods/public/simulation/components/tests/test_Technologies.js
diff --git a/binaries/data/mods/public/simulation/components/tests/test_Technologies.js b/binaries/data/mods/public/simulation/components/tests/test_Technologies.js new file mode 100644 index 000000000..0aea0b6b6
- + 1 // TODO: Move this to a folder of tests for GlobalScripts (once one is created) 2 3 // No requirements set in template 4 let template = {}; 5 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); 6 7 /** 8 * First, the basics: 9 */ 10 11 // Technology Requirement 12 template.requirements = { "tech": "expected_tech" }; 13 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["expected_tech"] }]); 14 15 // Entity Requirement: Count of entities matching given class 16 template.requirements = { "entity": { "class": "Village", "number": 5 } }; 17 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); 18 19 // Entity Requirement: Count of entities matching given class 20 template.requirements = { "entity": { "class": "Village", "numberOfTypes": 5 } }; 21 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); 22 23 // Single `civ` 24 template.requirements = { "civ": "athen" }; 25 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); 26 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false); 27 28 // Single `notciv` 29 template.requirements = { "notciv": "athen" }; 30 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); 31 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), []); 32 33 34 /** 35 * Basic `all`s: 36 */ 37 38 // Multiple techs 39 template.requirements = { "all": [{ "tech": "tech_A" }, { "tech": "tech_B" }, { "tech": "tech_C" }] }; 40 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A", "tech_B", "tech_C"] }]); 41 42 // Multiple entity definitions 43 template.requirements = { 44 "all": [ 45 { "entity": { "class": "class_A", "number": 5 } }, 46 { "entity": { "class": "class_B", "number": 5 } }, 47 { "entity": { "class": "class_C", "number": 5 } } 48 ] 49 }; 50 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), 51 [{ "entities": [{ "class": "class_A", "number": 5, "check": "count" }, { "class": "class_B", "number": 5, "check": "count" }, { "class": "class_C", "number": 5, "check": "count" }] }]); 52 53 // A `tech` and an `entity` (went to sea, in a beautiful pea-green boat) 54 template.requirements = { "all": [{ "tech": "tech_A" }, { "entity": { "class": "class_B", "number": 5, "check": "count" } }] }; 55 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A"], "entities": [{ "class": "class_B", "number": 5, "check": "count" }] }]); 56 57 // Multiple `civ`s 58 template.requirements = { "all": [{"civ": "civ_A"}, {"civ": "civ_B"}, {"civ": "civ_C"}] }; 59 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), []); 60 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), []); 61 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), []); 62 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), false); 63 64 // Multiple `notciv`s 65 template.requirements = { "all": [{"notciv": "civ_A"}, {"notciv": "civ_B"}, {"notciv": "civ_C"}] }; 66 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), false); 67 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), false); 68 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), false); 69 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), []); 70 71 // A `civ` with a tech/entity 72 template.requirements = { "all": [{ "civ": "athen" }, { "tech": "expected_tech" }] }; 73 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["expected_tech"] }]); 74 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false); 75 76 template.requirements = { "all": [{ "civ": "athen" }, { "entity": { "class": "Village", "number": 5 } }] }; 77 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); 78 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false); 79 80 template.requirements = { "all": [{ "civ": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] }; 81 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); 82 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false); 83 84 // A `notciv` with a tech/entity 85 template.requirements = { "all": [{ "notciv": "athen" }, { "tech": "expected_tech" }] }; 86 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); 87 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "techs": ["expected_tech"] }]); 88 89 template.requirements = { "all": [{ "notciv": "athen" }, { "entity": { "class": "Village", "number": 5 } }] }; 90 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); 91 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); 92 93 template.requirements = { "all": [{ "notciv": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] }; 94 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); 95 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); 96 97 98 /** 99 * Basic `any`s: 100 */ 101 102 // Multiple techs 103 template.requirements = { "any": [{ "tech": "tech_A" }, { "tech": "tech_B" }, { "tech": "tech_C" }] }; 104 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A"] }, { "techs": ["tech_B"] }, { "techs": ["tech_C"] }]); 105 106 // Multiple entity definitions 107 template.requirements = { 108 "any": [ 109 { "entity": { "class": "class_A", "number": 5 } }, 110 { "entity": { "class": "class_B", "number": 5 } }, 111 { "entity": { "class": "class_C", "number": 5 } } 112 ] 113 }; 114 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ 115 { "entities": [{ "class": "class_A", "number": 5, "check": "count" }] }, 116 { "entities": [{ "class": "class_B", "number": 5, "check": "count" }] }, 117 { "entities": [{ "class": "class_C", "number": 5, "check": "count" }] } 118 ]); 119 120 // A tech or an entity 121 template.requirements = { "any": [{ "tech": "tech_A" }, { "entity": { "class": "class_B", "number": 5, "check": "count" } }] }; 122 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A"] }, { "entities": [{ "class": "class_B", "number": 5, "check": "count" }] }]); 123 124 // Multiple `civ`s 125 template.requirements = { "any": [{"civ": "civ_A"}, {"civ": "civ_B"}, {"civ": "civ_C"}] }; 126 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), []); 127 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), []); 128 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), []); 129 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), false); 130 131 // Multiple `notciv`s 132 template.requirements = { "any": [{"notciv": "civ_A"}, {"notciv": "civ_B"}, {"notciv": "civ_C"}] }; 133 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), false); 134 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), false); 135 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), false); 136 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), []); 137 138 // A `civ` or a tech/entity 139 template.requirements = { "any": [{ "civ": "athen" }, { "tech": "expected_tech" }] }; 140 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); 141 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "techs": ["expected_tech"] }]); 142 143 template.requirements = { "any": [{ "civ": "athen" }, { "entity": { "class": "Village", "number": 5 } }] }; 144 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); 145 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); 146 147 template.requirements = { "any": [{ "civ": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] }; 148 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); 149 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); 150 151 // A `notciv` or a tech 152 template.requirements = { "any": [{ "notciv": "athen" }, { "tech": "expected_tech" }] }; 153 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); 154 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "techs": ["expected_tech"] }]); 155 156 template.requirements = { "any": [{ "notciv": "athen" }, { "entity": { "class": "Village", "number": 5 } }] }; 157 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); 158 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); 159 160 template.requirements = { "any": [{ "notciv": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] }; 161 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); 162 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); 163 164 165 /** 166 * Complicated `all`s, part 1 - an `all` inside an `all`: 167 */ 168 169 // Techs 170 template.requirements = { 171 "all": [ 172 { "all": [{ "tech": "tech_A" }, { "tech": "tech_B" }] }, 173 { "all": [{ "tech": "tech_C" }, { "tech": "tech_D" }] } 174 ] 175 }; 176 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A", "tech_B", "tech_C", "tech_D"] }]); 177 178 // Techs and entities 179 template.requirements = { 180 "all": [ 181 { "all": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] }, 182 { "all": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] } 183 ] 184 }; 185 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ 186 "techs": ["tech_A", "tech_B"], 187 "entities": [{ "class": "class_A", "number": 5, "check": "count" }, { "class": "class_B", "number": 5, "check": "variants" }] 188 }]); 189 190 // Two `civ`s, without and with a tech 191 template.requirements = { 192 "all": [ 193 { "all": [{ "civ": "athen" }, { "civ": "spart" }] } 194 ] 195 }; 196 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); 197 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); 198 199 template.requirements = { 200 "all": [ 201 { "tech": "required_tech" }, 202 { "all": [{ "civ": "athen" }, { "civ": "spart" }] } 203 ] 204 }; 205 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); 206 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); 207 208 // Two `notciv`s, without and with a tech 209 template.requirements = { 210 "all": [ 211 { "all": [{ "notciv": "athen" }, { "notciv": "spart" }] } 212 ] 213 }; 214 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); 215 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []); 216 217 template.requirements = { 218 "all": [ 219 { "tech": "required_tech" }, 220 { "all": [{ "notciv": "athen" }, { "notciv": "spart" }] } 221 ] 222 }; 223 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); 224 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); 225 226 // Inner `all` has a tech and a `civ`/`notciv` 227 template.requirements = { 228 "all": [ 229 { "all": [{ "tech": "tech_A" }, { "civ": "maur" }] }, 230 { "tech": "tech_B" } 231 ] 232 }; 233 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_B"] }]); 234 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["tech_A", "tech_B"] }]); 235 236 template.requirements = { 237 "all": [ 238 { "all": [{ "tech": "tech_A" }, { "notciv": "maur" }] }, 239 { "tech": "tech_B" } 240 ] 241 } 242 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A", "tech_B"] }]); 243 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["tech_B"] }]); 244 245 246 /** 247 * Complicated `all`s, part 2 - an `any` inside an `all`: 248 */ 249 250 // Techs 251 template.requirements = { 252 "all": [ 253 { "any": [{ "tech": "tech_A" }, { "tech": "tech_B" }] }, 254 { "any": [{ "tech": "tech_C" }, { "tech": "tech_D" }] } 255 ] 256 }; 257 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ 258 { "techs": ["tech_A", "tech_C"] }, 259 { "techs": ["tech_A", "tech_D"] }, 260 { "techs": ["tech_B", "tech_C"] }, 261 { "techs": ["tech_B", "tech_D"] } 262 ]); 263 264 // Techs and entities 265 template.requirements = { 266 "all": [ 267 { "any": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] }, 268 { "any": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] } 269 ] 270 }; 271 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ 272 { "techs": ["tech_A"], "entities": [{ "class": "class_B", "number": 5, "check": "variants" }] }, 273 { "techs": ["tech_A", "tech_B"] }, 274 { "entities": [{ "class": "class_A", "number": 5, "check": "count" }, { "class": "class_B", "number": 5, "check": "variants" }] }, 275 { "entities": [{ "class": "class_A", "number": 5, "check": "count" }], "techs": ["tech_B"] } 276 ]); 277 278 // Two `civ`s, without and with a tech 279 template.requirements = { 280 "all": [ 281 { "any": [{ "civ": "athen" }, { "civ": "spart" }] } 282 ] 283 }; 284 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); 285 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); 286 287 template.requirements = { 288 "all": [ 289 { "tech": "required_tech" }, 290 { "any": [{ "civ": "athen" }, { "civ": "spart" }] } 291 ] 292 }; 293 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); 294 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); 295 296 // Two `notciv`s, without and with a tech 297 template.requirements = { 298 "all": [ 299 { "any": [{ "notciv": "athen" }, { "notciv": "spart" }] } 300 ] 301 }; 302 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); 303 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []); 304 305 template.requirements = { 306 "all": [ 307 { "tech": "required_tech" }, 308 { "any": [{ "notciv": "athen" }, { "notciv": "spart" }] } 309 ] 310 }; 311 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); 312 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); 313 314 315 /** 316 * Complicated `any`s, part 1 - an `all` inside an `any`: 317 */ 318 319 // Techs 320 template.requirements = { 321 "any": [ 322 { "all": [{ "tech": "tech_A" }, { "tech": "tech_B" }] }, 323 { "all": [{ "tech": "tech_C" }, { "tech": "tech_D" }] } 324 ] 325 }; 326 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ 327 { "techs": ["tech_A", "tech_B"] }, 328 { "techs": ["tech_C", "tech_D"] } 329 ]); 330 331 // Techs and entities 332 template.requirements = { 333 "any": [ 334 { "all": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] }, 335 { "all": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] } 336 ] 337 }; 338 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ 339 { "techs": ["tech_A"], "entities": [{ "class": "class_A", "number": 5, "check": "count" }] }, 340 { "entities": [{ "class": "class_B", "number": 5, "check": "variants" }], "techs": ["tech_B"] } 341 ]); 342 343 // Two `civ`s, without and with a tech 344 template.requirements = { 345 "any": [ 346 { "all": [{ "civ": "athen" }, { "civ": "spart" }] } 347 ] 348 }; 349 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); 350 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); 351 352 template.requirements = { 353 "any": [ 354 { "tech": "required_tech" }, 355 { "all": [{ "civ": "athen" }, { "civ": "spart" }] } 356 ] 357 }; 358 // Note: these requirements don't really make sense, as the `any` makes the `civ`s in the the inner `all` irrelevant. 359 // We test it anyway as a precursor to later tests. 360 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); 361 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); 362 363 // Two `notciv`s, without and with a tech 364 template.requirements = { 365 "any": [ 366 { "all": [{ "notciv": "athen" }, { "notciv": "spart" }] } 367 ] 368 }; 369 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); 370 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []); 371 372 template.requirements = { 373 "any": [ 374 { "tech": "required_tech" }, 375 { "all": [{ "notciv": "athen" }, { "notciv": "spart" }] } 376 ] 377 }; 378 // Note: these requirements have a result that might seen unexpected at first glance. 379 // This is because the `notciv`s are rendered irrelevant by the `any`, and they have nothing else to operate on. 380 // We test it anyway as a precursor for later tests. 381 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); 382 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); 383 384 // Inner `all` has a tech and a `civ`/`notciv` 385 template.requirements = { 386 "any": [ 387 { "all": [{ "civ": "civA" }, { "tech": "tech1" }] }, 388 { "tech": "tech2" } 389 ] 390 }; 391 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }, { "techs": ["tech2"] }]); 392 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civB"), [{ "techs": ["tech2"] }]); 393 394 template.requirements = { 395 "any": [ 396 { "all": [{ "notciv": "civA" }, { "tech": "tech1" }] }, 397 { "tech": "tech2" } 398 ] 399 }; 400 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech2"] }]); 401 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civB"), [{ "techs": ["tech1"] }, { "techs": ["tech2"] }]); 402 403 404 /** 405 * Complicated `any`s, part 2 - an `any` inside an `any`: 406 */ 407 408 // Techs 409 template.requirements = { 410 "any": [ 411 { "any": [{ "tech": "tech_A" }, { "tech": "tech_B" }] }, 412 { "any": [{ "tech": "tech_C" }, { "tech": "tech_D" }] } 413 ] 414 }; 415 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ 416 { "techs": ["tech_A"] }, 417 { "techs": ["tech_B"] }, 418 { "techs": ["tech_C"] }, 419 { "techs": ["tech_D"] } 420 ]); 421 422 // Techs and entities 423 template.requirements = { 424 "any": [ 425 { "any": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] }, 426 { "any": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] } 427 ] 428 }; 429 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ 430 { "techs": ["tech_A"] }, 431 { "entities": [{ "class": "class_A", "number": 5, "check": "count" }] }, 432 { "entities": [{ "class": "class_B", "number": 5, "check": "variants" }] }, 433 { "techs": ["tech_B"] } 434 ]); 435 436 // Two `civ`s, without and with a tech 437 template.requirements = { 438 "any": [ 439 { "any": [{ "civ": "athen" }, { "civ": "spart" }] } 440 ] 441 }; 442 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); 443 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); 444 445 template.requirements = { 446 "any": [ 447 { "tech": "required_tech" }, 448 { "any": [{ "civ": "athen" }, { "civ": "spart" }] } 449 ] 450 }; 451 // These requirements may not make sense, as the `civ`s are unable to restrict the requirements due to the outer `any` 452 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); 453 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); 454 455 // Two `notciv`s, without and with a tech 456 template.requirements = { 457 "any": [ 458 { "any": [{ "notciv": "athen" }, { "notciv": "spart" }] } 459 ] 460 }; 461 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); 462 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []); 463 464 template.requirements = { 465 "any": [ 466 { "tech": "required_tech" }, 467 { "any": [{ "notciv": "athen" }, { "notciv": "spart" }] } 468 ] 469 }; 470 // These requirements may not make sense, as the `notciv`s are made irrelevant by the outer `any` 471 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); 472 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); 473 474 475 /** 476 * Further tests 477 */ 478 479 template.requirements = { 480 "all": [ 481 { "tech": "tech1" }, 482 { "any": [{ "civ": "civA" }, { "civ": "civB" }] }, 483 { "notciv": "civC" } 484 ] 485 }; 486 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }]); 487 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civC"), false); 488 489 template.requirements = { 490 "any": [ 491 { "all": [{ "civ": "civA" }, { "tech": "tech1" }] }, 492 { "all": [{ "civ": "civB" }, { "tech": "tech2" }] } 493 ] 494 }; 495 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }]); 496 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civB"), [{ "techs": ["tech2"] }]); 497 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civC"), false); 498 499 template.requirements = { 500 "any": [ 501 { "all": [{ "civ": "civA" }, { "tech": "tech1" }] }, 502 { "all": [ 503 { "any": [{ "civ": "civB" }, { "civ": "civC" }] }, 504 { "tech": "tech2" } 505 ] } 506 ] 507 }; 508 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }]); 509 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civC"), [{ "techs": ["tech2"] }]); 510 TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civD"), false); -
binaries/data/mods/public/simulation/data/technologies/phase_city.json
diff --git a/binaries/data/mods/public/simulation/data/technologies/phase_city.json b/binaries/data/mods/public/simulation/data/technologies/phase_city.json index 809a20696..c14fed5dd 100644
a b 6 6 }, 7 7 "description": "Advances from a bustling town to a veritable metropolis, full of the wonders of modern technology.", 8 8 "cost": { "food": 0, "wood": 0, "stone": 750, "metal": 750 }, 9 "requirements": { "all": [{ " class": "Town", "number": 4}, { "notciv": "athen" }] },9 "requirements": { "all": [{ "entity": { "class": "Town", "number": 4 } }, { "notciv": "athen" }] }, 10 10 "requirementsTooltip": "Requires 4 new Town Phase structures (except Walls and Civic Centers).", 11 11 "supersedes": "phase_town", 12 12 "icon": "city_phase.png", -
binaries/data/mods/public/simulation/data/technologies/phase_city_athen.json
diff --git a/binaries/data/mods/public/simulation/data/technologies/phase_city_athen.json b/binaries/data/mods/public/simulation/data/technologies/phase_city_athen.json index a2c2b1085..2aaf38dd9 100644
a b 5 5 }, 6 6 "description": "Advances from a bustling town to a veritable metropolis, full of the wonders of modern technology. This is the Athenian city phase, where metal gathering rates are boosted because of the 'Silver Owls' bonus.", 7 7 "cost": { "food": 0, "wood": 0, "stone": 750, "metal": 750 }, 8 "requirements": { "all": [{ " class": "Town", "number": 4}, { "civ": "athen" }] },8 "requirements": { "all": [{ "entity": { "class": "Town", "number": 4 } }, { "civ": "athen" }] }, 9 9 "requirementsTooltip": "Requires 4 new Town Phase structures (except Walls and Civic Centers).", 10 10 "supersedes": "phase_town_athen", 11 11 "replaces": ["phase_city"], -
binaries/data/mods/public/simulation/data/technologies/phase_town.json
diff --git a/binaries/data/mods/public/simulation/data/technologies/phase_town.json b/binaries/data/mods/public/simulation/data/technologies/phase_town.json index 087435dbb..2bb12d10f 100644
a b 6 6 }, 7 7 "description": "Advances from a small village to a bustling town, ready to expand rapidly.", 8 8 "cost": { "food": 500, "wood": 500, "stone": 0, "metal": 0 }, 9 "requirements": { "all": [{ " class": "Village", "number": 5}, { "notciv": "athen" }] },9 "requirements": { "all": [{ "entity": { "class": "Village", "number": 5 } }, { "notciv": "athen" }] }, 10 10 "requirementsTooltip": "Requires 5 Village Phase structures (except Palisades and Farm Fields).", 11 11 "supersedes": "phase_village", 12 12 "icon": "town_phase.png", -
binaries/data/mods/public/simulation/data/technologies/phase_town_athen.json
diff --git a/binaries/data/mods/public/simulation/data/technologies/phase_town_athen.json b/binaries/data/mods/public/simulation/data/technologies/phase_town_athen.json index e5ae4bf0f..9cf810c49 100644
a b 5 5 }, 6 6 "description": "Advances from a small village to a bustling town, ready to expand rapidly. This is the Athenian town phase, where metal gathering rates are boosted because of the 'Silver Owls' bonus.", 7 7 "cost": { "food": 500, "wood": 500, "stone": 0, "metal": 0 }, 8 "requirements": { "all": [{ " class": "Village", "number": 5}, { "civ": "athen" }] },8 "requirements": { "all": [{ "entity": { "class": "Village", "number": 5 } }, { "civ": "athen" }] }, 9 9 "requirementsTooltip": "Requires 5 Village Phase structures (except Palisades and Farm Fields).", 10 10 "supersedes": "phase_village", 11 11 "replaces": ["phase_town"], -
binaries/data/mods/public/simulation/data/technologies/siege_bolt_accuracy.json
diff --git a/binaries/data/mods/public/simulation/data/technologies/siege_bolt_accuracy.json b/binaries/data/mods/public/simulation/data/technologies/siege_bolt_accuracy.json index 8a969440a..134223281 100644
a b 5 5 "requirements": { 6 6 "all": [ 7 7 { "tech": "phase_city" }, 8 { 9 "all": [ 10 { "notciv": "brit"}, 11 { "notciv": "gaul" }, 12 { "notciv": "iber" }, 13 { "notciv": "maur" }, 14 { "notciv": "pers" }, 15 { "notciv": "sele" } 16 ] 17 } 8 { "notciv": "brit"}, 9 { "notciv": "gaul" }, 10 { "notciv": "iber" }, 11 { "notciv": "maur" }, 12 { "notciv": "pers" }, 13 { "notciv": "sele" } 18 14 ] 19 15 }, 20 16 "requirementsTooltip": "Unlocked in City Phase.", -
binaries/data/mods/public/simulation/data/technologies/unlock_shared_dropsites.json
diff --git a/binaries/data/mods/public/simulation/data/technologies/unlock_shared_dropsites.json b/binaries/data/mods/public/simulation/data/technologies/unlock_shared_dropsites.json index 57f8ad689..d6007e5ff 100644
a b 2 2 "genericName": "Diaspora", 3 3 "description": "The extension of trade leads to the permanent establishment of storekeepers and their families in foreign countries, allowing them to exploit the wealth of these countries.", 4 4 "cost": { "food": 200, "wood": 200, "stone": 100, "metal": 100 }, 5 "requirements": { " class": "Trader", "number": 3},5 "requirements": { "entity": { "class": "Trader", "number": 3 } }, 6 6 "requirementsTooltip": "Requires 3 Traders", 7 7 "supersedes": "unlock_shared_los", 8 8 "icon": "diaspora.png",