From 9d02495a96d2ff6cbd53cb99fa2aaec458909c28 Mon Sep 17 00:00:00 2001 From: wraitii Date: Sun, 12 Jan 2014 01:07:07 +0000 Subject: [PATCH] Fix a few bugs. Improve the AI gamestate to make better use of entity collections, should be very slightly faster, and it's cleaner. Remove enemy watchers that were no longer used. This was SVN commit r14574. --- .../mods/public/simulation/ai/aegis/aegis.js | 4 +- .../public/simulation/ai/aegis/attack_plan.js | 22 +- .../simulation/ai/aegis/base-manager.js | 8 +- .../simulation/ai/aegis/defence-helper.js | 2 + .../public/simulation/ai/aegis/defence.js | 10 +- .../simulation/ai/aegis/enemy-watcher.js | 222 ----------- .../simulation/ai/aegis/headquarters.js | 58 +-- .../public/simulation/ai/aegis/map-module.js | 16 +- .../simulation/ai/aegis/naval-manager.js | 2 +- .../simulation/ai/aegis/queue-manager.js | 2 +- .../simulation/ai/aegis/queueplan-building.js | 96 +++-- .../simulation/ai/common-api/gamestate.js | 364 +++++++++--------- 12 files changed, 279 insertions(+), 527 deletions(-) delete mode 100755 binaries/data/mods/public/simulation/ai/aegis/enemy-watcher.js diff --git a/binaries/data/mods/public/simulation/ai/aegis/aegis.js b/binaries/data/mods/public/simulation/ai/aegis/aegis.js index 0975f92344..5692eb4872 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/aegis.js +++ b/binaries/data/mods/public/simulation/ai/aegis/aegis.js @@ -146,7 +146,7 @@ m.AegisBot.prototype.OnUpdate = function(sharedScript) { // TODO: softcode this. if (this.gameState.canResearch(townPhase,true) && this.gameState.getTimeElapsed() > (this.Config.Economy.townPhase*1000) && this.gameState.getPopulation() > 40 && this.gameState.findResearchers(townPhase,true).length != 0 && this.queues.majorTech.length() === 0 - && this.gameState.getOwnEntities().filter(API3.Filters.byClass("Village")).length > 5) + && this.gameState.getOwnStructures().filter(API3.Filters.byClass("Village")).length > 5) { this.queueManager.pauseQueue("villager", true); this.queueManager.pauseQueue("citizenSoldier", true); @@ -155,7 +155,7 @@ m.AegisBot.prototype.OnUpdate = function(sharedScript) { m.debug ("Trying to reach town phase"); } else if (this.gameState.canResearch(cityPhase,true) && this.gameState.getTimeElapsed() > (this.Config.Economy.cityPhase*1000) - && this.gameState.getOwnEntitiesByRole("worker").length > 85 + && this.gameState.getOwnEntitiesByRole("worker", true).length > 85 && this.gameState.findResearchers(cityPhase, true).length != 0 && this.queues.majorTech.length() === 0) { m.debug ("Trying to reach city phase"); this.queues.majorTech.addItem(new m.ResearchPlan(this.gameState, cityPhase)); diff --git a/binaries/data/mods/public/simulation/ai/aegis/attack_plan.js b/binaries/data/mods/public/simulation/ai/aegis/attack_plan.js index 460509fb92..610cd8599f 100755 --- a/binaries/data/mods/public/simulation/ai/aegis/attack_plan.js +++ b/binaries/data/mods/public/simulation/ai/aegis/attack_plan.js @@ -37,7 +37,7 @@ m.CityAttack = function CityAttack(gameState, HQ, Config, uniqueID, targetEnemy, return false; } - var CCs = gameState.getOwnEntities().filter(API3.Filters.byClass("CivCentre")); + var CCs = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")); if (CCs.length === 0) { this.failed = true; @@ -125,7 +125,7 @@ m.CityAttack = function CityAttack(gameState, HQ, Config, uniqueID, targetEnemy, };*/ var filter = API3.Filters.and(API3.Filters.byMetadata(PlayerID, "plan",this.name),API3.Filters.byOwner(PlayerID)); - this.unitCollection = gameState.getOwnEntities().filter(filter); + this.unitCollection = gameState.getOwnUnits().filter(filter); this.unitCollection.registerUpdates(); this.unitCollection.length; @@ -141,7 +141,7 @@ m.CityAttack = function CityAttack(gameState, HQ, Config, uniqueID, targetEnemy, var Unit = this.unitStat[cat]; filter = API3.Filters.and(API3.Filters.byClassesAnd(Unit["classes"]),API3.Filters.and(API3.Filters.byMetadata(PlayerID, "plan",this.name),API3.Filters.byOwner(PlayerID))); - this.unit[cat] = gameState.getOwnEntities().filter(filter); + this.unit[cat] = gameState.getOwnUnits().filter(filter); this.unit[cat].registerUpdates(); this.unit[cat].length; this.buildOrder.push([0, Unit["classes"], this.unit[cat], Unit, cat]); @@ -321,7 +321,7 @@ m.CityAttack.prototype.addBuildOrder = function(gameState, name, unitStats, rese this.unitStat[name] = unitStats; var Unit = this.unitStat[name]; var filter = API3.Filters.and(API3.Filters.byClassesAnd(Unit["classes"]),API3.Filters.and(API3.Filters.byMetadata(PlayerID, "plan",this.name),API3.Filters.byOwner(PlayerID))); - this.unit[name] = gameState.getOwnEntities().filter(filter); + this.unit[name] = gameState.getOwnUnits().filter(filter); this.unit[name].registerUpdates(); this.buildOrder.push([0, Unit["classes"], this.unit[name], Unit, name]); if (resetQueue) @@ -592,9 +592,9 @@ m.CityAttack.prototype.assignUnits = function(gameState){ // TODO: assign myself units that fit only, right now I'm getting anything. // Assign all no-roles that fit (after a plan aborts, for example). - var NoRole = gameState.getOwnEntitiesByRole(undefined); + var NoRole = gameState.getOwnEntitiesByRole(undefined, false); if (this.type === "rush") - NoRole = gameState.getOwnEntitiesByRole("worker"); + NoRole = gameState.getOwnEntitiesByRole("worker", true); NoRole.forEach(function(ent) { if (ent.hasClass("Unit") && ent.attackTypes() !== undefined) { @@ -641,20 +641,20 @@ m.CityAttack.prototype.AllToRallyPoint = function(gameState, evenWorkers) { m.CityAttack.prototype.defaultTargetFinder = function(gameState, HQ){ var targets = undefined; - targets = HQ.enemyWatchers[this.targetPlayer].getEnemyBuildings(gameState, "CivCentre",true); + targets = gameState.getEnemyStructures(this.targetPlayer).filter(m.Filters.byClass("CivCentre")); if (targets.length == 0) { - targets = HQ.enemyWatchers[this.targetPlayer].getEnemyBuildings(gameState, "ConquestCritical"); + targets = gameState.getEnemyStructures(this.targetPlayer).filter(m.Filters.byClass("ConquestCritical")); } // If there's nothing, attack anything else that's less critical if (targets.length == 0) { - targets = HQ.enemyWatchers[this.targetPlayer].getEnemyBuildings(gameState, "Town",true); + targets = gameState.getEnemyStructures(this.targetPlayer).filter(m.Filters.byClass("Town")); } if (targets.length == 0) { - targets = HQ.enemyWatchers[this.targetPlayer].getEnemyBuildings(gameState, "Village",true); + targets = gameState.getEnemyStructures(this.targetPlayer).filter(m.Filters.byClass("Village")); } // no buildings, attack anything conquest critical, even units (it's assuming it won't move). if (targets.length == 0) { - targets = gameState.getEnemyEntities().filter(API3.Filters.and( API3.Filters.byOwner(this.targetPlayer),API3.Filters.byClass("ConquestCritical"))); + targets = gameState.getEnemyEntities(this.targetPlayer).filter(m.Filters.byClass("ConquestCritical")); } return targets; }; diff --git a/binaries/data/mods/public/simulation/ai/aegis/base-manager.js b/binaries/data/mods/public/simulation/ai/aegis/base-manager.js index dc0aadc8fa..ad3071d127 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/base-manager.js +++ b/binaries/data/mods/public/simulation/ai/aegis/base-manager.js @@ -37,8 +37,8 @@ m.BaseManager = function(Config) { m.BaseManager.prototype.init = function(gameState, events, unconstructed){ this.constructing = unconstructed; // entitycollections - this.units = gameState.getOwnEntities().filter(API3.Filters.and(API3.Filters.byClass("Unit"),API3.Filters.byMetadata(PlayerID, "base", this.ID))); - this.buildings = gameState.getOwnEntities().filter(API3.Filters.and(API3.Filters.byClass("Structure"),API3.Filters.byMetadata(PlayerID, "base", this.ID))); + this.units = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "base", this.ID)); + this.buildings = gameState.getOwnStructures().filter(API3.Filters.byMetadata(PlayerID, "base", this.ID)); this.workers = this.units.filter(API3.Filters.byMetadata(PlayerID,"role","worker")); @@ -623,7 +623,7 @@ m.BaseManager.prototype.checkResourceLevels = function (gameState,queues) { queues.field.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_field", { "base" : this.ID })); // TODO: refine count to only count my base. } - } else if (queues.dropsites.length() === 0 && gameState.countFoundationsWithType(gameState.applyCiv("structures/{civ}_storehouse")) === 0) { + } else if (queues.dropsites.length() === 0 && gameState.countFoundationsByType(gameState.applyCiv("structures/{civ}_storehouse")) === 0) { var wantedDPs = Math.ceil(this.gatherersByType(gameState, type).length / 12.0); var need = wantedDPs - this.getResourceLevel(gameState,type, "dropsites-dpcount",2000); if (need > 0) @@ -947,7 +947,7 @@ m.BaseManager.prototype.update = function(gameState, queues, events) { /*Engine.ProfileStart("Swap Workers"); var gathererGroups = {}; - gameState.getOwnEntitiesByRole("worker").forEach(function(ent){ }){ + gameState.getOwnEntitiesByRole("worker", true).forEach(function(ent){ }){ if (ent.hasClass("Cavalry")) return; var key = uneval(ent.resourceGatherRates()); diff --git a/binaries/data/mods/public/simulation/ai/aegis/defence-helper.js b/binaries/data/mods/public/simulation/ai/aegis/defence-helper.js index 382d454188..749cc4b8a2 100755 --- a/binaries/data/mods/public/simulation/ai/aegis/defence-helper.js +++ b/binaries/data/mods/public/simulation/ai/aegis/defence-helper.js @@ -140,6 +140,8 @@ m.Army.prototype.recalculatePosition = function(gameState, force) { var ent = gameState.getEntityById(this.entities[i]); var epos = ent.position(); + if (epos == undefined) + continue; pos[0] += epos[0]; pos[1] += epos[1]; } diff --git a/binaries/data/mods/public/simulation/ai/aegis/defence.js b/binaries/data/mods/public/simulation/ai/aegis/defence.js index 86c13c58b8..04ad40372f 100755 --- a/binaries/data/mods/public/simulation/ai/aegis/defence.js +++ b/binaries/data/mods/public/simulation/ai/aegis/defence.js @@ -60,13 +60,11 @@ m.Defence.prototype.isDangerous = function(gameState, entity) if (this.territoryMap.getOwner(entity.position()) === entity.owner() || entity.attackTypes() === undefined) return false; - // TODO: use global collections. - var myBuildings = gameState.getOwnEntities().filter(API3.Filters.byClass("Structure")); + var myBuildings = gameState.getOwnStructures(); for (var i in myBuildings._entities) - { if (API3.SquareVectorDistance(myBuildings._entities[i].position(), entity.position()) < 6000) return true; - } + return false; } @@ -186,12 +184,12 @@ m.Defence.prototype.assignDefenders = function(gameState, events) API3.Filters.not(API3.Filters.byHasMetadata(PlayerID, "DefManagerArmy"))), API3.Filters.and(API3.Filters.not(API3.Filters.byMetadata(PlayerID,"subrole","walking")), API3.Filters.not(API3.Filters.byMetadata(PlayerID,"subrole","attacking")))); - var potentialDefendersOne = gameState.getOwnEntities().filter(filter).toIdArray(); + var potentialDefendersOne = gameState.getOwnUnits().filter(filter).toIdArray(); filter = API3.Filters.and(API3.Filters.and(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID,"plan")), API3.Filters.not(API3.Filters.byHasMetadata(PlayerID, "DefManagerArmy"))), API3.Filters.byClassesOr(["Infantry","Cavalry"])); - var potentialDefendersTwo = gameState.getOwnEntities().filter(filter).toIdArray(); + var potentialDefendersTwo = gameState.getOwnUnits().filter(filter).toIdArray(); var potDefs = this.releasedDefenders.concat(potentialDefendersOne).concat(potentialDefendersTwo); diff --git a/binaries/data/mods/public/simulation/ai/aegis/enemy-watcher.js b/binaries/data/mods/public/simulation/ai/aegis/enemy-watcher.js deleted file mode 100755 index 873ca49527..0000000000 --- a/binaries/data/mods/public/simulation/ai/aegis/enemy-watcher.js +++ /dev/null @@ -1,222 +0,0 @@ -var AEGIS = function(m) -{ - -/* - * A class that keeps track of enemy buildings, units, and pretty much anything I can think of (still a LOT TODO here) - * Only watches one enemy, you'll need one per enemy. - * Note: pretty much unused in the current version. - */ - -m.enemyWatcher = function(gameState, playerToWatch) { - - this.watched = playerToWatch; - - // using global entity collections, shared by any AI that knows the name of this. - - var filter = API3.Filters.and(API3.Filters.byClass("Structure"), API3.Filters.byOwner(this.watched)); - this.enemyBuildings = gameState.updatingGlobalCollection("player-" +this.watched + "-structures", filter); - - filter = API3.Filters.and(API3.Filters.byClass("Unit"), API3.Filters.byOwner(this.watched)); - this.enemyUnits = gameState.updatingGlobalCollection("player-" +this.watched + "-units", filter); - - filter = API3.Filters.and(API3.Filters.byClass("Worker"), API3.Filters.byOwner(this.watched)); - this.enemyCivilians = gameState.updatingGlobalCollection("player-" +this.watched + "-civilians", filter); - - filter = API3.Filters.and(API3.Filters.byClassesOr(["CitizenSoldier", "Hero", "Champion", "Siege"]), API3.Filters.byOwner(this.watched)); - this.enemySoldiers = gameState.updatingGlobalCollection("player-" +this.watched + "-soldiers", filter); - - filter = API3.Filters.and(API3.Filters.byClass("Worker"), API3.Filters.byOwner(this.watched)); - this.enemyCivilians = gameState.getEnemyEntities().filter(filter); - this.enemyCivilians.registerUpdates(); - - filter = API3.Filters.and(API3.Filters.byClassesOr(["CitizenSoldier", "Hero", "Champion", "Siege"]), API3.Filters.byOwner(this.watched)); - this.enemySoldiers = gameState.getEnemyEntities().filter(filter); - this.enemySoldiers.registerUpdates(); - - // entity collections too. - this.armies = {}; - - this.enemyBuildingClass = {}; - this.totalNBofArmies = 0; - - // this is an array of integers, refering to "this.armies[ XX ]" - this.dangerousArmies = []; - -}; -m.enemyWatcher.prototype.getAllEnemySoldiers = function() { - return this.enemySoldiers; -}; -m.enemyWatcher.prototype.getAllEnemyBuildings = function() { - return this.enemyBuildings; -}; - -m.enemyWatcher.prototype.getEnemyBuildings = function(gameState, specialClass, OneTime) { - var filter = API3.Filters.byClass(specialClass); - - if (OneTime && gameState.getGEC("player-" +this.watched + "-structures-" +specialClass)) - return gameState.getGEC("player-" +this.watched + "-structures-" +specialClass); - else if (OneTime) - return this.enemyBuildings.filter(filter); - - return gameState.updatingGlobalCollection("player-" +this.watched + "-structures-" +specialClass, filter, gameState.getGEC("player-" +this.watched + "-structures")); -}; -m.enemyWatcher.prototype.getDangerousArmies = function() { - var toreturn = {}; - for (var i in this.dangerousArmies) - toreturn[this.dangerousArmies[i]] = this.armies[this.dangerousArmies[i]]; - return toreturn; -}; -m.enemyWatcher.prototype.getSafeArmies = function() { - var toreturn = {}; - for (var i in this.armies) - if (this.dangerousArmies.indexOf(i) == -1) - toreturn[i] = this.armies[i]; - return toreturn; -}; -m.enemyWatcher.prototype.resetDangerousArmies = function() { - this.dangerousArmies = []; -}; -m.enemyWatcher.prototype.setAsDangerous = function(armyID) { - if (this.dangerousArmies.indexOf(armyID) === -1) - this.dangerousArmies.push(armyID); -}; -m.enemyWatcher.prototype.isDangerous = function(armyID) { - if (this.dangerousArmies.indexOf(armyID) === -1) - return false; - return true; -}; -// returns [id, army] -m.enemyWatcher.prototype.getArmyFromMember = function(memberID) { - for (var i in this.armies) { - if (this.armies[i].toIdArray().indexOf(memberID) !== -1) - return [i,this.armies[i]]; - } - return undefined; -}; -m.enemyWatcher.prototype.isPartOfDangerousArmy = function(memberID) { - var armyID = this.getArmyFromMember(memberID)[0]; - if (this.isDangerous(armyID)) - return true; - return false; -}; -m.enemyWatcher.prototype.cleanDebug = function() { - for (var armyID in this.armies) { - var army = this.armies[armyID]; - m.debug ("Army " +armyID); - m.debug (army.length +" members, centered around " +army.getCentrePosition()); - } -} - -// this will monitor any unmonitored soldier. -m.enemyWatcher.prototype.detectArmies = function(gameState){ - //this.cleanDebug(); - - var self = this; - if (gameState.ai.playedTurn % 4 === 0) { - Engine.ProfileStart("Looking for new soldiers"); - // let's loop through unmonitored enemy soldiers - this.unmonitoredEnemySoldiers.forEach( function (enemy) { //}){ - if (enemy.position() === undefined) - return; - - // this was an unmonitored unit, we do not know any army associated with it. We assign it a new army (we'll merge later if needed) - enemy.setMetadata(PlayerID, "monitored","true"); - var armyID = gameState.player + "" + self.totalNBofArmies; - self.totalNBofArmies++, - enemy.setMetadata(PlayerID, "EnemyWatcherArmy",armyID); - var filter = API3.Filters.byMetadata(PlayerID, "EnemyWatcherArmy",armyID); - var army = self.enemySoldiers.filter(filter); - self.armies[armyID] = army; - self.armies[armyID].registerUpdates(); - self.armies[armyID].length; - }); - Engine.ProfileStop(); - } else if (gameState.ai.playedTurn % 16 === 3) { - Engine.ProfileStart("Merging"); - this.mergeArmies(); // calls "scrap empty armies" - Engine.ProfileStop(); - } else if (gameState.ai.playedTurn % 16 === 7) { - Engine.ProfileStart("Splitting"); - this.splitArmies(gameState); - Engine.ProfileStop(); - } -}; -// this will merge any two army who are too close together. The distance for "army" is fairly big. -// note: this doesn't actually merge two entity collections... It simply changes the unit metadatas, and will clear the empty entity collection -m.enemyWatcher.prototype.mergeArmies = function(){ - for (var army in this.armies) { - var firstArmy = this.armies[army]; - if (firstArmy.length !== 0) { - var firstAPos = firstArmy.getApproximatePosition(4); - for (var otherArmy in this.armies) { - if (otherArmy !== army && this.armies[otherArmy].length !== 0) { - var secondArmy = this.armies[otherArmy]; - // we're not self merging, so we check if the two armies are close together - if (m.inRange(firstAPos,secondArmy.getApproximatePosition(4), 4000 ) ) { - // okay so we merge the two together - - // if the other one was dangerous and we weren't, we're now. - if (this.dangerousArmies.indexOf(otherArmy) !== -1 && this.dangerousArmies.indexOf(army) === -1) - this.dangerousArmies.push(army); - - secondArmy.forEach( function(ent) { - ent.setMetadata(PlayerID, "EnemyWatcherArmy",army); - }); - } - } - } - } - } - this.ScrapEmptyArmies(); -}; -m.enemyWatcher.prototype.ScrapEmptyArmies = function(){ - var removelist = []; - for (var army in this.armies) { - if (this.armies[army].length === 0) { - removelist.push(army); - // if the army was dangerous, we remove it from the list - if (this.dangerousArmies.indexOf(army) !== -1) - this.dangerousArmies.splice(this.dangerousArmies.indexOf(army),1); - } - } - for each (var toRemove in removelist) { - delete this.armies[toRemove]; - } -}; -// splits any unit too far from the centerposition -m.enemyWatcher.prototype.splitArmies = function(gameState){ - var self = this; - - var map = m.createTerritoryMap(gameState); - - for (var armyID in this.armies) { - var army = this.armies[armyID]; - var centre = army.getApproximatePosition(4); - - if (map.getOwner(centre) === gameState.player) - continue; - - army.forEach( function (enemy) { - if (enemy.position() === undefined) - return; - - if (!m.inRange(enemy.position(),centre, 3500) ) { - var newArmyID = gameState.player + "" + self.totalNBofArmies; - if (self.dangerousArmies.indexOf(armyID) !== -1) - self.dangerousArmies.push(newArmyID); - - self.totalNBofArmies++, - enemy.setMetadata(PlayerID, "EnemyWatcherArmy",newArmyID); - var filter = API3.Filters.byMetadata(PlayerID, "EnemyWatcherArmy",newArmyID); - var newArmy = self.enemySoldiers.filter(filter); - self.armies[newArmyID] = newArmy; - self.armies[newArmyID].registerUpdates(); - self.armies[newArmyID].length; - } - }); - } -}; - - -return m; -}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/aegis/headquarters.js b/binaries/data/mods/public/simulation/ai/aegis/headquarters.js index e03c8207eb..de59034411 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/headquarters.js +++ b/binaries/data/mods/public/simulation/ai/aegis/headquarters.js @@ -74,7 +74,7 @@ m.HQ.prototype.init = function(gameState, events, queues){ if (ents.filter(API3.Filters.byClass("Cavalry")).length > 0) hasScout = true; - // tODO: take multiple CCs into account. + // TODO: take multiple CCs into account. if (hasCC) { var CC = ents.filter(API3.Filters.byClass("CivCentre")).toEntityArray()[0]; @@ -121,10 +121,9 @@ m.HQ.prototype.init = function(gameState, events, queues){ //this.reassignIdleWorkers(gameState); - this.navalManager.init(gameState, events, queues); - // TODO: change that. + // TODO: change that to something dynamic. var civ = gameState.playerData.civ; // load units and buildings from the config files @@ -153,27 +152,6 @@ m.HQ.prototype.init = function(gameState, events, queues){ for (var i in this.bFort){ this.bFort[i] = gameState.applyCiv(this.bFort[i]); } - - // TODO: figure out how to make this generic - for (var i in this.attackManagers){ - this.availableAttacks[i] = new this.attackManagers[i](gameState, this); - } - - var enemies = gameState.getEnemyEntities(); - var filter = API3.Filters.byClassesOr(["CitizenSoldier", "Champion", "Hero", "Siege"]); - this.enemySoldiers = enemies.filter(filter); // TODO: cope with diplomacy changes - this.enemySoldiers.registerUpdates(); - - // each enemy watchers keeps a list of entity collections about the enemy it watches - // It also keeps track of enemy armies, merging/splitting as needed - // TODO: remove those. - this.enemyWatchers = {}; - this.ennWatcherIndex = []; - for (var i = 1; i <= 8; i++) - if (PlayerID != i && gameState.isPlayerEnemy(i)) { - this.enemyWatchers[i] = new m.enemyWatcher(gameState, i); - this.ennWatcherIndex.push(i); - } }; m.HQ.prototype.checkEvents = function (gameState, events, queues) { @@ -244,14 +222,16 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues) { // TODO: This should probably be changed to favor a more mixed approach for better defense. // (or even to adapt based on estimated enemy strategy). // TODO: this should probably set which base it wants them in. -m.HQ.prototype.trainMoreWorkers = function(gameState, queues) { +m.HQ.prototype.trainMoreWorkers = function(gameState, queues) +{ + // Get some data. // Count the workers in the world and in progress var numFemales = gameState.countEntitiesAndQueuedByType(gameState.applyCiv("units/{civ}_support_female_citizen")); numFemales += queues.villager.countQueuedUnitsWithClass("Support"); // counting the workers that aren't part of a plan var numWorkers = 0; - gameState.getOwnEntities().forEach (function (ent) { + gameState.getOwnunits().forEach (function (ent) { if (ent.getMetadata(PlayerID, "role") == "worker" && ent.getMetadata(PlayerID, "plan") == undefined) numWorkers++; }); @@ -273,12 +253,12 @@ m.HQ.prototype.trainMoreWorkers = function(gameState, queues) { var template = gameState.applyCiv("units/{civ}_support_female_citizen"); var size = Math.min(5, Math.ceil(numTotal / 10)); - if (numFemales/numTotal > this.femaleRatio && (numTotal > 20 || (this.fastStart && numTotal > 10))) { if (numTotal < 100) template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], [ ["cost",1], ["speed",0.5], ["costsResource", 0.5, "stone"], ["costsResource", 0.5, "metal"]]); else template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], [ ["strength",1] ]); + if (!template) template = gameState.applyCiv("units/{civ}_support_female_citizen"); if (gameState.currentPhase() === 1) @@ -286,10 +266,10 @@ m.HQ.prototype.trainMoreWorkers = function(gameState, queues) { } // TODO: improve that logic. - if (numFemales/numTotal > this.femaleRatio * 1.3 && numTotal > 25) + if (numFemales/numTotal > this.femaleRatio * 1.3 && numWorkers > 25) queues.villager.paused = true; else if ((numFemales/numTotal < this.femaleRatio * 1.1) || gameState.ai.queueManager.getAvailableResources(gameState)["food"] > 250 - || numTotal <= 25) + || numWorkers <= 25) queues.villager.paused = false; // TODO: perhaps assign them a default resource and check the base according to that. @@ -557,8 +537,8 @@ m.HQ.prototype.findBestEcoCCLocation = function(gameState, resource){ // copy the resource map as initialization. var friendlyTiles = new API3.Map(gameState.sharedScript, gameState.sharedScript.CCResourceMaps[resource].map, true); friendlyTiles.setMaxVal(255); - var ents = gameState.getOwnEntities().filter(API3.Filters.byClass("CivCentre")).toEntityArray(); - var eEnts = gameState.getEnemyEntities().filter(API3.Filters.byClass("CivCentre")).toEntityArray(); + var ents = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")).toEntityArray(); + var eEnts = gameState.getEnemyStructures().filter(API3.Filters.byClass("CivCentre")).toEntityArray(); var dps = gameState.getOwnDropsites().toEntityArray(); @@ -806,10 +786,10 @@ m.HQ.prototype.checkBasesRessLevel = function(gameState,queues) { for (var type in count) { if (count[type] === 0 || need[type] - || capacity[type] < gameState.getOwnEntities().filter(API3.Filters.and(API3.Filters.byMetadata(PlayerID, "subrole", "gatherer"), API3.Filters.byMetadata(PlayerID, "gather-type", type))).length * 1.05) + || capacity[type] < gameState.getOwnUnits().filter(API3.Filters.and(API3.Filters.byMetadata(PlayerID, "subrole", "gatherer"), API3.Filters.byMetadata(PlayerID, "gather-type", type))).length * 1.05) { // plan a new base. - if (gameState.countFoundationsWithType(gameState.applyCiv("structures/{civ}_civil_centre")) === 0 && queues.civilCentre.length() === 0) { + if (gameState.countFoundationsByType(gameState.applyCiv("structures/{civ}_civil_centre")) === 0 && queues.civilCentre.length() === 0) { if (this.outOf[type] && gameState.ai.playedTurn % 10 !== 0) continue; var pos = this.findBestEcoCCLocation(gameState, type); @@ -831,7 +811,7 @@ m.HQ.prototype.checkBasesRessLevel = function(gameState,queues) { // TODO: Fortresses are placed randomly atm. m.HQ.prototype.buildDefences = function(gameState, queues){ - var workersNumber = gameState.getOwnEntitiesByRole("worker").filter(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID,"plan"))).length; + var workersNumber = gameState.getOwnEntitiesByRole("worker", true).filter(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID,"plan"))).length; if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv('structures/{civ}_defense_tower')) + queues.defenceBuilding.length() < gameState.getEntityLimits()["DefenseTower"] && queues.defenceBuilding.length() < 4 && gameState.currentPhase() > 1) { @@ -904,7 +884,7 @@ m.HQ.prototype.buildBlacksmith = function(gameState, queues){ // TODO: building placement is bad. Choice of buildings is also fairly dumb. m.HQ.prototype.constructTrainingBuildings = function(gameState, queues) { Engine.ProfileStart("Build buildings"); - var workersNumber = gameState.getOwnEntitiesByRole("worker").filter(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID, "plan"))).length; + var workersNumber = gameState.getOwnEntitiesByRole("worker", true).filter(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID, "plan"))).length; if (workersNumber > this.Config.Military.popForBarracks1) { if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0])) + queues.militaryBuilding.length() < 1) { @@ -931,7 +911,7 @@ m.HQ.prototype.constructTrainingBuildings = function(gameState, queues) { if (queues.militaryBuilding.length() === 0){ var inConst = 0; for (var i in this.bAdvanced) - inConst += gameState.countFoundationsWithType(gameState.applyCiv(this.bAdvanced[i])); + inConst += gameState.countFoundationsByType(gameState.applyCiv(this.bAdvanced[i])); if (inConst == 0 && this.bAdvanced && this.bAdvanced.length !== 0) { var i = Math.floor(Math.random() * this.bAdvanced.length); if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bAdvanced[i])) < 1){ @@ -960,8 +940,8 @@ m.HQ.prototype.constructTrainingBuildings = function(gameState, queues) { // TODO: use pop(). Currently unused as this is too gameable. m.HQ.prototype.garrisonAllFemales = function(gameState) { - var buildings = gameState.getOwnEntities().filter(API3.Filters.byCanGarrison()).toEntityArray(); - var females = gameState.getOwnEntities().filter(API3.Filters.byClass("Support")); + var buildings = gameState.getOwnStructures().filter(API3.Filters.byCanGarrison()).toEntityArray(); + var females = gameState.getOwnUnits().filter(API3.Filters.byClass("Support")); var cache = {}; @@ -986,7 +966,7 @@ m.HQ.prototype.garrisonAllFemales = function(gameState) { }; m.HQ.prototype.ungarrisonAll = function(gameState) { this.hasGarrisonedFemales = false; - var buildings = gameState.getOwnEntities().filter(API3.Filters.and(API3.Filters.byClass("Structure"),API3.Filters.byCanGarrison())).toEntityArray(); + var buildings = gameState.getOwnStructures().filter(API3.Filters.and(API3.Filters.byClass("Structure"),API3.Filters.byCanGarrison())).toEntityArray(); buildings.forEach( function (struct) { if (struct.garrisoned() && struct.garrisoned().length) struct.unloadAll(); diff --git a/binaries/data/mods/public/simulation/ai/aegis/map-module.js b/binaries/data/mods/public/simulation/ai/aegis/map-module.js index a62bce4003..3f3da6a90a 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/map-module.js +++ b/binaries/data/mods/public/simulation/ai/aegis/map-module.js @@ -116,14 +116,14 @@ m.createObstructionMap = function(gameState, accessIndex, template){ var minDist = template.buildDistance().MinDistance; var category = template.buildDistance().FromCategory; if (minDist !== undefined && category !== undefined){ - gameState.getOwnEntities().forEach(function(ent) { - if (ent.buildCategory() === category && ent.position()){ - var pos = ent.position(); - var x = Math.round(pos[0] / gameState.cellSize); - var z = Math.round(pos[1] / gameState.cellSize); - map.addInfluence(x, z, minDist/gameState.cellSize, -255, 'constant'); - } - }); + gameState.getOwnStructures().forEach(function(ent) { + if (ent.buildCategory() === category && ent.position()){ + var pos = ent.position(); + var x = Math.round(pos[0] / gameState.cellSize); + var z = Math.round(pos[1] / gameState.cellSize); + map.addInfluence(x, z, minDist/gameState.cellSize, -255, 'constant'); + } + }); } } return map; diff --git a/binaries/data/mods/public/simulation/ai/aegis/naval-manager.js b/binaries/data/mods/public/simulation/ai/aegis/naval-manager.js index fd3a359a27..c63dc40350 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/naval-manager.js +++ b/binaries/data/mods/public/simulation/ai/aegis/naval-manager.js @@ -37,7 +37,7 @@ m.NavalManager = function() { // More initialisation for stuff that needs the gameState m.NavalManager.prototype.init = function(gameState, events, queues) { // finished docks - this.docks = gameState.getOwnEntities().filter(API3.Filters.and(API3.Filters.byClass("Dock"), API3.Filters.not(API3.Filters.isFoundation()))); + this.docks = gameState.getOwnStructures().filter(API3.Filters.and(API3.Filters.byClass("Dock"), API3.Filters.not(API3.Filters.isFoundation()))); this.docks.allowQuickIter(); this.docks.registerUpdates(); diff --git a/binaries/data/mods/public/simulation/ai/aegis/queue-manager.js b/binaries/data/mods/public/simulation/ai/aegis/queue-manager.js index eb95196aa2..cb212e8188 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/queue-manager.js +++ b/binaries/data/mods/public/simulation/ai/aegis/queue-manager.js @@ -482,7 +482,7 @@ m.QueueManager.prototype.update = function(gameState) { Engine.ProfileStop(); - if (gameState.ai.playedTurn % 10 === 5) + if (gameState.ai.playedTurn % 30 === 5) this.HTMLprintQueues(gameState); Engine.ProfileStop(); diff --git a/binaries/data/mods/public/simulation/ai/aegis/queueplan-building.js b/binaries/data/mods/public/simulation/ai/aegis/queueplan-building.js index 6d197dfb2a..7128c9d256 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/queueplan-building.js +++ b/binaries/data/mods/public/simulation/ai/aegis/queueplan-building.js @@ -118,56 +118,54 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState) { friendlyTiles.addInfluence(x, z, 255); } else { // No position was specified so try and find a sensible place to build - gameState.getOwnEntities().forEach(function(ent) { - if (ent.hasClass("Structure")) { - var infl = 32; - if (ent.hasClass("CivCentre")) - infl *= 4; - - var pos = ent.position(); - var x = Math.round(pos[0] / cellSize); - var z = Math.round(pos[1] / cellSize); - - if (ent.buildCategory() == "Wall") { // no real blockers, but can't build where they are - friendlyTiles.addInfluence(x, z, 2,-1000); - return; - } + gameState.getOwnStructures().forEach(function(ent) { + var infl = 32; + if (ent.hasClass("CivCentre")) + infl *= 4; - if (template._template.BuildRestrictions.Category === "Field"){ - if (ent.resourceDropsiteTypes() && ent.resourceDropsiteTypes().indexOf("food") !== -1){ - if (ent.hasClass("CivCentre")) - friendlyTiles.addInfluence(x, z, infl/4, infl); - else - friendlyTiles.addInfluence(x, z, infl, infl); - - } - }else{ - if (template.genericName() == "House" && ent.genericName() == "House") { - friendlyTiles.addInfluence(x, z, 15.0,20,'linear'); // houses are close to other houses - alreadyHasHouses = true; - } else if (template.hasClass("GarrisonFortress") && ent.genericName() == "House") - { - friendlyTiles.addInfluence(x, z, 30, -50); - } else if (template.genericName() == "House") { - friendlyTiles.addInfluence(x, z, Math.ceil(infl/4.0),-infl/2.0); // houses are farther away from other buildings but houses - } else if (template.hasClass("GarrisonFortress")) - { - friendlyTiles.addInfluence(x, z, 20, 10); - friendlyTiles.addInfluence(x, z, 10, -40, 'linear'); - } else if (ent.genericName() != "House") // houses have no influence on other buildings - { - friendlyTiles.addInfluence(x, z, infl); - //avoid building too close to each other if possible. - friendlyTiles.addInfluence(x, z, 5, -5, 'linear'); - } - // If this is not a field add a negative influence near the CivCentre because we want to leave this - // area for fields. - if (ent.hasClass("CivCentre") && template.genericName() != "House"){ - friendlyTiles.addInfluence(x, z, Math.floor(infl/8), Math.floor(-infl/2)); - } else if (ent.hasClass("CivCentre")) { - friendlyTiles.addInfluence(x, z, infl/3.0, infl + 1); - friendlyTiles.addInfluence(x, z, Math.ceil(infl/5.0), -(infl/2.0), 'linear'); - } + var pos = ent.position(); + var x = Math.round(pos[0] / cellSize); + var z = Math.round(pos[1] / cellSize); + + if (ent.buildCategory() == "Wall") { // no real blockers, but can't build where they are + friendlyTiles.addInfluence(x, z, 2,-1000); + return; + } + + if (template._template.BuildRestrictions.Category === "Field"){ + if (ent.resourceDropsiteTypes() && ent.resourceDropsiteTypes().indexOf("food") !== -1){ + if (ent.hasClass("CivCentre")) + friendlyTiles.addInfluence(x, z, infl/4, infl); + else + friendlyTiles.addInfluence(x, z, infl, infl); + + } + }else{ + if (template.genericName() == "House" && ent.genericName() == "House") { + friendlyTiles.addInfluence(x, z, 15.0,20,'linear'); // houses are close to other houses + alreadyHasHouses = true; + } else if (template.hasClass("GarrisonFortress") && ent.genericName() == "House") + { + friendlyTiles.addInfluence(x, z, 30, -50); + } else if (template.genericName() == "House") { + friendlyTiles.addInfluence(x, z, Math.ceil(infl/4.0),-infl/2.0); // houses are farther away from other buildings but houses + } else if (template.hasClass("GarrisonFortress")) + { + friendlyTiles.addInfluence(x, z, 20, 10); + friendlyTiles.addInfluence(x, z, 10, -40, 'linear'); + } else if (ent.genericName() != "House") // houses have no influence on other buildings + { + friendlyTiles.addInfluence(x, z, infl); + //avoid building too close to each other if possible. + friendlyTiles.addInfluence(x, z, 5, -5, 'linear'); + } + // If this is not a field add a negative influence near the CivCentre because we want to leave this + // area for fields. + if (ent.hasClass("CivCentre") && template.genericName() != "House"){ + friendlyTiles.addInfluence(x, z, Math.floor(infl/8), Math.floor(-infl/2)); + } else if (ent.hasClass("CivCentre")) { + friendlyTiles.addInfluence(x, z, infl/3.0, infl + 1); + friendlyTiles.addInfluence(x, z, Math.ceil(infl/5.0), -(infl/2.0), 'linear'); } } }); diff --git a/binaries/data/mods/public/simulation/ai/common-api/gamestate.js b/binaries/data/mods/public/simulation/ai/common-api/gamestate.js index 7b5c43bb9d..920ac4e666 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/gamestate.js +++ b/binaries/data/mods/public/simulation/ai/common-api/gamestate.js @@ -44,9 +44,9 @@ m.GameState.prototype.update = function(SharedScript, state) { }; m.GameState.prototype.updatingCollection = function(id, filter, collection, allowQuick){ - // automatically add the player ID + // automatically add the player ID in front. id = this.player + "-" + id; - if (!this.EntCollecNames[id]){ + if (!this.EntCollecNames[id]) { if (collection !== undefined) this.EntCollecNames[id] = collection.filter(filter); else { @@ -106,6 +106,30 @@ m.GameState.prototype.getGEC = function(id) return undefined; }; +m.GameState.prototype.getTimeElapsed = function() +{ + return this.timeElapsed; +}; + +m.GameState.prototype.getTemplate = function(type) +{ + if (this.techTemplates[type] !== undefined) + return new m.Technology(this.techTemplates, type); + + if (!this.templates[type]) + return null; + + return new m.EntityTemplate(this.templates[type], this.techModifications); +}; + +m.GameState.prototype.applyCiv = function(str) { + return str.replace(/\{civ\}/g, this.playerData.civ); +}; + +m.GameState.prototype.civ = function() { + return this.playerData.civ; +}; + m.GameState.prototype.currentPhase = function() { if (this.isResearched("phase_city")) @@ -133,13 +157,14 @@ m.GameState.prototype.isResearched = function(template) { return this.playerData.researchedTechs[template] !== undefined; }; + // true if started or queued m.GameState.prototype.isResearching = function(template) { return (this.playerData.researchStarted[template] !== undefined || this.playerData.researchQueued[template] !== undefined); }; -// this is an absolute check that doesn't check if we have a building to research from. +// this is an "in-absolute" check that doesn't check if we have a building to research from. m.GameState.prototype.canResearch = function(techTemplateName, noRequirementCheck) { var template = this.getTemplate(techTemplateName); @@ -232,42 +257,21 @@ m.GameState.prototype.checkTechRequirements = function (reqs) return false; }; - -m.GameState.prototype.getTimeElapsed = function() -{ - return this.timeElapsed; -}; - -m.GameState.prototype.getTemplate = function(type) -{ - if (this.techTemplates[type] !== undefined) - return new m.Technology(this.techTemplates, type); - - if (!this.templates[type]) - return null; - - return new m.EntityTemplate(this.templates[type], this.techModifications); -}; - -m.GameState.prototype.applyCiv = function(str) { - return str.replace(/\{civ\}/g, this.playerData.civ); -}; - -m.GameState.prototype.civ = function() { - return this.playerData.civ; -}; - -/** - * @returns {Resources} - */ -m.GameState.prototype.getResources = function() { - return new m.Resources(this.playerData.resourceCounts); -}; - m.GameState.prototype.getMap = function() { return this.sharedScript.passabilityMap; }; +m.GameState.prototype.getPassabilityClassMask = function(name) { + if (!(name in this.sharedScript.passabilityClasses)){ + error("Tried to use invalid passability class name '" + name + "'"); + } + return this.sharedScript.passabilityClasses[name]; +}; + +m.GameState.prototype.getResources = function() { + return new m.Resources(this.playerData.resourceCounts); +}; + m.GameState.prototype.getPopulation = function() { return this.playerData.popCount; }; @@ -280,13 +284,6 @@ m.GameState.prototype.getPopulationMax = function() { return this.playerData.popMax; }; -m.GameState.prototype.getPassabilityClassMask = function(name) { - if (!(name in this.sharedScript.passabilityClasses)){ - error("Tried to use invalid passability class name '" + name + "'"); - } - return this.sharedScript.passabilityClasses[name]; -}; - m.GameState.prototype.getPlayerID = function() { return this.player; }; @@ -336,80 +333,89 @@ m.GameState.prototype.isEntityOwn = function(ent) { return false; }; -m.GameState.prototype.getOwnEntities = function() { - return this.updatingCollection("own-entities", m.Filters.byOwner(this.player)); -}; +m.GameState.prototype.getEntityById = function(id){ + if (this.entities._entities[id]) + return this.entities._entities[id]; -m.GameState.prototype.getEnemyEntities = function() { - var diplomacyChange = false; - var enemies = this.getEnemies(); - if (this.enemies){ - if (this.enemies.length != enemies.length){ - diplomacyChange = true; - }else{ - for (var i = 0; i < enemies.length; i++){ - if (enemies[i] !== this.enemies[i]){ - diplomacyChange = true; - } - } - } - } - if (diplomacyChange || !this.enemies){ - return this.updatingCollection("enemy-entities", m.Filters.byOwners(enemies)); - this.enemies = enemies; - } - return this.getEC("enemy-entities"); + return undefined; }; m.GameState.prototype.getEntities = function() { return this.entities; }; -m.GameState.prototype.getEntityById = function(id){ - if (this.entities._entities[id]) { - return this.entities._entities[id]; - }else{ - //debug("Entity " + id + " requested does not exist"); - } - return undefined; +m.GameState.prototype.getOwnEntities = function() { + return this.updatingGlobalCollection("" + this.player + "-entities", m.Filters.byOwner(this.player)); }; +m.GameState.prototype.getOwnStructures = function() { + return this.updatingGlobalCollection("" + this.player + "-structures", m.Filters.byClass("Structure"), this.getOwnEntities()); +}; + +m.GameState.prototype.getOwnUnits = function() { + return this.updatingGlobalCollection("" + this.player + "-units", m.Filters.byClass("Unit"), this.getOwnEntities()); +}; + +// Try to use a parameter for those three, it'll be a lot faster. +m.GameState.prototype.getEnemyEntities = function(enemyID) { + if (enemyID === undefined) + return this.entities.filter(m.Filters.byOwners(this.getEnemies())); + + return this.updatingGlobalCollection("" + enemyID + "-entities", m.Filters.byOwner(enemyID)); +}; + +m.GameState.prototype.getEnemyStructures = function(enemyID) { + if (enemyID === undefined) + return this.getEnemyEntities().filter(m.Filters.byClass("Structure")); + + return this.updatingGlobalCollection("" + enemyID + "-structures", m.Filters.byClass("Structure"), this.getEnemyEntities(enemyID)); +}; + +m.GameState.prototype.getEnemyUnits = function(enemyID) { + if (enemyID === undefined) + return this.getEnemyEntities().filter(m.Filters.byClass("Unit")); + + return this.updatingGlobalCollection("" + enemyID + "-units", m.Filters.byClass("Unit"), this.getEnemyEntities(enemyID)); +}; + +// if maintain is true, this will be stored. Otherwise it's one-shot. m.GameState.prototype.getOwnEntitiesByMetadata = function(key, value, maintain){ if (maintain === true) return this.updatingCollection(key + "-" + value, m.Filters.byMetadata(this.player, key, value),this.getOwnEntities()); return this.getOwnEntities().filter(m.Filters.byMetadata(this.player, key, value)); }; -m.GameState.prototype.getOwnEntitiesByRole = function(role){ - return this.getOwnEntitiesByMetadata("role", role, true); -}; - -m.GameState.prototype.getOwnTrainingFacilities = function(){ - return this.updatingCollection("own-training-facilities", m.Filters.byTrainingQueue(), this.getOwnEntities(), true); -}; - -m.GameState.prototype.getOwnResearchFacilities = function(){ - return this.updatingCollection("own-research-facilities", m.Filters.byResearchAvailable(), this.getOwnEntities(), true); +m.GameState.prototype.getOwnEntitiesByRole = function(role, maintain){ + return this.getOwnEntitiesByMetadata("role", role, maintain); }; m.GameState.prototype.getOwnEntitiesByType = function(type, maintain){ var filter = m.Filters.byType(type); if (maintain === true) - return this.updatingCollection("own-by-type-" + type, filter, this.getOwnEntities()); + return this.updatingCollection("type-" + type, filter, this.getOwnEntities()); return this.getOwnEntities().filter(filter); }; +m.GameState.prototype.getOwnTrainingFacilities = function(){ + return this.updatingGlobalCollection("" + this.player + "-training-facilities", m.Filters.byTrainingQueue(), this.getOwnEntities(), true); +}; + +m.GameState.prototype.getOwnResearchFacilities = function(){ + return this.updatingGlobalCollection("" + this.player + "-research-facilities", m.Filters.byResearchAvailable(), this.getOwnEntities(), true); +}; + + m.GameState.prototype.countEntitiesByType = function(type, maintain) { return this.getOwnEntitiesByType(type, maintain).length; }; -m.GameState.prototype.countEntitiesAndQueuedByType = function(type) { - var count = this.countEntitiesByType(type, true); +m.GameState.prototype.countEntitiesAndQueuedByType = function(type, maintain) { + var count = this.countEntitiesByType(type, maintain); // Count building foundations if (this.getTemplate(type).hasClass("Structure") === true) - count += this.countEntitiesByType("foundation|" + type, true); + count += this.countFoundationsByType(type, true); else if (this.getTemplate(type).resourceSupplyType() !== undefined) // animal resources count += this.countEntitiesByType("resource|" + type, true); else @@ -428,10 +434,14 @@ m.GameState.prototype.countEntitiesAndQueuedByType = function(type) { return count; }; -m.GameState.prototype.countFoundationsWithType = function(type) { +m.GameState.prototype.countFoundationsByType = function(type, maintain) { var foundationType = "foundation|" + type; + + if (maintain === true) + return this.updatingCollection("foundation-type-" + type, m.Filters.byType(foundationType), this.getOwnFoundations()); + var count = 0; - this.getOwnEntities().forEach(function(ent) { + this.getOwnStructures().forEach(function(ent) { var t = ent.templateName(); if (t == foundationType) ++count; @@ -468,126 +478,43 @@ m.GameState.prototype.countOwnQueuedEntitiesWithMetadata = function(data, value) return count; }; -/** - * Find buildings that are capable of training the given unit type, and aren't - * already too busy. - */ -m.GameState.prototype.findTrainers = function(template) { - var maxQueueLength = 3; // avoid tying up resources in giant training queues - - return this.getOwnTrainingFacilities().filter(function(ent) { - - var trainable = ent.trainableEntities(); - if (!trainable || trainable.indexOf(template) == -1) - return false; - - var queue = ent.trainingQueue(); - if (queue) { - if (queue.length >= maxQueueLength) - return false; - } - - return true; - }); -}; - -/** - * Find units that are capable of constructing the given building type. - */ -m.GameState.prototype.findBuilders = function(template) { - return this.getOwnEntities().filter(function(ent) { - - var buildable = ent.buildableEntities(); - if (!buildable || buildable.indexOf(template) == -1) - return false; - - return true; - }); -}; - -/** - * Find buildings that are capable of researching the given tech, and aren't - * already too busy. - */ -m.GameState.prototype.findResearchers = function(templateName, noRequirementCheck) { - // let's check we can research the tech. - if (!this.canResearch(templateName, noRequirementCheck)) - return []; - - var template = this.getTemplate(templateName); - var self = this; - - return this.getOwnResearchFacilities().filter(function(ent) { //}){ - var techs = ent.researchableTechs(); - for (var i in techs) - { - var thisTemp = self.getTemplate(techs[i]); - if (thisTemp.pairDef()) - { - var pairedTechs = thisTemp.getPairedTechs(); - if (pairedTechs[0]._templateName == templateName || pairedTechs[1]._templateName == templateName) - return true; - } else { - if (techs[i] == templateName) - return true; - } - } - return false; - }); -}; - m.GameState.prototype.getOwnFoundations = function() { - return this.updatingCollection("ownFoundations", m.Filters.isFoundation(), this.getOwnEntities()); + return this.updatingGlobalCollection("" + this.player + "-foundations", m.Filters.isFoundation(), this.getOwnStructures()); }; m.GameState.prototype.getOwnDropsites = function(resource){ if (resource !== undefined) - return this.updatingCollection("dropsite-own-" + resource, m.Filters.isDropsite(resource), this.getOwnEntities(), true); - return this.updatingCollection("dropsite-own", m.Filters.isDropsite(), this.getOwnEntities(), true); + return this.updatingCollection("dropsite-" + resource, m.Filters.isDropsite(resource), this.getOwnEntities(), true); + return this.updatingCollection("dropsite-all", m.Filters.isDropsite(), this.getOwnEntities(), true); }; m.GameState.prototype.getResourceSupplies = function(resource){ return this.updatingGlobalCollection("resource-" + resource, m.Filters.byResource(resource), this.getEntities(), true); }; -m.GameState.prototype.getEntityLimits = function() { - return this.playerData.entityLimits; -}; - -m.GameState.prototype.getEntityCounts = function() { - return this.playerData.entityCounts; -}; - -// Checks whether the maximum number of buildings have been cnstructed for a certain catergory -m.GameState.prototype.isEntityLimitReached = function(category) { - if(this.playerData.entityLimits[category] === undefined || this.playerData.entityCounts[category] === undefined) - return false; - return (this.playerData.entityCounts[category] >= this.playerData.entityLimits[category]); -}; - +// This returns only units from buildings. m.GameState.prototype.findTrainableUnits = function(classes){ var allTrainable = []; - this.getOwnEntities().forEach(function(ent) { + this.getOwnStructures().forEach(function(ent) { var trainable = ent.trainableEntities(); - if (ent.hasClass("Structure")) - for (var i in trainable){ - if (allTrainable.indexOf(trainable[i]) === -1){ - allTrainable.push(trainable[i]); - } + for (var i in trainable){ + if (allTrainable.indexOf(trainable[i]) === -1){ + allTrainable.push(trainable[i]); } + } }); var ret = []; for (var i in allTrainable) { var template = this.getTemplate(allTrainable[i]); - var okay = true; + + if (template.hasClass("Hero")) // disabling heroes for now + continue; + var okay = true; for (var o in classes) if (!template.hasClass(classes[o])) okay = false; - - if (template.hasClass("Hero")) // disabling heroes for now - okay = false; - + if (okay) ret.push( [allTrainable[i], template] ); } @@ -628,6 +555,75 @@ m.GameState.prototype.findAvailableTech = function() { return ret; }; +/** + * Find buildings that are capable of training said template. + * Getting the best is up to the AI. + */ +m.GameState.prototype.findTrainers = function(template) { + return this.getOwnTrainingFacilities().filter(function(ent) { + var trainable = ent.trainableEntities(); + if (!trainable || trainable.indexOf(template) == -1) + return false; + return true; + }); +}; + +/** + * Find units that are capable of constructing the given building type. + */ +m.GameState.prototype.findBuilders = function(template) { + return this.getOwnUnits().filter(function(ent) { + var buildable = ent.buildableEntities(); + if (!buildable || buildable.indexOf(template) == -1) + return false; + + return true; + }); +}; + +// Find buildings that are capable of researching the given tech +m.GameState.prototype.findResearchers = function(templateName, noRequirementCheck) { + // let's check we can research the tech. + if (!this.canResearch(templateName, noRequirementCheck)) + return []; + + var template = this.getTemplate(templateName); + var self = this; + + return this.getOwnResearchFacilities().filter(function(ent) { + var techs = ent.researchableTechs(); + for (var i in techs) + { + var thisTemp = self.getTemplate(techs[i]); + if (thisTemp.pairDef()) + { + var pairedTechs = thisTemp.getPairedTechs(); + if (pairedTechs[0]._templateName == templateName || pairedTechs[1]._templateName == templateName) + return true; + } else { + if (techs[i] == templateName) + return true; + } + } + return false; + }); +}; + +m.GameState.prototype.getEntityLimits = function() { + return this.playerData.entityLimits; +}; + +m.GameState.prototype.getEntityCounts = function() { + return this.playerData.entityCounts; +}; + +// Checks whether the maximum number of buildings have been cnstructed for a certain catergory +m.GameState.prototype.isEntityLimitReached = function(category) { + if(this.playerData.entityLimits[category] === undefined || this.playerData.entityCounts[category] === undefined) + return false; + return (this.playerData.entityCounts[category] >= this.playerData.entityLimits[category]); +}; + // defcon utilities m.GameState.prototype.timeSinceDefconChange = function() { return this.getTimeElapsed()-this.ai.defconChangeTime;