Line data Source code
1 : /** 2 : * Manage the research 3 : */ 4 0 : PETRA.ResearchManager = function(Config) 5 : { 6 0 : this.Config = Config; 7 : }; 8 : 9 : /** 10 : * Check if we can go to the next phase 11 : */ 12 0 : PETRA.ResearchManager.prototype.checkPhase = function(gameState, queues) 13 : { 14 0 : if (queues.majorTech.hasQueuedUnits()) 15 0 : return; 16 : // Don't try to phase up if already trying to gather resources for a civil-centre or wonder 17 0 : if (queues.civilCentre.hasQueuedUnits() || queues.wonder.hasQueuedUnits()) 18 0 : return; 19 : 20 0 : let currentPhaseIndex = gameState.currentPhase(); 21 0 : let nextPhaseName = gameState.getPhaseName(currentPhaseIndex+1); 22 0 : if (!nextPhaseName) 23 0 : return; 24 : 25 : let petraRequirements = 26 0 : currentPhaseIndex == 1 && gameState.ai.HQ.getAccountedPopulation(gameState) >= this.Config.Economy.popPhase2 || 27 : currentPhaseIndex == 2 && gameState.ai.HQ.getAccountedWorkers(gameState) > this.Config.Economy.workPhase3 || 28 : currentPhaseIndex >= 3 && gameState.ai.HQ.getAccountedWorkers(gameState) > this.Config.Economy.workPhase4; 29 0 : if (petraRequirements && gameState.hasResearchers(nextPhaseName, true)) 30 : { 31 0 : gameState.ai.HQ.phasing = currentPhaseIndex + 1; 32 : // Reset the queue priority in case it was changed during a previous phase update 33 0 : gameState.ai.queueManager.changePriority("majorTech", gameState.ai.Config.priorities.majorTech); 34 0 : queues.majorTech.addPlan(new PETRA.ResearchPlan(gameState, nextPhaseName, true)); 35 : } 36 : }; 37 : 38 0 : PETRA.ResearchManager.prototype.researchPopulationBonus = function(gameState, queues) 39 : { 40 0 : if (queues.minorTech.hasQueuedUnits()) 41 0 : return; 42 : 43 0 : let techs = gameState.findAvailableTech(); 44 0 : for (let tech of techs) 45 : { 46 0 : if (!tech[1]._template.modifications) 47 0 : continue; 48 : // TODO may-be loop on all modifs and check if the effect if positive ? 49 0 : if (tech[1]._template.modifications[0].value !== "Population/Bonus") 50 0 : continue; 51 0 : queues.minorTech.addPlan(new PETRA.ResearchPlan(gameState, tech[0])); 52 0 : break; 53 : } 54 : }; 55 : 56 0 : PETRA.ResearchManager.prototype.researchTradeBonus = function(gameState, queues) 57 : { 58 0 : if (queues.minorTech.hasQueuedUnits()) 59 0 : return; 60 : 61 0 : let techs = gameState.findAvailableTech(); 62 0 : for (let tech of techs) 63 : { 64 0 : if (!tech[1]._template.modifications || !tech[1]._template.affects) 65 0 : continue; 66 0 : if (tech[1]._template.affects.indexOf("Trader") === -1) 67 0 : continue; 68 : // TODO may-be loop on all modifs and check if the effect if positive ? 69 0 : if (tech[1]._template.modifications[0].value !== "UnitMotion/WalkSpeed" && 70 : tech[1]._template.modifications[0].value !== "Trader/GainMultiplier") 71 0 : continue; 72 0 : queues.minorTech.addPlan(new PETRA.ResearchPlan(gameState, tech[0])); 73 0 : break; 74 : } 75 : }; 76 : 77 : /** Techs to be searched for as soon as they are available */ 78 0 : PETRA.ResearchManager.prototype.researchWantedTechs = function(gameState, techs) 79 : { 80 0 : let phase1 = gameState.currentPhase() === 1; 81 0 : let available = phase1 ? gameState.ai.queueManager.getAvailableResources(gameState) : null; 82 0 : const numWorkers = phase1 ? gameState.getOwnEntitiesByRole(PETRA.Worker.ROLE_WORKER, true).length : 0; 83 0 : for (let tech of techs) 84 : { 85 0 : if (tech[0].indexOf("unlock_champion") == 0) 86 0 : return { "name": tech[0], "increasePriority": true }; 87 0 : if (tech[0] == "traditional_army_sele" || tech[0] == "reformed_army_sele") 88 0 : return { "name": pickRandom(["traditional_army_sele", "reformed_army_sele"]), "increasePriority": true }; 89 : 90 0 : if (!tech[1]._template.modifications) 91 0 : continue; 92 0 : let template = tech[1]._template; 93 0 : if (phase1) 94 : { 95 0 : let cost = template.cost; 96 0 : let costMax = 0; 97 0 : for (let res in cost) 98 0 : costMax = Math.max(costMax, Math.max(cost[res]-available[res], 0)); 99 0 : if (10*numWorkers < costMax) 100 0 : continue; 101 : } 102 0 : for (let i in template.modifications) 103 : { 104 0 : if (gameState.ai.HQ.navalMap && template.modifications[i].value === "ResourceGatherer/Rates/food.fish") 105 0 : return { "name": tech[0], "increasePriority": this.CostSum(template.cost) < 400 }; 106 0 : else if (template.modifications[i].value === "ResourceGatherer/Rates/food.fruit") 107 0 : return { "name": tech[0], "increasePriority": this.CostSum(template.cost) < 400 }; 108 0 : else if (template.modifications[i].value === "ResourceGatherer/Rates/food.grain") 109 0 : return { "name": tech[0], "increasePriority": false }; 110 0 : else if (template.modifications[i].value === "ResourceGatherer/Rates/wood.tree") 111 0 : return { "name": tech[0], "increasePriority": this.CostSum(template.cost) < 400 }; 112 0 : else if (template.modifications[i].value.startsWith("ResourceGatherer/Capacities")) 113 0 : return { "name": tech[0], "increasePriority": false }; 114 0 : else if (template.modifications[i].value === "Attack/Ranged/MaxRange") 115 0 : return { "name": tech[0], "increasePriority": false }; 116 : } 117 : } 118 0 : return null; 119 : }; 120 : 121 : /** Techs to be searched for as soon as they are available, but only after phase 2 */ 122 0 : PETRA.ResearchManager.prototype.researchPreferredTechs = function(gameState, techs) 123 : { 124 0 : let phase2 = gameState.currentPhase() === 2; 125 0 : let available = phase2 ? gameState.ai.queueManager.getAvailableResources(gameState) : null; 126 0 : const numWorkers = phase2 ? gameState.getOwnEntitiesByRole(PETRA.Worker.ROLE_WORKER, true).length : 0; 127 0 : for (let tech of techs) 128 : { 129 0 : if (!tech[1]._template.modifications) 130 0 : continue; 131 0 : let template = tech[1]._template; 132 0 : if (phase2) 133 : { 134 0 : let cost = template.cost; 135 0 : let costMax = 0; 136 0 : for (let res in cost) 137 0 : costMax = Math.max(costMax, Math.max(cost[res]-available[res], 0)); 138 0 : if (10*numWorkers < costMax) 139 0 : continue; 140 : } 141 0 : for (let i in template.modifications) 142 : { 143 0 : if (template.modifications[i].value === "ResourceGatherer/Rates/stone.rock") 144 0 : return { "name": tech[0], "increasePriority": this.CostSum(template.cost) < 400 }; 145 0 : else if (template.modifications[i].value === "ResourceGatherer/Rates/metal.ore") 146 0 : return { "name": tech[0], "increasePriority": this.CostSum(template.cost) < 400 }; 147 0 : else if (template.modifications[i].value === "BuildingAI/DefaultArrowCount") 148 0 : return { "name": tech[0], "increasePriority": this.CostSum(template.cost) < 400 }; 149 0 : else if (template.modifications[i].value === "Health/RegenRate") 150 0 : return { "name": tech[0], "increasePriority": false }; 151 0 : else if (template.modifications[i].value === "Health/IdleRegenRate") 152 0 : return { "name": tech[0], "increasePriority": false }; 153 : } 154 : } 155 0 : return null; 156 : }; 157 : 158 0 : PETRA.ResearchManager.prototype.update = function(gameState, queues) 159 : { 160 0 : if (queues.minorTech.hasQueuedUnits() || queues.majorTech.hasQueuedUnits()) 161 0 : return; 162 : 163 0 : let techs = gameState.findAvailableTech(); 164 : 165 0 : let techName = this.researchWantedTechs(gameState, techs); 166 0 : if (techName) 167 : { 168 0 : if (techName.increasePriority) 169 : { 170 0 : gameState.ai.queueManager.changePriority("minorTech", 2*this.Config.priorities.minorTech); 171 0 : let plan = new PETRA.ResearchPlan(gameState, techName.name); 172 0 : plan.queueToReset = "minorTech"; 173 0 : queues.minorTech.addPlan(plan); 174 : } 175 : else 176 0 : queues.minorTech.addPlan(new PETRA.ResearchPlan(gameState, techName.name)); 177 0 : return; 178 : } 179 : 180 0 : if (gameState.currentPhase() < 2) 181 0 : return; 182 : 183 0 : techName = this.researchPreferredTechs(gameState, techs); 184 0 : if (techName) 185 : { 186 0 : if (techName.increasePriority) 187 : { 188 0 : gameState.ai.queueManager.changePriority("minorTech", 2*this.Config.priorities.minorTech); 189 0 : let plan = new PETRA.ResearchPlan(gameState, techName.name); 190 0 : plan.queueToReset = "minorTech"; 191 0 : queues.minorTech.addPlan(plan); 192 : } 193 : else 194 0 : queues.minorTech.addPlan(new PETRA.ResearchPlan(gameState, techName.name)); 195 0 : return; 196 : } 197 : 198 0 : if (gameState.currentPhase() < 3) 199 0 : return; 200 : 201 : // remove some techs not yet used by this AI 202 : // remove also sharedLos if we have no ally 203 0 : for (let i = 0; i < techs.length; ++i) 204 : { 205 0 : let template = techs[i][1]._template; 206 0 : if (template.affects && template.affects.length === 1 && 207 : (template.affects[0] === "Healer" || template.affects[0] === "Outpost" || template.affects[0] === "Wall")) 208 : { 209 0 : techs.splice(i--, 1); 210 0 : continue; 211 : } 212 0 : if (template.modifications && template.modifications.length === 1 && 213 : this.Config.unusedNoAllyTechs.includes(template.modifications[0].value) && 214 : !gameState.hasAllies()) 215 : { 216 0 : techs.splice(i--, 1); 217 0 : continue; 218 : } 219 : } 220 0 : if (!techs.length) 221 0 : return; 222 : 223 : // randomly pick one. No worries about pairs in that case. 224 0 : queues.minorTech.addPlan(new PETRA.ResearchPlan(gameState, pickRandom(techs)[0])); 225 : }; 226 : 227 0 : PETRA.ResearchManager.prototype.CostSum = function(cost) 228 : { 229 0 : let costSum = 0; 230 0 : for (let res in cost) 231 0 : costSum += cost[res]; 232 0 : return costSum; 233 : }; 234 : 235 0 : PETRA.ResearchManager.prototype.Serialize = function() 236 : { 237 0 : return {}; 238 : }; 239 : 240 0 : PETRA.ResearchManager.prototype.Deserialize = function(data) 241 : { 242 : };