LCOV - code coverage report
Current view: top level - simulation/ai/common-api - gamestate.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 490 0.0 %
Date: 2023-04-02 12:52:40 Functions: 0 110 0.0 %

          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             : 

Generated by: LCOV version 1.14