Line data Source code
1 0 : var API3 = function(m)
2 : {
3 :
4 : /**
5 : * Provides an API for the rest of the AI scripts to query the world state at a
6 : * higher level than the raw data.
7 : */
8 0 : m.GameState = function() {
9 0 : this.ai = null; // must be updated by the AIs.
10 : };
11 :
12 0 : m.GameState.prototype.init = function(SharedScript, state, player)
13 : {
14 0 : this.sharedScript = SharedScript;
15 0 : this.EntCollecNames = SharedScript._entityCollectionsName;
16 0 : this.timeElapsed = SharedScript.timeElapsed;
17 0 : this.circularMap = SharedScript.circularMap;
18 0 : this.templates = SharedScript._templates;
19 0 : this.entities = SharedScript.entities;
20 0 : this.player = player;
21 0 : this.playerData = SharedScript.playersData[this.player];
22 0 : this.victoryConditions = SharedScript.victoryConditions;
23 0 : this.alliedVictory = SharedScript.alliedVictory;
24 0 : this.ceasefireActive = SharedScript.ceasefireActive;
25 0 : this.ceasefireTimeRemaining = SharedScript.ceasefireTimeRemaining;
26 :
27 : // get the list of possible phases for this civ:
28 : // we assume all of them are researchable from the civil center
29 0 : this.phases = [];
30 0 : let cctemplate = this.getTemplate(this.applyCiv("structures/{civ}/civil_centre"));
31 0 : if (!cctemplate)
32 0 : return;
33 0 : let civ = this.getPlayerCiv();
34 0 : let techs = cctemplate.researchableTechs(this, civ);
35 :
36 0 : let phaseData = {};
37 0 : let phaseMap = {};
38 0 : for (let techName of techs)
39 : {
40 0 : if (!techName.startsWith("phase"))
41 0 : continue;
42 0 : let techData = this.getTemplate(techName);
43 :
44 0 : if (techData._definesPair)
45 : {
46 : // Randomly pick a non-disabled choice from the phase-pair.
47 0 : techName = pickRandom([techData._template.top, techData._template.bottom].filter(tech => !this.playerData.disabledTechnologies[tech])) || techData._template.top;
48 :
49 0 : let supersedes = techData._template.supersedes;
50 0 : techData = clone(this.getTemplate(techName));
51 0 : if (supersedes)
52 0 : techData._template.supersedes = supersedes;
53 : }
54 :
55 0 : phaseData[techName] = GetTechnologyBasicDataHelper(techData._template, civ);
56 0 : if (phaseData[techName].replaces)
57 0 : phaseMap[phaseData[techName].replaces[0]] = techName;
58 : }
59 :
60 0 : this.phases = UnravelPhases(phaseData).map(phaseName => ({
61 : "name": phaseMap[phaseName] || phaseName,
62 : "requirements": phaseMap[phaseName] ? phaseData[phaseMap[phaseName]].reqs : []
63 : }));
64 : };
65 :
66 0 : m.GameState.prototype.update = function(SharedScript)
67 : {
68 0 : this.timeElapsed = SharedScript.timeElapsed;
69 0 : this.playerData = SharedScript.playersData[this.player];
70 0 : this.ceasefireActive = SharedScript.ceasefireActive;
71 0 : this.ceasefireTimeRemaining = SharedScript.ceasefireTimeRemaining;
72 : };
73 :
74 0 : m.GameState.prototype.updatingCollection = function(id, filter, parentCollection)
75 : {
76 0 : let gid = "player-" + this.player + "-" + id; // automatically add the player ID
77 0 : return this.updatingGlobalCollection(gid, filter, parentCollection);
78 : };
79 :
80 0 : m.GameState.prototype.destroyCollection = function(id)
81 : {
82 0 : let gid = "player-" + this.player + "-" + id; // automatically add the player ID
83 0 : this.destroyGlobalCollection(gid);
84 : };
85 :
86 0 : m.GameState.prototype.updatingGlobalCollection = function(gid, filter, parentCollection)
87 : {
88 0 : if (this.EntCollecNames.has(gid))
89 0 : return this.EntCollecNames.get(gid);
90 :
91 0 : let collection = parentCollection ? parentCollection.filter(filter) : this.entities.filter(filter);
92 0 : collection.registerUpdates();
93 0 : this.EntCollecNames.set(gid, collection);
94 0 : return collection;
95 : };
96 :
97 0 : m.GameState.prototype.destroyGlobalCollection = function(gid)
98 : {
99 0 : if (!this.EntCollecNames.has(gid))
100 0 : return;
101 :
102 0 : this.sharedScript.removeUpdatingEntityCollection(this.EntCollecNames.get(gid));
103 0 : this.EntCollecNames.delete(gid);
104 : };
105 :
106 : /**
107 : * Reset the entities collections which depend on diplomacy
108 : */
109 0 : m.GameState.prototype.resetOnDiplomacyChanged = function()
110 : {
111 0 : for (let name of this.EntCollecNames.keys())
112 0 : if (name.startsWith("player-" + this.player + "-diplo"))
113 0 : this.destroyGlobalCollection(name);
114 : };
115 :
116 0 : m.GameState.prototype.getTimeElapsed = function()
117 : {
118 0 : return this.timeElapsed;
119 : };
120 :
121 0 : m.GameState.prototype.getBarterPrices = function()
122 : {
123 0 : return this.playerData.barterPrices;
124 : };
125 :
126 0 : m.GameState.prototype.getVictoryConditions = function()
127 : {
128 0 : return this.victoryConditions;
129 : };
130 :
131 0 : m.GameState.prototype.getAlliedVictory = function()
132 : {
133 0 : return this.alliedVictory;
134 : };
135 :
136 0 : m.GameState.prototype.isCeasefireActive = function()
137 : {
138 0 : return this.ceasefireActive;
139 : };
140 :
141 0 : m.GameState.prototype.getTemplate = function(type)
142 : {
143 0 : if (TechnologyTemplates.Has(type))
144 0 : return new m.Technology(type);
145 :
146 0 : if (this.templates[type] === undefined)
147 0 : this.sharedScript.GetTemplate(type);
148 :
149 0 : return this.templates[type] ? new m.Template(this.sharedScript, type, this.templates[type]) : null;
150 : };
151 :
152 : /** Return the template of the structure built from this foundation */
153 0 : m.GameState.prototype.getBuiltTemplate = function(foundationName)
154 : {
155 0 : if (!foundationName.startsWith("foundation|"))
156 : {
157 0 : warn("Foundation " + foundationName + " not recognised as a foundation.");
158 0 : return null;
159 : }
160 0 : return this.getTemplate(foundationName.substr(11));
161 : };
162 :
163 0 : m.GameState.prototype.applyCiv = function(str)
164 : {
165 0 : return str.replace(/\{civ\}/g, this.playerData.civ);
166 : };
167 :
168 0 : m.GameState.prototype.getPlayerCiv = function(player)
169 : {
170 0 : return player !== undefined ? this.sharedScript.playersData[player].civ : this.playerData.civ;
171 : };
172 :
173 0 : m.GameState.prototype.currentPhase = function()
174 : {
175 0 : for (let i = this.phases.length; i > 0; --i)
176 0 : if (this.isResearched(this.phases[i-1].name))
177 0 : return i;
178 0 : return 0;
179 : };
180 :
181 0 : m.GameState.prototype.getNumberOfPhases = function()
182 : {
183 0 : return this.phases.length;
184 : };
185 :
186 0 : m.GameState.prototype.getPhaseName = function(i)
187 : {
188 0 : return this.phases[i-1] ? this.phases[i-1].name : undefined;
189 : };
190 :
191 0 : m.GameState.prototype.getPhaseEntityRequirements = function(i)
192 : {
193 0 : let entityReqs = [];
194 :
195 0 : for (let requirement of this.phases[i-1].requirements)
196 : {
197 0 : if (!requirement.entities)
198 0 : continue;
199 0 : for (let entity of requirement.entities)
200 0 : if (entity.check == "count")
201 0 : entityReqs.push({
202 : "class": entity.class,
203 : "count": entity.number
204 : });
205 : }
206 :
207 0 : return entityReqs;
208 : };
209 :
210 0 : m.GameState.prototype.isResearched = function(template)
211 : {
212 0 : return this.playerData.researchedTechs.has(template);
213 : };
214 :
215 0 : m.GameState.prototype.isResearching = function(template)
216 : {
217 0 : return this.playerData.researchQueued.has(template);
218 : };
219 :
220 : /** this is an "in-absolute" check that doesn't check if we have a building to research from. */
221 0 : m.GameState.prototype.canResearch = function(techTemplateName, noRequirementCheck)
222 : {
223 0 : if (this.playerData.disabledTechnologies[techTemplateName])
224 0 : return false;
225 :
226 0 : let template = this.getTemplate(techTemplateName);
227 0 : if (!template)
228 0 : return false;
229 :
230 0 : if (this.playerData.researchQueued.has(techTemplateName) ||
231 : this.playerData.researchedTechs.has(techTemplateName))
232 0 : return false;
233 :
234 0 : if (noRequirementCheck)
235 0 : return true;
236 :
237 : // if this is a pair, we must check that the pair tech is not being researched
238 0 : if (template.pair())
239 : {
240 0 : let other = template.pairedWith();
241 0 : if (this.playerData.researchQueued.has(other) ||
242 : this.playerData.researchedTechs.has(other))
243 0 : return false;
244 : }
245 :
246 0 : return this.checkTechRequirements(template.requirements(this.playerData.civ));
247 : };
248 :
249 : /**
250 : * Private function for checking a set of requirements is met.
251 : * Basically copies TechnologyManager, but compares against
252 : * variables only available within the AI
253 : */
254 0 : m.GameState.prototype.checkTechRequirements = function(reqs)
255 : {
256 0 : if (!reqs)
257 0 : return false;
258 :
259 0 : if (!reqs.length)
260 0 : return true;
261 :
262 : function doesEntitySpecPass(entity)
263 : {
264 0 : switch (entity.check)
265 : {
266 : case "count":
267 0 : if (!this.playerData.classCounts[entity.class] || this.playerData.classCounts[entity.class] < entity.number)
268 0 : return false;
269 0 : break;
270 :
271 : case "variants":
272 0 : if (!this.playerData.typeCountsByClass[entity.class] || Object.keys(this.playerData.typeCountsByClass[entity.class]).length < entity.number)
273 0 : return false;
274 0 : break;
275 : }
276 0 : return true;
277 : }
278 :
279 0 : return reqs.some(req => {
280 0 : return Object.keys(req).every(type => {
281 0 : switch (type)
282 : {
283 : case "techs":
284 0 : return req[type].every(tech => this.playerData.researchedTechs.has(tech));
285 :
286 : case "entities":
287 0 : return req[type].every(doesEntitySpecPass, this);
288 : }
289 0 : return false;
290 : });
291 : });
292 : };
293 :
294 0 : m.GameState.prototype.getPassabilityMap = function()
295 : {
296 0 : return this.sharedScript.passabilityMap;
297 : };
298 :
299 0 : m.GameState.prototype.getPassabilityClassMask = function(name)
300 : {
301 0 : if (!this.sharedScript.passabilityClasses[name])
302 0 : error("Tried to use invalid passability class name '" + name + "'");
303 0 : return this.sharedScript.passabilityClasses[name];
304 : };
305 :
306 0 : m.GameState.prototype.getResources = function()
307 : {
308 0 : return new m.Resources(this.playerData.resourceCounts);
309 : };
310 :
311 0 : m.GameState.prototype.getPopulation = function()
312 : {
313 0 : return this.playerData.popCount;
314 : };
315 :
316 0 : m.GameState.prototype.getPopulationLimit = function() {
317 0 : return this.playerData.popLimit;
318 : };
319 :
320 0 : m.GameState.prototype.getPopulationMax = function() {
321 0 : return this.playerData.popMax;
322 : };
323 :
324 0 : m.GameState.prototype.getPlayerID = function()
325 : {
326 0 : return this.player;
327 : };
328 :
329 0 : m.GameState.prototype.hasAllies = function()
330 : {
331 0 : for (let i in this.playerData.isAlly)
332 0 : if (this.playerData.isAlly[i] && +i !== this.player &&
333 : this.sharedScript.playersData[i].state !== "defeated")
334 0 : return true;
335 0 : return false;
336 : };
337 :
338 0 : m.GameState.prototype.hasEnemies = function()
339 : {
340 0 : for (let i in this.playerData.isEnemy)
341 0 : if (this.playerData.isEnemy[i] && +i !== 0 &&
342 : this.sharedScript.playersData[i].state !== "defeated")
343 0 : return true;
344 0 : return false;
345 : };
346 :
347 0 : m.GameState.prototype.hasNeutrals = function()
348 : {
349 0 : for (let i in this.playerData.isNeutral)
350 0 : if (this.playerData.isNeutral[i] &&
351 : this.sharedScript.playersData[i].state !== "defeated")
352 0 : return true;
353 0 : return false;
354 : };
355 :
356 0 : m.GameState.prototype.isPlayerNeutral = function(id)
357 : {
358 0 : return this.playerData.isNeutral[id];
359 : };
360 :
361 0 : m.GameState.prototype.isPlayerAlly = function(id)
362 : {
363 0 : return this.playerData.isAlly[id];
364 : };
365 :
366 0 : m.GameState.prototype.isPlayerMutualAlly = function(id)
367 : {
368 0 : return this.playerData.isMutualAlly[id];
369 : };
370 :
371 0 : m.GameState.prototype.isPlayerEnemy = function(id)
372 : {
373 0 : return this.playerData.isEnemy[id];
374 : };
375 :
376 : /** Return the number of players currently enemies, not including gaia */
377 0 : m.GameState.prototype.getNumPlayerEnemies = function()
378 : {
379 0 : let num = 0;
380 0 : for (let i = 1; i < this.playerData.isEnemy.length; ++i)
381 0 : if (this.playerData.isEnemy[i] &&
382 : this.sharedScript.playersData[i].state != "defeated")
383 0 : ++num;
384 0 : return num;
385 : };
386 :
387 0 : m.GameState.prototype.getEnemies = function()
388 : {
389 0 : let ret = [];
390 0 : for (let i in this.playerData.isEnemy)
391 0 : if (this.playerData.isEnemy[i])
392 0 : ret.push(+i);
393 0 : return ret;
394 : };
395 :
396 0 : m.GameState.prototype.getNeutrals = function()
397 : {
398 0 : let ret = [];
399 0 : for (let i in this.playerData.isNeutral)
400 0 : if (this.playerData.isNeutral[i])
401 0 : ret.push(+i);
402 0 : return ret;
403 : };
404 :
405 0 : m.GameState.prototype.getAllies = function()
406 : {
407 0 : let ret = [];
408 0 : for (let i in this.playerData.isAlly)
409 0 : if (this.playerData.isAlly[i])
410 0 : ret.push(+i);
411 0 : return ret;
412 : };
413 :
414 0 : m.GameState.prototype.getExclusiveAllies = function()
415 : { // Player is not included
416 0 : let ret = [];
417 0 : for (let i in this.playerData.isAlly)
418 0 : if (this.playerData.isAlly[i] && +i !== this.player)
419 0 : ret.push(+i);
420 0 : return ret;
421 : };
422 :
423 0 : m.GameState.prototype.getMutualAllies = function()
424 : {
425 0 : let ret = [];
426 0 : for (let i in this.playerData.isMutualAlly)
427 0 : if (this.playerData.isMutualAlly[i] &&
428 : this.sharedScript.playersData[i].isMutualAlly[this.player])
429 0 : ret.push(+i);
430 0 : return ret;
431 : };
432 :
433 0 : m.GameState.prototype.isEntityAlly = function(ent)
434 : {
435 0 : if (!ent)
436 0 : return false;
437 0 : return this.playerData.isAlly[ent.owner()];
438 : };
439 :
440 0 : m.GameState.prototype.isEntityExclusiveAlly = function(ent)
441 : {
442 0 : if (!ent)
443 0 : return false;
444 0 : return this.playerData.isAlly[ent.owner()] && ent.owner() !== this.player;
445 : };
446 :
447 0 : m.GameState.prototype.isEntityEnemy = function(ent)
448 : {
449 0 : if (!ent)
450 0 : return false;
451 0 : return this.playerData.isEnemy[ent.owner()];
452 : };
453 :
454 0 : m.GameState.prototype.isEntityOwn = function(ent)
455 : {
456 0 : if (!ent)
457 0 : return false;
458 0 : return ent.owner() === this.player;
459 : };
460 :
461 0 : m.GameState.prototype.getEntityById = function(id)
462 : {
463 0 : return this.entities._entities.get(+id);
464 : };
465 :
466 0 : m.GameState.prototype.getEntities = function(id)
467 : {
468 0 : if (id === undefined)
469 0 : return this.entities;
470 :
471 0 : return this.updatingGlobalCollection("player-" + id + "-entities", m.Filters.byOwner(id));
472 : };
473 :
474 0 : m.GameState.prototype.getStructures = function()
475 : {
476 0 : return this.updatingGlobalCollection("structures", m.Filters.byClass("Structure"), this.entities);
477 : };
478 :
479 0 : m.GameState.prototype.getOwnEntities = function()
480 : {
481 0 : return this.updatingGlobalCollection("player-" + this.player + "-entities", m.Filters.byOwner(this.player));
482 : };
483 :
484 0 : m.GameState.prototype.getOwnStructures = function()
485 : {
486 0 : return this.updatingGlobalCollection("player-" + this.player + "-structures", m.Filters.byClass("Structure"), this.getOwnEntities());
487 : };
488 :
489 0 : m.GameState.prototype.getOwnUnits = function()
490 : {
491 0 : return this.updatingGlobalCollection("player-" + this.player + "-units", m.Filters.byClass("Unit"), this.getOwnEntities());
492 : };
493 :
494 0 : m.GameState.prototype.getAllyEntities = function()
495 : {
496 0 : return this.entities.filter(m.Filters.byOwners(this.getAllies()));
497 : };
498 :
499 0 : m.GameState.prototype.getExclusiveAllyEntities = function()
500 : {
501 0 : return this.entities.filter(m.Filters.byOwners(this.getExclusiveAllies()));
502 : };
503 :
504 0 : m.GameState.prototype.getAllyStructures = function(allyID)
505 : {
506 0 : if (allyID == undefined)
507 0 : return this.updatingCollection("diplo-ally-structures", m.Filters.byOwners(this.getAllies()), this.getStructures());
508 :
509 0 : return this.updatingGlobalCollection("player-" + allyID + "-structures", m.Filters.byOwner(allyID), this.getStructures());
510 : };
511 :
512 0 : m.GameState.prototype.getNeutralStructures = function()
513 : {
514 0 : return this.getStructures().filter(m.Filters.byOwners(this.getNeutrals()));
515 : };
516 :
517 0 : m.GameState.prototype.getEnemyEntities = function()
518 : {
519 0 : return this.entities.filter(m.Filters.byOwners(this.getEnemies()));
520 : };
521 :
522 0 : m.GameState.prototype.getEnemyStructures = function(enemyID)
523 : {
524 0 : if (enemyID === undefined)
525 0 : return this.updatingCollection("diplo-enemy-structures", m.Filters.byOwners(this.getEnemies()), this.getStructures());
526 :
527 0 : return this.updatingGlobalCollection("player-" + enemyID + "-structures", m.Filters.byOwner(enemyID), this.getStructures());
528 : };
529 :
530 0 : m.GameState.prototype.getEnemyUnits = function(enemyID)
531 : {
532 0 : if (enemyID === undefined)
533 0 : return this.getEnemyEntities().filter(m.Filters.byClass("Unit"));
534 :
535 0 : return this.updatingGlobalCollection("player-" + enemyID + "-units", m.Filters.byClass("Unit"), this.getEntities(enemyID));
536 : };
537 :
538 : /** if maintain is true, this will be stored. Otherwise it's one-shot. */
539 0 : m.GameState.prototype.getOwnEntitiesByMetadata = function(key, value, maintain)
540 : {
541 0 : if (maintain)
542 0 : return this.updatingCollection(key + "-" + value, m.Filters.byMetadata(this.player, key, value), this.getOwnEntities());
543 0 : return this.getOwnEntities().filter(m.Filters.byMetadata(this.player, key, value));
544 : };
545 :
546 0 : m.GameState.prototype.getOwnEntitiesByRole = function(role, maintain)
547 : {
548 0 : return this.getOwnEntitiesByMetadata("role", role, maintain);
549 : };
550 :
551 0 : m.GameState.prototype.getOwnEntitiesByType = function(type, maintain)
552 : {
553 0 : let filter = m.Filters.byType(type);
554 0 : if (maintain)
555 0 : return this.updatingCollection("type-" + type, filter, this.getOwnEntities());
556 0 : return this.getOwnEntities().filter(filter);
557 : };
558 :
559 0 : m.GameState.prototype.getOwnEntitiesByClass = function(cls, maintain)
560 : {
561 0 : let filter = m.Filters.byClass(cls);
562 0 : if (maintain)
563 0 : return this.updatingCollection("class-" + cls, filter, this.getOwnEntities());
564 0 : return this.getOwnEntities().filter(filter);
565 : };
566 :
567 0 : m.GameState.prototype.getOwnFoundationsByClass = function(cls, maintain)
568 : {
569 0 : let filter = m.Filters.byClass(cls);
570 0 : if (maintain)
571 0 : return this.updatingCollection("foundations-class-" + cls, filter, this.getOwnFoundations());
572 0 : return this.getOwnFoundations().filter(filter);
573 : };
574 :
575 0 : m.GameState.prototype.getOwnTrainingFacilities = function()
576 : {
577 0 : return this.updatingGlobalCollection("player-" + this.player + "-training-facilities", m.Filters.byTrainingQueue(), this.getOwnEntities());
578 : };
579 :
580 0 : m.GameState.prototype.getOwnResearchFacilities = function()
581 : {
582 0 : return this.updatingGlobalCollection("player-" + this.player + "-research-facilities", m.Filters.byResearchAvailable(this, this.playerData.civ), this.getOwnEntities());
583 : };
584 :
585 :
586 0 : m.GameState.prototype.countEntitiesByType = function(type, maintain)
587 : {
588 0 : return this.getOwnEntitiesByType(type, maintain).length;
589 : };
590 :
591 0 : m.GameState.prototype.countEntitiesAndQueuedByType = function(type, maintain)
592 : {
593 0 : let template = this.getTemplate(type);
594 0 : if (!template)
595 0 : return 0;
596 :
597 0 : let count = this.countEntitiesByType(type, maintain);
598 :
599 : // Count building foundations
600 0 : if (template.hasClass("Structure") === true)
601 0 : count += this.countFoundationsByType(type, true);
602 0 : else if (template.resourceSupplyType() !== undefined) // animal resources
603 0 : count += this.countEntitiesByType("resource|" + type, true);
604 : else
605 : {
606 : // Count entities in building production queues
607 : // TODO: maybe this fails for corrals.
608 0 : this.getOwnTrainingFacilities().forEach(function(ent) {
609 0 : for (let item of ent.trainingQueue())
610 0 : if (item.unitTemplate == type)
611 0 : count += item.count;
612 : });
613 : }
614 :
615 0 : return count;
616 : };
617 :
618 0 : m.GameState.prototype.countFoundationsByType = function(type, maintain)
619 : {
620 0 : let foundationType = "foundation|" + type;
621 :
622 0 : if (maintain)
623 0 : return this.updatingCollection("foundation-type-" + type, m.Filters.byType(foundationType), this.getOwnFoundations()).length;
624 :
625 0 : let count = 0;
626 0 : this.getOwnStructures().forEach(function(ent) {
627 0 : if (ent.templateName() == foundationType)
628 0 : ++count;
629 : });
630 0 : return count;
631 : };
632 :
633 0 : m.GameState.prototype.countOwnEntitiesByRole = function(role)
634 : {
635 0 : return this.getOwnEntitiesByRole(role, "true").length;
636 : };
637 :
638 0 : m.GameState.prototype.countOwnEntitiesAndQueuedWithRole = function(role)
639 : {
640 0 : let count = this.countOwnEntitiesByRole(role);
641 :
642 : // Count entities in building production queues
643 0 : this.getOwnTrainingFacilities().forEach(function(ent) {
644 0 : for (let item of ent.trainingQueue())
645 0 : if (item.metadata && item.metadata.role && item.metadata.role == role)
646 0 : count += item.count;
647 : });
648 0 : return count;
649 : };
650 :
651 0 : m.GameState.prototype.countOwnQueuedEntitiesWithMetadata = function(data, value)
652 : {
653 : // Count entities in building production queues
654 0 : let count = 0;
655 0 : this.getOwnTrainingFacilities().forEach(function(ent) {
656 0 : for (let item of ent.trainingQueue())
657 0 : if (item.metadata && item.metadata[data] && item.metadata[data] == value)
658 0 : count += item.count;
659 : });
660 0 : return count;
661 : };
662 :
663 0 : m.GameState.prototype.getOwnFoundations = function()
664 : {
665 0 : return this.updatingGlobalCollection("player-" + this.player + "-foundations", m.Filters.isFoundation(), this.getOwnStructures());
666 : };
667 :
668 0 : m.GameState.prototype.getOwnDropsites = function(resource)
669 : {
670 0 : if (resource)
671 0 : return this.updatingCollection("ownDropsite-" + resource, m.Filters.isDropsite(resource), this.getOwnEntities());
672 0 : return this.updatingCollection("ownDropsite-all", m.Filters.isDropsite(), this.getOwnEntities());
673 : };
674 :
675 0 : m.GameState.prototype.getAnyDropsites = function(resource)
676 : {
677 0 : if (resource)
678 0 : return this.updatingGlobalCollection("anyDropsite-" + resource, m.Filters.isDropsite(resource), this.getEntities());
679 0 : return this.updatingGlobalCollection("anyDropsite-all", m.Filters.isDropsite(), this.getEntities());
680 : };
681 :
682 0 : m.GameState.prototype.getResourceSupplies = function(resource)
683 : {
684 0 : return this.updatingGlobalCollection("resource-" + resource, m.Filters.byResource(resource), this.getEntities());
685 : };
686 :
687 0 : m.GameState.prototype.getHuntableSupplies = function()
688 : {
689 0 : return this.updatingGlobalCollection("resource-hunt", m.Filters.isHuntable(), this.getEntities());
690 : };
691 :
692 0 : m.GameState.prototype.getFishableSupplies = function()
693 : {
694 0 : return this.updatingGlobalCollection("resource-fish", m.Filters.isFishable(), this.getEntities());
695 : };
696 :
697 : /** This returns only units from buildings. */
698 0 : m.GameState.prototype.findTrainableUnits = function(classes, anticlasses)
699 : {
700 0 : let allTrainable = [];
701 0 : let civ = this.playerData.civ;
702 0 : this.getOwnTrainingFacilities().forEach(function(ent) {
703 0 : let trainable = ent.trainableEntities(civ);
704 0 : if (!trainable)
705 0 : return;
706 0 : for (let unit of trainable)
707 0 : if (allTrainable.indexOf(unit) === -1)
708 0 : allTrainable.push(unit);
709 : });
710 0 : let ret = [];
711 0 : let limits = this.getEntityLimits();
712 0 : let current = this.getEntityCounts();
713 0 : let matchCounts = this.getEntityMatchCounts();
714 0 : for (let trainable of allTrainable)
715 : {
716 0 : if (this.isTemplateDisabled(trainable))
717 0 : continue;
718 0 : let template = this.getTemplate(trainable);
719 0 : if (!template || !template.available(this))
720 0 : continue;
721 0 : let limit = template.matchLimit();
722 0 : if (matchCounts && limit && matchCounts[trainable] >= limit)
723 0 : continue;
724 0 : if (!template.hasClasses(classes) || template.hasClasses(anticlasses))
725 0 : continue;
726 0 : let category = template.trainingCategory();
727 0 : if (category && limits[category] && current[category] >= limits[category])
728 0 : continue;
729 :
730 0 : ret.push([trainable, template]);
731 : }
732 0 : return ret;
733 : };
734 :
735 : /**
736 : * Return all techs which can currently be researched
737 : * Does not factor cost.
738 : * If there are pairs, both techs are returned.
739 : */
740 0 : m.GameState.prototype.findAvailableTech = function()
741 : {
742 0 : let allResearchable = [];
743 0 : let civ = this.playerData.civ;
744 0 : for (let ent of this.getOwnEntities().values())
745 : {
746 0 : let searchable = ent.researchableTechs(this, civ);
747 0 : if (!searchable)
748 0 : continue;
749 0 : for (let tech of searchable)
750 0 : if (!this.playerData.disabledTechnologies[tech] && allResearchable.indexOf(tech) === -1)
751 0 : allResearchable.push(tech);
752 : }
753 :
754 0 : let ret = [];
755 0 : for (let tech of allResearchable)
756 : {
757 0 : let template = this.getTemplate(tech);
758 0 : if (template.pairDef())
759 : {
760 0 : let techs = template.getPairedTechs();
761 0 : if (this.canResearch(techs[0]._templateName))
762 0 : ret.push([techs[0]._templateName, techs[0]]);
763 0 : if (this.canResearch(techs[1]._templateName))
764 0 : ret.push([techs[1]._templateName, techs[1]]);
765 : }
766 0 : else if (this.canResearch(tech))
767 : {
768 : // Phases are treated separately
769 0 : if (this.phases.every(phase => template._templateName != phase.name))
770 0 : ret.push([tech, template]);
771 : }
772 : }
773 0 : return ret;
774 : };
775 :
776 : /**
777 : * Return true if we have a building able to train that template
778 : */
779 0 : m.GameState.prototype.hasTrainer = function(template)
780 : {
781 0 : let civ = this.playerData.civ;
782 0 : for (let ent of this.getOwnTrainingFacilities().values())
783 : {
784 0 : let trainable = ent.trainableEntities(civ);
785 0 : if (trainable && trainable.indexOf(template) !== -1)
786 0 : return true;
787 : }
788 0 : return false;
789 : };
790 :
791 : /**
792 : * Find buildings able to train that template.
793 : */
794 0 : m.GameState.prototype.findTrainers = function(template)
795 : {
796 0 : let civ = this.playerData.civ;
797 0 : return this.getOwnTrainingFacilities().filter(function(ent) {
798 0 : let trainable = ent.trainableEntities(civ);
799 0 : return trainable && trainable.indexOf(template) !== -1;
800 : });
801 : };
802 :
803 : /**
804 : * Get any unit that is capable of constructing the given building type.
805 : */
806 0 : m.GameState.prototype.findBuilder = function(template)
807 : {
808 0 : let civ = this.getPlayerCiv();
809 0 : for (let ent of this.getOwnUnits().values())
810 : {
811 0 : let buildable = ent.buildableEntities(civ);
812 0 : if (buildable && buildable.indexOf(template) !== -1)
813 0 : return ent;
814 : }
815 0 : return undefined;
816 : };
817 :
818 : /** Return true if one of our buildings is capable of researching the given tech */
819 0 : m.GameState.prototype.hasResearchers = function(templateName, noRequirementCheck)
820 : {
821 : // let's check we can research the tech.
822 0 : if (!this.canResearch(templateName, noRequirementCheck))
823 0 : return false;
824 :
825 0 : let template = this.getTemplate(templateName);
826 0 : if (template.autoResearch)
827 0 : return true;
828 :
829 0 : let civ = this.playerData.civ;
830 :
831 0 : for (let ent of this.getOwnResearchFacilities().values())
832 : {
833 0 : let techs = ent.researchableTechs(this, civ);
834 0 : for (let tech of techs)
835 : {
836 0 : let temp = this.getTemplate(tech);
837 0 : if (temp.pairDef())
838 : {
839 0 : let pairedTechs = temp.getPairedTechs();
840 0 : if (pairedTechs[0]._templateName == templateName ||
841 : pairedTechs[1]._templateName == templateName)
842 0 : return true;
843 : }
844 0 : else if (tech == templateName)
845 0 : return true;
846 : }
847 : }
848 0 : return false;
849 : };
850 :
851 : /** Find buildings that are capable of researching the given tech */
852 0 : m.GameState.prototype.findResearchers = function(templateName, noRequirementCheck)
853 : {
854 : // let's check we can research the tech.
855 0 : if (!this.canResearch(templateName, noRequirementCheck))
856 0 : return undefined;
857 :
858 0 : let self = this;
859 0 : let civ = this.playerData.civ;
860 :
861 0 : return this.getOwnResearchFacilities().filter(function(ent) {
862 0 : let techs = ent.researchableTechs(self, civ);
863 0 : for (let tech of techs)
864 : {
865 0 : let thisTemp = self.getTemplate(tech);
866 0 : if (thisTemp.pairDef())
867 : {
868 0 : let pairedTechs = thisTemp.getPairedTechs();
869 0 : if (pairedTechs[0]._templateName == templateName ||
870 : pairedTechs[1]._templateName == templateName)
871 0 : return true;
872 : }
873 0 : else if (tech == templateName)
874 0 : return true;
875 : }
876 0 : return false;
877 : });
878 : };
879 :
880 0 : m.GameState.prototype.getEntityLimits = function()
881 : {
882 0 : return this.playerData.entityLimits;
883 : };
884 :
885 0 : m.GameState.prototype.getEntityMatchCounts = function()
886 : {
887 0 : return this.playerData.matchEntityCounts;
888 : };
889 :
890 0 : m.GameState.prototype.getEntityCounts = function()
891 : {
892 0 : return this.playerData.entityCounts;
893 : };
894 :
895 0 : m.GameState.prototype.isTemplateAvailable = function(templateName)
896 : {
897 0 : if (this.templates[templateName] === undefined)
898 0 : this.sharedScript.GetTemplate(templateName);
899 0 : return this.templates[templateName] && !this.isTemplateDisabled(templateName);
900 : };
901 :
902 0 : m.GameState.prototype.isTemplateDisabled = function(templateName)
903 : {
904 0 : if (!this.playerData.disabledTemplates[templateName])
905 0 : return false;
906 0 : return this.playerData.disabledTemplates[templateName];
907 : };
908 :
909 : /** Checks whether the maximum number of buildings have been constructed for a certain catergory */
910 0 : m.GameState.prototype.isEntityLimitReached = function(category)
911 : {
912 0 : if (this.playerData.entityLimits[category] === undefined ||
913 : this.playerData.entityCounts[category] === undefined)
914 0 : return false;
915 0 : return this.playerData.entityCounts[category] >= this.playerData.entityLimits[category];
916 : };
917 :
918 0 : m.GameState.prototype.getTraderTemplatesGains = function()
919 : {
920 0 : let shipMechantTemplateName = this.applyCiv("units/{civ}/ship_merchant");
921 0 : let supportTraderTemplateName = this.applyCiv("units/{civ}/support_trader");
922 0 : let shipMerchantTemplate = !this.isTemplateDisabled(shipMechantTemplateName) && this.getTemplate(shipMechantTemplateName);
923 0 : let supportTraderTemplate = !this.isTemplateDisabled(supportTraderTemplateName) && this.getTemplate(supportTraderTemplateName);
924 0 : let norm = TradeGainNormalization(this.sharedScript.mapSize);
925 0 : let ret = {};
926 0 : if (supportTraderTemplate)
927 0 : ret.landGainMultiplier = norm * supportTraderTemplate.gainMultiplier();
928 0 : if (shipMerchantTemplate)
929 0 : ret.navalGainMultiplier = norm * shipMerchantTemplate.gainMultiplier();
930 0 : return ret;
931 : };
932 :
933 0 : return m;
934 :
935 : }(API3);
936 :
|