Ticket #3993: notciv_v6.patch
File notciv_v6.patch, 39.2 KB (added by , 8 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 b6783e2..88da612 100644
a b function DoesModificationApply(modification, classes) 50 50 { 51 51 return MatchesClassList(classes, modification.affects); 52 52 } 53 54 55 /** 56 * Fetch the civ-specific technology requirements of a technology 57 * 58 * @param {object} techReqs - The technology requirements, derived from 59 * InterpretTechRequirements 60 * @param {string} civ - The civ 61 * 62 * @return An object containing the requirements specific to this civ 63 * for this technology or false if the civ cannot research this 64 * technology 65 */ 66 function GetCivSpecificReqsOfTech(techReqs, civ) 67 { 68 if (civ in techReqs) 69 { 70 if (techReqs[civ] === false) 71 return false; 72 return techReqs[civ] || {}; 73 } 74 75 if (techReqs.generic) 76 return techReqs.generic || {}; 77 78 return !Object.keys(techReqs).length; 79 } 80 81 /** 82 * Derives the technology requirements from a given technology template. 83 * Takes into account the `supersedes` attribute. 84 * 85 * @param {object} template - The template object. Loading of the template must have already occured. 86 * 87 * @return Derived technology requirements. See `InterpretTechRequirements` for object's syntax. 88 */ 89 function DeriveTechnologyRequirements(template) 90 { 91 let requirements = {} 92 93 if (template.requirements) 94 { 95 let op = Object.keys(template.requirements)[0]; 96 let val = template.requirements[op]; 97 requirements = InterpretTechRequirements(op, val); 98 } 99 100 if (template.supersedes) 101 { 102 if (requirements.generic) 103 for (let k in requirements.generic) 104 { 105 if (!requirements.generic[k].techs) 106 requirements.generic[k].techs = [[]]; 107 for (let t in requirements.generic[k].techs) 108 requirements.generic[k].techs[t].push(template.supersedes); 109 } 110 else if (!Object.keys(requirements).length) 111 requirements.generic = [{ "techs": [[template.supersedes]] }]; 112 else 113 for (let civ in requirements) 114 if (requirements[civ]) 115 for (let k in requirements[civ]) 116 { 117 if (!requirements[civ][k].techs) 118 requirements[civ][k].techs = [[]]; 119 for (let t in requirements[civ][k].techs) 120 requirements[civ][k].techs[t].push(template.supersedes); 121 } 122 } 123 124 return requirements; 125 } 126 127 /** 128 * Interprets the prerequisite requirements of a technology. 129 * 130 * Takes the initial { key: value } from the short-form requirements object in entity templates, 131 * and parses it into an object that can be more easily checked by simulation and gui. 132 * 133 * Works recursively if needed. 134 * 135 * The returned object is in the form: 136 * ``` 137 * { 138 * "civA": [ 139 * { "techs": [["tech1", "tech2"]] } 140 * ], 141 * "civB": [ 142 * { "entities": [[{ 143 * "class": "human", 144 * "number": 2, 145 * "variant": "count" 146 * }]] } 147 * ], 148 * "civC": false, 149 * "generic": [ 150 * { "techs": [["tech3"], ["tech4"]] } 151 * ] 152 * } 153 * ``` 154 * (Or, to translate: 155 * - `civA` needs both `tech1` and `tech2` 156 * - `civB` needs 2 entities with the `human` class 157 * - `civC` cannot research this tech at all 158 * - and everyone else needs either `tech2` or `tech3`) 159 * 160 * @param {string} operator - The base operation. Can be "civ", "notciv", "tech", "entity", "all" or "any". 161 * @param {mixed} value - The value associated with the above operation. 162 * 163 * @return Object containing the requirements, sorted. 164 */ 165 function InterpretTechRequirements(operator, value) 166 { 167 let requirements = {}; 168 const subReqTypes = ["techs", "entities"]; 169 170 switch (operator) 171 { 172 case "civ": 173 requirements[value] = []; 174 break; 175 176 case "notciv": 177 requirements[value] = false; 178 requirements.generic = []; 179 break; 180 181 case "class": 182 case "number": 183 case "numberOfTypes": 184 // do nothing 185 break; 186 187 case "entity": 188 requirements.generic = [{ "entities": [[{ 189 "class": value.class, 190 "number": value.number || value.numberOfTypes, 191 "check": value.number ? "count" : "variants" 192 }]] }]; 193 break; 194 195 case "tech": 196 requirements.generic = [{ "techs": [[value]] }]; 197 break; 198 199 case "all": 200 { 201 let civs = []; 202 let subReqs = [{}]; 203 for (let subvalue of value) 204 for (let newOper in subvalue) 205 { 206 let newValue = subvalue[newOper]; 207 let result = InterpretTechRequirements(newOper, newValue) 208 209 let subtype = subReqTypes.length; 210 switch (newOper) 211 { 212 case "civ": 213 civs.push(Object.keys(result)[0]); 214 break; 215 216 case "notciv": 217 for (let civ in result) 218 requirements[civ] = result[civ]; 219 break; 220 221 case "tech": 222 --subtype; 223 case "entity": 224 subtype = subReqTypes[--subtype]; 225 for (let sr in subReqs) 226 { 227 if (!subReqs[sr][subtype]) 228 subReqs[sr][subtype] = [[]]; 229 for (let e in subReqs[sr][subtype]) 230 subReqs[sr][subtype][e] = subReqs[sr][subtype][e].concat(result.generic[0][subtype][0]); 231 } 232 break; 233 234 case "any": 235 { 236 for (let civ in result) 237 if (civ === "generic") 238 { 239 let newReqs = []; 240 for (let sr in subReqs) 241 for (let type of subReqTypes) 242 for (let option of result.generic) 243 if (option[type]) 244 for (let t in option[type]) 245 if (!subReqs[sr][type] || !subReqs[sr][type].length) 246 newReqs.push(option); 247 else 248 for (let k in subReqs[sr][type]) 249 { 250 if (!newReqs[sr]) 251 newReqs[sr] = {}; 252 if (!newReqs[sr][type]) 253 newReqs[sr][type] = []; 254 newReqs[sr][type].push(subReqs[sr][type][k].concat(option[type][t])); 255 } 256 if (newReqs.length) 257 subReqs = newReqs; 258 } 259 else if (result[civ] === false) 260 requirements[civ] = false; 261 else 262 civs.push(civ); 263 } 264 break 265 266 case "all": 267 { 268 for (let civ in result) 269 { 270 if (civ === "generic" && result.generic[0]) 271 { 272 for (let sr in subReqs) 273 for (let type of subReqTypes) 274 if (result.generic[0][type]) 275 { 276 if (!subReqs[sr][type]) 277 subReqs[sr][type] = []; 278 if (subReqs[sr][type].length) 279 for (let k in result.generic[0][type]) 280 result.generic[0][type][k] = subReqs[sr][type][0].concat(result.generic[0][type][k]); 281 subReqs[sr][type] = result.generic[0][type]; 282 } 283 } 284 else if (result[civ] === false) 285 { 286 requirements[civ] = false; 287 requirements.generic = []; 288 } 289 else if (civ !== "generic") 290 civs.push(civ); 291 } 292 } 293 break; 294 295 } 296 } 297 298 if (!Object.keys(subReqs[0]).length) 299 subReqs = []; 300 301 if (!civs.length && subReqs.length) 302 requirements.generic = subReqs; 303 else 304 for (let civ of civs) 305 requirements[civ] = subReqs; 306 } 307 break; 308 309 case "any": 310 { 311 for (let subvalue of value) 312 for (let newOper in subvalue) 313 { 314 let newValue = subvalue[newOper]; 315 let result = InterpretTechRequirements(newOper, newValue) 316 317 let subtype = subReqTypes.length; 318 switch (newOper) 319 { 320 case "civ": 321 case "notciv": 322 for (let civ in result) 323 requirements[civ] = result[civ]; 324 break; 325 326 case "tech": 327 --subtype; 328 case "entity": 329 { 330 subtype = subReqTypes[--subtype]; 331 332 if (!requirements.generic) 333 requirements.generic = []; 334 let newReq = {}; 335 newReq[subtype] = [result.generic[0][subtype][0]]; 336 requirements.generic.push(newReq); 337 } 338 break; 339 340 case "all": 341 for (let civ in result) 342 { 343 requirements[civ] = [{}]; 344 for (let type of subReqTypes) 345 if (result[civ][0][type]) 346 requirements[civ][0][type] = result[civ][0][type]; 347 } 348 break; 349 350 case "any": 351 for (let civ in result) 352 { 353 if (!requirements[civ]) 354 requirements[civ] = []; 355 for (let opt of result[civ]) 356 requirements[civ].push(opt); 357 } 358 break; 359 } 360 } 361 } 362 break; 363 364 default: 365 warn("Unknown requirement operator: "+operator); 366 } 367 368 return requirements; 369 } -
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 9b622db..8f76311 100644
a b function GetTechnologyDataHelper(template, civ) 371 371 ret.tooltip = template.tooltip; 372 372 ret.requirementsTooltip = template.requirementsTooltip || ""; 373 373 374 if (template.requirements && template.requirements.class) 375 ret.classRequirements = { 376 "class": template.requirements.class, 377 "number": template.requirements.number 378 }; 374 ret.reqs = DeriveTechnologyRequirements(template); 379 375 380 376 ret.description = template.description; 381 377 -
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 6146b46..eaef316 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 depath(path) 44 { 45 return path.slice(path.lastIndexOf("/") + 1); 46 } -
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 fb12b00..6ee4fbf 100644
a b g_SelectionPanels.Research = { 731 731 setPanelObjectPosition(pair, data.i, data.rowLength); 732 732 733 733 // Handle one or two techs 734 let player = data.unitEntState.player; 734 735 for (let i in techs) 735 736 { 736 737 let tech = techs[i]; … … g_SelectionPanels.Research = { 743 744 744 745 let neededResources = Engine.GuiInterfaceCall("GetNeededResources", { 745 746 "cost": template.cost, 746 "player": data.unitEntState.player747 "player": player 747 748 }); 748 749 749 750 let requirementsPassed = Engine.GuiInterfaceCall("CheckTechnologyRequirements", { 750 751 "tech": tech, 751 "player": data.unitEntState.player752 "player": player 752 753 }); 753 754 754 755 let button = Engine.GetGUIObjectByName("unitResearchButton[" + position + "]"); … … g_SelectionPanels.Research = { 763 764 if (!requirementsPassed) 764 765 { 765 766 let tip = template.requirementsTooltip; 766 if (template.classRequirements) 767 let reqs = GetCivSpecificReqsOfTech(template.reqs, GetSimState().players[player].civ); 768 for (let req of reqs) 767 769 { 768 let player = data.unitEntState.player; 769 let current = GetSimState().players[player].classCounts[template.classRequirements.class] || 0; 770 let remaining = template.classRequirements.number - current; 771 tip += " " + sprintf(translatePlural("Remaining: %(number)s to build.", "Remaining: %(number)s to build.", remaining), { 772 "number": remaining 770 if (!req.entities) 771 continue; 772 773 let entityCounts = []; 774 for (let option of req.entities) 775 for (let entity of option) 776 { 777 let current = 0; 778 switch (entity.check) 779 { 780 case "count": 781 current = GetSimState().players[player].classCounts[entity.class] || 0; 782 break; 783 784 case "variants": 785 current = GetSimState().players[player].typeCountsByClass[entity.class] ? Object.keys(GetSimState().players[player].typeCountsByClass[entity.class]).length : 0; 786 break; 787 } 788 789 let remaining = entity.number - current; 790 if (remaining < 1) 791 continue; 792 793 entityCounts.push(sprintf(translatePlural("%(number)s entity of class %(class)s", "%(number)s entities of class %(class)s", remaining), { 794 "number": remaining, 795 "class": entity.class 796 })); 797 } 798 799 tip += " " + sprintf(translate("Remaining: %(entityCounts)s"), { 800 "entityCounts": entityCounts.join(translate(", ")) 773 801 }); 774 802 } 775 803 tooltips.push(tip); -
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 e5f76b6..6502fb5 100644
a b function fetchTokens(templateName, keypath) 92 92 return val._string.split(" "); 93 93 } 94 94 95 function depath(path)96 {97 return path.slice(path.lastIndexOf("/") + 1);98 }99 100 95 /** 101 96 * This is needed because getEntityCostTooltip in tooltip.js needs to get 102 97 * the template data of the different wallSet pieces. In the session this … … function GetTemplateData(templateName) 107 102 var template = loadTemplate(templateName); 108 103 return GetTemplateDataHelper(template, null, g_AuraData); 109 104 } 105 106 /** 107 * Determines and returns the phase in which a given technology can be 108 * first researched. Works recursively through the given tech's 109 * pre-requisite and superseded techs if necessary. 110 * 111 * @param {string} techName - The Technology's name 112 * @return The name of the phase the technology belongs to, or false if 113 * the current civ can't research this tech 114 */ 115 function GetPhaseOfTechnology(techName) 116 { 117 let phaseIdx = -1; 118 119 if (depath(techName).slice(0, 5) === "phase") 120 { 121 phaseIdx = g_ParsedData.phaseList.indexOf(GetActualPhase(techName)); 122 if (phaseIdx > 0) 123 return g_ParsedData.phaseList[phaseIdx - 1]; 124 } 125 126 let techReqs = GetCivSpecificReqsOfTech(g_ParsedData.techs[techName].reqs, g_SelectedCiv); 127 if (!techReqs) 128 return false; 129 130 for (let option in techReqs) 131 if (techReqs[option].techs) 132 for (let techs in techReqs[option].techs) 133 for (let req of techReqs[option].techs[techs]) 134 { 135 if (depath(req).slice(0, 5) === "phase") 136 return req; 137 phaseIdx = Math.max(phaseIdx, g_ParsedData.phaseList.indexOf(GetPhaseOfTechnology(req))); 138 } 139 return g_ParsedData.phaseList[phaseIdx] || false; 140 } 141 142 function GetActualPhase(phaseName) 143 { 144 if (g_ParsedData.phases[phaseName]) 145 return g_ParsedData.phases[phaseName].actualPhase; 146 147 warn("Unrecognised phase (" + techName + ")"); 148 return g_ParsedData.phaseList[0]; 149 } 150 151 function GetPhaseOfTemplate(template) 152 { 153 if (!template.requiredTechnology) 154 return g_ParsedData.phaseList[0]; 155 156 if (depath(template.requiredTechnology).slice(0, 5) == "phase") 157 return GetActualPhase(template.requiredTechnology); 158 159 return GetPhaseOfTechnology(template.requiredTechnology); 160 } -
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 1dfd332..82c0784 100644
a b function loadUnit(templateName) 39 39 { 40 40 if (!Engine.TemplateExists(templateName)) 41 41 return null; 42 var template = loadTemplate(templateName); 43 44 var unit = GetTemplateDataHelper(template, null, g_AuraData); 45 unit.phase = false; 46 47 if (unit.requiredTechnology) 48 { 49 if (depath(unit.requiredTechnology).slice(0, 5) == "phase") 50 unit.phase = unit.requiredTechnology; 51 else if (unit.requiredTechnology.length) 52 unit.required = unit.requiredTechnology; 53 } 42 let template = loadTemplate(templateName); 43 let unit = GetTemplateDataHelper(template, null, g_AuraData); 54 44 55 45 unit.gather = getGatherRates(templateName); 56 46 … … function loadUnit(templateName) 93 83 94 84 function loadStructure(templateName) 95 85 { 96 var template = loadTemplate(templateName); 97 var structure = GetTemplateDataHelper(template, null, g_AuraData); 98 structure.phase = false; 99 100 if (structure.requiredTechnology) 101 { 102 if (depath(structure.requiredTechnology).slice(0, 5) == "phase") 103 structure.phase = structure.requiredTechnology; 104 else if (structure.requiredTechnology.length) 105 structure.required = structure.requiredTechnology; 106 } 86 let template = loadTemplate(templateName); 87 let structure = GetTemplateDataHelper(template, null, g_AuraData); 107 88 108 89 structure.production = { 109 90 "technology": [], … … function loadStructure(templateName) 179 160 180 161 function loadTechnology(techName) 181 162 { 182 var template = loadTechData(techName); 183 var tech = GetTechnologyDataHelper(template, g_SelectedCiv); 184 tech.reqs = {}; 163 let template = loadTechData(techName); 164 let tech = GetTechnologyDataHelper(template, g_SelectedCiv); 185 165 186 166 if (template.pair !== undefined) 187 167 tech.pair = template.pair; 188 168 189 if (template.requirements !== undefined)190 {191 for (let op in template.requirements)192 {193 let val = template.requirements[op];194 let req = calcReqs(op, val);195 196 switch (op)197 {198 case "tech":199 tech.reqs.generic = req;200 break;201 202 case "civ":203 tech.reqs[req] = [];204 break;205 206 case "any":207 if (req[0].length > 0)208 for (let r of req[0])209 {210 let v = req[0][r];211 if (typeof r == "number")212 tech.reqs[v] = [];213 else214 tech.reqs[r] = v;215 }216 if (req[1].length > 0)217 tech.reqs.generic = req[1];218 break;219 220 case "all":221 if (!req[0].length)222 tech.reqs.generic = req[1];223 else224 for (let r of req[0])225 tech.reqs[r] = req[1];226 break;227 }228 }229 }230 231 if (template.supersedes !== undefined)232 {233 if (tech.reqs.generic !== undefined)234 tech.reqs.generic.push(template.supersedes);235 else236 for (let ck of Object.keys(tech.reqs))237 tech.reqs[ck].push(template.supersedes);238 }239 240 169 return tech; 241 170 } 242 171 … … function loadTechnologyPair(pairCode) 258 187 259 188 return { 260 189 "techs": [ pairInfo.top, pairInfo.bottom ], 261 "req ": pairInfo.supersedes || ""190 "reqs": DeriveTechnologyRequirements(pairInfo) 262 191 }; 263 192 } 264 193 265 194 /** 266 * Calculate the prerequisite requirements of a technology.267 * Works recursively if needed.268 *269 * @param op The base operation. Can be "civ", "tech", "all" or "any".270 * @param val The value associated with the above operation.271 *272 * @return Sorted requirments.273 */274 function calcReqs(op, val)275 {276 switch (op)277 {278 case "civ":279 case "class":280 case "notciv":281 case "number":282 // nothing needs doing283 break;284 285 case "tech":286 if (depath(val).slice(0,4) === "pair")287 return loadTechnologyPair(val).techs;288 return [ val ];289 290 case "all":291 case "any":292 let t = [];293 let c = [];294 for (let nv of val)295 {296 for (let o in nv)297 {298 let v = nv[o];299 let r = calcReqs(o, v);300 switch (o)301 {302 case "civ":303 case "notciv":304 c.push(r);305 break;306 307 case "tech":308 t = t.concat(r);309 break;310 311 case "any":312 c = c.concat(r[0]);313 t = t.concat(r[1]);314 break;315 316 case "all":317 for (let ci in r[0])318 c[ci] = r[1];319 t = t;320 }321 }322 }323 return [ c, t ];324 325 default:326 warn("Unknown reqs operator: "+op);327 }328 return val;329 }330 331 /**332 195 * Unravel phases 333 196 * 334 197 * @param techs The current available store of techs … … function unravelPhases(techs) 343 206 { 344 207 let techdata = techs[techcode]; 345 208 346 if (! ("generic" in techdata.reqs) || techdata.reqs.generic.length < 2)209 if (!techdata.reqs.generic || !techdata.reqs.generic[0].techs || techdata.reqs.generic[0].techs[0].length < 2) 347 210 continue; 348 211 349 let reqTech = techs[techcode].reqs.generic[ 1];212 let reqTech = techs[techcode].reqs.generic[0].techs[0][1]; 350 213 351 214 // Tech that can't be researched anywhere 352 215 if (!(reqTech in techs)) … … function unravelPhases(techs) 355 218 if (!("generic" in techs[reqTech].reqs)) 356 219 continue; 357 220 358 let reqPhase = techs[reqTech].reqs.generic[0] ;359 let myPhase = techs[techcode].reqs.generic[0] ;221 let reqPhase = techs[reqTech].reqs.generic[0].techs[0][0]; 222 let myPhase = techs[techcode].reqs.generic[0].techs[0][0]; 360 223 361 224 if (reqPhase == myPhase || depath(reqPhase).slice(0,5) !== "phase" || depath(myPhase).slice(0,5) !== "phase") 362 225 continue; … … function unravelPhases(techs) 364 227 let reqPhasePos = phaseList.indexOf(reqPhase); 365 228 let myPhasePos = phaseList.indexOf(myPhase); 366 229 367 if ( phaseList.length === 0)230 if (!phaseList.length) 368 231 phaseList = [reqPhase, myPhase]; 369 232 else if (reqPhasePos < 0 && myPhasePos > -1) 370 233 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 65bfd9e..04d6049 100644
a b function selectCiv(civCode) 62 62 }; 63 63 64 64 // get initial units 65 var startStructs = [];66 65 for (let entity of g_CivData[civCode].StartEntities) 67 66 { 68 67 if (entity.Template.slice(0, 5) == "units") 69 68 g_Lists.units.push(entity.Template); 70 69 else if (entity.Template.slice(0, 6) == "struct") 71 {72 70 g_Lists.structures.push(entity.Template); 73 startStructs.push(entity.Template);74 }75 71 } 76 72 77 73 // Load units and structures … … function selectCiv(civCode) 115 111 else 116 112 { 117 113 let newTech = loadTechnology(techcode); 118 if (pair.req !== "")114 for (let civ in pair.reqs) 119 115 { 120 if ("generic" in newTech.reqs) 121 newTech.reqs.generic.concat(techPairs[pair.req].techs); 122 else 116 if (!newTech.reqs[civ]) 117 newTech.reqs[civ] = {}; 118 else if (newTech.reqs[civ] === false) 119 continue; 120 121 if (pair.reqs[civ] === false) 123 122 { 124 for (let civkey of Object.keys(newTech.reqs))125 newTech.reqs[civkey].concat(techPairs[pair.req].techs);123 newTech.reqs[civ] = false; 124 continue; 126 125 } 126 127 for (let type in pair.reqs[civ]) 128 if (pair.reqs[civ][type]) 129 newTech.reqs[civ][type].concat(pair.reqs[civ][type]) 127 130 } 128 131 g_ParsedData.techs[techcode] = newTech; 129 132 } … … function selectCiv(civCode) 169 172 for (let structCode of g_Lists.structures) 170 173 { 171 174 let structInfo = g_ParsedData.structures[structCode]; 175 structInfo.phase = GetPhaseOfTemplate(structInfo); 172 176 let structPhaseIdx = g_ParsedData.phaseList.indexOf(structInfo.phase); 173 177 174 178 // If this building is shared with another civ, … … function selectCiv(civCode) 180 184 for (let prod of structInfo.production.technology) 181 185 if (prod in techPairs) 182 186 structInfo.production.technology.splice( 183 structInfo.production.technology.indexOf(prod), 1,184 techPairs[prod].techs[0], techPairs[prod].techs[1]187 structInfo.production.technology.indexOf(prod), 188 1, ...techPairs[prod].techs 185 189 ); 186 190 187 191 // Sort techs by phase 188 192 let newProdTech = {}; 189 193 for (let prod of structInfo.production.technology) 190 194 { 191 let phase = ""; 192 193 if (depath(prod).slice(0,5) === "phase") 194 { 195 phase = g_ParsedData.phaseList.indexOf(g_ParsedData.phases[prod].actualPhase); 196 if (phase > 0) 197 phase = g_ParsedData.phaseList[phase - 1]; 198 } 199 else if (g_SelectedCiv in g_ParsedData.techs[prod].reqs) 200 { 201 for (let req of g_ParsedData.techs[prod].reqs[g_SelectedCiv]) 202 if (depath(req).slice(0,5) === "phase") 203 phase = req; 204 } 205 else if ("generic" in g_ParsedData.techs[prod].reqs) 206 { 207 for (let req of g_ParsedData.techs[prod].reqs.generic) 208 if (depath(req).slice(0,5) === "phase") 209 phase = req; 210 } 195 let phase = GetPhaseOfTechnology(prod); 196 if (phase === false) 197 continue; 211 198 212 if (depath(phase).slice(0,5) !== "phase" || 213 g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx) 214 { 215 if (structInfo.phase !== false) 216 phase = structInfo.phase; 217 else 218 phase = g_ParsedData.phaseList[0]; 219 } 199 if (g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx) 200 phase = structInfo.phase; 220 201 221 202 if (!(phase in newProdTech)) 222 203 newProdTech[phase] = []; … … function selectCiv(civCode) 224 205 newProdTech[phase].push(prod); 225 206 } 226 207 227 // Determine phase for units208 // Sort units by phase 228 209 let newProdUnits = {}; 229 210 for (let prod of structInfo.production.units) 230 211 { 231 212 if (!g_ParsedData.units[prod]) 232 213 continue; 233 214 234 let unit = g_ParsedData.units[prod]; 235 let phase = ""; 236 237 if (unit.phase !== false) 238 { 239 if (g_ParsedData.phaseList.indexOf(unit.phase) < 0) 240 phase = g_ParsedData.phases[unit.phase].actualPhase; 241 else 242 phase = unit.phase; 243 } 244 else if (unit.required !== undefined) 245 { 246 if (g_ParsedData.phases[unit.required]) 247 phase = g_ParsedData.phases[unit.required].actualPhase; 248 else if (g_ParsedData.techs[unit.required]) 249 { 250 let reqs = g_ParsedData.techs[unit.required].reqs; 251 if (reqs[g_SelectedCiv]) 252 phase = reqs[g_SelectedCiv][0]; 253 else if (reqs.generic) 254 phase = reqs.generic[0]; 255 else 256 warn("Empty requirements found on technology " + unit.required); 257 } 258 else 259 warn("Technology " + unit.required + " for " + prod + " not found."); 260 } 215 let phase = GetPhaseOfTemplate(g_ParsedData.units[prod]); 216 if (phase === false) 217 continue; 261 218 262 if (depath(phase).slice(0,5) !== "phase" || g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx) 263 if (structInfo.phase !== false) 264 phase = structInfo.phase; 265 else 266 phase = g_ParsedData.phaseList[0]; 219 if (g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx) 220 phase = structInfo.phase; 267 221 268 222 if (!(phase in newProdUnits)) 269 223 newProdUnits[phase] = []; … … function selectCiv(civCode) 278 232 } 279 233 280 234 // Determine the buildList for the civ (grouped by phase) 281 varbuildList = {};282 vartrainerList = [];235 let buildList = {}; 236 let trainerList = []; 283 237 for (let pha of g_ParsedData.phaseList) 284 238 buildList[pha] = []; 285 239 for (let structCode of g_Lists.structures) 286 240 { 287 if (!g_ParsedData.structures[structCode].phase || startStructs.indexOf(structCode) > -1) 288 g_ParsedData.structures[structCode].phase = g_ParsedData.phaseList[0]; 289 290 let myPhase = g_ParsedData.structures[structCode].phase; 291 if (g_ParsedData.phaseList.indexOf(myPhase) === -1) 292 myPhase = g_ParsedData.phases[myPhase].actualPhase; 293 294 buildList[myPhase].push(structCode); 241 let phase = g_ParsedData.structures[structCode].phase; 242 buildList[phase].push(structCode); 295 243 } 296 244 for (let unitCode of g_Lists.units) 297 245 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 2aad561..bae3a05 100644
a b m.GameState.prototype.canResearch = function(techTemplateName, noRequirementChec 199 199 200 200 if (noRequirementCheck === true) 201 201 return true; 202 203 // not already researched, check if we can.204 // basically a copy of the function in technologyManager since we can't use it.205 // Checks the requirements for a technology to see if it can be researched at the current time206 207 // The technology which this technology supersedes is required208 if (template.supersedes() && !this.playerData.researchedTechs[template.supersedes()])209 return false;210 202 211 203 // if this is a pair, we must check that the pair tech is not being researched 212 204 if (template.pair()) … … m.GameState.prototype.canResearch = function(techTemplateName, noRequirementChec 222 214 }; 223 215 224 216 /** 225 * Private function for checking a set of requirements is met 226 * basically copies TechnologyManager 217 * Private function for checking a set of requirements is met. 218 * Basically copies TechnologyManager, but compares against 219 * variables only available within the AI 227 220 */ 228 221 m.GameState.prototype.checkTechRequirements = function(reqs) 229 222 { 230 // If there are no requirements then all requirements are met 223 reqs = GetCivSpecificReqsOfTech(reqs, this.playerData.civ); 224 231 225 if (!reqs) 232 return true; 233 234 if (reqs.all) 235 return reqs.all.every(r => this.checkTechRequirements(r)); 236 if (reqs.any) 237 return reqs.any.some(r => this.checkTechRequirements(r)); 238 if (reqs.civ) 239 return this.playerData.civ == reqs.civ; 240 if (reqs.notciv) 241 return this.playerData.civ != reqs.notciv; 242 if (reqs.tech) 243 return this.playerData.researchedTechs[reqs.tech] !== undefined && this.playerData.researchedTechs[reqs.tech]; 244 if (reqs.class && reqs.numberOfTypes) 245 return this.playerData.typeCountsByClass[reqs.class] && 246 Object.keys(this.playerData.typeCountsByClass[reqs.class]).length >= reqs.numberOfTypes; 247 if (reqs.class && reqs.number) 248 return this.playerData.classCounts[reqs.class] && 249 this.playerData.classCounts[reqs.class] >= reqs.number; 250 251 // The technologies requirements are not a recognised format 252 error("Bad requirements " + uneval(reqs)); 253 return false; 226 return false; 227 228 if (reqs.techs) 229 for (let prerequisite of reqs.techs) 230 if (!this.playerData.researchedTechs[prerequisite]) 231 return false; 232 233 if (reqs.entities) 234 for (let entity of reqs.entities) 235 { 236 switch (entity.check) 237 { 238 case "count": 239 if (!this.playerData.classCounts[entity.class] || this.playerData.classCounts[entity.class] < entity.number) 240 return false; 241 break; 242 243 case "variants": 244 if (!this.playerData.typeCountsByClass[entity.class] || Object.keys(this.playerData.typeCountsByClass[entity.class]).length < entity.number) 245 return false; 246 break; 247 } 248 } 249 250 return true; 254 251 }; 255 252 256 253 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 09f6504..5ae1fc5 100644
a b m.Technology.prototype.researchTime = function() 99 99 100 100 m.Technology.prototype.requirements = function() 101 101 { 102 if (!this._template.requirements) 103 return undefined; 104 return this._template.requirements; 102 return DeriveTechnologyRequirements(this._template); 105 103 }; 106 104 107 105 m.Technology.prototype.autoResearch = function() -
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 6c72202..6d8cb98 100644
a b ProductionQueue.prototype.GetTechnologiesList = function() 168 168 169 169 // Remove any technologies that can't be researched by this civ 170 170 techs = techs.filter(tech => { 171 let reqs = cmpTechnologyManager.GetTechnologyTemplate(tech).requirements || null;171 let reqs = DeriveTechnologyRequirements(cmpTechnologyManager.GetTechnologyTemplate(tech)); 172 172 return cmpTechnologyManager.CheckTechnologyRequirements(reqs, true); 173 173 }); 174 174 -
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 f1f9cc7..aa40694 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)); 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 reqs = GetCivSpecificReqsOfTech(reqs, cmpPlayer.GetCiv()); 135 138 136 if (!reqs) 137 return false; 138 139 if (civonly) 139 140 return true; 140 141 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) 142 return reqs.some(req => { 143 return Object.keys(req).every(type => { 144 switch (type) 145 { 146 case "techs": 147 return req[type].some(tech => tech.every(this.IsTechnologyResearched, this)); 148 149 case "entities": 150 return req[type].some(entity => entity.every(this.DoesEntitySpecPass, this)); 151 } 152 return false; 153 }); 154 }); 155 } 156 157 TechnologyManager.prototype.DoesEntitySpecPass = function(entity) 158 { 159 switch (entity.check) 151 160 { 152 let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 153 return cmpPlayer && cmpPlayer.GetCiv() != reqs.notciv; 161 case "count": 162 if (!this.classCounts[entity.class] || this.classCounts[entity.class] < entity.number) 163 return false; 164 break; 165 166 case "variants": 167 if (!this.typeCountsByClass[entity.class] || Object.keys(this.typeCountsByClass[entity.class]).length < entity.number) 168 return false; 169 break; 154 170 } 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; 171 return true; 168 172 }; 169 173 170 174 TechnologyManager.prototype.OnGlobalOwnershipChanged = function(msg) -
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 01c5345..73d37a4 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 97b63da..42dd624 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 087435d..2bb12d1 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 e5ae4bf..9cf810c 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/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 57f8ad6..d6007e5 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",