Line data Source code
1 : /**
2 : * This class makes a worker do as instructed by the economy manager
3 : */
4 0 : PETRA.Worker = function(base)
5 : {
6 0 : this.ent = undefined;
7 0 : this.base = base;
8 0 : this.baseID = base.ID;
9 : };
10 :
11 0 : PETRA.Worker.ROLE_ATTACK = "attack";
12 0 : PETRA.Worker.ROLE_TRADER = "trader";
13 0 : PETRA.Worker.ROLE_SWITCH_TO_TRADER = "switchToTrader";
14 0 : PETRA.Worker.ROLE_WORKER = "worker";
15 0 : PETRA.Worker.ROLE_CRITICAL_ENT_GUARD = "criticalEntGuard";
16 0 : PETRA.Worker.ROLE_CRITICAL_ENT_HEALER = "criticalEntHealer";
17 :
18 0 : PETRA.Worker.SUBROLE_DEFENDER = "defender";
19 0 : PETRA.Worker.SUBROLE_IDLE = "idle";
20 0 : PETRA.Worker.SUBROLE_BUILDER = "builder";
21 0 : PETRA.Worker.SUBROLE_COMPLETING = "completing";
22 0 : PETRA.Worker.SUBROLE_WALKING = "walking";
23 0 : PETRA.Worker.SUBROLE_ATTACKING = "attacking";
24 0 : PETRA.Worker.SUBROLE_GATHERER = "gatherer";
25 0 : PETRA.Worker.SUBROLE_HUNTER = "hunter";
26 0 : PETRA.Worker.SUBROLE_FISHER = "fisher";
27 0 : PETRA.Worker.SUBROLE_GARRISONING = "garrisoning";
28 :
29 0 : PETRA.Worker.prototype.update = function(gameState, ent)
30 : {
31 0 : if (!ent.position() || ent.getMetadata(PlayerID, "plan") == -2 || ent.getMetadata(PlayerID, "plan") == -3)
32 0 : return;
33 :
34 0 : let subrole = ent.getMetadata(PlayerID, "subrole");
35 :
36 : // If we are waiting for a transport or we are sailing, just wait
37 0 : if (ent.getMetadata(PlayerID, "transport") !== undefined)
38 : {
39 : // Except if builder with their foundation destroyed, in which case cancel the transport if not yet on board
40 0 : if (subrole === PETRA.Worker.SUBROLE_BUILDER && ent.getMetadata(PlayerID, "target-foundation") !== undefined)
41 : {
42 0 : let plan = gameState.ai.HQ.navalManager.getPlan(ent.getMetadata(PlayerID, "transport"));
43 0 : let target = gameState.getEntityById(ent.getMetadata(PlayerID, "target-foundation"));
44 0 : if (!target && plan && plan.state === PETRA.TransportPlan.BOARDING && ent.position())
45 0 : plan.removeUnit(gameState, ent);
46 : }
47 : // and gatherer if there are no more dropsite accessible in the base the ent is going to
48 0 : if (subrole === PETRA.Worker.SUBROLE_GATHERER || subrole === PETRA.Worker.SUBROLE_HUNTER)
49 : {
50 0 : let plan = gameState.ai.HQ.navalManager.getPlan(ent.getMetadata(PlayerID, "transport"));
51 0 : if (plan.state === PETRA.TransportPlan.BOARDING && ent.position())
52 : {
53 0 : let hasDropsite = false;
54 0 : let gatherType = ent.getMetadata(PlayerID, "gather-type") || "food";
55 0 : for (let structure of gameState.getOwnStructures().values())
56 : {
57 0 : if (PETRA.getLandAccess(gameState, structure) != plan.endIndex)
58 0 : continue;
59 0 : let resourceDropsiteTypes = PETRA.getBuiltEntity(gameState, structure).resourceDropsiteTypes();
60 0 : if (!resourceDropsiteTypes || resourceDropsiteTypes.indexOf(gatherType) == -1)
61 0 : continue;
62 0 : hasDropsite = true;
63 0 : break;
64 : }
65 0 : if (!hasDropsite)
66 : {
67 0 : for (let unit of gameState.getOwnUnits().filter(API3.Filters.byClass("Support")).values())
68 : {
69 0 : if (!unit.position() || PETRA.getLandAccess(gameState, unit) != plan.endIndex)
70 0 : continue;
71 0 : let resourceDropsiteTypes = unit.resourceDropsiteTypes();
72 0 : if (!resourceDropsiteTypes || resourceDropsiteTypes.indexOf(gatherType) == -1)
73 0 : continue;
74 0 : hasDropsite = true;
75 0 : break;
76 : }
77 : }
78 0 : if (!hasDropsite)
79 0 : plan.removeUnit(gameState, ent);
80 : }
81 : }
82 0 : if (ent.getMetadata(PlayerID, "transport") !== undefined)
83 0 : return;
84 : }
85 :
86 0 : this.entAccess = PETRA.getLandAccess(gameState, ent);
87 : // Base for unassigned entities has no accessIndex, so take the one from the entity.
88 0 : if (this.baseID == gameState.ai.HQ.basesManager.baselessBase().ID)
89 0 : this.baseAccess = this.entAccess;
90 : else
91 0 : this.baseAccess = this.base.accessIndex;
92 :
93 0 : if (subrole == undefined) // subrole may-be undefined after a transport, garrisoning, army, ...
94 : {
95 0 : ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_IDLE);
96 0 : this.base.reassignIdleWorkers(gameState, [ent]);
97 0 : this.update(gameState, ent);
98 0 : return;
99 : }
100 :
101 0 : this.ent = ent;
102 :
103 0 : let unitAIState = ent.unitAIState();
104 0 : if ((subrole === PETRA.Worker.SUBROLE_HUNTER || subrole === PETRA.Worker.SUBROLE_GATHERER) &&
105 : (unitAIState == "INDIVIDUAL.GATHER.GATHERING" || unitAIState == "INDIVIDUAL.GATHER.APPROACHING" ||
106 : unitAIState == "INDIVIDUAL.COMBAT.APPROACHING"))
107 : {
108 0 : if (this.isInaccessibleSupply(gameState))
109 : {
110 0 : if (this.retryWorking(gameState, subrole))
111 0 : return;
112 0 : ent.stopMoving();
113 : }
114 :
115 0 : if (unitAIState == "INDIVIDUAL.COMBAT.APPROACHING" && ent.unitAIOrderData().length)
116 : {
117 0 : let orderData = ent.unitAIOrderData()[0];
118 0 : if (orderData && orderData.target)
119 : {
120 : // Check that we have not drifted too far when hunting
121 0 : let target = gameState.getEntityById(orderData.target);
122 0 : if (target && target.resourceSupplyType() && target.resourceSupplyType().generic == "food")
123 : {
124 0 : let territoryOwner = gameState.ai.HQ.territoryMap.getOwner(target.position());
125 0 : if (gameState.isPlayerEnemy(territoryOwner))
126 : {
127 0 : if (this.retryWorking(gameState, subrole))
128 0 : return;
129 0 : ent.stopMoving();
130 : }
131 0 : else if (!gameState.isPlayerAlly(territoryOwner))
132 : {
133 0 : let distanceSquare = PETRA.isFastMoving(ent) ? 90000 : 30000;
134 0 : let targetAccess = PETRA.getLandAccess(gameState, target);
135 0 : let foodDropsites = gameState.playerData.hasSharedDropsites ?
136 : gameState.getAnyDropsites("food") : gameState.getOwnDropsites("food");
137 0 : let hasFoodDropsiteWithinDistance = false;
138 0 : for (let dropsite of foodDropsites.values())
139 : {
140 0 : if (!dropsite.position())
141 0 : continue;
142 0 : let owner = dropsite.owner();
143 : // owner != PlayerID can only happen when hasSharedDropsites == true, so no need to test it again
144 0 : if (owner != PlayerID && (!dropsite.isSharedDropsite() || !gameState.isPlayerMutualAlly(owner)))
145 0 : continue;
146 0 : if (targetAccess != PETRA.getLandAccess(gameState, dropsite))
147 0 : continue;
148 0 : if (API3.SquareVectorDistance(target.position(), dropsite.position()) < distanceSquare)
149 : {
150 0 : hasFoodDropsiteWithinDistance = true;
151 0 : break;
152 : }
153 : }
154 0 : if (!hasFoodDropsiteWithinDistance)
155 : {
156 0 : if (this.retryWorking(gameState, subrole))
157 0 : return;
158 0 : ent.stopMoving();
159 : }
160 : }
161 : }
162 : }
163 : }
164 : }
165 0 : else if (ent.getMetadata(PlayerID, "approachingTarget"))
166 : {
167 0 : ent.setMetadata(PlayerID, "approachingTarget", undefined);
168 0 : ent.setMetadata(PlayerID, "alreadyTried", undefined);
169 : }
170 :
171 0 : let unitAIStateOrder = unitAIState.split(".")[1];
172 : // If we're fighting or hunting, let's not start gathering except if inaccessible target
173 : // but for fishers where UnitAI must have made us target a moving whale.
174 : // Also, if we are attacking, do not capture
175 0 : if (unitAIStateOrder == "COMBAT")
176 : {
177 0 : if (subrole === PETRA.Worker.SUBROLE_FISHER)
178 0 : this.startFishing(gameState);
179 0 : else if (unitAIState == "INDIVIDUAL.COMBAT.APPROACHING" && ent.unitAIOrderData().length &&
180 : !ent.getMetadata(PlayerID, "PartOfArmy"))
181 : {
182 0 : let orderData = ent.unitAIOrderData()[0];
183 0 : if (orderData && orderData.target)
184 : {
185 0 : let target = gameState.getEntityById(orderData.target);
186 0 : if (target && (!target.position() || PETRA.getLandAccess(gameState, target) != this.entAccess))
187 : {
188 0 : if (this.retryWorking(gameState, subrole))
189 0 : return;
190 0 : ent.stopMoving();
191 : }
192 : }
193 : }
194 0 : else if (unitAIState == "INDIVIDUAL.COMBAT.ATTACKING" && ent.unitAIOrderData().length &&
195 : !ent.getMetadata(PlayerID, "PartOfArmy"))
196 : {
197 0 : let orderData = ent.unitAIOrderData()[0];
198 0 : if (orderData && orderData.target && orderData.attackType && orderData.attackType == "Capture")
199 : {
200 : // If we are here, an enemy structure must have targeted one of our workers
201 : // and UnitAI sent it fight back with allowCapture=true
202 0 : let target = gameState.getEntityById(orderData.target);
203 0 : if (target && target.owner() > 0 && !gameState.isPlayerAlly(target.owner()))
204 0 : ent.attack(orderData.target, PETRA.allowCapture(gameState, ent, target));
205 : }
206 : }
207 0 : return;
208 : }
209 :
210 : // Okay so we have a few tasks.
211 : // If we're gathering, we'll check that we haven't run idle.
212 : // And we'll also check that we're gathering a resource we want to gather.
213 :
214 0 : if (subrole === PETRA.Worker.SUBROLE_GATHERER)
215 : {
216 0 : if (ent.isIdle())
217 : {
218 : // if we aren't storing resources or it's the same type as what we're about to gather,
219 : // let's just pick a new resource.
220 : // TODO if we already carry the max we can -> returnresources
221 0 : if (!ent.resourceCarrying() || !ent.resourceCarrying().length ||
222 : ent.resourceCarrying()[0].type == ent.getMetadata(PlayerID, "gather-type"))
223 : {
224 0 : this.startGathering(gameState);
225 : }
226 0 : else if (!PETRA.returnResources(gameState, ent)) // try to deposit resources
227 : {
228 : // no dropsite, abandon old resources and start gathering new ones
229 0 : this.startGathering(gameState);
230 : }
231 : }
232 0 : else if (unitAIStateOrder == "GATHER")
233 : {
234 : // we're already gathering. But let's check if there is nothing better
235 : // in case UnitAI did something bad
236 0 : if (ent.unitAIOrderData().length)
237 : {
238 0 : let supplyId = ent.unitAIOrderData()[0].target;
239 0 : let supply = gameState.getEntityById(supplyId);
240 0 : if (supply && !supply.hasClasses(["Field", "Animal"]) &&
241 : supplyId != ent.getMetadata(PlayerID, "supply"))
242 : {
243 0 : const nbGatherers = supply.resourceSupplyNumGatherers() + this.base.GetTCGatherer(supplyId);
244 0 : if (nbGatherers > 1 && supply.resourceSupplyAmount()/nbGatherers < 30)
245 : {
246 0 : this.base.RemoveTCGatherer(supplyId);
247 0 : this.startGathering(gameState);
248 : }
249 : else
250 : {
251 0 : let gatherType = ent.getMetadata(PlayerID, "gather-type");
252 0 : let nearby = this.base.dropsiteSupplies[gatherType].nearby;
253 0 : if (nearby.some(sup => sup.id == supplyId))
254 0 : ent.setMetadata(PlayerID, "supply", supplyId);
255 0 : else if (nearby.length)
256 : {
257 0 : this.base.RemoveTCGatherer(supplyId);
258 0 : this.startGathering(gameState);
259 : }
260 : else
261 : {
262 0 : let medium = this.base.dropsiteSupplies[gatherType].medium;
263 0 : if (medium.length && !medium.some(sup => sup.id == supplyId))
264 : {
265 0 : this.base.RemoveTCGatherer(supplyId);
266 0 : this.startGathering(gameState);
267 : }
268 : else
269 0 : ent.setMetadata(PlayerID, "supply", supplyId);
270 : }
271 : }
272 : }
273 : }
274 0 : if (unitAIState == "INDIVIDUAL.GATHER.RETURNINGRESOURCE.APPROACHING")
275 : {
276 0 : if (gameState.ai.playedTurn % 10 == 0)
277 : {
278 : // Check from time to time that UnitAI does not send us to an inaccessible dropsite
279 0 : let dropsite = gameState.getEntityById(ent.unitAIOrderData()[0].target);
280 0 : if (dropsite && dropsite.position() && this.entAccess != PETRA.getLandAccess(gameState, dropsite))
281 0 : PETRA.returnResources(gameState, this.ent);
282 : }
283 :
284 : // If gathering a sparse resource, we may have been sent to a faraway resource if the one nearby was full.
285 : // Let's check if it is still the case. If so, we reset its metadata supplyId so that the unit will be
286 : // reordered to gather after having returned the resources (when comparing its supplyId with the UnitAI one).
287 0 : let gatherType = ent.getMetadata(PlayerID, "gather-type");
288 0 : let influenceGroup = Resources.GetResource(gatherType).aiAnalysisInfluenceGroup;
289 0 : if (influenceGroup && influenceGroup == "sparse")
290 : {
291 0 : let supplyId = ent.getMetadata(PlayerID, "supply");
292 0 : if (supplyId)
293 : {
294 0 : let nearby = this.base.dropsiteSupplies[gatherType].nearby;
295 0 : if (!nearby.some(sup => sup.id == supplyId))
296 : {
297 0 : if (nearby.length)
298 0 : ent.setMetadata(PlayerID, "supply", undefined);
299 : else
300 : {
301 0 : let medium = this.base.dropsiteSupplies[gatherType].medium;
302 0 : if (!medium.some(sup => sup.id == supplyId) && medium.length)
303 0 : ent.setMetadata(PlayerID, "supply", undefined);
304 : }
305 : }
306 : }
307 : }
308 : }
309 : }
310 : }
311 0 : else if (subrole === PETRA.Worker.SUBROLE_BUILDER)
312 : {
313 0 : if (unitAIStateOrder == "REPAIR")
314 : {
315 : // Update our target in case UnitAI sent us to a different foundation because of autocontinue
316 : // and abandon it if UnitAI has sent us to build a field (as we build them only when needed)
317 0 : if (ent.unitAIOrderData()[0] && ent.unitAIOrderData()[0].target &&
318 : ent.getMetadata(PlayerID, "target-foundation") != ent.unitAIOrderData()[0].target)
319 : {
320 0 : let targetId = ent.unitAIOrderData()[0].target;
321 0 : let target = gameState.getEntityById(targetId);
322 0 : if (target && !target.hasClass("Field"))
323 : {
324 0 : ent.setMetadata(PlayerID, "target-foundation", targetId);
325 0 : return;
326 : }
327 0 : ent.setMetadata(PlayerID, "target-foundation", undefined);
328 0 : ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_IDLE);
329 0 : ent.stopMoving();
330 0 : if (this.baseID != gameState.ai.HQ.basesManager.baselessBase().ID)
331 : {
332 : // reassign it to something useful
333 0 : this.base.reassignIdleWorkers(gameState, [ent]);
334 0 : this.update(gameState, ent);
335 0 : return;
336 : }
337 : }
338 : // Otherwise check that the target still exists (useful in REPAIR.APPROACHING)
339 0 : let targetId = ent.getMetadata(PlayerID, "target-foundation");
340 0 : if (targetId && gameState.getEntityById(targetId))
341 0 : return;
342 0 : ent.stopMoving();
343 : }
344 : // okay so apparently we aren't working.
345 : // Unless we've been explicitely told to keep our role, make us idle.
346 0 : let target = gameState.getEntityById(ent.getMetadata(PlayerID, "target-foundation"));
347 0 : if (!target || target.foundationProgress() === undefined && target.needsRepair() === false)
348 : {
349 0 : ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_IDLE);
350 0 : ent.setMetadata(PlayerID, "target-foundation", undefined);
351 : // If worker elephant, move away to avoid being trapped in between constructions
352 0 : if (ent.hasClass("Elephant"))
353 0 : this.moveToGatherer(gameState, ent, true);
354 0 : else if (this.baseID != gameState.ai.HQ.basesManager.baselessBase().ID)
355 : {
356 : // reassign it to something useful
357 0 : this.base.reassignIdleWorkers(gameState, [ent]);
358 0 : this.update(gameState, ent);
359 0 : return;
360 : }
361 : }
362 : else
363 : {
364 0 : let goalAccess = PETRA.getLandAccess(gameState, target);
365 0 : let queued = PETRA.returnResources(gameState, ent);
366 0 : if (this.entAccess == goalAccess)
367 0 : ent.repair(target, target.hasClass("House"), queued); // autocontinue=true for houses
368 : else
369 0 : gameState.ai.HQ.navalManager.requireTransport(gameState, ent, this.entAccess, goalAccess, target.position());
370 : }
371 : }
372 0 : else if (subrole === PETRA.Worker.SUBROLE_HUNTER)
373 : {
374 0 : let lastHuntSearch = ent.getMetadata(PlayerID, "lastHuntSearch");
375 0 : if (ent.isIdle() && (!lastHuntSearch || gameState.ai.elapsedTime - lastHuntSearch > 20))
376 : {
377 0 : if (!this.startHunting(gameState))
378 : {
379 : // nothing to hunt around. Try another region if any
380 0 : let nowhereToHunt = true;
381 0 : for (const base of gameState.ai.HQ.baseManagers())
382 : {
383 0 : if (!base.anchor || !base.anchor.position())
384 0 : continue;
385 0 : let basePos = base.anchor.position();
386 0 : if (this.startHunting(gameState, basePos))
387 : {
388 0 : ent.setMetadata(PlayerID, "base", base.ID);
389 0 : if (base.accessIndex == this.entAccess)
390 0 : ent.move(basePos[0], basePos[1]);
391 : else
392 0 : gameState.ai.HQ.navalManager.requireTransport(gameState, ent, this.entAccess, base.accessIndex, basePos);
393 0 : nowhereToHunt = false;
394 0 : break;
395 : }
396 : }
397 0 : if (nowhereToHunt)
398 0 : ent.setMetadata(PlayerID, "lastHuntSearch", gameState.ai.elapsedTime);
399 : }
400 : }
401 : else // Perform some sanity checks
402 : {
403 0 : if (unitAIStateOrder == "GATHER")
404 : {
405 : // we may have drifted towards ennemy territory during the hunt, if yes go home
406 0 : let territoryOwner = gameState.ai.HQ.territoryMap.getOwner(ent.position());
407 0 : if (territoryOwner != 0 && !gameState.isPlayerAlly(territoryOwner)) // player is its own ally
408 0 : this.startHunting(gameState);
409 0 : else if (unitAIState == "INDIVIDUAL.GATHER.RETURNINGRESOURCE.APPROACHING")
410 : {
411 : // Check that UnitAI does not send us to an inaccessible dropsite
412 0 : let dropsite = gameState.getEntityById(ent.unitAIOrderData()[0].target);
413 0 : if (dropsite && dropsite.position() && this.entAccess != PETRA.getLandAccess(gameState, dropsite))
414 0 : PETRA.returnResources(gameState, ent);
415 : }
416 : }
417 : }
418 : }
419 0 : else if (subrole === PETRA.Worker.SUBROLE_FISHER)
420 : {
421 0 : if (ent.isIdle())
422 0 : this.startFishing(gameState);
423 : else // if we have drifted towards ennemy territory during the fishing, go home
424 : {
425 0 : let territoryOwner = gameState.ai.HQ.territoryMap.getOwner(ent.position());
426 0 : if (territoryOwner != 0 && !gameState.isPlayerAlly(territoryOwner)) // player is its own ally
427 0 : this.startFishing(gameState);
428 : }
429 : }
430 : };
431 :
432 0 : PETRA.Worker.prototype.retryWorking = function(gameState, subrole)
433 : {
434 0 : switch (subrole)
435 : {
436 : case PETRA.Worker.SUBROLE_GATHERER:
437 0 : return this.startGathering(gameState);
438 : case PETRA.Worker.SUBROLE_HUNTER:
439 0 : return this.startHunting(gameState);
440 : case PETRA.Worker.SUBROLE_FISHER:
441 0 : return this.startFishing(gameState);
442 : case PETRA.Worker.SUBROLE_BUILDER:
443 0 : return this.startBuilding(gameState);
444 : default:
445 0 : return false;
446 : }
447 : };
448 :
449 0 : PETRA.Worker.prototype.startBuilding = function(gameState)
450 : {
451 0 : let target = gameState.getEntityById(this.ent.getMetadata(PlayerID, "target-foundation"));
452 0 : if (!target || target.foundationProgress() === undefined && target.needsRepair() == false)
453 0 : return false;
454 0 : if (PETRA.getLandAccess(gameState, target) != this.entAccess)
455 0 : return false;
456 0 : this.ent.repair(target, target.hasClass("House")); // autocontinue=true for houses
457 0 : return true;
458 : };
459 :
460 0 : PETRA.Worker.prototype.startGathering = function(gameState)
461 : {
462 : // First look for possible treasure if any
463 0 : if (PETRA.gatherTreasure(gameState, this.ent))
464 0 : return true;
465 :
466 0 : let resource = this.ent.getMetadata(PlayerID, "gather-type");
467 :
468 : // If we are gathering food, try to hunt first
469 0 : if (resource == "food" && this.startHunting(gameState))
470 0 : return true;
471 :
472 0 : const findSupply = function(worker, supplies) {
473 0 : const ent = worker.ent;
474 0 : let ret = false;
475 0 : let gatherRates = ent.resourceGatherRates();
476 0 : for (let i = 0; i < supplies.length; ++i)
477 : {
478 : // exhausted resource, remove it from this list
479 0 : if (!supplies[i].ent || !gameState.getEntityById(supplies[i].id))
480 : {
481 0 : supplies.splice(i--, 1);
482 0 : continue;
483 : }
484 0 : if (PETRA.IsSupplyFull(gameState, supplies[i].ent))
485 0 : continue;
486 0 : let inaccessibleTime = supplies[i].ent.getMetadata(PlayerID, "inaccessibleTime");
487 0 : if (inaccessibleTime && gameState.ai.elapsedTime < inaccessibleTime)
488 0 : continue;
489 0 : let supplyType = supplies[i].ent.get("ResourceSupply/Type");
490 0 : if (!gatherRates[supplyType])
491 0 : continue;
492 : // check if available resource is worth one additionnal gatherer (except for farms)
493 0 : const nbGatherers = supplies[i].ent.resourceSupplyNumGatherers() + worker.base.GetTCGatherer(supplies[i].id);
494 0 : if (supplies[i].ent.resourceSupplyType().specific != "grain" && nbGatherers > 0 &&
495 : supplies[i].ent.resourceSupplyAmount()/(1+nbGatherers) < 30)
496 0 : continue;
497 : // not in ennemy territory
498 0 : let territoryOwner = gameState.ai.HQ.territoryMap.getOwner(supplies[i].ent.position());
499 0 : if (territoryOwner != 0 && !gameState.isPlayerAlly(territoryOwner)) // player is its own ally
500 0 : continue;
501 0 : worker.base.AddTCGatherer(supplies[i].id);
502 0 : ent.setMetadata(PlayerID, "supply", supplies[i].id);
503 0 : ret = supplies[i].ent;
504 0 : break;
505 : }
506 0 : return ret;
507 : };
508 :
509 0 : let navalManager = gameState.ai.HQ.navalManager;
510 : let supply;
511 :
512 : // first look in our own base if accessible from our present position
513 0 : if (this.baseAccess == this.entAccess)
514 : {
515 0 : supply = findSupply(this, this.base.dropsiteSupplies[resource].nearby);
516 0 : if (supply)
517 : {
518 0 : this.ent.gather(supply);
519 0 : return true;
520 : }
521 : // --> for food, try to gather from fields if any, otherwise build one if any
522 0 : if (resource == "food")
523 : {
524 0 : supply = this.gatherNearestField(gameState, this.baseID);
525 0 : if (supply)
526 : {
527 0 : this.ent.gather(supply);
528 0 : return true;
529 : }
530 0 : supply = this.buildAnyField(gameState, this.baseID);
531 0 : if (supply)
532 : {
533 0 : this.ent.repair(supply);
534 0 : return true;
535 : }
536 : }
537 0 : supply = findSupply(this, this.base.dropsiteSupplies[resource].medium);
538 0 : if (supply)
539 : {
540 0 : this.ent.gather(supply);
541 0 : return true;
542 : }
543 : }
544 : // So if we're here we have checked our whole base for a proper resource (or it was not accessible)
545 : // --> check other bases directly accessible
546 0 : for (const base of gameState.ai.HQ.baseManagers())
547 : {
548 0 : if (base.ID == this.baseID)
549 0 : continue;
550 0 : if (base.accessIndex != this.entAccess)
551 0 : continue;
552 0 : supply = findSupply(this, base.dropsiteSupplies[resource].nearby);
553 0 : if (supply)
554 : {
555 0 : this.ent.setMetadata(PlayerID, "base", base.ID);
556 0 : this.ent.gather(supply);
557 0 : return true;
558 : }
559 : }
560 0 : if (resource == "food") // --> for food, try to gather from fields if any, otherwise build one if any
561 : {
562 0 : for (const base of gameState.ai.HQ.baseManagers())
563 : {
564 0 : if (base.ID == this.baseID)
565 0 : continue;
566 0 : if (base.accessIndex != this.entAccess)
567 0 : continue;
568 0 : supply = this.gatherNearestField(gameState, base.ID);
569 0 : if (supply)
570 : {
571 0 : this.ent.setMetadata(PlayerID, "base", base.ID);
572 0 : this.ent.gather(supply);
573 0 : return true;
574 : }
575 0 : supply = this.buildAnyField(gameState, base.ID);
576 0 : if (supply)
577 : {
578 0 : this.ent.setMetadata(PlayerID, "base", base.ID);
579 0 : this.ent.repair(supply);
580 0 : return true;
581 : }
582 : }
583 : }
584 0 : for (const base of gameState.ai.HQ.baseManagers())
585 : {
586 0 : if (base.ID == this.baseID)
587 0 : continue;
588 0 : if (base.accessIndex != this.entAccess)
589 0 : continue;
590 0 : supply = findSupply(this, base.dropsiteSupplies[resource].medium);
591 0 : if (supply)
592 : {
593 0 : this.ent.setMetadata(PlayerID, "base", base.ID);
594 0 : this.ent.gather(supply);
595 0 : return true;
596 : }
597 : }
598 :
599 : // Okay may-be we haven't found any appropriate dropsite anywhere.
600 : // Try to help building one if any accessible foundation available
601 0 : let foundations = gameState.getOwnFoundations().toEntityArray();
602 0 : let shouldBuild = this.ent.isBuilder() && foundations.some(function(foundation) {
603 0 : if (!foundation || PETRA.getLandAccess(gameState, foundation) != this.entAccess)
604 0 : return false;
605 0 : let structure = gameState.getBuiltTemplate(foundation.templateName());
606 0 : if (structure.resourceDropsiteTypes() && structure.resourceDropsiteTypes().indexOf(resource) != -1)
607 : {
608 0 : if (foundation.getMetadata(PlayerID, "base") != this.baseID)
609 0 : this.ent.setMetadata(PlayerID, "base", foundation.getMetadata(PlayerID, "base"));
610 0 : this.ent.setMetadata(PlayerID, "target-foundation", foundation.id());
611 0 : this.ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_BUILDER);
612 0 : this.ent.repair(foundation);
613 0 : return true;
614 : }
615 0 : return false;
616 : }, this);
617 0 : if (shouldBuild)
618 0 : return true;
619 :
620 : // Still nothing ... try bases which need a transport
621 0 : for (const base of gameState.ai.HQ.baseManagers())
622 : {
623 0 : if (base.accessIndex == this.entAccess)
624 0 : continue;
625 0 : supply = findSupply(this, base.dropsiteSupplies[resource].nearby);
626 0 : if (supply && navalManager.requireTransport(gameState, this.ent, this.entAccess, base.accessIndex, supply.position()))
627 : {
628 0 : if (base.ID != this.baseID)
629 0 : this.ent.setMetadata(PlayerID, "base", base.ID);
630 0 : return true;
631 : }
632 : }
633 0 : if (resource == "food") // --> for food, try to gather from fields if any, otherwise build one if any
634 : {
635 0 : for (const base of gameState.ai.HQ.baseManagers())
636 : {
637 0 : if (base.accessIndex == this.entAccess)
638 0 : continue;
639 0 : supply = this.gatherNearestField(gameState, base.ID);
640 0 : if (supply && navalManager.requireTransport(gameState, this.ent, this.entAccess, base.accessIndex, supply.position()))
641 : {
642 0 : if (base.ID != this.baseID)
643 0 : this.ent.setMetadata(PlayerID, "base", base.ID);
644 0 : return true;
645 : }
646 0 : supply = this.buildAnyField(gameState, base.ID);
647 0 : if (supply && navalManager.requireTransport(gameState, this.ent, this.entAccess, base.accessIndex, supply.position()))
648 : {
649 0 : if (base.ID != this.baseID)
650 0 : this.ent.setMetadata(PlayerID, "base", base.ID);
651 0 : return true;
652 : }
653 : }
654 : }
655 0 : for (const base of gameState.ai.HQ.baseManagers())
656 : {
657 0 : if (base.accessIndex == this.entAccess)
658 0 : continue;
659 0 : supply = findSupply(this, base.dropsiteSupplies[resource].medium);
660 0 : if (supply && navalManager.requireTransport(gameState, this.ent, this.entAccess, base.accessIndex, supply.position()))
661 : {
662 0 : if (base.ID != this.baseID)
663 0 : this.ent.setMetadata(PlayerID, "base", base.ID);
664 0 : return true;
665 : }
666 : }
667 : // Okay so we haven't found any appropriate dropsite anywhere.
668 : // Try to help building one if any non-accessible foundation available
669 0 : shouldBuild = this.ent.isBuilder() && foundations.some(function(foundation) {
670 0 : if (!foundation || PETRA.getLandAccess(gameState, foundation) == this.entAccess)
671 0 : return false;
672 0 : let structure = gameState.getBuiltTemplate(foundation.templateName());
673 0 : if (structure.resourceDropsiteTypes() && structure.resourceDropsiteTypes().indexOf(resource) != -1)
674 : {
675 0 : let foundationAccess = PETRA.getLandAccess(gameState, foundation);
676 0 : if (navalManager.requireTransport(gameState, this.ent, this.entAccess, foundationAccess, foundation.position()))
677 : {
678 0 : if (foundation.getMetadata(PlayerID, "base") != this.baseID)
679 0 : this.ent.setMetadata(PlayerID, "base", foundation.getMetadata(PlayerID, "base"));
680 0 : this.ent.setMetadata(PlayerID, "target-foundation", foundation.id());
681 0 : this.ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_BUILDER);
682 0 : return true;
683 : }
684 : }
685 0 : return false;
686 : }, this);
687 0 : if (shouldBuild)
688 0 : return true;
689 :
690 : // Still nothing, we look now for faraway resources, first in the accessible ones, then in the others
691 : // except for food when farms or corrals can be used
692 0 : let allowDistant = true;
693 0 : if (resource == "food")
694 : {
695 0 : if (gameState.ai.HQ.turnCache.allowDistantFood === undefined)
696 0 : gameState.ai.HQ.turnCache.allowDistantFood =
697 : !gameState.ai.HQ.canBuild(gameState, "structures/{civ}/field") &&
698 : !gameState.ai.HQ.canBuild(gameState, "structures/{civ}/corral");
699 0 : allowDistant = gameState.ai.HQ.turnCache.allowDistantFood;
700 : }
701 0 : if (allowDistant)
702 : {
703 0 : if (this.baseAccess == this.entAccess)
704 : {
705 0 : supply = findSupply(this, this.base.dropsiteSupplies[resource].faraway);
706 0 : if (supply)
707 : {
708 0 : this.ent.gather(supply);
709 0 : return true;
710 : }
711 : }
712 0 : for (const base of gameState.ai.HQ.baseManagers())
713 : {
714 0 : if (base.ID == this.baseID)
715 0 : continue;
716 0 : if (base.accessIndex != this.entAccess)
717 0 : continue;
718 0 : supply = findSupply(this, base.dropsiteSupplies[resource].faraway);
719 0 : if (supply)
720 : {
721 0 : this.ent.setMetadata(PlayerID, "base", base.ID);
722 0 : this.ent.gather(supply);
723 0 : return true;
724 : }
725 : }
726 0 : for (const base of gameState.ai.HQ.baseManagers())
727 : {
728 0 : if (base.accessIndex == this.entAccess)
729 0 : continue;
730 0 : supply = findSupply(this, base.dropsiteSupplies[resource].faraway);
731 0 : if (supply && navalManager.requireTransport(gameState, this.ent, this.entAccess, base.accessIndex, supply.position()))
732 : {
733 0 : if (base.ID != this.baseID)
734 0 : this.ent.setMetadata(PlayerID, "base", base.ID);
735 0 : return true;
736 : }
737 : }
738 : }
739 :
740 : // If we are here, we have nothing left to gather ... certainly no more resources of this type
741 0 : gameState.ai.HQ.lastFailedGather[resource] = gameState.ai.elapsedTime;
742 0 : if (gameState.ai.Config.debug > 2)
743 0 : API3.warn(" >>>>> worker with gather-type " + resource + " with nothing to gather ");
744 0 : this.ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_IDLE);
745 0 : return false;
746 : };
747 :
748 : /**
749 : * if position is given, we only check if we could hunt from this position but do nothing
750 : * otherwise the position of the entity is taken, and if something is found, we directly start the hunt
751 : */
752 0 : PETRA.Worker.prototype.startHunting = function(gameState, position)
753 : {
754 : // First look for possible treasure if any
755 0 : if (!position && PETRA.gatherTreasure(gameState, this.ent))
756 0 : return true;
757 :
758 0 : let resources = gameState.getHuntableSupplies();
759 0 : if (!resources.hasEntities())
760 0 : return false;
761 :
762 0 : let nearestSupplyDist = Math.min();
763 : let nearestSupply;
764 :
765 0 : let isFastMoving = PETRA.isFastMoving(this.ent);
766 0 : let isRanged = this.ent.hasClass("Ranged");
767 0 : let entPosition = position ? position : this.ent.position();
768 0 : let foodDropsites = gameState.playerData.hasSharedDropsites ?
769 : gameState.getAnyDropsites("food") : gameState.getOwnDropsites("food");
770 :
771 0 : let hasFoodDropsiteWithinDistance = function(supplyPosition, supplyAccess, distSquare)
772 : {
773 0 : for (let dropsite of foodDropsites.values())
774 : {
775 0 : if (!dropsite.position())
776 0 : continue;
777 0 : let owner = dropsite.owner();
778 : // owner != PlayerID can only happen when hasSharedDropsites == true, so no need to test it again
779 0 : if (owner != PlayerID && (!dropsite.isSharedDropsite() || !gameState.isPlayerMutualAlly(owner)))
780 0 : continue;
781 0 : if (supplyAccess != PETRA.getLandAccess(gameState, dropsite))
782 0 : continue;
783 0 : if (API3.SquareVectorDistance(supplyPosition, dropsite.position()) < distSquare)
784 0 : return true;
785 : }
786 0 : return false;
787 : };
788 :
789 0 : let gatherRates = this.ent.resourceGatherRates();
790 0 : for (let supply of resources.values())
791 : {
792 0 : if (!supply.position())
793 0 : continue;
794 :
795 0 : let inaccessibleTime = supply.getMetadata(PlayerID, "inaccessibleTime");
796 0 : if (inaccessibleTime && gameState.ai.elapsedTime < inaccessibleTime)
797 0 : continue;
798 :
799 0 : let supplyType = supply.get("ResourceSupply/Type");
800 0 : if (!gatherRates[supplyType])
801 0 : continue;
802 :
803 0 : if (PETRA.IsSupplyFull(gameState, supply))
804 0 : continue;
805 : // Check if available resource is worth one additionnal gatherer (except for farms).
806 0 : const nbGatherers = supply.resourceSupplyNumGatherers() + this.base.GetTCGatherer(supply.id());
807 0 : if (nbGatherers > 0 && supply.resourceSupplyAmount()/(1+nbGatherers) < 30)
808 0 : continue;
809 :
810 0 : let canFlee = !supply.hasClass("Domestic") && supply.templateName().indexOf("resource|") == -1;
811 : // Only FastMoving and Ranged units should hunt fleeing animals.
812 0 : if (canFlee && !isFastMoving && !isRanged)
813 0 : continue;
814 :
815 0 : let supplyAccess = PETRA.getLandAccess(gameState, supply);
816 0 : if (supplyAccess != this.entAccess)
817 0 : continue;
818 :
819 : // measure the distance to the resource.
820 0 : let dist = API3.SquareVectorDistance(entPosition, supply.position());
821 0 : if (dist > nearestSupplyDist)
822 0 : continue;
823 :
824 : // Only FastMoving should hunt faraway.
825 0 : if (!isFastMoving && dist > 25000)
826 0 : continue;
827 :
828 : // Avoid enemy territory.
829 0 : let territoryOwner = gameState.ai.HQ.territoryMap.getOwner(supply.position());
830 0 : if (territoryOwner != 0 && !gameState.isPlayerAlly(territoryOwner)) // Player is its own ally.
831 0 : continue;
832 : // And if in ally territory, don't hunt this ally's cattle.
833 0 : if (territoryOwner != 0 && territoryOwner != PlayerID && supply.owner() == territoryOwner)
834 0 : continue;
835 :
836 : // Only FastMoving should hunt far from dropsite (specially for non-Domestic animals which flee).
837 0 : if (!isFastMoving && canFlee && territoryOwner == 0)
838 0 : continue;
839 0 : let distanceSquare = isFastMoving ? 35000 : (canFlee ? 7000 : 12000);
840 0 : if (!hasFoodDropsiteWithinDistance(supply.position(), supplyAccess, distanceSquare))
841 0 : continue;
842 :
843 0 : nearestSupplyDist = dist;
844 0 : nearestSupply = supply;
845 : }
846 :
847 0 : if (nearestSupply)
848 : {
849 0 : if (position)
850 0 : return true;
851 0 : this.base.AddTCGatherer(nearestSupply.id());
852 0 : this.ent.gather(nearestSupply);
853 0 : this.ent.setMetadata(PlayerID, "supply", nearestSupply.id());
854 0 : this.ent.setMetadata(PlayerID, "target-foundation", undefined);
855 0 : return true;
856 : }
857 0 : return false;
858 : };
859 :
860 0 : PETRA.Worker.prototype.startFishing = function(gameState)
861 : {
862 0 : if (!this.ent.position())
863 0 : return false;
864 :
865 0 : let resources = gameState.getFishableSupplies();
866 0 : if (!resources.hasEntities())
867 : {
868 0 : gameState.ai.HQ.navalManager.resetFishingBoats(gameState);
869 0 : this.ent.destroy();
870 0 : return false;
871 : }
872 :
873 0 : let nearestSupplyDist = Math.min();
874 : let nearestSupply;
875 :
876 0 : let fisherSea = PETRA.getSeaAccess(gameState, this.ent);
877 0 : let fishDropsites = (gameState.playerData.hasSharedDropsites ? gameState.getAnyDropsites("food") : gameState.getOwnDropsites("food")).
878 : filter(API3.Filters.byClass("Dock")).toEntityArray();
879 :
880 0 : let nearestDropsiteDist = function(supply) {
881 0 : let distMin = 1000000;
882 0 : let pos = supply.position();
883 0 : for (let dropsite of fishDropsites)
884 : {
885 0 : if (!dropsite.position())
886 0 : continue;
887 0 : let owner = dropsite.owner();
888 : // owner != PlayerID can only happen when hasSharedDropsites == true, so no need to test it again
889 0 : if (owner != PlayerID && (!dropsite.isSharedDropsite() || !gameState.isPlayerMutualAlly(owner)))
890 0 : continue;
891 0 : if (fisherSea != PETRA.getSeaAccess(gameState, dropsite))
892 0 : continue;
893 0 : distMin = Math.min(distMin, API3.SquareVectorDistance(pos, dropsite.position()));
894 : }
895 0 : return distMin;
896 : };
897 :
898 0 : let exhausted = true;
899 0 : let gatherRates = this.ent.resourceGatherRates();
900 0 : resources.forEach((supply) => {
901 0 : if (!supply.position())
902 0 : return;
903 :
904 : // check that it is accessible
905 0 : if (gameState.ai.HQ.navalManager.getFishSea(gameState, supply) != fisherSea)
906 0 : return;
907 :
908 0 : exhausted = false;
909 :
910 0 : let supplyType = supply.get("ResourceSupply/Type");
911 0 : if (!gatherRates[supplyType])
912 0 : return;
913 :
914 0 : if (PETRA.IsSupplyFull(gameState, supply))
915 0 : return;
916 : // check if available resource is worth one additionnal gatherer (except for farms)
917 0 : const nbGatherers = supply.resourceSupplyNumGatherers() + this.base.GetTCGatherer(supply.id());
918 0 : if (nbGatherers > 0 && supply.resourceSupplyAmount()/(1+nbGatherers) < 30)
919 0 : return;
920 :
921 : // Avoid ennemy territory
922 0 : if (!gameState.ai.HQ.navalManager.canFishSafely(gameState, supply))
923 0 : return;
924 :
925 : // measure the distance from the resource to the nearest dropsite
926 0 : let dist = nearestDropsiteDist(supply);
927 0 : if (dist > nearestSupplyDist)
928 0 : return;
929 :
930 0 : nearestSupplyDist = dist;
931 0 : nearestSupply = supply;
932 : });
933 :
934 0 : if (exhausted)
935 : {
936 0 : gameState.ai.HQ.navalManager.resetFishingBoats(gameState, fisherSea);
937 0 : this.ent.destroy();
938 0 : return false;
939 : }
940 :
941 0 : if (nearestSupply)
942 : {
943 0 : this.base.AddTCGatherer(nearestSupply.id());
944 0 : this.ent.gather(nearestSupply);
945 0 : this.ent.setMetadata(PlayerID, "supply", nearestSupply.id());
946 0 : this.ent.setMetadata(PlayerID, "target-foundation", undefined);
947 0 : return true;
948 : }
949 0 : if (this.ent.getMetadata(PlayerID, "subrole") === PETRA.Worker.SUBROLE_FISHER)
950 0 : this.ent.setMetadata(PlayerID, "subrole", PETRA.Worker.SUBROLE_IDLE);
951 0 : return false;
952 : };
953 :
954 0 : PETRA.Worker.prototype.gatherNearestField = function(gameState, baseID)
955 : {
956 0 : let ownFields = gameState.getOwnEntitiesByClass("Field", true).filter(API3.Filters.isBuilt()).filter(API3.Filters.byMetadata(PlayerID, "base", baseID));
957 : let bestFarm;
958 :
959 0 : let gatherRates = this.ent.resourceGatherRates();
960 0 : for (let field of ownFields.values())
961 : {
962 0 : if (PETRA.IsSupplyFull(gameState, field))
963 0 : continue;
964 0 : let supplyType = field.get("ResourceSupply/Type");
965 0 : if (!gatherRates[supplyType])
966 0 : continue;
967 :
968 0 : let rate = 1;
969 0 : let diminishing = field.getDiminishingReturns();
970 0 : if (diminishing < 1)
971 : {
972 0 : const num = field.resourceSupplyNumGatherers() + this.base.GetTCGatherer(field.id());
973 0 : if (num > 0)
974 0 : rate = Math.pow(diminishing, num);
975 : }
976 : // Add a penalty distance depending on rate
977 0 : let dist = API3.SquareVectorDistance(field.position(), this.ent.position()) + (1 - rate) * 160000;
978 0 : if (!bestFarm || dist < bestFarm.dist)
979 0 : bestFarm = { "ent": field, "dist": dist, "rate": rate };
980 : }
981 : // If other field foundations available, better build them when rate becomes too small
982 0 : if (!bestFarm || bestFarm.rate < 0.70 &&
983 : gameState.getOwnFoundations().filter(API3.Filters.byClass("Field")).filter(API3.Filters.byMetadata(PlayerID, "base", baseID)).hasEntities())
984 0 : return false;
985 0 : this.base.AddTCGatherer(bestFarm.ent.id());
986 0 : this.ent.setMetadata(PlayerID, "supply", bestFarm.ent.id());
987 0 : return bestFarm.ent;
988 : };
989 :
990 : /**
991 : * WARNING with the present options of AI orders, the unit will not gather after building the farm.
992 : * This is done by calling the gatherNearestField function when construction is completed.
993 : */
994 0 : PETRA.Worker.prototype.buildAnyField = function(gameState, baseID)
995 : {
996 0 : if (!this.ent.isBuilder())
997 0 : return false;
998 0 : let bestFarmEnt = false;
999 0 : let bestFarmDist = 10000000;
1000 0 : let pos = this.ent.position();
1001 0 : for (let found of gameState.getOwnFoundations().values())
1002 : {
1003 0 : if (found.getMetadata(PlayerID, "base") != baseID || !found.hasClass("Field"))
1004 0 : continue;
1005 0 : let current = found.getBuildersNb();
1006 0 : if (current === undefined ||
1007 : current >= gameState.getBuiltTemplate(found.templateName()).maxGatherers())
1008 0 : continue;
1009 0 : let dist = API3.SquareVectorDistance(found.position(), pos);
1010 0 : if (dist > bestFarmDist)
1011 0 : continue;
1012 0 : bestFarmEnt = found;
1013 0 : bestFarmDist = dist;
1014 : }
1015 0 : return bestFarmEnt;
1016 : };
1017 :
1018 : /**
1019 : * Workers elephant should move away from the buildings they've built to avoid being trapped in between constructions.
1020 : * For the time being, we move towards the nearest gatherer (providing him a dropsite).
1021 : * BaseManager does also use that function to deal with its mobile dropsites.
1022 : */
1023 0 : PETRA.Worker.prototype.moveToGatherer = function(gameState, ent, forced)
1024 : {
1025 0 : let pos = ent.position();
1026 0 : if (!pos || ent.getMetadata(PlayerID, "target-foundation") !== undefined)
1027 0 : return;
1028 0 : if (!forced && gameState.ai.elapsedTime < (ent.getMetadata(PlayerID, "nextMoveToGatherer") || 5))
1029 0 : return;
1030 0 : const gatherers = this.base.workersBySubrole(gameState, PETRA.Worker.SUBROLE_GATHERER);
1031 0 : let dist = Math.min();
1032 : let destination;
1033 0 : let access = PETRA.getLandAccess(gameState, ent);
1034 0 : let types = ent.resourceDropsiteTypes();
1035 0 : for (let gatherer of gatherers.values())
1036 : {
1037 0 : let gathererType = gatherer.getMetadata(PlayerID, "gather-type");
1038 0 : if (!gathererType || types.indexOf(gathererType) == -1)
1039 0 : continue;
1040 0 : if (!gatherer.position() || gatherer.getMetadata(PlayerID, "transport") !== undefined ||
1041 : PETRA.getLandAccess(gameState, gatherer) != access || gatherer.isIdle())
1042 0 : continue;
1043 0 : let distance = API3.SquareVectorDistance(pos, gatherer.position());
1044 0 : if (distance > dist)
1045 0 : continue;
1046 0 : dist = distance;
1047 0 : destination = gatherer.position();
1048 : }
1049 0 : ent.setMetadata(PlayerID, "nextMoveToGatherer", gameState.ai.elapsedTime + (destination ? 12 : 5));
1050 0 : if (destination && dist > 10)
1051 0 : ent.move(destination[0], destination[1]);
1052 : };
1053 :
1054 : /**
1055 : * Check accessibility of the target when in approach (in RMS maps, we quite often have chicken or bushes
1056 : * inside obstruction of other entities). The resource will be flagged as inaccessible during 10 mn (in case
1057 : * it will be cleared later).
1058 : */
1059 0 : PETRA.Worker.prototype.isInaccessibleSupply = function(gameState)
1060 : {
1061 0 : if (!this.ent.unitAIOrderData()[0] || !this.ent.unitAIOrderData()[0].target)
1062 0 : return false;
1063 0 : let targetId = this.ent.unitAIOrderData()[0].target;
1064 0 : let target = gameState.getEntityById(targetId);
1065 0 : if (!target)
1066 0 : return true;
1067 :
1068 0 : if (!target.resourceSupplyType())
1069 0 : return false;
1070 :
1071 0 : let approachingTarget = this.ent.getMetadata(PlayerID, "approachingTarget");
1072 0 : let carriedAmount = this.ent.resourceCarrying().length ? this.ent.resourceCarrying()[0].amount : 0;
1073 0 : if (!approachingTarget || approachingTarget != targetId)
1074 : {
1075 0 : this.ent.setMetadata(PlayerID, "approachingTarget", targetId);
1076 0 : this.ent.setMetadata(PlayerID, "approachingTime", undefined);
1077 0 : this.ent.setMetadata(PlayerID, "approachingPos", undefined);
1078 0 : this.ent.setMetadata(PlayerID, "carriedBefore", carriedAmount);
1079 0 : let alreadyTried = this.ent.getMetadata(PlayerID, "alreadyTried");
1080 0 : if (alreadyTried && alreadyTried != targetId)
1081 0 : this.ent.setMetadata(PlayerID, "alreadyTried", undefined);
1082 : }
1083 :
1084 0 : let carriedBefore = this.ent.getMetadata(PlayerID, "carriedBefore");
1085 0 : if (carriedBefore != carriedAmount)
1086 : {
1087 0 : this.ent.setMetadata(PlayerID, "approachingTarget", undefined);
1088 0 : this.ent.setMetadata(PlayerID, "alreadyTried", undefined);
1089 0 : if (target.getMetadata(PlayerID, "inaccessibleTime"))
1090 0 : target.setMetadata(PlayerID, "inaccessibleTime", 0);
1091 0 : return false;
1092 : }
1093 :
1094 0 : let inaccessibleTime = target.getMetadata(PlayerID, "inaccessibleTime");
1095 0 : if (inaccessibleTime && gameState.ai.elapsedTime < inaccessibleTime)
1096 0 : return true;
1097 :
1098 0 : let approachingTime = this.ent.getMetadata(PlayerID, "approachingTime");
1099 0 : if (!approachingTime || gameState.ai.elapsedTime - approachingTime > 3)
1100 : {
1101 0 : let presentPos = this.ent.position();
1102 0 : let approachingPos = this.ent.getMetadata(PlayerID, "approachingPos");
1103 0 : if (!approachingPos || approachingPos[0] != presentPos[0] || approachingPos[1] != presentPos[1])
1104 : {
1105 0 : this.ent.setMetadata(PlayerID, "approachingTime", gameState.ai.elapsedTime);
1106 0 : this.ent.setMetadata(PlayerID, "approachingPos", presentPos);
1107 0 : return false;
1108 : }
1109 0 : if (gameState.ai.elapsedTime - approachingTime > 10)
1110 : {
1111 0 : if (this.ent.getMetadata(PlayerID, "alreadyTried"))
1112 : {
1113 0 : target.setMetadata(PlayerID, "inaccessibleTime", gameState.ai.elapsedTime + 600);
1114 0 : return true;
1115 : }
1116 : // let's try again to reach it
1117 0 : this.ent.setMetadata(PlayerID, "alreadyTried", targetId);
1118 0 : this.ent.setMetadata(PlayerID, "approachingTarget", undefined);
1119 0 : this.ent.gather(target);
1120 0 : return false;
1121 : }
1122 : }
1123 0 : return false;
1124 : };
|