diff --git a/binaries/data/mods/public/simulation/ai/aegis/aegis.js b/binaries/data/mods/public/simulation/ai/aegis/aegis.js index fdeaa0c530..d02b714cd2 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/aegis.js +++ b/binaries/data/mods/public/simulation/ai/aegis/aegis.js @@ -1,28 +1,33 @@ + +var AEGIS = (function() { +var m = {}; + // "local" global variables for stuffs that will need a unique ID // Note that since order of loading is alphabetic, this means this file must go before any other file using them. -var uniqueIDBOPlans = 0; // training/building/research plans -var uniqueIDBases = 1; // base manager ID. Starts at one because "0" means "no base" on the map -var uniqueIDTPlans = 1; // transport plans. starts at 1 because 0 might be used as none. +m.playerGlobals = []; +m.DebugEnabled = false; -function AegisBot(settings) { - BaseAI.call(this, settings); +m.AegisBot = function AegisBot(settings) { + API3.BaseAI.call(this, settings); - Config.updateDifficulty(settings.difficulty); + this.Config = new m.Config(); + + this.Config.updateDifficulty(settings.difficulty); this.turn = 0; this.playedTurn = 0; - - this.priorities = Config.priorities; + + this.priorities = this.Config.priorities; // this.queues can only be modified by the queue manager or things will go awry. this.queues = {}; for (i in this.priorities) - this.queues[i] = new Queue(); + this.queues[i] = new m.Queue(); - this.queueManager = new QueueManager(this.queues, this.priorities); + this.queueManager = new m.QueueManager(this.Config, this.queues, this.priorities); - this.HQ = new HQ(); + this.HQ = new m.HQ(this.Config); this.firstTime = true; @@ -30,34 +35,39 @@ function AegisBot(settings) { this.defcon = 5; this.defconChangeTime = -10000000; -} +}; -AegisBot.prototype = new BaseAI(); +m.AegisBot.prototype = new API3.BaseAI(); + +m.AegisBot.prototype.CustomInit = function(gameState, sharedScript) { + + m.playerGlobals[PlayerID] = {}; + m.playerGlobals[PlayerID].uniqueIDBOPlans = 0; // training/building/research plans + m.playerGlobals[PlayerID].uniqueIDBases = 1; // base manager ID. Starts at one because "0" means "no base" on the map + m.playerGlobals[PlayerID].uniqueIDTPlans = 1; // transport plans. starts at 1 because 0 might be used as none. -AegisBot.prototype.CustomInit = function(gameState, sharedScript) { - this.HQ.init(gameState,sharedScript.events,this.queues); - debug ("Initialized with the difficulty " + Config.difficulty); + m.debug ("Initialized with the difficulty " + this.Config.difficulty); - var ents = gameState.getEntities().filter(Filters.byOwner(PlayerID)); + var ents = gameState.getEntities().filter(API3.Filters.byOwner(this.player)); var myKeyEntities = ents.filter(function(ent) { return ent.hasClass("CivCentre"); }); if (myKeyEntities.length == 0){ - myKeyEntities = gameState.getEntities().filter(Filters.byOwner(PlayerID)); + myKeyEntities = gameState.getEntities().filter(API3.Filters.byOwner(this.player)); } - var filter = Filters.byClass("CivCentre"); - var enemyKeyEntities = gameState.getEntities().filter(Filters.not(Filters.byOwner(PlayerID))).filter(filter); + var filter = API3.Filters.byClass("CivCentre"); + var enemyKeyEntities = gameState.getEntities().filter(API3.Filters.not(API3.Filters.byOwner(this.player))).filter(filter); if (enemyKeyEntities.length == 0){ - enemyKeyEntities = gameState.getEntities().filter(Filters.not(Filters.byOwner(PlayerID))); + enemyKeyEntities = gameState.getEntities().filter(API3.Filters.not(API3.Filters.byOwner(this.player))); } this.myIndex = this.accessibility.getAccessValue(myKeyEntities.toEntityArray()[0].position()); - this.pathFinder = new aStarPath(gameState, false, true); + this.pathFinder = new API3.aStarPath(gameState, false, true); this.pathsToMe = []; this.pathInfo = { "angle" : 0, "needboat" : true, "mkeyPos" : myKeyEntities.toEntityArray()[0].position(), "ekeyPos" : enemyKeyEntities.toEntityArray()[0].position() }; @@ -65,7 +75,7 @@ AegisBot.prototype.CustomInit = function(gameState, sharedScript) { var pos = [this.pathInfo.mkeyPos[0] + 150*Math.cos(this.pathInfo.angle),this.pathInfo.mkeyPos[1] + 150*Math.sin(this.pathInfo.angle)]; var path = this.pathFinder.getPath(this.pathInfo.ekeyPos, pos, 2, 2);// uncomment for debug:*/, 300000, gameState); - //Engine.DumpImage("initialPath" + PlayerID + ".png", this.pathFinder.TotorMap.map, this.pathFinder.TotorMap.width,this.pathFinder.TotorMap.height,255); + //Engine.DumpImage("initialPath" + this.player + ".png", this.pathFinder.TotorMap.map, this.pathFinder.TotorMap.width,this.pathFinder.TotorMap.height,255); if (path !== undefined && path[1] !== undefined && path[1] == false) { // path is viable and doesn't require boating. @@ -80,7 +90,8 @@ AegisBot.prototype.CustomInit = function(gameState, sharedScript) { this.chooseRandomStrategy(); } -AegisBot.prototype.OnUpdate = function(sharedScript) { +m.AegisBot.prototype.OnUpdate = function(sharedScript) { + if (this.gameFinished){ return; } @@ -120,7 +131,7 @@ AegisBot.prototype.OnUpdate = function(sharedScript) { { if (this.pathInfo.needboat) { - debug ("Assuming this is a water map"); + m.debug ("Assuming this is a water map"); this.HQ.waterMap = true; } delete this.pathFinder; @@ -132,27 +143,27 @@ AegisBot.prototype.OnUpdate = function(sharedScript) { var cityPhase = this.gameState.cityPhase(); // try going up phases. // TODO: softcode this. - if (this.gameState.canResearch(townPhase,true) && this.gameState.getTimeElapsed() > (Config.Economy.townPhase*1000) && this.gameState.getPopulation() > 40 + 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(Filters.byClass("Village")).length > 5) + && this.gameState.getOwnEntities().filter(API3.Filters.byClass("Village")).length > 5) { this.queueManager.pauseQueue("villager", true); this.queueManager.pauseQueue("citizenSoldier", true); this.queueManager.pauseQueue("house", true); - this.queues.majorTech.addItem(new ResearchPlan(this.gameState, townPhase,0,-1,true)); // we rush the town phase. - debug ("Trying to reach town phase"); + this.queues.majorTech.addItem(new m.ResearchPlan(this.gameState, townPhase,0,-1,true)); // we rush the town phase. + m.debug ("Trying to reach town phase"); } - else if (this.gameState.canResearch(cityPhase,true) && this.gameState.getTimeElapsed() > (Config.Economy.cityPhase*1000) + else if (this.gameState.canResearch(cityPhase,true) && this.gameState.getTimeElapsed() > (this.Config.Economy.cityPhase*1000) && this.gameState.getOwnEntitiesByRole("worker").length > 85 && this.gameState.findResearchers(cityPhase, true).length != 0 && this.queues.majorTech.length() === 0) { - debug ("Trying to reach city phase"); - this.queues.majorTech.addItem(new ResearchPlan(this.gameState, cityPhase)); + m.debug ("Trying to reach city phase"); + this.queues.majorTech.addItem(new m.ResearchPlan(this.gameState, cityPhase)); } // defcon cooldown if (this.defcon < 5 && this.gameState.timeSinceDefconChange() > 20000) { this.defcon++; - debug ("updefconing to " +this.defcon); + m.debug ("updefconing to " +this.defcon); if (this.defcon >= 4 && this.HQ.hasGarrisonedFemales) this.HQ.ungarrisonAll(this.gameState); } @@ -210,24 +221,24 @@ AegisBot.prototype.OnUpdate = function(sharedScript) { this.turn++; }; -AegisBot.prototype.chooseRandomStrategy = function() +m.AegisBot.prototype.chooseRandomStrategy = function() { // deactivated for now. this.strategy = "normal"; // rarely and if we can assume it's not a water map. - if (!this.pathInfo.needboat && 0)//Math.random() < 0.2 && Config.difficulty == 2) + if (!this.pathInfo.needboat && 0)//Math.random() < 0.2 && this.Config.difficulty == 2) { this.strategy = "rush"; // going to rush. this.HQ.targetNumWorkers = 0; - Config.Economy.townPhase = 480; - Config.Economy.cityPhase = 900; - Config.Economy.farmsteadStartTime = 600; - Config.Economy.femaleRatio = 0; // raise it since we'll want to rush age 2. + this.Config.Economy.townPhase = 480; + this.Config.Economy.cityPhase = 900; + this.Config.Economy.farmsteadStartTime = 600; + this.Config.Economy.femaleRatio = 0; // raise it since we'll want to rush age 2. } }; -/*AegisBot.prototype.Deserialize = function(data, sharedScript) +/*m.AegisBot.prototype.Deserialize = function(data, sharedScript) { }; @@ -237,21 +248,24 @@ AegisBot.prototype.Serialize = function() return {}; };*/ -function debug(output){ - if (Config.debug){ +m.debug = function(output){ + if (m.DebugEnabled){ if (typeof output === "string"){ warn(output); }else{ warn(uneval(output)); } } -} +}; -function copyPrototype(descendant, parent) { +m.copyPrototype = function(descendant, parent) { var sConstructor = parent.toString(); var aMatch = sConstructor.match( /\s*function (.*)\(/ ); if ( aMatch != null ) { descendant.prototype[aMatch[1]] = parent; } for (var m in parent.prototype) { descendant.prototype[m] = parent.prototype[m]; } -} +}; + +return m; +}()); 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 f2a9c5716a..a0ef0194fe 100755 --- a/binaries/data/mods/public/simulation/ai/aegis/attack_plan.js +++ b/binaries/data/mods/public/simulation/ai/aegis/attack_plan.js @@ -1,3 +1,6 @@ +var AEGIS = function(m) +{ + /* This is an attack plan (despite the name, it's a relic of older times). * It deals with everything in an attack, from picking a target to picking a path to it * To making sure units rae built, and pushing elements to the queue manager otherwise @@ -6,8 +9,9 @@ * There is a basic support for naval expeditions here. */ -function CityAttack(gameState, HQ, uniqueID, targetEnemy, type , targetFinder) { +m.CityAttack = function CityAttack(gameState, HQ, Config, uniqueID, targetEnemy, type , targetFinder) { + this.Config = Config; //This is the list of IDs of the units in the plan this.idList=[]; @@ -33,14 +37,14 @@ function CityAttack(gameState, HQ, uniqueID, targetEnemy, type , targetFinder) { return false; } - var CCs = gameState.getOwnEntities().filter(Filters.byClass("CivCentre")); + var CCs = gameState.getOwnEntities().filter(API3.Filters.byClass("CivCentre")); if (CCs.length === 0) { this.failed = true; return false; } - debug ("Target (" + PlayerID +") = " +this.targetPlayer); + m.debug ("Target (" + PlayerID +") = " +this.targetPlayer); this.targetFinder = targetFinder || this.defaultTargetFinder; this.type = type || "normal"; this.name = uniqueID; @@ -50,7 +54,7 @@ function CityAttack(gameState, HQ, uniqueID, targetEnemy, type , targetFinder) { this.maxPreparationTime = 210*1000; // in this case we want to have the attack ready by the 13th minute. Countdown. Minimum 2 minutes. - if (type !== "superSized" && Config.difficulty >= 1) + if (type !== "superSized" && this.Config.difficulty >= 1) this.maxPreparationTime = 780000 - gameState.getTimeElapsed() < 120000 ? 120000 : 780000 - gameState.getTimeElapsed(); this.pausingStart = 0; @@ -120,7 +124,7 @@ function CityAttack(gameState, HQ, uniqueID, targetEnemy, type , targetFinder) { return (strength[0] > 15 || strength[1] > 15); };*/ - var filter = Filters.and(Filters.byMetadata(PlayerID, "plan",this.name),Filters.byOwner(PlayerID)); + var filter = API3.Filters.and(API3.Filters.byMetadata(PlayerID, "plan",this.name),API3.Filters.byOwner(PlayerID)); this.unitCollection = gameState.getOwnEntities().filter(filter); this.unitCollection.registerUpdates(); this.unitCollection.length; @@ -136,7 +140,7 @@ function CityAttack(gameState, HQ, uniqueID, targetEnemy, type , targetFinder) { var cat = unitCat; var Unit = this.unitStat[cat]; - filter = Filters.and(Filters.byClassesAnd(Unit["classes"]),Filters.and(Filters.byMetadata(PlayerID, "plan",this.name),Filters.byOwner(PlayerID))); + 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].registerUpdates(); this.unit[cat].length; @@ -186,7 +190,7 @@ function CityAttack(gameState, HQ, uniqueID, targetEnemy, type , targetFinder) { this.anyNotMinimal = true; // used for support plans - var myFortresses = gameState.getOwnTrainingFacilities().filter(Filters.byClass("GarrisonFortress")); + var myFortresses = gameState.getOwnTrainingFacilities().filter(API3.Filters.byClass("GarrisonFortress")); if (myFortresses.length !== 0) { // make this our rallypoint @@ -237,11 +241,11 @@ function CityAttack(gameState, HQ, uniqueID, targetEnemy, type , targetFinder) { this.assignUnits(gameState); - //debug ("Before"); + //m.debug ("Before"); //Engine.DumpHeap(); // get a good path to an estimated target. - this.pathFinder = new aStarPath(gameState,false,false, this.targetPlayer); + this.pathFinder = new API3.aStarPath(gameState,false,false, this.targetPlayer); //Engine.DumpImage("widthmap.png", this.pathFinder.widthMap, this.pathFinder.width,this.pathFinder.height,255); this.pathWidth = 6; // prefer a path far from entities. This will avoid units getting stuck in trees and also results in less straight paths. @@ -249,21 +253,21 @@ function CityAttack(gameState, HQ, uniqueID, targetEnemy, type , targetFinder) { this.onBoat = false; // tells us if our units are loaded on boats. this.needsShip = false; - //debug ("after"); + //m.debug ("after"); //Engine.DumpHeap(); return true; }; -CityAttack.prototype.getName = function(){ +m.CityAttack.prototype.getName = function(){ return this.name; }; -CityAttack.prototype.getType = function(){ +m.CityAttack.prototype.getType = function(){ return this.type; }; // Returns true if the attack can be executed at the current time // Basically his checks we have enough units. // We run a count of our units. -CityAttack.prototype.canStart = function(gameState){ +m.CityAttack.prototype.canStart = function(gameState){ for (var unitCat in this.unitStat) { var Unit = this.unitStat[unitCat]; if (this.unit[unitCat].length < Unit["minSize"]) @@ -273,27 +277,27 @@ CityAttack.prototype.canStart = function(gameState){ // TODO: check if our target is valid and a few other stuffs (good moment to attack?) }; -CityAttack.prototype.isStarted = function(){ +m.CityAttack.prototype.isStarted = function(){ if ((this.state !== "unexecuted")) - debug ("Attack plan already started"); + m.debug ("Attack plan already started"); return !(this.state == "unexecuted"); }; -CityAttack.prototype.isPaused = function(){ +m.CityAttack.prototype.isPaused = function(){ return this.paused; }; -CityAttack.prototype.setPaused = function(gameState, boolValue){ +m.CityAttack.prototype.setPaused = function(gameState, boolValue){ if (!this.paused && boolValue === true) { this.pausingStart = gameState.getTimeElapsed(); this.paused = true; - debug ("Pausing attack plan " +this.name); + m.debug ("Pausing attack plan " +this.name); } else if (this.paused && boolValue === false) { this.totalPausingTime += gameState.getTimeElapsed() - this.pausingStart; this.paused = false; - debug ("Unpausing attack plan " +this.name); + m.debug ("Unpausing attack plan " +this.name); } }; -CityAttack.prototype.mustStart = function(gameState){ +m.CityAttack.prototype.mustStart = function(gameState){ if (this.isPaused() || this.path === undefined) return false; var MaxReachedEverywhere = true; @@ -309,14 +313,14 @@ CityAttack.prototype.mustStart = function(gameState){ }; // Adds a build order. If resetQueue is true, this will reset the queue. -CityAttack.prototype.addBuildOrder = function(gameState, name, unitStats, resetQueue) { +m.CityAttack.prototype.addBuildOrder = function(gameState, name, unitStats, resetQueue) { if (!this.isStarted()) { - debug ("Adding a build order for " + name); + m.debug ("Adding a build order for " + name); // no minsize as we don't want the plan to fail at the last minute though. this.unitStat[name] = unitStats; var Unit = this.unitStat[name]; - var filter = Filters.and(Filters.byClassesAnd(Unit["classes"]),Filters.and(Filters.byMetadata(PlayerID, "plan",this.name),Filters.byOwner(PlayerID))); + 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].registerUpdates(); this.buildOrder.push([0, Unit["classes"], this.unit[name], Unit, name]); @@ -330,7 +334,7 @@ CityAttack.prototype.addBuildOrder = function(gameState, name, unitStats, resetQ // Three returns possible: 1 is "keep going", 0 is "failed plan", 2 is "start" // 3 is a special case: no valid path returned. Right now I stop attacking alltogether. -CityAttack.prototype.updatePreparation = function(gameState, HQ,events) { +m.CityAttack.prototype.updatePreparation = function(gameState, HQ,events) { var self = this; if (this.path == undefined || this.target == undefined || this.path === "toBeContinued") { @@ -342,14 +346,14 @@ CityAttack.prototype.updatePreparation = function(gameState, HQ,events) { targets = this.defaultTargetFinder(gameState, HQ); if (targets.length !== 0) { - debug ("Aiming for " + targets); + m.debug ("Aiming for " + targets); // picking a target var maxDist = -1; var index = 0; for (var i in targets._entities) { // we're sure it has a position has TargetFinder already checks that. - var dist = SquareVectorDistance(targets._entities[i].position(), this.rallyPoint); + var dist = API3.SquareVectorDistance(targets._entities[i].position(), this.rallyPoint); if (dist < maxDist || maxDist === -1) { maxDist = dist; @@ -385,11 +389,11 @@ CityAttack.prototype.updatePreparation = function(gameState, HQ,events) { // Basically we'll add it as a new class to train compulsorily, and we'll recompute our path. if (!gameState.ai.HQ.waterMap) { - debug ("This is actually a water map."); + m.debug ("This is actually a water map."); gameState.ai.HQ.waterMap = true; return 0; } - debug ("We need a ship."); + m.debug ("We need a ship."); this.needsShip = true; this.pathWidth = 3; this.pathSampling = 3; @@ -400,7 +404,7 @@ CityAttack.prototype.updatePreparation = function(gameState, HQ,events) { { // my pathfinder returns arrays in arrays in arrays. var waypointPos = this.path[i][0]; - var territory = Map.createTerritoryMap(gameState); + var territory = m.createTerritoryMap(gameState); if (territory.getOwner(waypointPos) !== PlayerID || this.path[i][1] === true) { // if we're suddenly out of our territory or this is the point where we change transportation method. @@ -426,7 +430,7 @@ CityAttack.prototype.updatePreparation = function(gameState, HQ,events) { { // my pathfinder returns arrays in arrays in arrays. var waypointPos = this.path[i][0]; - var territory = Map.createTerritoryMap(gameState); + var territory = m.createTerritoryMap(gameState); if (territory.getOwner(waypointPos) !== PlayerID || this.path[i][1] === true) { // if we're suddenly out of our territory or this is the point where we change transportation method. @@ -511,7 +515,7 @@ CityAttack.prototype.updatePreparation = function(gameState, HQ,events) { if (this.buildOrder[0][0] < 1 && queue.length() <= 5) { var template = HQ.findBestTrainableSoldier(gameState, this.buildOrder[0][1], this.buildOrder[0][3]["interests"] ); - //debug ("tried " + uneval(this.buildOrder[0][1]) +", and " + template); + //m.debug ("tried " + uneval(this.buildOrder[0][1]) +", and " + template); // HACK (TODO replace) : if we have no trainable template... Then we'll simply remove the buildOrder, effectively removing the unit from the plan. if (template === undefined) { // TODO: this is a complete hack. @@ -523,9 +527,9 @@ CityAttack.prototype.updatePreparation = function(gameState, HQ,events) { if (gameState.getTimeElapsed() > 1800000) max *= 2; if (gameState.getTemplate(template).hasClass("CitizenSoldier")) - queue.addItem( new TrainingPlan(gameState,template, { "role" : "worker", "plan" : this.name, "special" : specialData, "base" : 1 }, this.buildOrder[0][3]["batchSize"],max ) ); + queue.addItem( new m.TrainingPlan(gameState,template, { "role" : "worker", "plan" : this.name, "special" : specialData, "base" : 1 }, this.buildOrder[0][3]["batchSize"],max ) ); else - queue.addItem( new TrainingPlan(gameState,template, { "role" : "attack", "plan" : this.name, "special" : specialData, "base" : 1 }, this.buildOrder[0][3]["batchSize"],max ) ); + queue.addItem( new m.TrainingPlan(gameState,template, { "role" : "attack", "plan" : this.name, "special" : specialData, "base" : 1 }, this.buildOrder[0][3]["batchSize"],max ) ); } } } @@ -546,7 +550,7 @@ CityAttack.prototype.updatePreparation = function(gameState, HQ,events) { this.targetPos = target.position(); count++; if (count > 1000){ - debug("No target with a valid position found"); + m.debug("No target with a valid position found"); return false; } } @@ -559,7 +563,7 @@ CityAttack.prototype.updatePreparation = function(gameState, HQ,events) { if (path !== "toBeContinued") { this.startedPathing = false; this.path = path; - debug("Pathing ended"); + m.debug("Pathing ended"); } } */ @@ -567,7 +571,7 @@ CityAttack.prototype.updatePreparation = function(gameState, HQ,events) { Engine.ProfileStop(); // can happen for now if (this.buildOrder.length === 0) { - debug ("Ending plan: no build orders"); + m.debug ("Ending plan: no build orders"); return 0; // will abort the plan, should return something else } return 1; @@ -583,7 +587,7 @@ CityAttack.prototype.updatePreparation = function(gameState, HQ,events) { return 0; return 0; }; -CityAttack.prototype.assignUnits = function(gameState){ +m.CityAttack.prototype.assignUnits = function(gameState){ var self = this; // TODO: assign myself units that fit only, right now I'm getting anything. @@ -604,14 +608,14 @@ CityAttack.prototype.assignUnits = function(gameState){ }; // this sends a unit by ID back to the "rally point" -CityAttack.prototype.ToRallyPoint = function(gameState,id) +m.CityAttack.prototype.ToRallyPoint = function(gameState,id) { // Move back to nearest rallypoint gameState.getEntityById(id).move(this.rallyPoint[0],this.rallyPoint[1]); } // this sends all units back to the "rally point" by entity collections. // It doesn't disturb ones that could be currently defending, even if the plan is not (yet) paused. -CityAttack.prototype.AllToRallyPoint = function(gameState, evenWorkers) { +m.CityAttack.prototype.AllToRallyPoint = function(gameState, evenWorkers) { var self = this; if (evenWorkers) { for (var unitCat in this.unit) { @@ -634,7 +638,7 @@ CityAttack.prototype.AllToRallyPoint = function(gameState, evenWorkers) { } // Default target finder aims for conquest critical targets -CityAttack.prototype.defaultTargetFinder = function(gameState, HQ){ +m.CityAttack.prototype.defaultTargetFinder = function(gameState, HQ){ var targets = undefined; targets = HQ.enemyWatchers[this.targetPlayer].getEnemyBuildings(gameState, "CivCentre",true); @@ -650,13 +654,13 @@ CityAttack.prototype.defaultTargetFinder = function(gameState, HQ){ } // no buildings, attack anything conquest critical, even units (it's assuming it won't move). if (targets.length == 0) { - targets = gameState.getEnemyEntities().filter(Filters.and( Filters.byOwner(this.targetPlayer),Filters.byClass("ConquestCritical"))); + targets = gameState.getEnemyEntities().filter(API3.Filters.and( API3.Filters.byOwner(this.targetPlayer),API3.Filters.byClass("ConquestCritical"))); } return targets; }; // tupdate -CityAttack.prototype.raidingTargetFinder = function(gameState, HQ, Target){ +m.CityAttack.prototype.raidingTargetFinder = function(gameState, HQ, Target){ var targets = undefined; if (Target == "villager") { @@ -684,7 +688,7 @@ CityAttack.prototype.raidingTargetFinder = function(gameState, HQ, Target){ // Executes the attack plan, after this is executed the update function will be run every turn // If we're here, it's because we have in our IDlist enough units. // now the IDlist units are treated turn by turn -CityAttack.prototype.StartAttack = function(gameState, HQ){ +m.CityAttack.prototype.StartAttack = function(gameState, HQ){ // check we have a target and a path. if (this.targetPos && this.path !== undefined) { @@ -701,19 +705,19 @@ CityAttack.prototype.StartAttack = function(gameState, HQ){ this.unitCollection.move(this.path[0][0][0], this.path[0][0][1]); this.unitCollection.setStance("aggressive"); - this.unitCollection.filter(Filters.byClass("Siege")).setStance("defensive"); + this.unitCollection.filter(API3.Filters.byClass("Siege")).setStance("defensive"); this.state = "walking"; } else { gameState.ai.gameFinished = true; - debug ("I do not have any target. So I'll just assume I won the game."); + m.debug ("I do not have any target. So I'll just assume I won the game."); return false; } return true; }; // Runs every turn after the attack is executed -CityAttack.prototype.update = function(gameState, HQ, events){ +m.CityAttack.prototype.update = function(gameState, HQ, events){ var self = this; Engine.ProfileStart("Update Attack"); @@ -750,7 +754,7 @@ CityAttack.prototype.update = function(gameState, HQ, events){ if (attacker && attacker.position() && attacker.hasClass("Unit") && attacker.owner() != 0 && attacker.owner() != PlayerID) { - var territoryMap = Map.createTerritoryMap(gameState); + var territoryMap = m.createTerritoryMap(gameState); if ( +territoryMap.point(attacker.position()) - 64 === +this.targetPlayer) { attackedNB++; @@ -769,7 +773,7 @@ CityAttack.prototype.update = function(gameState, HQ, events){ } } if (attackedNB > 4) { - debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination."); + m.debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination."); // we must assume we've arrived at the end of the trail. this.state = "arrived"; } @@ -799,7 +803,7 @@ CityAttack.prototype.update = function(gameState, HQ, events){ var enemy = enemySoldiers[j]; if (enemy.position() === undefined) // likely garrisoned continue; - if (inRange(enemy.position(), attacker.position(), 1000) && this.threatList.indexOf(enemy.id()) === -1) + if (m.inRange(enemy.position(), attacker.position(), 1000) && this.threatList.indexOf(enemy.id()) === -1) this.threatList.push(enemy.id()); } this.threatList.push(e.msg.attacker); @@ -844,16 +848,16 @@ CityAttack.prototype.update = function(gameState, HQ, events){ } // basically haven't moved an inch: very likely stuck) - if (SquareVectorDistance(this.position, this.position5TurnsAgo) < 10 && this.path.length > 0 && gameState.ai.playedTurn % 5 === 0) { + if (API3.SquareVectorDistance(this.position, this.position5TurnsAgo) < 10 && this.path.length > 0 && gameState.ai.playedTurn % 5 === 0) { // check for stuck siege units - var sieges = this.unitCollection.filter(Filters.byClass("Siege")); + var sieges = this.unitCollection.filter(API3.Filters.byClass("Siege")); var farthest = 0; var farthestEnt = -1; sieges.forEach (function (ent) { - if (SquareVectorDistance(ent.position(),self.position) > farthest) + if (API3.SquareVectorDistance(ent.position(),self.position) > farthest) { - farthest = SquareVectorDistance(ent.position(),self.position); + farthest = API3.SquareVectorDistance(ent.position(),self.position); farthestEnt = ent; } }); @@ -863,42 +867,42 @@ CityAttack.prototype.update = function(gameState, HQ, events){ if (gameState.ai.playedTurn % 5 === 0) this.position5TurnsAgo = this.position; - if (this.lastPosition && SquareVectorDistance(this.position, this.lastPosition) < 20 && this.path.length > 0) { + if (this.lastPosition && API3.SquareVectorDistance(this.position, this.lastPosition) < 20 && this.path.length > 0) { this.unitCollection.moveIndiv(this.path[0][0][0], this.path[0][0][1]); // We're stuck, presumably. Check if there are no walls just close to us. If so, we're arrived, and we're gonna tear down some serious stone. - var walls = gameState.getEnemyEntities().filter(Filters.and(Filters.byOwner(this.targetPlayer), Filters.byClass("StoneWall"))); + var walls = gameState.getEnemyEntities().filter(API3.Filters.and(API3.Filters.byOwner(this.targetPlayer), API3.Filters.byClass("StoneWall"))); var nexttoWalls = false; walls.forEach( function (ent) { - if (!nexttoWalls && SquareVectorDistance(self.position, ent.position()) < 800) + if (!nexttoWalls && API3.SquareVectorDistance(self.position, ent.position()) < 800) nexttoWalls = true; }); // there are walls but we can attack - if (nexttoWalls && this.unitCollection.filter(Filters.byCanAttack("StoneWall")).length !== 0) + if (nexttoWalls && this.unitCollection.filter(API3.Filters.byCanAttack("StoneWall")).length !== 0) { - debug ("Attack Plan " +this.type +" " +this.name +" has met walls and is not happy."); + m.debug ("Attack Plan " +this.type +" " +this.name +" has met walls and is not happy."); this.state = "arrived"; } else if (nexttoWalls) { // abort plan. - debug ("Attack Plan " +this.type +" " +this.name +" has met walls and gives up."); + m.debug ("Attack Plan " +this.type +" " +this.name +" has met walls and gives up."); Engine.ProfileStop(); return 0; } } // check if our land units are close enough from the next waypoint. - if (SquareVectorDistance(this.unitCollection.getCentrePosition(), this.targetPos) < 7500 || - SquareVectorDistance(this.unitCollection.getCentrePosition(), this.path[0][0]) < 650) { - if (this.unitCollection.filter(Filters.byClass("Siege")).length !== 0 - && SquareVectorDistance(this.unitCollection.getCentrePosition(), this.targetPos) >= 7500 - && SquareVectorDistance(this.unitCollection.filter(Filters.byClass("Siege")).getCentrePosition(), this.path[0][0]) >= 650) + if (API3.SquareVectorDistance(this.unitCollection.getCentrePosition(), this.targetPos) < 7500 || + API3.SquareVectorDistance(this.unitCollection.getCentrePosition(), this.path[0][0]) < 650) { + if (this.unitCollection.filter(API3.Filters.byClass("Siege")).length !== 0 + && API3.SquareVectorDistance(this.unitCollection.getCentrePosition(), this.targetPos) >= 7500 + && API3.SquareVectorDistance(this.unitCollection.filter(API3.Filters.byClass("Siege")).getCentrePosition(), this.path[0][0]) >= 650) { } else { for (var i = 0; i < this.path.length; ++i) { - debug ("path waypoint " + i + "," + this.path[i][1] + " at " + uneval(this.path[i][0])); + m.debug ("path waypoint " + i + "," + this.path[i][1] + " at " + uneval(this.path[i][0])); } - debug ("position is " + this.unitCollection.getCentrePosition()); + m.debug ("position is " + this.unitCollection.getCentrePosition()); // okay so here basically two cases. The first one is "we need a boat at this point". // the second one is "we need to unload at this point". The third is "normal". @@ -908,7 +912,7 @@ CityAttack.prototype.update = function(gameState, HQ, events){ if (this.path.length > 0){ this.unitCollection.move(this.path[0][0][0], this.path[0][0][1]); } else { - debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination."); + m.debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination."); // we must assume we've arrived at the end of the trail. this.state = "arrived"; } @@ -917,15 +921,15 @@ CityAttack.prototype.update = function(gameState, HQ, events){ // TODO: make this require an escort later on. this.path.shift(); if (this.path.length === 0) { - debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination."); + m.debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination."); // we must assume we've arrived at the end of the trail. this.state = "arrived"; } else { /* - var plan = new TransportPlan(gameState, this.unitCollection.toIdArray(), this.path[0][0], false); + var plan = new m.TransportPlan(gameState, this.unitCollection.toIdArray(), this.path[0][0], false); this.tpPlanID = plan.ID; HQ.navalManager.transportPlans.push(plan); - debug ("Transporting over sea"); + m.debug ("Transporting over sea"); this.state = "transporting"; */ // TODO: fix this above @@ -962,7 +966,7 @@ CityAttack.prototype.update = function(gameState, HQ, events){ }); var targetList = []; enemyCitizens.forEach( function (enemy) { - if (inRange(enemy.position(), units.getCentrePosition(), 2500) && targetList.indexOf(enemy.id()) === -1) + if (m.inRange(enemy.position(), units.getCentrePosition(), 2500) && targetList.indexOf(enemy.id()) === -1) targetList.push(enemy.id()); }); if (targetList.length > 0) @@ -989,7 +993,7 @@ CityAttack.prototype.update = function(gameState, HQ, events){ if (attacker && attacker.position() && attacker.hasClass("Unit") && attacker.owner() != 0 && attacker.owner() != PlayerID) { if (ourUnit.hasClass("Siege")) { - var collec = this.unitCollection.filterNearest(ourUnit.position(), 8).filter(Filters.not(Filters.byClass("Siege"))).toEntityArray(); + var collec = this.unitCollection.filterNearest(ourUnit.position(), 8).filter(API3.Filters.not(API3.Filters.byClass("Siege"))).toEntityArray(); if (collec.length !== 0) { collec[0].attack(attacker.id()); @@ -1018,7 +1022,7 @@ CityAttack.prototype.update = function(gameState, HQ, events){ this.unitCollUpdateArray = this.unitCollection.toIdArray(); } else { // some stuffs for locality and speed - var territoryMap = Map.createTerritoryMap(gameState); + var territoryMap = m.createTerritoryMap(gameState); var timeElapsed = gameState.getTimeElapsed(); // Let's check a few units each time we update. Currently 10 @@ -1057,7 +1061,7 @@ CityAttack.prototype.update = function(gameState, HQ, events){ if (!enemy.position() || (enemy.hasClass("StoneWall") && ent.canAttackClass("StoneWall"))) { return false; } - if (SquareVectorDistance(enemy.position(),ent.position()) > 3000) { + if (API3.SquareVectorDistance(enemy.position(),ent.position()) > 3000) { return false; } return true; @@ -1070,7 +1074,7 @@ CityAttack.prototype.update = function(gameState, HQ, events){ } if (!enemy.hasClass("Support")) return false; - if (SquareVectorDistance(enemy.position(),ent.position()) > 10000) { + if (API3.SquareVectorDistance(enemy.position(),ent.position()) > 10000) { return false; } return true; @@ -1081,7 +1085,7 @@ CityAttack.prototype.update = function(gameState, HQ, events){ if (!enemy.position()) { return false; } - if (SquareVectorDistance(enemy.position(),ent.position()) > 10000) { + if (API3.SquareVectorDistance(enemy.position(),ent.position()) > 10000) { return false; } return true; @@ -1118,19 +1122,19 @@ CityAttack.prototype.update = function(gameState, HQ, events){ { var rand = Math.floor(Math.random() * mStruct.length*0.1); ent.attack(mStruct[+rand].id()); - //debug ("Siege units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName()); + //m.debug ("Siege units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName()); } - } else if (SquareVectorDistance(self.targetPos, ent.position()) > 900 ){ - //debug ("Siege units moving to " + uneval(self.targetPos)); + } else if (API3.SquareVectorDistance(self.targetPos, ent.position()) > 900 ){ + //m.debug ("Siege units moving to " + uneval(self.targetPos)); ent.move(self.targetPos[0],self.targetPos[1]); } } else { if (mUnit.length !== 0) { var rand = Math.floor(Math.random() * mUnit.length*0.99); ent.attack(mUnit[(+rand)].id()); - //debug ("Units attacking a unit from " +mUnit[+rand].owner() + " , " +mUnit[+rand].templateName()); - } else if (SquareVectorDistance(self.targetPos, ent.position()) > 900 ){ - //debug ("Units moving to " + uneval(self.targetPos)); + //m.debug ("Units attacking a unit from " +mUnit[+rand].owner() + " , " +mUnit[+rand].templateName()); + } else if (API3.SquareVectorDistance(self.targetPos, ent.position()) > 900 ){ + //m.debug ("Units moving to " + uneval(self.targetPos)); ent.move(self.targetPos[0],self.targetPos[1]); } else if (mStruct.length !== 0) { mStruct.sort(function (structa,structb) { //}){ @@ -1156,7 +1160,7 @@ CityAttack.prototype.update = function(gameState, HQ, events){ { var rand = Math.floor(Math.random() * mStruct.length*0.1); ent.attack(mStruct[+rand].id()); - //debug ("Units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName()); + //m.debug ("Units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName()); } } } @@ -1172,8 +1176,8 @@ CityAttack.prototype.update = function(gameState, HQ, events){ targets = this.defaultTargetFinder(gameState, HQ); } if (targets.length) { - debug ("Seems like our target has been destroyed. Switching."); - debug ("Aiming for " + targets); + m.debug ("Seems like our target has been destroyed. Switching."); + m.debug ("Aiming for " + targets); // picking a target this.targetPos = undefined; var count = 0; @@ -1183,7 +1187,7 @@ CityAttack.prototype.update = function(gameState, HQ, events){ this.targetPos = this.target.position(); count++; if (count > 1000){ - debug("No target with a valid position found"); + m.debug("No target with a valid position found"); Engine.ProfileStop(); return false; } @@ -1215,7 +1219,7 @@ CityAttack.prototype.update = function(gameState, HQ, events){ return this.unitCollection.length; }; -CityAttack.prototype.totalCountUnits = function(gameState){ +m.CityAttack.prototype.totalCountUnits = function(gameState){ var totalcount = 0; for (var i in this.idList) { @@ -1224,7 +1228,7 @@ CityAttack.prototype.totalCountUnits = function(gameState){ return totalcount; }; // reset any units -CityAttack.prototype.Abort = function(gameState){ +m.CityAttack.prototype.Abort = function(gameState){ this.unitCollection.forEach(function(ent) { ent.setMetadata(PlayerID, "role",undefined); ent.setMetadata(PlayerID, "subrole",undefined); @@ -1238,3 +1242,6 @@ CityAttack.prototype.Abort = function(gameState){ gameState.ai.queueManager.removeQueue("plan_" + this.name); gameState.ai.queueManager.removeQueue("plan_" + this.name + "_champ"); }; + +return m; +}(AEGIS); 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 097716890d..46d7e394b5 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/base-manager.js +++ b/binaries/data/mods/public/simulation/ai/aegis/base-manager.js @@ -1,3 +1,5 @@ +var AEGIS = function(m) +{ /* Base Manager * Handles lower level economic stuffs. * Some tasks: @@ -10,9 +12,10 @@ -updating whatever needs updating, keeping track of stuffs (rebuilding needs…) */ -var BaseManager = function() { +m.BaseManager = function(Config) { + this.Config = Config; this.farmingFields = false; - this.ID = uniqueIDBases++; + this.ID = m.playerGlobals[PlayerID].uniqueIDBases++; // anchor building: seen as the main building of the base. Needs to have territorial influence this.anchor = undefined; @@ -31,13 +34,13 @@ var BaseManager = function() { this.territoryIndices = []; }; -BaseManager.prototype.init = function(gameState, events, unconstructed){ +m.BaseManager.prototype.init = function(gameState, events, unconstructed){ this.constructing = unconstructed; // entitycollections - this.units = gameState.getOwnEntities().filter(Filters.and(Filters.byClass("Unit"),Filters.byMetadata(PlayerID, "base", this.ID))); - this.buildings = gameState.getOwnEntities().filter(Filters.and(Filters.byClass("Structure"),Filters.byMetadata(PlayerID, "base", this.ID))); + 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.workers = this.units.filter(Filters.byMetadata(PlayerID,"role","worker")); + this.workers = this.units.filter(API3.Filters.byMetadata(PlayerID,"role","worker")); this.workers.allowQuickIter(); this.buildings.allowQuickIter(); @@ -62,7 +65,7 @@ BaseManager.prototype.init = function(gameState, events, unconstructed){ this.bigRadius = { 'food':70*70,'wood':200*200,'stone':200*200,'metal':200*200 }; }; -BaseManager.prototype.assignEntity = function(unit){ +m.BaseManager.prototype.assignEntity = function(unit){ unit.setMetadata(PlayerID, "base", this.ID); this.units.updateEnt(unit); this.workers.updateEnt(unit); @@ -73,7 +76,7 @@ BaseManager.prototype.assignEntity = function(unit){ this.territoryBuildings.push(unit.id()); }; -BaseManager.prototype.setAnchor = function(anchorEntity) { +m.BaseManager.prototype.setAnchor = function(anchorEntity) { if (!anchorEntity.hasClass("Structure") || !anchorEntity.hasTerritoryInfluence()) { warn("Error: Aegis' base " + this.ID + " has been assigned an anchor building that has no territorial influence. Please report this on the forum.") @@ -90,7 +93,7 @@ BaseManager.prototype.setAnchor = function(anchorEntity) { } // affects the HQ map. -BaseManager.prototype.initTerritory = function(HQ, gameState) { +m.BaseManager.prototype.initTerritory = function(HQ, gameState) { if (!this.anchor) warn ("Error: Aegis tried to initialize the territory of base " + this.ID + " without assigning it an anchor building first"); var radius = Math.round((this.anchor.territoryInfluenceRadius() / 4.0) * 1.25); @@ -122,7 +125,7 @@ BaseManager.prototype.initTerritory = function(HQ, gameState) { } } -BaseManager.prototype.initGatheringFunctions = function(HQ, gameState, specTypes) { +m.BaseManager.prototype.initGatheringFunctions = function(HQ, gameState, specTypes) { // init our gathering functions. var types = ["food","wood","stone","metal"]; if (specTypes !== undefined) @@ -136,7 +139,7 @@ BaseManager.prototype.initGatheringFunctions = function(HQ, gameState, specTypes var type = types[i]; // TODO: set us as "X" gatherer - this.buildings.filter(Filters.isDropsite(type)).forEach(function(ent) { self.initializeDropsite(gameState, ent,type) }); + this.buildings.filter(API3.Filters.isDropsite(type)).forEach(function(ent) { self.initializeDropsite(gameState, ent,type) }); if (this.getResourceLevel(gameState, type, "all") > 1000) this.willGather[type] = 1; @@ -151,13 +154,13 @@ BaseManager.prototype.initGatheringFunctions = function(HQ, gameState, specTypes if (needFarm) this.willGather["food"] = 1; } - debug ("food" + this.willGather["food"]); - debug (this.willGather["wood"]); - debug (this.willGather["stone"]); - debug (this.willGather["metal"]); + m.debug ("food" + this.willGather["food"]); + m.debug (this.willGather["wood"]); + m.debug (this.willGather["stone"]); + m.debug (this.willGather["metal"]); } -BaseManager.prototype.checkEvents = function (gameState, events, queues) { +m.BaseManager.prototype.checkEvents = function (gameState, events, queues) { for (i in events) { if (events[i].type == "Destroy") @@ -185,10 +188,10 @@ BaseManager.prototype.checkEvents = function (gameState, events, queues) { if (ent.hasClass("CivCentre")) { // TODO: might want to tell the queue manager to pause other stuffs if we are the only base. - queues.civilCentre.addItem(new ConstructionPlan(gameState, "structures/{civ}_civil_centre", { "base" : this.ID, "baseAnchor" : true }, 0 , -1,ent.position())); + queues.civilCentre.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_civil_centre", { "base" : this.ID, "baseAnchor" : true }, 0 , -1,ent.position())); } else { // TODO - queues.civilCentre.addItem(new ConstructionPlan(gameState, "structures/{civ}_civil_centre", { "base" : this.ID, "baseAnchor" : true },0,-1,ent.position())); + queues.civilCentre.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_civil_centre", { "base" : this.ID, "baseAnchor" : true },0,-1,ent.position())); } } } @@ -238,7 +241,7 @@ BaseManager.prototype.checkEvents = function (gameState, events, queues) { }; // If no specific dropsite, it'll assign to the closest -BaseManager.prototype.assignResourceToDP = function (gameState, supply, specificDP) { +m.BaseManager.prototype.assignResourceToDP = function (gameState, supply, specificDP) { var type = supply.resourceSupplyType()["generic"]; if (type == "treasure") type = supply.resourceSupplyType()["specific"]; @@ -249,7 +252,7 @@ BaseManager.prototype.assignResourceToDP = function (gameState, supply, specific for (i in this.dropsites) { var dp = gameState.getEntityById(i); - var distance = SquareVectorDistance(supply.position(), dp.position()); + var distance = API3.SquareVectorDistance(supply.position(), dp.position()); if (distance < dist && distance < this.bigRadius[type]) { closest = dp.id(); @@ -267,7 +270,7 @@ BaseManager.prototype.assignResourceToDP = function (gameState, supply, specific // TODO: ought to recount immediatly. } -BaseManager.prototype.initializeDropsite = function (gameState, ent, type) { +m.BaseManager.prototype.initializeDropsite = function (gameState, ent, type) { var count = 0, farCount = 0; var self = this; @@ -278,7 +281,7 @@ BaseManager.prototype.initializeDropsite = function (gameState, ent, type) { resources.filter( function (supply) { //}){ if (!supply.position() || !ent.position()) return; - var distance = SquareVectorDistance(supply.position(), ent.position()); + var distance = API3.SquareVectorDistance(supply.position(), ent.position()); if (supply.getMetadata(PlayerID, "linked-dropsite") == undefined || supply.getMetadata(PlayerID, "linked-dropsite-dist") > distance) { if (distance < self.bigRadius[type]) { @@ -294,19 +297,19 @@ BaseManager.prototype.initializeDropsite = function (gameState, ent, type) { } }); // This one is both for the nearby and the linked - var filter = Filters.byMetadata(PlayerID, "linked-dropsite", ent.id()); + var filter = API3.Filters.byMetadata(PlayerID, "linked-dropsite", ent.id()); var collection = resources.filter(filter); collection.registerUpdates(); - filter = Filters.byMetadata(PlayerID, "linked-dropsite-close",true); + filter = API3.Filters.byMetadata(PlayerID, "linked-dropsite-close",true); var collection2 = collection.filter(filter); collection2.registerUpdates(); - filter = Filters.byMetadata(PlayerID, "linked-dropsite-nearby",true); + filter = API3.Filters.byMetadata(PlayerID, "linked-dropsite-nearby",true); var collection3 = collection.filter(filter); collection3.registerUpdates(); - filter = Filters.byMetadata(PlayerID, "linked-to-dropsite", ent.id()); + filter = API3.Filters.byMetadata(PlayerID, "linked-to-dropsite", ent.id()); var WkCollection = this.workers.filter(filter); WkCollection.registerUpdates(); @@ -318,51 +321,51 @@ BaseManager.prototype.initializeDropsite = function (gameState, ent, type) { // TODO: get workers on those resources and do something with them. } - if (Config.debug) + if (m.DebugEnabled) { // Make resources glow wildly if (type == "food") { self.dropsites[ent.id()][type][2].forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0.5,0,0]}); + Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [0.5,0,0]}); }); self.dropsites[ent.id()][type][1].forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [2,0,0]}); + Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [2,0,0]}); }); self.dropsites[ent.id()][type][0].forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [10,0,0]}); + Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [10,0,0]}); }); } if (type == "wood") { self.dropsites[ent.id()][type][2].forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,0.5,0]}); + Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,0.5,0]}); }); self.dropsites[ent.id()][type][1].forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,2,0]}); + Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,2,0]}); }); self.dropsites[ent.id()][type][0].forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,10,0]}); + Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,10,0]}); }); } if (type == "stone") { self.dropsites[ent.id()][type][2].forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0.5,0.5,0]}); + Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [0.5,0.5,0]}); }); self.dropsites[ent.id()][type][1].forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [2,2,0]}); + Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [2,2,0]}); }); self.dropsites[ent.id()][type][0].forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [10,10,0]}); + Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [10,10,0]}); }); } if (type == "metal") { self.dropsites[ent.id()][type][2].forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,0.5,0.5]}); + Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,0.5,0.5]}); }); self.dropsites[ent.id()][type][1].forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,2,2]}); + Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,2,2]}); }); self.dropsites[ent.id()][type][0].forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,10,10]}); + Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,10,10]}); }); } } @@ -371,7 +374,7 @@ BaseManager.prototype.initializeDropsite = function (gameState, ent, type) { // completely and "safely" remove a dropsite from our list. // this also removes any linked resource and so on. // TODO: should re-add the resources to another dropsite. -BaseManager.prototype.scrapDropsite = function (gameState, ent) { +m.BaseManager.prototype.scrapDropsite = function (gameState, ent) { if (this.dropsites[ent.id()] === undefined) return true; @@ -398,7 +401,7 @@ BaseManager.prototype.scrapDropsite = function (gameState, ent) { }; // Returns the position of the best place to build a new dropsite for the specified resource -BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource){ +m.BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource){ var storeHousePlate = gameState.getTemplate(gameState.applyCiv("structures/{civ}_storehouse")); @@ -407,15 +410,15 @@ BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource){ // Then checks for a good spot in the territory. If none, and town/city phase, checks outside // The AI will currently not build a CC if it wouldn't connect with an existing CC. - var territory = Map.createTerritoryMap(gameState); + var territory = m.createTerritoryMap(gameState); - var obstructions = Map.createObstructionMap(gameState,this.accessIndex,storeHousePlate); + var obstructions = m.createObstructionMap(gameState,this.accessIndex,storeHousePlate); obstructions.expandInfluences(); // copy the resource map as initialization. - var friendlyTiles = new Map(gameState.sharedScript, gameState.sharedScript.resourceMaps[resource].map, true); + var friendlyTiles = new API3.Map(gameState.sharedScript, gameState.sharedScript.resourceMaps[resource].map, true); - var DPFoundations = gameState.getOwnFoundations().filter(Filters.byType(gameState.applyCiv("foundation|structures/{civ}_storehouse"))); + var DPFoundations = gameState.getOwnFoundations().filter(API3.Filters.byType(gameState.applyCiv("foundation|structures/{civ}_storehouse"))); // TODO: might be better to check dropsites someplace else. // loop over this in this.terrytoryindices. It's usually a little too much, but it's always enough. @@ -434,25 +437,25 @@ BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource){ { var pos = [j%friendlyTiles.width, Math.floor(j/friendlyTiles.width)]; var dpPos = gameState.getEntityById(i).position(); - if (dpPos && SquareVectorDistance(friendlyTiles.gamePosToMapPos(dpPos), pos) < 250) + if (dpPos && API3.SquareVectorDistance(friendlyTiles.gamePosToMapPos(dpPos), pos) < 250) { friendlyTiles.map[j] = 0; continue; - } else if (dpPos && SquareVectorDistance(friendlyTiles.gamePosToMapPos(dpPos), pos) < 450) + } else if (dpPos && API3.SquareVectorDistance(friendlyTiles.gamePosToMapPos(dpPos), pos) < 450) friendlyTiles.map[j] /= 2; } for (var i in DPFoundations._entities) { var pos = [j%friendlyTiles.width, Math.floor(j/friendlyTiles.width)]; var dpPos = gameState.getEntityById(i).position(); - if (dpPos && SquareVectorDistance(friendlyTiles.gamePosToMapPos(dpPos), pos) < 250) + if (dpPos && API3.SquareVectorDistance(friendlyTiles.gamePosToMapPos(dpPos), pos) < 250) friendlyTiles.map[j] = 0; - else if (dpPos && SquareVectorDistance(friendlyTiles.gamePosToMapPos(dpPos), pos) < 450) + else if (dpPos && API3.SquareVectorDistance(friendlyTiles.gamePosToMapPos(dpPos), pos) < 450) friendlyTiles.map[j] /= 2; } } - if (Config.debug) + if (m.DebugEnabled) friendlyTiles.dumpIm("DP_" + resource + "_" + gameState.getTimeElapsed() + ".png"); var best = friendlyTiles.findBestTile(2, obstructions); // try to find a spot to place a DP. @@ -469,7 +472,7 @@ BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource){ }; // update the resource level of a dropsite. -BaseManager.prototype.updateDropsite = function (gameState, ent, type) { +m.BaseManager.prototype.updateDropsite = function (gameState, ent, type) { if (this.dropsites[ent.id()] === undefined || this.dropsites[ent.id()][type] === undefined) return undefined; // should initialize it first. @@ -490,7 +493,7 @@ BaseManager.prototype.updateDropsite = function (gameState, ent, type) { }; // Updates dropsites. -BaseManager.prototype.updateDropsites = function (gameState) { +m.BaseManager.prototype.updateDropsites = function (gameState) { // for each dropsite, recalculate for (i in this.dropsites) { @@ -507,7 +510,7 @@ BaseManager.prototype.updateDropsites = function (gameState) { // we're assuming Max - 3 for metal/stone mines, and 20 for any dropsite that has wood. // TODO: for wood might want to count the trees too. // TODO: this returns "future" worker capacity, might want to have a current one. -BaseManager.prototype.getWorkerCapacity = function (gameState, type) { +m.BaseManager.prototype.getWorkerCapacity = function (gameState, type) { var count = 0; if (type == "food") return 1000000; // TODO: perhaps return something sensible here. @@ -530,12 +533,12 @@ BaseManager.prototype.getWorkerCapacity = function (gameState, type) { // TODO: ought to be cached or something probably // Returns the amount of resource left -BaseManager.prototype.getResourceLevel = function (gameState, type, searchType, threshold) { +m.BaseManager.prototype.getResourceLevel = function (gameState, type, searchType, threshold) { var count = 0; if (searchType == "all") { // return all resources in the base area. - gameState.getResourceSupplies(type).filter(Filters.byTerritory(gameState.ai.HQ.basesMap, this.ID)).forEach( function (ent) { //}){ + gameState.getResourceSupplies(type).filter(API3.Filters.byTerritory(gameState.ai.HQ.basesMap, this.ID)).forEach( function (ent) { //}){ count += ent.resourceSupplyAmount(); }); return count; @@ -574,7 +577,7 @@ BaseManager.prototype.getResourceLevel = function (gameState, type, searchType, }; // check our resource levels and react accordingly -BaseManager.prototype.checkResourceLevels = function (gameState,queues) { +m.BaseManager.prototype.checkResourceLevels = function (gameState,queues) { for (type in this.willGather) { if (this.willGather[type] === 0) @@ -589,9 +592,9 @@ BaseManager.prototype.checkResourceLevels = function (gameState,queues) { if (!this.isFarming && count < 1600 && queues.field.length === 0) { // tell the queue manager we'll be trying to build fields shortly. - for (var i = 0; i < Config.Economy.initialFields;++i) + for (var i = 0; i < this.Config.Economy.initialFields;++i) { - var plan = new ConstructionPlan(gameState, "structures/{civ}_field", { "base" : this.ID }); + var plan = new m.ConstructionPlan(gameState, "structures/{civ}_field", { "base" : this.ID }); plan.isGo = function() { return false; }; // don't start right away. queues.field.addItem(plan); } @@ -604,7 +607,7 @@ BaseManager.prototype.checkResourceLevels = function (gameState,queues) { if (this.isFarming) { var numFarms = 0; - this.buildings.filter(Filters.byClass("Field")).forEach(function (field) { + this.buildings.filter(API3.Filters.byClass("Field")).forEach(function (field) { if (field.resourceSupplyAmount() > 400) numFarms++; }); @@ -615,7 +618,7 @@ BaseManager.prototype.checkResourceLevels = function (gameState,queues) { // let's see if we need to push new farms. if (numFd < 2) if (numFarms < Math.round(this.gatherersByType(gameState, "food").length / 4.6) || numFarms < Math.round(this.workers.length / 15.0)) - queues.field.addItem(new ConstructionPlan(gameState, "structures/{civ}_field", { "base" : this.ID })); + 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) { @@ -626,12 +629,12 @@ BaseManager.prototype.checkResourceLevels = function (gameState,queues) { var pos = this.findBestDropsiteLocation(gameState, type); if (!pos) { - debug ("Found no right position for a " + type + " dropsite, going into \"noSpot\" mode"); + m.debug ("Found no right position for a " + type + " dropsite, going into \"noSpot\" mode"); this.willGather[type] = 2; // won't build // TODO: tell the HQ we'll be needing a new base for this resource, or tell it we've ran out of resource Z. } else { - debug ("planning new dropsite for " + type); - queues.dropsites.addItem(new ConstructionPlan(gameState, "structures/{civ}_storehouse",{ "base" : this.ID }, 0, -1, pos)); + m.debug ("planning new dropsite for " + type); + queues.dropsites.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_storehouse",{ "base" : this.ID }, 0, -1, pos)); } } } @@ -640,13 +643,13 @@ BaseManager.prototype.checkResourceLevels = function (gameState,queues) { }; // let's return the estimated gather rates. -BaseManager.prototype.getGatherRates = function(gameState, currentRates) { +m.BaseManager.prototype.getGatherRates = function(gameState, currentRates) { }; -BaseManager.prototype.assignRolelessUnits = function(gameState) { +m.BaseManager.prototype.assignRolelessUnits = function(gameState) { // TODO: make this cleverer. - var roleless = this.units.filter(Filters.not(Filters.byHasMetadata(PlayerID, "role"))); + var roleless = this.units.filter(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID, "role"))); var self = this; roleless.forEach(function(ent) { if (ent.hasClass("Worker") || ent.hasClass("CitizenSoldier")) { @@ -660,7 +663,7 @@ BaseManager.prototype.assignRolelessUnits = function(gameState) { // If the numbers of workers on the resources is unbalanced then set some of workers to idle so // they can be reassigned by reassignIdleWorkers. // TODO: actually this probably should be in the HQ. -BaseManager.prototype.setWorkersIdleByPriority = function(gameState){ +m.BaseManager.prototype.setWorkersIdleByPriority = function(gameState){ var self = this; if (gameState.currentPhase() < 2 && gameState.getTimeElapsed() < 360000) return; // not in the first phase or the first 6 minutes. @@ -685,7 +688,7 @@ BaseManager.prototype.setWorkersIdleByPriority = function(gameState){ this.gatherersByType(gameState,types.types[i]).forEach( function (ent) { //}){ if (nb > 0) { - //debug ("Moving " +ent.id() + " from " + types.types[i]); + //m.debug ("Moving " +ent.id() + " from " + types.types[i]); nb--; // TODO: might want to direct assign. ent.stopMoving(); @@ -693,16 +696,16 @@ BaseManager.prototype.setWorkersIdleByPriority = function(gameState){ } }); } - //debug (currentRates); + //m.debug (currentRates); }; // TODO: work on this. -BaseManager.prototype.reassignIdleWorkers = function(gameState) { +m.BaseManager.prototype.reassignIdleWorkers = function(gameState) { var self = this; // Search for idle workers, and tell them to gather resources based on demand - var filter = Filters.or(Filters.byMetadata(PlayerID,"subrole","idle"), Filters.not(Filters.byHasMetadata(PlayerID,"subrole"))); + var filter = API3.Filters.or(API3.Filters.byMetadata(PlayerID,"subrole","idle"), API3.Filters.not(API3.Filters.byHasMetadata(PlayerID,"subrole"))); var idleWorkers = gameState.updatingCollection("idle-workers-base-" + this.ID, filter, this.workers); if (idleWorkers.length) { @@ -713,7 +716,7 @@ BaseManager.prototype.reassignIdleWorkers = function(gameState) { } if (ent.hasClass("Worker")) { var types = gameState.ai.HQ.pickMostNeededResources(gameState); - //debug ("assigning " +ent.id() + " to " + types[0]); + //m.debug ("assigning " +ent.id() + " to " + types[0]); ent.setMetadata(PlayerID, "subrole", "gatherer"); ent.setMetadata(PlayerID, "gather-type", types[0]); @@ -734,21 +737,21 @@ BaseManager.prototype.reassignIdleWorkers = function(gameState) { } }; -BaseManager.prototype.workersBySubrole = function(gameState, subrole) { - return gameState.updatingCollection("subrole-" + subrole +"-base-" + this.ID, Filters.byMetadata(PlayerID, "subrole", subrole), this.workers, true); +m.BaseManager.prototype.workersBySubrole = function(gameState, subrole) { + return gameState.updatingCollection("subrole-" + subrole +"-base-" + this.ID, API3.Filters.byMetadata(PlayerID, "subrole", subrole), this.workers, true); }; -BaseManager.prototype.gatherersByType = function(gameState, type) { - return gameState.updatingCollection("workers-gathering-" + type +"-base-" + this.ID, Filters.byMetadata(PlayerID, "gather-type", type), this.workersBySubrole(gameState, "gatherer")); +m.BaseManager.prototype.gatherersByType = function(gameState, type) { + return gameState.updatingCollection("workers-gathering-" + type +"-base-" + this.ID, API3.Filters.byMetadata(PlayerID, "gather-type", type), this.workersBySubrole(gameState, "gatherer")); }; // returns an entity collection of workers. // They are idled immediatly and their subrole set to idle. -BaseManager.prototype.pickBuilders = function(gameState, number) { - var collec = new EntityCollection(gameState.sharedScript); +m.BaseManager.prototype.pickBuilders = function(gameState, number) { + var collec = new API3.EntityCollection(gameState.sharedScript); // TODO: choose better. - var workers = this.workers.filter(Filters.not(Filters.byClass("Cavalry"))).toEntityArray(); + var workers = this.workers.filter(API3.Filters.not(API3.Filters.byClass("Cavalry"))).toEntityArray(); workers.sort(function (a,b) { var vala = 0, valb = 0; if (a.getMetadata(PlayerID,"subrole") == "builder") @@ -770,7 +773,8 @@ BaseManager.prototype.pickBuilders = function(gameState, number) { return collec; } -BaseManager.prototype.assignToFoundations = function(gameState, noRepair) { +m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair) { + // If we have some foundations, and we don't have enough builder-workers, // try reassigning some other workers who are nearby @@ -778,21 +782,21 @@ BaseManager.prototype.assignToFoundations = function(gameState, noRepair) { var self = this; - var foundations = this.buildings.filter(Filters.and(Filters.isFoundation(),Filters.not(Filters.byClass("Field")))).toEntityArray(); + var foundations = this.buildings.filter(API3.Filters.and(API3.Filters.isFoundation(),API3.Filters.not(API3.Filters.byClass("Field")))).toEntityArray(); var damagedBuildings = this.buildings.filter(function (ent) { if (ent.needsRepair() && ent.getMetadata(PlayerID, "plan") == undefined) { return true; } return false; }).toEntityArray(); // Check if nothing to build if (!foundations.length && !damagedBuildings.length){ return; } - var workers = this.workers.filter(Filters.not(Filters.byClass("Cavalry"))); + var workers = this.workers.filter(API3.Filters.not(API3.Filters.byClass("Cavalry"))); var builderWorkers = this.workersBySubrole(gameState, "builder"); - var idleBuilderWorkers = this.workersBySubrole(gameState, "builder").filter(Filters.isIdle()); + var idleBuilderWorkers = this.workersBySubrole(gameState, "builder").filter(API3.Filters.isIdle()); // if we're constructing and we have the foundations to our base anchor, only try building that. - if (this.constructing == true && this.buildings.filter(Filters.and(Filters.isFoundation(), Filters.byMetadata(PlayerID, "baseAnchor", true))).length !== 0) + if (this.constructing == true && this.buildings.filter(API3.Filters.and(API3.Filters.isFoundation(), API3.Filters.byMetadata(PlayerID, "baseAnchor", true))).length !== 0) { - foundations = this.buildings.filter(Filters.byMetadata(PlayerID, "baseAnchor", true)).toEntityArray(); + foundations = this.buildings.filter(API3.Filters.byMetadata(PlayerID, "baseAnchor", true)).toEntityArray(); var tID = foundations[0].id(); workers.forEach(function (ent) { //}){ var target = ent.getMetadata(PlayerID, "target-foundation"); @@ -832,7 +836,7 @@ BaseManager.prototype.assignToFoundations = function(gameState, noRepair) { var assigned = gameState.getOwnEntitiesByMetadata("target-foundation", target.id()).length; - var targetNB = Config.Economy.targetNumBuilders; // TODO: dynamic that. + var targetNB = this.Config.Economy.targetNumBuilders; // TODO: dynamic that. if (target.hasClass("CivCentre") || target.buildTime() > 150 || target.hasClass("House")) targetNB *= 2; if (target.getMetadata(PlayerID, "baseAnchor") == true) @@ -844,7 +848,7 @@ BaseManager.prototype.assignToFoundations = function(gameState, noRepair) { var addedToThis = 0; idleBuilderWorkers.forEach(function(ent) { - if (ent.position() && SquareVectorDistance(ent.position(), target.position()) < 10000 && assigned + addedToThis < targetNB) + if (ent.position() && API3.SquareVectorDistance(ent.position(), target.position()) < 10000 && assigned + addedToThis < targetNB) { addedWorkers++; addedToThis++; @@ -876,7 +880,7 @@ BaseManager.prototype.assignToFoundations = function(gameState, noRepair) { } else if (noRepair && !target.hasClass("CivCentre")) continue; - var territory = Map.createTerritoryMap(gameState); + var territory = m.createTerritoryMap(gameState); if (territory.getOwner(target.position()) !== PlayerID || territory.getOwner([target.position()[0] + 5, target.position()[1]]) !== PlayerID) continue; @@ -900,7 +904,7 @@ BaseManager.prototype.assignToFoundations = function(gameState, noRepair) { } }; -BaseManager.prototype.update = function(gameState, queues, events) { +m.BaseManager.prototype.update = function(gameState, queues, events) { Engine.ProfileStart("Base update - base " + this.ID); var self = this; @@ -957,10 +961,14 @@ BaseManager.prototype.update = function(gameState, queues, events) { Engine.ProfileStart("Run Workers"); this.workers.forEach(function(ent) { if (!ent.getMetadata(PlayerID, "worker-object")) - ent.setMetadata(PlayerID, "worker-object", new Worker(ent)); + ent.setMetadata(PlayerID, "worker-object", new m.Worker(ent)); ent.getMetadata(PlayerID, "worker-object").update(self, gameState); }); Engine.ProfileStop(); Engine.ProfileStop(); }; + +return m; + +}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/aegis/config.js b/binaries/data/mods/public/simulation/ai/aegis/config.js index 789999fcf9..41a5d4dc82 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/config.js +++ b/binaries/data/mods/public/simulation/ai/aegis/config.js @@ -1,6 +1,11 @@ -// Baseconfig is the highest difficulty. -var baseConfig = { - "Military" : { +var AEGIS = function(m) +{ + +m.Config = function() { + this.difficulty = 2; // 0 is sandbox, 1 is easy, 2 is medium, 3 is hard, 4 is very hard. + // overriden by the GUI + + this.Military = { "fortressLapseTime" : 540, // Time to wait between building 2 fortresses "defenceBuildingTime" : 600, // Time to wait before building towers or fortresses "attackPlansStartTime" : 0, // time to wait before attacking. Start as soon as possible (first barracks) @@ -8,8 +13,8 @@ var baseConfig = { "popForBarracks1" : 15, "popForBarracks2" : 95, "timeForBlacksmith" : 900, - }, - "Economy" : { + }; + this.Economy = { "townPhase" : 180, // time to start trying to reach town phase (might be a while after. Still need the requirements + ress ) "cityPhase" : 840, // time to start trying to reach city phase "popForMarket" : 80, @@ -19,19 +24,21 @@ var baseConfig = { "targetNumBuilders" : 1.5, // Base number of builders per foundation. "femaleRatio" : 0.4, // percent of females among the workforce. "initialFields" : 2 - }, + }; // Note: attack settings are set directly in attack_plan.js // defence - "Defence" : { + this.Defence = + { "defenceRatio" : 5, // see defence.js for more info. "armyCompactSize" : 700, // squared. Half-diameter of an army. "armyBreakawaySize" : 900 // squared. - }, + }; // military - "buildings" : { + this.buildings = + { "moderate" : { "default" : [ "structures/{civ}_barracks" ] }, @@ -51,10 +58,11 @@ var baseConfig = { "default" : [ "structures/{civ}_fortress" ], "celt" : [ "structures/{civ}_fortress_b", "structures/{civ}_fortress_g" ] } - }, + }; // qbot - "priorities" : { // Note these are dynamic, you are only setting the initial values + this.priorities = + { // Note these are dynamic, you are only setting the initial values "house" : 350, "villager" : 40, "citizenSoldier" : 60, @@ -67,49 +75,46 @@ var baseConfig = { "majorTech" : 700, "minorTech" : 50, "civilCentre" : 400 - }, - "difficulty" : 2, // 0 is sandbox, 1 is easy, 2 is medium, 3 is hard, 4 is very hard. - "debug" : false + }; }; -var Config = { - "debug": false, - "difficulty" : 2, // overriden by the GUI - updateDifficulty: function(difficulty) +//Config.prototype = new BaseConfig(); + +m.Config.prototype.updateDifficulty = function(difficulty) +{ + this.difficulty = difficulty; + // changing settings based on difficulty. + if (this.difficulty === 1) { - Config.difficulty = difficulty; - // changing settings based on difficulty. - if (Config.difficulty === 1) - { - Config.Military.defenceBuildingTime = 1200; - Config.Military.attackPlansStartTime = 960; - Config.Military.popForBarracks1 = 35; - Config.Military.popForBarracks2 = 150; // shouldn't reach it - Config.Military.popForBlacksmith = 150; // shouldn't reach it + this.Military.defenceBuildingTime = 1200; + this.Military.attackPlansStartTime = 960; + this.Military.popForBarracks1 = 35; + this.Military.popForBarracks2 = 150; // shouldn't reach it + this.Military.popForBlacksmith = 150; // shouldn't reach it - Config.Economy.cityPhase = 1800; - Config.Economy.popForMarket = 80; - Config.Economy.techStartTime = 600; - Config.Economy.femaleRatio = 0.6; - Config.Economy.initialFields = 1; - // Config.Economy.targetNumWorkers will be set by AI scripts. - } - else if (Config.difficulty === 0) - { - Config.Military.defenceBuildingTime = 450; - Config.Military.attackPlansStartTime = 9600000; // never - Config.Military.popForBarracks1 = 60; - Config.Military.popForBarracks2 = 150; // shouldn't reach it - Config.Military.popForBlacksmith = 150; // shouldn't reach it + this.Economy.cityPhase = 1800; + this.Economy.popForMarket = 80; + this.Economy.techStartTime = 600; + this.Economy.femaleRatio = 0.6; + this.Economy.initialFields = 1; + // Config.Economy.targetNumWorkers will be set by AI scripts. + } + else if (this.difficulty === 0) + { + this.Military.defenceBuildingTime = 450; + this.Military.attackPlansStartTime = 9600000; // never + this.Military.popForBarracks1 = 60; + this.Military.popForBarracks2 = 150; // shouldn't reach it + this.Military.popForBlacksmith = 150; // shouldn't reach it - Config.Economy.cityPhase = 240000; - Config.Economy.popForMarket = 200; - Config.Economy.techStartTime = 1800; - Config.Economy.femaleRatio = 0.2; - Config.Economy.initialFields = 1; - // Config.Economy.targetNumWorkers will be set by AI scripts. - } + this.Economy.cityPhase = 240000; + this.Economy.popForMarket = 200; + this.Economy.techStartTime = 1800; + this.Economy.femaleRatio = 0.2; + this.Economy.initialFields = 1; + // Config.Economy.targetNumWorkers will be set by AI scripts. } }; -Config.__proto__ = baseConfig; +return m; +}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/aegis/data.json b/binaries/data/mods/public/simulation/ai/aegis/data.json index 4aeb576372..0ec1529b25 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/data.json +++ b/binaries/data/mods/public/simulation/ai/aegis/data.json @@ -1,6 +1,7 @@ { "name": "Aegis Bot", "description": "Wraitii's improvement of qBot. It is more reliable and generally a better player. Note that it doesn't support saved games yet, and there may be other bugs. Please report issues to Wildfire Games (see the link in the main menu).", + "moduleName" : "AEGIS", "constructor": "AegisBot", "useShared": true } diff --git a/binaries/data/mods/public/simulation/ai/aegis/defence.js b/binaries/data/mods/public/simulation/ai/aegis/defence.js index be768c3a41..f666daf66d 100755 --- a/binaries/data/mods/public/simulation/ai/aegis/defence.js +++ b/binaries/data/mods/public/simulation/ai/aegis/defence.js @@ -1,6 +1,9 @@ +var AEGIS = function(m) +{ + // directly imported from Marilyn, with slight modifications to work with qBot. -function Defence(){ +m.Defence = function(Config){ this.defenceRatio = Config.Defence.defenceRatio;// How many defenders we want per attacker. Need to balance fewer losses vs. lost economy // note: the choice should be a no-brainer most of the time: better deflect the attack. // This is also sometimes forcebly overcome by the defense manager. @@ -48,36 +51,36 @@ function Defence(){ // 1: Huge army in the base, outnumbering us. -Defence.prototype.update = function(gameState, events, HQ){ +m.Defence.prototype.update = function(gameState, events, HQ){ Engine.ProfileStart("Defence Manager"); // a litlle cache-ing if (!this.idleDefs) { - var filter = Filters.and(Filters.byMetadata(PlayerID, "role", "defence"), Filters.isIdle()); + var filter = API3.Filters.and(API3.Filters.byMetadata(PlayerID, "role", "defence"), API3.Filters.isIdle()); this.idleDefs = gameState.getOwnEntities().filter(filter); this.idleDefs.registerUpdates(); } if (!this.defenders) { - var filter = Filters.byMetadata(PlayerID, "role", "defence"); + var filter = API3.Filters.byMetadata(PlayerID, "role", "defence"); this.defenders = gameState.getOwnEntities().filter(filter); this.defenders.registerUpdates(); } /*if (!this.listedEnemyCollection) { - var filter = Filters.byMetadata(PlayerID, "listed-enemy", true); + var filter = API3.Filters.byMetadata(PlayerID, "listed-enemy", true); this.listedEnemyCollection = gameState.getEnemyEntities().filter(filter); this.listedEnemyCollection.registerUpdates(); } - this.myBuildings = gameState.getOwnEntities().filter(Filters.byClass("Structure")).toEntityArray(); - this.myUnits = gameState.getOwnEntities().filter(Filters.byClass("Unit")); + this.myBuildings = gameState.getOwnEntities().filter(API3.Filters.byClass("Structure")).toEntityArray(); + this.myUnits = gameState.getOwnEntities().filter(API3.Filters.byClass("Unit")); */ - var filter = Filters.and(Filters.byClassesOr(["CitizenSoldier", "Hero", "Champion", "Siege"]), Filters.byOwner(PlayerID)); + var filter = API3.Filters.and(API3.Filters.byClassesOr(["CitizenSoldier", "Hero", "Champion", "Siege"]), API3.Filters.byOwner(PlayerID)); this.myUnits = gameState.updatingGlobalCollection("player-" +PlayerID + "-soldiers", filter); - filter = Filters.and(Filters.byClass("Structure"), Filters.byOwner(PlayerID)); + filter = API3.Filters.and(API3.Filters.byClass("Structure"), API3.Filters.byOwner(PlayerID)); this.myBuildings = gameState.updatingGlobalCollection("player-" +PlayerID + "-structures", filter); - this.territoryMap = Map.createTerritoryMap(gameState); // used by many func + this.territoryMap = m.createTerritoryMap(gameState); // used by many func // First step: we deal with enemy armies, those are the highest priority. this.defendFromEnemies(gameState, events, HQ); @@ -118,7 +121,7 @@ Defence.prototype.reevaluateDangerousArmies = function(gameState, armies) { } for (var o in this.myBuildings) { // if the armies out of my buildings LOS (with a little more, because we're cheating right now and big armies could go undetected) - if (inRange(pos, this.myBuildings[o].position(),this.myBuildings[o].visionRange()*this.myBuildings[o].visionRange() + 2500)) { + if (m.inRange(pos, this.myBuildings[o].position(),this.myBuildings[o].visionRange()*this.myBuildings[o].visionRange() + 2500)) { stillDangerousArmies[i] = armies[i]; break; } @@ -138,7 +141,7 @@ Defence.prototype.evaluateArmies = function(gameState, armies) { }*/ // Incorporates an entity in an army. If no army fits, it creates a new one around this one. // an army is basically an entity collection. -Defence.prototype.armify = function(gameState, entity, HQ, minNBForArmy) { +m.Defence.prototype.armify = function(gameState, entity, HQ, minNBForArmy) { if (entity.position() === undefined) return; if (this.enemyArmy[entity.owner()] === undefined) @@ -151,7 +154,7 @@ Defence.prototype.armify = function(gameState, entity, HQ, minNBForArmy) { if (army.getCentrePosition() === undefined) { } else { - if (SquareVectorDistance(army.getCentrePosition(), entity.position()) < this.armyCompactSize) + if (API3.SquareVectorDistance(army.getCentrePosition(), entity.position()) < this.armyCompactSize) { entity.setMetadata(PlayerID, "inArmy", armyIndex); army.addEnt(entity); @@ -163,11 +166,11 @@ Defence.prototype.armify = function(gameState, entity, HQ, minNBForArmy) { if (HQ) { var self = this; - var close = HQ.enemyWatchers[entity.owner()].enemySoldiers.filter(Filters.byDistance(entity.position(), self.armyCompactSize)); + var close = HQ.enemyWatchers[entity.owner()].enemySoldiers.filter(API3.Filters.byDistance(entity.position(), self.armyCompactSize)); if (!minNBForArmy || close.length >= minNBForArmy) { // if we're here, we need to create an army for it, and freeze it to make sure no unit will be added automatically - var newArmy = new EntityCollection(gameState.sharedScript, {}, [Filters.byOwner(entity.owner())]); + var newArmy = new API3.EntityCollection(gameState.sharedScript, {}, [API3.Filters.byOwner(entity.owner())]); newArmy.addEnt(entity); newArmy.freeze(); newArmy.registerUpdates(); @@ -184,7 +187,7 @@ Defence.prototype.armify = function(gameState, entity, HQ, minNBForArmy) { } } else { // if we're here, we need to create an army for it, and freeze it to make sure no unit will be added automatically - var newArmy = new EntityCollection(gameState.sharedScript, {}, [Filters.byOwner(entity.owner())]); + var newArmy = new API3.EntityCollection(gameState.sharedScript, {}, [API3.Filters.byOwner(entity.owner())]); newArmy.addEnt(entity); newArmy.freeze(); newArmy.registerUpdates(); @@ -195,12 +198,12 @@ Defence.prototype.armify = function(gameState, entity, HQ, minNBForArmy) { return; } // Returns if a unit should be seen as dangerous or not. -Defence.prototype.evaluateRawEntity = function(gameState, entity) { +m.Defence.prototype.evaluateRawEntity = function(gameState, entity) { if (entity.position && +this.territoryMap.point(entity.position) - 64 === +PlayerID && entity._template.Attack !== undefined) return true; return false; } -Defence.prototype.evaluateEntity = function(gameState, entity) { +m.Defence.prototype.evaluateEntity = function(gameState, entity) { if (!entity.position()) return false; if (this.territoryMap.point(entity.position()) - 64 === entity.owner() || entity.attackTypes() === undefined) @@ -210,20 +213,20 @@ Defence.prototype.evaluateEntity = function(gameState, entity) { { if (!this.myBuildings._entities[i].hasClass("ConquestCritical")) continue; - if (SquareVectorDistance(this.myBuildings._entities[i].position(), entity.position()) < 6000) + if (API3.SquareVectorDistance(this.myBuildings._entities[i].position(), entity.position()) < 6000) return true; } return false; } // returns false if the unit is in its territory -Defence.prototype.reevaluateEntity = function(gameState, entity) { +m.Defence.prototype.reevaluateEntity = function(gameState, entity) { if ( (entity.position() && +this.territoryMap.point(entity.position()) - 64 === +entity.owner()) || entity.attackTypes() === undefined) return false; return true; } // This deals with incoming enemy armies, setting the defcon if needed. It will take new soldiers, and assign them to attack // TODO: still is still pretty dumb, it could use improvements. -Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { +m.Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { var self = this; // New, faster system will loop for enemy soldiers, and also females on occasions ( TODO ) @@ -313,7 +316,7 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { } else { army.forEach(function (ent) { //}){ // check if the unit is a breakaway - if (ent.position() && SquareVectorDistance(position, ent.position()) > self.armyBreakawaySize) + if (ent.position() && API3.SquareVectorDistance(position, ent.position()) > self.armyBreakawaySize) { ent.setMetadata(PlayerID, "inArmy", undefined); army.removeEnt(ent); @@ -322,12 +325,12 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { } else { // check if we have registered that unit already. if (self.listOfEnemies[ent.id()] === undefined) { - self.listOfEnemies[ent.id()] = new EntityCollection(gameState.sharedScript, {}, [Filters.byOwner(ent.owner())]); + self.listOfEnemies[ent.id()] = new API3.EntityCollection(gameState.sharedScript, {}, [API3.Filters.byOwner(ent.owner())]); self.listOfEnemies[ent.id()].freeze(); self.listOfEnemies[ent.id()].addEnt(ent); self.listOfEnemies[ent.id()].registerUpdates(); - self.attackerCache[ent.id()] = self.myUnits.filter(Filters.byTargetedEntity(ent.id())); + self.attackerCache[ent.id()] = self.myUnits.filter(API3.Filters.byTargetedEntity(ent.id())); self.attackerCache[ent.id()].registerUpdates(); nbOfAttackers++; self.nbAttackers++; @@ -373,7 +376,7 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { if (this.nbAttackers === 0 && this.nbDefenders === 0) { // Release all our units - this.myUnits.filter(Filters.byMetadata(PlayerID, "role","defence")).forEach(function (defender) { //}){ + this.myUnits.filter(API3.Filters.byMetadata(PlayerID, "role","defence")).forEach(function (defender) { //}){ defender.stopMoving(); if (defender.getMetadata(PlayerID, "formerrole")) defender.setMetadata(PlayerID, "role", defender.getMetadata(PlayerID, "formerrole") ); @@ -387,7 +390,7 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { return; } else if (this.nbAttackers === 0 && this.nbDefenders !== 0) { // Release all our units - this.myUnits.filter(Filters.byMetadata(PlayerID, "role","defence")).forEach(function (defender) { //}){ + this.myUnits.filter(API3.Filters.byMetadata(PlayerID, "role","defence")).forEach(function (defender) { //}){ defender.stopMoving(); if (defender.getMetadata(PlayerID, "formerrole")) defender.setMetadata(PlayerID, "role", defender.getMetadata(PlayerID, "formerrole") ); @@ -404,8 +407,8 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { HQ.ungarrisonAll(gameState); } - //debug ("total number of attackers:"+ this.nbAttackers); - //debug ("total number of defenders:"+ this.nbDefenders); + //m.debug ("total number of attackers:"+ this.nbAttackers); + //m.debug ("total number of defenders:"+ this.nbDefenders); // If I'm here, I have a list of enemy units, and a list of my units attacking it (in absolute terms, I could use a list of any unit attacking it). // now I'll list my idle defenders, then my idle soldiers that could defend. @@ -419,8 +422,8 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { } // we're having too many. Release those that attack units already dealt with, or idle ones. - if (this.myUnits.filter(Filters.byMetadata(PlayerID, "role","defence")).length > nbOfAttackers*this.defenceRatio*1.2) { - this.myUnits.filter(Filters.byMetadata(PlayerID, "role","defence")).forEach(function (defender) { //}){ + if (this.myUnits.filter(API3.Filters.byMetadata(PlayerID, "role","defence")).length > nbOfAttackers*this.defenceRatio*1.2) { + this.myUnits.filter(API3.Filters.byMetadata(PlayerID, "role","defence")).forEach(function (defender) { //}){ if ( defender.isIdle() || (defender.unitAIOrderData() && defender.unitAIOrderData()["target"])) { if ( defender.isIdle() || (self.attackerCache[defender.unitAIOrderData()["target"]] && self.attackerCache[defender.unitAIOrderData()["target"]].length > 3)) { // okay release me. @@ -437,9 +440,9 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { } - var nonDefenders = this.myUnits.filter(Filters.or(Filters.not(Filters.byMetadata(PlayerID, "role","defence")),Filters.isIdle())); - nonDefenders = nonDefenders.filter(Filters.not(Filters.byClass("Female"))); - nonDefenders = nonDefenders.filter(Filters.not(Filters.byMetadata(PlayerID, "subrole","attacking"))); + var nonDefenders = this.myUnits.filter(API3.Filters.or(API3.Filters.not(API3.Filters.byMetadata(PlayerID, "role","defence")),API3.Filters.isIdle())); + nonDefenders = nonDefenders.filter(API3.Filters.not(API3.Filters.byClass("Female"))); + nonDefenders = nonDefenders.filter(API3.Filters.not(API3.Filters.byMetadata(PlayerID, "subrole","attacking"))); var defenceRatio = this.defenceRatio; if (newEnemies.length + this.nbAttackers > (this.nbDefenders + nonDefenders.length) * 0.8 && this.nbAttackers > 9) @@ -448,8 +451,8 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { if (newEnemies.length + this.nbAttackers > (this.nbDefenders + nonDefenders.length) * 1.5 && this.nbAttackers > 5) gameState.setDefcon(1); - //debug ("newEnemies.length "+ newEnemies.length); - //debug ("nonDefenders.length "+ nonDefenders.length); + //m.debug ("newEnemies.length "+ newEnemies.length); + //m.debug ("nonDefenders.length "+ nonDefenders.length); if (gameState.defcon() > 3) HQ.unpauseAllPlans(gameState); @@ -457,7 +460,7 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { if ( (nonDefenders.length + this.nbDefenders > newEnemies.length + this.nbAttackers) || this.nbDefenders + nonDefenders.length < 4) { - var buildings = gameState.getOwnEntities().filter(Filters.byCanGarrison()).toEntityArray(); + var buildings = gameState.getOwnEntities().filter(API3.Filters.byCanGarrison()).toEntityArray(); buildings.forEach( function (struct) { if (struct.garrisoned() && struct.garrisoned().length) struct.unloadAll(); @@ -513,7 +516,7 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { { var ent = nonDefenders._entities[id]; if (ent.position()) - data.push([id, ent, SquareVectorDistance(enemy.position(), ent.position())]); + data.push([id, ent, API3.SquareVectorDistance(enemy.position(), ent.position())]); } // refine the defenders we want. Since it's the distance squared, it has the effect // of tending to always prefer closer units, though this refinement should change it slighty. @@ -547,13 +550,13 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { for each (var val in data.slice(0, Math.min(nonDefenders._length, defRatio - assigned))) ret[val[0]] = val[1]; - var defs = new EntityCollection(nonDefenders._ai, ret); + var defs = new API3.EntityCollection(nonDefenders._ai, ret); // successfully sorted defs.forEach(function (defender) { //}){ if (defender.getMetadata(PlayerID, "plan") != undefined && (gameState.defcon() < 4 || defender.getMetadata(PlayerID,"subrole") == "walking")) HQ.pausePlan(gameState, defender.getMetadata(PlayerID, "plan")); - //debug ("Against " +enemy.id() + " Assigning " + defender.id()); + //m.debug ("Against " +enemy.id() + " Assigning " + defender.id()); if (defender.getMetadata(PlayerID, "role") == "worker" || defender.getMetadata(PlayerID, "role") == "attack") defender.setMetadata(PlayerID, "formerrole", defender.getMetadata(PlayerID, "role")); defender.setMetadata(PlayerID, "role","defence"); @@ -571,8 +574,8 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { /*if (gameState.defcon() <= 3) { // let's try to garrison neighboring females. - var buildings = gameState.getOwnEntities().filter(Filters.byCanGarrison()).toEntityArray(); - var females = gameState.getOwnEntities().filter(Filters.byClass("Support")); + var buildings = gameState.getOwnEntities().filter(API3.Filters.byCanGarrison()).toEntityArray(); + var females = gameState.getOwnEntities().filter(API3.Filters.byClass("Support")); var cache = {}; var garrisoned = false; @@ -580,7 +583,7 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { garrisoned = false; if (ent.position()) { - if (SquareVectorDistance(ent.position(), enemy.position()) < 3500) + if (API3.SquareVectorDistance(ent.position(), enemy.position()) < 3500) { for (var i in buildings) { @@ -611,7 +614,7 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { // this processes the attackmessages // So that a unit that gets attacked will not be completely dumb. // warning: huge levels of indentation coming. -Defence.prototype.MessageProcess = function(gameState,events, HQ) { +m.Defence.prototype.MessageProcess = function(gameState,events, HQ) { var self = this; for (var key in events){ @@ -646,12 +649,12 @@ Defence.prototype.MessageProcess = function(gameState,events, HQ) { if (territory === PlayerID) { // anyway we'll register the animal as dangerous, and attack it (note: only on our territory. Don't care otherwise). - this.listOfWantedUnits[attacker.id()] = new EntityCollection(gameState.sharedScript); + this.listOfWantedUnits[attacker.id()] = new API3.EntityCollection(gameState.sharedScript); this.listOfWantedUnits[attacker.id()].addEnt(attacker); this.listOfWantedUnits[attacker.id()].freeze(); this.listOfWantedUnits[attacker.id()].registerUpdates(); - var filter = Filters.byTargetedEntity(attacker.id()); + var filter = API3.Filters.byTargetedEntity(attacker.id()); this.WantedUnitsAttacker[attacker.id()] = this.myUnits.filter(filter); this.WantedUnitsAttacker[attacker.id()].registerUpdates(); } @@ -666,7 +669,7 @@ Defence.prototype.MessageProcess = function(gameState,events, HQ) { // Right now, to make the AI less gameable, we'll mark any surrounding resource as inaccessible. // usual tower range is 80. Be on the safe side. - var close = gameState.getResourceSupplies("wood").filter(Filters.byDistance(attacker.position(), 90)); + var close = gameState.getResourceSupplies("wood").filter(API3.Filters.byDistance(attacker.position(), 90)); close.forEach(function (supply) { //}){ supply.setMetadata(PlayerID, "inaccessible", true); }); @@ -683,7 +686,7 @@ Defence.prototype.MessageProcess = function(gameState,events, HQ) { if (this.reevaluateEntity(gameState, attacker)) { var position = attacker.position(); - var close = HQ.enemyWatchers[attacker.owner()].enemySoldiers.filter(Filters.byDistance(position, self.armyCompactSize)); + var close = HQ.enemyWatchers[attacker.owner()].enemySoldiers.filter(API3.Filters.byDistance(position, self.armyCompactSize)); if (close.length > 2 || ourUnit.hasClass("Support") || attacker.hasClass("Siege")) { @@ -692,7 +695,7 @@ Defence.prototype.MessageProcess = function(gameState,events, HQ) { armyID = attacker.getMetadata(PlayerID, "inArmy"); close.forEach(function (ent) { //}){ - if (SquareVectorDistance(position, ent.position()) < self.armyCompactSize) + if (API3.SquareVectorDistance(position, ent.position()) < self.armyCompactSize) { ent.setMetadata(PlayerID, "inArmy", armyID); self.enemyArmy[ent.owner()][armyID].addEnt(ent); @@ -708,7 +711,7 @@ Defence.prototype.MessageProcess = function(gameState,events, HQ) { // let's try to garrison this support unit. if (ourUnit.position()) { - var buildings = gameState.getOwnEntities().filter(Filters.byCanGarrison()).filterNearest(ourUnit.position(),4).toEntityArray(); + var buildings = gameState.getOwnEntities().filter(API3.Filters.byCanGarrison()).filterNearest(ourUnit.position(),4).toEntityArray(); var garrisoned = false; for (var i in buildings) { @@ -742,7 +745,7 @@ Defence.prototype.MessageProcess = function(gameState,events, HQ) { }; // nice sets of closing brackets, isn't it? // At most, this will put defcon to 4 -Defence.prototype.DealWithWantedUnits = function(gameState, events, HQ) { +m.Defence.prototype.DealWithWantedUnits = function(gameState, events, HQ) { //if (gameState.defcon() < 3) // return; @@ -841,3 +844,6 @@ Defence.prototype.DealWithWantedUnits = function(gameState, events, HQ) { } return; } + +return m; +}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/aegis/enemy-watcher.js b/binaries/data/mods/public/simulation/ai/aegis/enemy-watcher.js index 0256e48433..873ca49527 100755 --- a/binaries/data/mods/public/simulation/ai/aegis/enemy-watcher.js +++ b/binaries/data/mods/public/simulation/ai/aegis/enemy-watcher.js @@ -1,32 +1,35 @@ +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. */ -var enemyWatcher = function(gameState, playerToWatch) { +m.enemyWatcher = function(gameState, playerToWatch) { this.watched = playerToWatch; // using global entity collections, shared by any AI that knows the name of this. - var filter = Filters.and(Filters.byClass("Structure"), Filters.byOwner(this.watched)); + var filter = API3.Filters.and(API3.Filters.byClass("Structure"), API3.Filters.byOwner(this.watched)); this.enemyBuildings = gameState.updatingGlobalCollection("player-" +this.watched + "-structures", filter); - filter = Filters.and(Filters.byClass("Unit"), Filters.byOwner(this.watched)); + filter = API3.Filters.and(API3.Filters.byClass("Unit"), API3.Filters.byOwner(this.watched)); this.enemyUnits = gameState.updatingGlobalCollection("player-" +this.watched + "-units", filter); - filter = Filters.and(Filters.byClass("Worker"), Filters.byOwner(this.watched)); + filter = API3.Filters.and(API3.Filters.byClass("Worker"), API3.Filters.byOwner(this.watched)); this.enemyCivilians = gameState.updatingGlobalCollection("player-" +this.watched + "-civilians", filter); - filter = Filters.and(Filters.byClassesOr(["CitizenSoldier", "Hero", "Champion", "Siege"]), Filters.byOwner(this.watched)); + 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 = Filters.and(Filters.byClass("Worker"), Filters.byOwner(this.watched)); + filter = API3.Filters.and(API3.Filters.byClass("Worker"), API3.Filters.byOwner(this.watched)); this.enemyCivilians = gameState.getEnemyEntities().filter(filter); this.enemyCivilians.registerUpdates(); - filter = Filters.and(Filters.byClassesOr(["CitizenSoldier", "Hero", "Champion", "Siege"]), Filters.byOwner(this.watched)); + 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(); @@ -40,15 +43,15 @@ var enemyWatcher = function(gameState, playerToWatch) { this.dangerousArmies = []; }; -enemyWatcher.prototype.getAllEnemySoldiers = function() { +m.enemyWatcher.prototype.getAllEnemySoldiers = function() { return this.enemySoldiers; }; -enemyWatcher.prototype.getAllEnemyBuildings = function() { +m.enemyWatcher.prototype.getAllEnemyBuildings = function() { return this.enemyBuildings; }; -enemyWatcher.prototype.getEnemyBuildings = function(gameState, specialClass, OneTime) { - var filter = Filters.byClass(specialClass); +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); @@ -57,55 +60,55 @@ enemyWatcher.prototype.getEnemyBuildings = function(gameState, specialClass, One return gameState.updatingGlobalCollection("player-" +this.watched + "-structures-" +specialClass, filter, gameState.getGEC("player-" +this.watched + "-structures")); }; -enemyWatcher.prototype.getDangerousArmies = function() { +m.enemyWatcher.prototype.getDangerousArmies = function() { var toreturn = {}; for (var i in this.dangerousArmies) toreturn[this.dangerousArmies[i]] = this.armies[this.dangerousArmies[i]]; return toreturn; }; -enemyWatcher.prototype.getSafeArmies = function() { +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; }; -enemyWatcher.prototype.resetDangerousArmies = function() { +m.enemyWatcher.prototype.resetDangerousArmies = function() { this.dangerousArmies = []; }; -enemyWatcher.prototype.setAsDangerous = function(armyID) { +m.enemyWatcher.prototype.setAsDangerous = function(armyID) { if (this.dangerousArmies.indexOf(armyID) === -1) this.dangerousArmies.push(armyID); }; -enemyWatcher.prototype.isDangerous = function(armyID) { +m.enemyWatcher.prototype.isDangerous = function(armyID) { if (this.dangerousArmies.indexOf(armyID) === -1) return false; return true; }; // returns [id, army] -enemyWatcher.prototype.getArmyFromMember = function(memberID) { +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; }; -enemyWatcher.prototype.isPartOfDangerousArmy = function(memberID) { +m.enemyWatcher.prototype.isPartOfDangerousArmy = function(memberID) { var armyID = this.getArmyFromMember(memberID)[0]; if (this.isDangerous(armyID)) return true; return false; }; -enemyWatcher.prototype.cleanDebug = function() { +m.enemyWatcher.prototype.cleanDebug = function() { for (var armyID in this.armies) { var army = this.armies[armyID]; - debug ("Army " +armyID); - debug (army.length +" members, centered around " +army.getCentrePosition()); + m.debug ("Army " +armyID); + m.debug (army.length +" members, centered around " +army.getCentrePosition()); } } // this will monitor any unmonitored soldier. -enemyWatcher.prototype.detectArmies = function(gameState){ +m.enemyWatcher.prototype.detectArmies = function(gameState){ //this.cleanDebug(); var self = this; @@ -121,7 +124,7 @@ enemyWatcher.prototype.detectArmies = function(gameState){ var armyID = gameState.player + "" + self.totalNBofArmies; self.totalNBofArmies++, enemy.setMetadata(PlayerID, "EnemyWatcherArmy",armyID); - var filter = Filters.byMetadata(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(); @@ -140,7 +143,7 @@ enemyWatcher.prototype.detectArmies = function(gameState){ }; // 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 -enemyWatcher.prototype.mergeArmies = function(){ +m.enemyWatcher.prototype.mergeArmies = function(){ for (var army in this.armies) { var firstArmy = this.armies[army]; if (firstArmy.length !== 0) { @@ -149,7 +152,7 @@ enemyWatcher.prototype.mergeArmies = function(){ 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 (inRange(firstAPos,secondArmy.getApproximatePosition(4), 4000 ) ) { + 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. @@ -166,7 +169,7 @@ enemyWatcher.prototype.mergeArmies = function(){ } this.ScrapEmptyArmies(); }; -enemyWatcher.prototype.ScrapEmptyArmies = function(){ +m.enemyWatcher.prototype.ScrapEmptyArmies = function(){ var removelist = []; for (var army in this.armies) { if (this.armies[army].length === 0) { @@ -181,10 +184,10 @@ enemyWatcher.prototype.ScrapEmptyArmies = function(){ } }; // splits any unit too far from the centerposition -enemyWatcher.prototype.splitArmies = function(gameState){ +m.enemyWatcher.prototype.splitArmies = function(gameState){ var self = this; - var map = Map.createTerritoryMap(gameState); + var map = m.createTerritoryMap(gameState); for (var armyID in this.armies) { var army = this.armies[armyID]; @@ -197,14 +200,14 @@ enemyWatcher.prototype.splitArmies = function(gameState){ if (enemy.position() === undefined) return; - if (!inRange(enemy.position(),centre, 3500) ) { + 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 = Filters.byMetadata(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(); @@ -213,3 +216,7 @@ enemyWatcher.prototype.splitArmies = function(gameState){ }); } }; + + +return m; +}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/aegis/entity-extend.js b/binaries/data/mods/public/simulation/ai/aegis/entity-extend.js index 382b32b869..90773f38fe 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/entity-extend.js +++ b/binaries/data/mods/public/simulation/ai/aegis/entity-extend.js @@ -1,5 +1,8 @@ +var AEGIS = function(m) +{ + // returns some sort of DPS * health factor. If you specify a class, it'll use the modifiers against that class too. -function getMaxStrength(ent, againstClass) +m.getMaxStrength = function(ent, againstClass) { var strength = 0.0; var attackTypes = ent.attackTypes(); @@ -61,3 +64,6 @@ function getMaxStrength(ent, againstClass) } return strength * hp; }; + +return m; +}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/aegis/entitycollection-extend.js b/binaries/data/mods/public/simulation/ai/aegis/entitycollection-extend.js index 9aff32f42c..065bf83c0b 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/entitycollection-extend.js +++ b/binaries/data/mods/public/simulation/ai/aegis/entitycollection-extend.js @@ -1,4 +1,7 @@ -function EntityCollectionFromIds(gameState, idList){ +var AEGIS = function(m) +{ + +m.EntityCollectionFromIds = function(gameState, idList){ var ents = {}; for (var i in idList){ var id = idList[i]; @@ -6,5 +9,8 @@ function EntityCollectionFromIds(gameState, idList){ ents[id] = gameState.entities._entities[id]; } } - return new EntityCollection(gameState.sharedScript, ents); + return new API3.EntityCollection(gameState.sharedScript, ents); } + +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 f8bc055711..b948ed6ca2 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/headquarters.js +++ b/binaries/data/mods/public/simulation/ai/aegis/headquarters.js @@ -1,3 +1,5 @@ +var AEGIS = function(m) +{ /* Headquarters * Deal with high level logic for the AI. Most of the interesting stuff gets done here. * Some tasks: @@ -12,11 +14,13 @@ -picking new CC locations. */ -var HQ = function() { - this.targetNumBuilders = Config.Economy.targetNumBuilders; // number of workers we want building stuff +m.HQ = function(Config) { - this.dockStartTime = Config.Economy.dockStartTime * 1000; - this.techStartTime = Config.Economy.techStartTime * 1000; + this.Config = Config; + this.targetNumBuilders = this.Config.Economy.targetNumBuilders; // number of workers we want building stuff + + this.dockStartTime = this.Config.Economy.dockStartTime * 1000; + this.techStartTime = this.Config.Economy.techStartTime * 1000; this.dockFailed = false; // sanity check this.waterMap = false; // set by the aegis.js file. @@ -27,15 +31,15 @@ var HQ = function() { this.baseManagers = {}; // this means we'll have about a big third of women, and thus we can maximize resource gathering rates. - this.femaleRatio = Config.Economy.femaleRatio; + this.femaleRatio = this.Config.Economy.femaleRatio; this.fortressStartTime = 0; - this.fortressLapseTime = Config.Military.fortressLapseTime * 1000; - this.defenceBuildingTime = Config.Military.defenceBuildingTime * 1000; - this.attackPlansStartTime = Config.Military.attackPlansStartTime * 1000; - this.defenceManager = new Defence(); + this.fortressLapseTime = this.Config.Military.fortressLapseTime * 1000; + this.defenceBuildingTime = this.Config.Military.defenceBuildingTime * 1000; + this.attackPlansStartTime = this.Config.Military.attackPlansStartTime * 1000; + this.defenceManager = new m.Defence(this.Config); - this.navalManager = new NavalManager(); + this.navalManager = new m.NavalManager(); this.TotalAttackNumber = 0; this.upcomingAttacks = { "CityAttack" : [] }; @@ -43,49 +47,49 @@ var HQ = function() { }; // More initialisation for stuff that needs the gameState -HQ.prototype.init = function(gameState, events, queues){ +m.HQ.prototype.init = function(gameState, events, queues){ // initialize base map. Each pixel is a base ID, or 0 if none - this.basesMap = new Map(gameState.sharedScript, new Uint8Array(gameState.getMap().data.length)); + this.basesMap = new API3.Map(gameState.sharedScript, new Uint8Array(gameState.getMap().data.length)); this.basesMap.setMaxVal(255); - if (Config.Economy.targetNumWorkers) - this.targetNumWorkers = Config.Economy.targetNumWorkers; + if (this.Config.Economy.targetNumWorkers) + this.targetNumWorkers = this.Config.Economy.targetNumWorkers; else if (this.targetNumWorkers === undefined) - this.targetNumWorkers = Math.max(Math.floor(gameState.getPopulationMax()*(0.2 + Math.min(+(Config.difficulty)*0.125,0.3))), 1); + this.targetNumWorkers = Math.max(Math.floor(gameState.getPopulationMax()*(0.2 + Math.min(+(this.Config.difficulty)*0.125,0.3))), 1); // Let's get our initial situation here. // TODO: improve on this. // TODO: aknowledge bases, assign workers already. - var ents = gameState.getEntities().filter(Filters.byOwner(PlayerID)); + var ents = gameState.getEntities().filter(API3.Filters.byOwner(PlayerID)); var workersNB = 0; var hasScout = false; var treasureAmount = { 'food': 0, 'wood': 0, 'stone': 0, 'metal': 0 }; var hasCC = false; - if (ents.filter(Filters.byClass("CivCentre")).length > 0) + if (ents.filter(API3.Filters.byClass("CivCentre")).length > 0) hasCC = true; - workersNB = ents.filter(Filters.byClass("Worker")).length; - if (ents.filter(Filters.byClass("Cavalry")).length > 0) + workersNB = ents.filter(API3.Filters.byClass("Worker")).length; + if (ents.filter(API3.Filters.byClass("Cavalry")).length > 0) hasScout = true; // tODO: take multiple CCs into account. if (hasCC) { - var CC = ents.filter(Filters.byClass("CivCentre")).toEntityArray()[0]; + var CC = ents.filter(API3.Filters.byClass("CivCentre")).toEntityArray()[0]; for (i in treasureAmount) gameState.getResourceSupplies(i).forEach( function (ent) { - if (ent.resourceSupplyType().generic === "treasure" && SquareVectorDistance(ent.position(), CC.position()) < 5000) + if (ent.resourceSupplyType().generic === "treasure" && API3.SquareVectorDistance(ent.position(), CC.position()) < 5000) treasureAmount[i] += ent.resourceSupplyMax(); }); - this.baseManagers[1] = new BaseManager(); + this.baseManagers[1] = new m.BaseManager(this.Config); this.baseManagers[1].init(gameState, events); this.baseManagers[1].setAnchor(CC); this.baseManagers[1].initTerritory(this, gameState); this.baseManagers[1].initGatheringFunctions(this, gameState); - if (Config.debug) + if (m.DebugEnabled) this.basesMap.dumpIm("basesMap.png"); var self = this; @@ -105,14 +109,14 @@ HQ.prototype.init = function(gameState, events, queues){ var pos = this.baseManagers[1].findBestDropsiteLocation(gameState, "wood"); if (pos) { - queues.dropsites.addItem(new ConstructionPlan(gameState, "structures/{civ}_storehouse",{ "base" : 1 }, 0, -1, pos)); - queues.minorTech.addItem(new ResearchPlan(gameState, "gather_capacity_wheelbarrow")); + queues.dropsites.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_storehouse",{ "base" : 1 }, 0, -1, pos)); + queues.minorTech.addItem(new m.ResearchPlan(gameState, "gather_capacity_wheelbarrow")); } } } - var map = new Map(gameState.sharedScript, gameState.sharedScript.CCResourceMaps["wood"].map); - if (Config.debug) + var map = new API3.Map(gameState.sharedScript, gameState.sharedScript.CCResourceMaps["wood"].map); + if (m.DebugEnabled) map.dumpIm("map_CC_Wood.png"); //this.reassignIdleWorkers(gameState); @@ -125,22 +129,22 @@ HQ.prototype.init = function(gameState, events, queues){ // load units and buildings from the config files - if (civ in Config.buildings.moderate){ - this.bModerate = Config.buildings.moderate[civ]; + if (civ in this.Config.buildings.moderate){ + this.bModerate = this.Config.buildings.moderate[civ]; }else{ - this.bModerate = Config.buildings.moderate['default']; + this.bModerate = this.Config.buildings.moderate['default']; } - if (civ in Config.buildings.advanced){ - this.bAdvanced = Config.buildings.advanced[civ]; + if (civ in this.Config.buildings.advanced){ + this.bAdvanced = this.Config.buildings.advanced[civ]; }else{ - this.bAdvanced = Config.buildings.advanced['default']; + this.bAdvanced = this.Config.buildings.advanced['default']; } - if (civ in Config.buildings.fort){ - this.bFort = Config.buildings.fort[civ]; + if (civ in this.Config.buildings.fort){ + this.bFort = this.Config.buildings.fort[civ]; }else{ - this.bFort = Config.buildings.fort['default']; + this.bFort = this.Config.buildings.fort['default']; } for (var i in this.bAdvanced){ @@ -156,7 +160,7 @@ HQ.prototype.init = function(gameState, events, queues){ } var enemies = gameState.getEnemyEntities(); - var filter = Filters.byClassesOr(["CitizenSoldier", "Champion", "Hero", "Siege"]); + var filter = API3.Filters.byClassesOr(["CitizenSoldier", "Champion", "Hero", "Siege"]); this.enemySoldiers = enemies.filter(filter); // TODO: cope with diplomacy changes this.enemySoldiers.registerUpdates(); @@ -167,13 +171,13 @@ HQ.prototype.init = function(gameState, events, queues){ this.ennWatcherIndex = []; for (var i = 1; i <= 8; i++) if (PlayerID != i && gameState.isPlayerEnemy(i)) { - this.enemyWatchers[i] = new enemyWatcher(gameState, i); + this.enemyWatchers[i] = new m.enemyWatcher(gameState, i); this.ennWatcherIndex.push(i); this.defenceManager.enemyArmy[i] = []; } }; -HQ.prototype.checkEvents = function (gameState, events, queues) { +m.HQ.prototype.checkEvents = function (gameState, events, queues) { for (i in events) { if (events[i].type == "Destroy") @@ -193,8 +197,8 @@ HQ.prototype.checkEvents = function (gameState, events, queues) { if (ent.isOwn(PlayerID) && ent.getMetadata(PlayerID, "base") === -1) { // Okay so let's try to create a new base around this. - var bID = uniqueIDBases; - this.baseManagers[bID] = new BaseManager(); + var bID = m.playerGlobals[PlayerID].uniqueIDBases; + this.baseManagers[bID] = new m.BaseManager(this.Config); this.baseManagers[bID].init(gameState, events, true); this.baseManagers[bID].setAnchor(ent); this.baseManagers[bID].initTerritory(this, gameState); @@ -243,7 +247,7 @@ 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. -HQ.prototype.trainMoreWorkers = function(gameState, queues) { +m.HQ.prototype.trainMoreWorkers = function(gameState, queues) { // 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"); @@ -293,14 +297,14 @@ HQ.prototype.trainMoreWorkers = function(gameState, queues) { // base "0" means "auto" if (template === gameState.applyCiv("units/{civ}_support_female_citizen")) - queues.villager.addItem(new TrainingPlan(gameState, template, { "role" : "worker", "base" : 0 }, size, 0, -1, size )); + queues.villager.addItem(new m.TrainingPlan(gameState, template, { "role" : "worker", "base" : 0 }, size, 0, -1, size )); else - queues.citizenSoldier.addItem(new TrainingPlan(gameState, template, { "role" : "worker", "base" : 0 }, size, 0, -1, size)); + queues.citizenSoldier.addItem(new m.TrainingPlan(gameState, template, { "role" : "worker", "base" : 0 }, size, 0, -1, size)); } }; // picks the best template based on parameters and classes -HQ.prototype.findBestTrainableUnit = function(gameState, classes, parameters) { +m.HQ.prototype.findBestTrainableUnit = function(gameState, classes, parameters) { var units = gameState.findTrainableUnits(classes); if (units.length === 0) @@ -317,8 +321,8 @@ HQ.prototype.findBestTrainableUnit = function(gameState, classes, parameters) { bTopParam = param[1]; } if (param[0] == "strength") { - aTopParam += getMaxStrength(a[1]) * param[1]; - bTopParam += getMaxStrength(b[1]) * param[1]; + aTopParam += m.getMaxStrength(a[1]) * param[1]; + bTopParam += m.getMaxStrength(b[1]) * param[1]; } if (param[0] == "speed") { aTopParam += a[1].walkSpeed() * param[1]; @@ -343,7 +347,7 @@ HQ.prototype.findBestTrainableUnit = function(gameState, classes, parameters) { }; // picks the best template based on parameters and classes -HQ.prototype.findBestTrainableSoldier = function(gameState, classes, parameters) { +m.HQ.prototype.findBestTrainableSoldier = function(gameState, classes, parameters) { var units = gameState.findTrainableUnits(classes); if (units.length === 0) @@ -361,12 +365,12 @@ HQ.prototype.findBestTrainableSoldier = function(gameState, classes, parameters) bTopParam = param[1]; } if (param[0] == "strength") { - aTopParam += getMaxStrength(a[1]) * param[1]; - bTopParam += getMaxStrength(b[1]) * param[1]; + aTopParam += m.getMaxStrength(a[1]) * param[1]; + bTopParam += m.getMaxStrength(b[1]) * param[1]; } if (param[0] == "siegeStrength") { - aTopParam += getMaxStrength(a[1], "Structure") * param[1]; - bTopParam += getMaxStrength(b[1], "Structure") * param[1]; + aTopParam += m.getMaxStrength(a[1], "Structure") * param[1]; + bTopParam += m.getMaxStrength(b[1], "Structure") * param[1]; } if (param[0] == "speed") { aTopParam += a[1].walkSpeed() * param[1]; @@ -394,7 +398,7 @@ HQ.prototype.findBestTrainableSoldier = function(gameState, classes, parameters) // Tries to research any available tech // Only one at once. Also does military tech (selection is completely random atm) // TODO: Lots, lots, lots here. -HQ.prototype.tryResearchTechs = function(gameState, queues) { +m.HQ.prototype.tryResearchTechs = function(gameState, queues) { if (queues.minorTech.length() === 0) { var possibilities = gameState.findAvailableTech(); @@ -402,15 +406,16 @@ HQ.prototype.tryResearchTechs = function(gameState, queues) { return; // randomly pick one. No worries about pairs in that case. var p = Math.floor((Math.random()*possibilities.length)); - queues.minorTech.addItem(new ResearchPlan(gameState, possibilities[p][0])); + queues.minorTech.addItem(new m.ResearchPlan(gameState, possibilities[p][0])); } } // We're given a worker and a resource type // We'll assign the worker for the best base for that resource type. // TODO: improve choice alogrithm -HQ.prototype.switchWorkerBase = function(gameState, worker, type) { +m.HQ.prototype.switchWorkerBase = function(gameState, worker, type) { var bestBase = 0; + for (var i in this.baseManagers) { if (this.baseManagers[i].willGather[type] >= 1) @@ -435,12 +440,12 @@ HQ.prototype.switchWorkerBase = function(gameState, worker, type) { // returns an entity collection of workers through BaseManager.pickBuilders // TODO: better the choice algo. // TODO: also can't get over multiple bases right now. -HQ.prototype.bulkPickWorkers = function(gameState, newBaseID, number) { +m.HQ.prototype.bulkPickWorkers = function(gameState, newBaseID, number) { var accessIndex = this.baseManagers[newBaseID].accessIndex; if (!accessIndex) return false; // sorting bases by whether they are on the same accessindex or not. - var baseBest = AssocArraytoArray(this.baseManagers).sort(function (a,b) { + var baseBest = m.AssocArraytoArray(this.baseManagers).sort(function (a,b) { if (a.accessIndex === accessIndex && b.accessIndex !== accessIndex) return -1; else if (b.accessIndex === accessIndex && a.accessIndex !== accessIndex) @@ -459,7 +464,7 @@ HQ.prototype.bulkPickWorkers = function(gameState, newBaseID, number) { // returns the current gather rate // This is not per-se exact, it performs a few adjustments ad-hoc to account for travel distance, stuffs like that. -HQ.prototype.GetCurrentGatherRates = function(gameState) { +m.HQ.prototype.GetCurrentGatherRates = function(gameState) { var self = this; var currentRates = {}; @@ -474,7 +479,7 @@ HQ.prototype.GetCurrentGatherRates = function(gameState) { // Pick the resource which most needs another worker -HQ.prototype.pickMostNeededResources = function(gameState) { +m.HQ.prototype.pickMostNeededResources = function(gameState) { var self = this; this.wantedRates = gameState.ai.queueManager.wantedGatherRates(gameState); @@ -517,7 +522,7 @@ HQ.prototype.pickMostNeededResources = function(gameState) { // If all the CC's are destroyed then build a new one // TODO: rehabilitate. -HQ.prototype.buildNewCC= function(gameState, queues) { +m.HQ.prototype.buildNewCC= function(gameState, queues) { var numCCs = gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_civil_centre")); numCCs += queues.civilCentre.length(); @@ -529,14 +534,14 @@ HQ.prototype.buildNewCC= function(gameState, queues) { this.baseNeed["wood"] = 50; this.baseNeed["stone"] = 50; this.baseNeed["metal"] = 50; - queues.civilCentre.addItem(new ConstructionPlan(gameState, "structures/{civ}_civil_centre")); + queues.civilCentre.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_civil_centre")); } return (gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_civil_centre"), true) == 0 && gameState.currentPhase() > 1); }; // Returns the best position to build a new Civil Centre // Whose primary function would be to reach new resources of type "resource". -HQ.prototype.findBestEcoCCLocation = function(gameState, resource){ +m.HQ.prototype.findBestEcoCCLocation = function(gameState, resource){ var CCPlate = gameState.getTemplate("structures/{civ}_civil_centre"); @@ -545,16 +550,16 @@ HQ.prototype.findBestEcoCCLocation = function(gameState, resource){ // Then checks for a good spot in the territory. If none, and town/city phase, checks outside // The AI will currently not build a CC if it wouldn't connect with an existing CC. - var territory = Map.createTerritoryMap(gameState); + var territory = m.createTerritoryMap(gameState); - var obstructions = Map.createObstructionMap(gameState, 0); + var obstructions = m.createObstructionMap(gameState, 0); obstructions.expandInfluences(); // copy the resource map as initialization. - var friendlyTiles = new Map(gameState.sharedScript, gameState.sharedScript.CCResourceMaps[resource].map, true); + var friendlyTiles = new API3.Map(gameState.sharedScript, gameState.sharedScript.CCResourceMaps[resource].map, true); friendlyTiles.setMaxVal(255); - var ents = gameState.getOwnEntities().filter(Filters.byClass("CivCentre")).toEntityArray(); - var eEnts = gameState.getEnemyEntities().filter(Filters.byClass("CivCentre")).toEntityArray(); + var ents = gameState.getOwnEntities().filter(API3.Filters.byClass("CivCentre")).toEntityArray(); + var eEnts = gameState.getEnemyEntities().filter(API3.Filters.byClass("CivCentre")).toEntityArray(); var dps = gameState.getOwnDropsites().toEntityArray(); @@ -576,7 +581,7 @@ HQ.prototype.findBestEcoCCLocation = function(gameState, resource){ var entPos = ents[i].position(); entPos = [entPos[0]/4.0,entPos[1]/4.0]; - var dist = SquareVectorDistance(entPos, pos); + var dist = API3.SquareVectorDistance(entPos, pos); if (dist < 2120) { canBuild = false; @@ -598,7 +603,7 @@ HQ.prototype.findBestEcoCCLocation = function(gameState, resource){ var entPos = eEnts[i].position(); entPos = [entPos[0]/4.0,entPos[1]/4.0]; // 7100 works well as a limit. - if (SquareVectorDistance(entPos, pos) < 2500) + if (API3.SquareVectorDistance(entPos, pos) < 2500) { canBuild = false; continue; @@ -620,11 +625,11 @@ HQ.prototype.findBestEcoCCLocation = function(gameState, resource){ continue; } dpPos = [dpPos[0]/4.0,dpPos[1]/4.0]; - if (SquareVectorDistance(dpPos, pos) < 100) + if (API3.SquareVectorDistance(dpPos, pos) < 100) { friendlyTiles.map[j] = 0; continue; - } else if (SquareVectorDistance(dpPos, pos) < 400) + } else if (API3.SquareVectorDistance(dpPos, pos) < 400) friendlyTiles.map[j] /= 2; } @@ -645,7 +650,7 @@ HQ.prototype.findBestEcoCCLocation = function(gameState, resource){ var best = friendlyTiles.findBestTile(6, obstructions); var bestIdx = best[0]; - if (Config.debug) + if (m.DebugEnabled) { friendlyTiles.map[bestIdx] = 270; friendlyTiles.dumpIm("cc_placement_base_" + gameState.getTimeElapsed() + "_" + resource + "_" + best[1] + ".png",301); @@ -659,44 +664,44 @@ HQ.prototype.findBestEcoCCLocation = function(gameState, resource){ var x = ((bestIdx % friendlyTiles.width) + 0.5) * gameState.cellSize; var z = (Math.floor(bestIdx / friendlyTiles.width) + 0.5) * gameState.cellSize; - debug ("Best for value " + best[1] + " at " + uneval([x,z])); + m.debug ("Best for value " + best[1] + " at " + uneval([x,z])); return [x,z]; }; -HQ.prototype.buildTemple = function(gameState, queues){ +m.HQ.prototype.buildTemple = function(gameState, queues){ if (gameState.currentPhase() >= 2 ) { if (queues.economicBuilding.countQueuedUnits() === 0 && gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_temple")) === 0){ - queues.economicBuilding.addItem(new ConstructionPlan(gameState, "structures/{civ}_temple", { "base" : 1 })); + queues.economicBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_temple", { "base" : 1 })); } } }; -HQ.prototype.buildMarket = function(gameState, queues){ - if (gameState.getPopulation() > Config.Economy.popForMarket && gameState.currentPhase() >= 2 ) { +m.HQ.prototype.buildMarket = function(gameState, queues){ + if (gameState.getPopulation() > this.Config.Economy.popForMarket && gameState.currentPhase() >= 2 ) { if (queues.economicBuilding.countQueuedUnitsWithClass("BarterMarket") === 0 && gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_market")) === 0){ //only ever build one storehouse/CC/market at a time - queues.economicBuilding.addItem(new ConstructionPlan(gameState, "structures/{civ}_market", { "base" : 1 })); + queues.economicBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_market", { "base" : 1 })); } } }; // Build a farmstead to go to town phase faster and prepare for research. Only really active on higher diff mode. -HQ.prototype.buildFarmstead = function(gameState, queues){ - if (gameState.getPopulation() > Config.Economy.popForFarmstead) { +m.HQ.prototype.buildFarmstead = function(gameState, queues){ + if (gameState.getPopulation() > this.Config.Economy.popForFarmstead) { // achtung: "DropsiteFood" does not refer to CCs. if (queues.economicBuilding.countQueuedUnitsWithClass("DropsiteFood") === 0 && gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_farmstead")) === 0){ //only ever build one storehouse/CC/market at a time - queues.economicBuilding.addItem(new ConstructionPlan(gameState, "structures/{civ}_farmstead", { "base" : 1 })); + queues.economicBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_farmstead", { "base" : 1 })); } } }; // TODO: generic this, probably per-base -HQ.prototype.buildDock = function(gameState, queues){ +m.HQ.prototype.buildDock = function(gameState, queues){ if (!this.waterMap || this.dockFailed) return; if (gameState.getTimeElapsed() > this.dockStartTime) { @@ -710,7 +715,7 @@ HQ.prototype.buildDock = function(gameState, queues){ if (tp !== "") { var remaining = this.navalManager.getUnconnectedSeas(gameState, this.baseManagers[1].accessIndex); - queues.economicBuilding.addItem(new ConstructionPlan(gameState, tp, { "base" : 1, "sea" : remaining[0] })); + queues.economicBuilding.addItem(new m.ConstructionPlan(gameState, tp, { "base" : 1, "sea" : remaining[0] })); } } } @@ -719,7 +724,7 @@ HQ.prototype.buildDock = function(gameState, queues){ // if Aegis has resources it doesn't need, it'll try to barter it for resources it needs // once per turn because the info doesn't update between a turn and I don't want to fix it. // Not sure how efficient it is but it seems to be sane, at least. -HQ.prototype.tryBartering = function(gameState){ +m.HQ.prototype.tryBartering = function(gameState){ var done = false; if (gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_market"), true) >= 1) { @@ -732,7 +737,7 @@ HQ.prototype.tryBartering = function(gameState){ if ( (ress[buy] < 400) || needs[buy] > 0) { // if we need that other resource/ have too little of it var markets = gameState.getOwnEntitiesByType(gameState.applyCiv("structures/{civ}_market"), true).toEntityArray(); markets[0].barter(buy,sell,100); - //debug ("bartered " +sell +" for " + buy + ", value 100"); + //m.debug ("bartered " +sell +" for " + buy + ", value 100"); done = true; } } @@ -743,7 +748,7 @@ HQ.prototype.tryBartering = function(gameState){ // build more houses if needed. // kinda ugly, lots of special cases to both build enough houses but not tooo many… -HQ.prototype.buildMoreHouses = function(gameState,queues) { +m.HQ.prototype.buildMoreHouses = function(gameState,queues) { if (gameState.getPopulationLimit() < gameState.getPopulationMax()) { @@ -751,7 +756,9 @@ HQ.prototype.buildMoreHouses = function(gameState,queues) { if (numPlanned < 3 || (numPlanned < 5 && gameState.getPopulation() > 80)) { - var plan = new ConstructionPlan(gameState, "structures/{civ}_house", { "base" : 1 }); + var plan = new m.ConstructionPlan(gameState, "structures/{civ}_house", { "base" : 1 }); + // make the difficulty available to the isGo function without having to pass it as argument + var difficulty = this.Config.difficulty; // change the starting condition to "less than 15 slots left". plan.isGo = function (gameState) { var HouseNb = gameState.countEntitiesByType(gameState.applyCiv("foundation|structures/{civ}_house"), true); @@ -761,9 +768,9 @@ HQ.prototype.buildMoreHouses = function(gameState,queues) { freeSlots = gameState.getPopulationLimit() + HouseNb*5 - gameState.getPopulation(); else freeSlots = gameState.getPopulationLimit() + HouseNb*10 - gameState.getPopulation(); - if (gameState.getPopulation() > 55 && Config.difficulty > 1) + if (gameState.getPopulation() > 55 && difficulty > 1) return (freeSlots <= 21); - else if (gameState.getPopulation() >= 20 && Config.difficulty !== 0) + else if (gameState.getPopulation() >= 20 && difficulty !== 0) return (freeSlots <= 16); else return (freeSlots <= 10); @@ -774,19 +781,20 @@ HQ.prototype.buildMoreHouses = function(gameState,queues) { }; // checks if we have bases for all resource types (bar food for now) or if we need to expand. -HQ.prototype.checkBasesRessLevel = function(gameState,queues) { +m.HQ.prototype.checkBasesRessLevel = function(gameState,queues) { if (gameState.currentPhase() === 1 && !gameState.isResearching(gameState.townPhase())) return; var count = { "wood" : 0, "stone" : 0, "metal" : 0 } var capacity = { "wood" : 0, "stone" : 0, "metal" : 0 } var need = { "wood" : true, "stone" : true, "metal" : true }; var posss = []; + for (i in this.baseManagers) { var base = this.baseManagers[i]; for (type in count) { - if (base.getResourceLevel(gameState, type, "all") > 1500*Math.max(Config.difficulty,2)) + if (base.getResourceLevel(gameState, type, "all") > 1500*Math.max(this.Config.difficulty,2)) count[type]++; capacity[type] += base.getWorkerCapacity(gameState, type); if (base.willGather[type] !== 2) @@ -796,7 +804,7 @@ HQ.prototype.checkBasesRessLevel = function(gameState,queues) { for (type in count) { if (count[type] === 0 || need[type] - || capacity[type] < gameState.getOwnEntities().filter(Filters.and(Filters.byMetadata(PlayerID, "subrole", "gatherer"), Filters.byMetadata(PlayerID, "gather-type", type))).length * 1.05) + || capacity[type] < gameState.getOwnEntities().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) { @@ -809,7 +817,7 @@ HQ.prototype.checkBasesRessLevel = function(gameState,queues) { this.outOf[type] = true; } else { // base "-1" means new base. - queues.civilCentre.addItem(new ConstructionPlan(gameState, "structures/{civ}_civil_centre",{ "base" : -1 }, 0, -1, pos)); + queues.civilCentre.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_civil_centre",{ "base" : -1 }, 0, -1, pos)); } } } @@ -819,9 +827,9 @@ HQ.prototype.checkBasesRessLevel = function(gameState,queues) { // Deals with building fortresses and towers. // Currently build towers next to every useful dropsites. // TODO: Fortresses are placed randomly atm. -HQ.prototype.buildDefences = function(gameState, queues){ +m.HQ.prototype.buildDefences = function(gameState, queues){ - var workersNumber = gameState.getOwnEntitiesByRole("worker").filter(Filters.not(Filters.byHasMetadata(PlayerID,"plan"))).length; + var workersNumber = gameState.getOwnEntitiesByRole("worker").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) { @@ -836,7 +844,7 @@ HQ.prototype.buildDefences = function(gameState, queues){ { var position = dpEnt.position(); if (position) { - queues.defenceBuilding.addItem(new ConstructionPlan(gameState, 'structures/{civ}_defense_tower', { "base" : i }, 0 , -1, position)); + queues.defenceBuilding.addItem(new m.ConstructionPlan(gameState, 'structures/{civ}_defense_tower', { "base" : i }, 0 , -1, position)); } dpEnt.setMetadata(PlayerID, "defenseTower", true); } @@ -855,8 +863,8 @@ HQ.prototype.buildDefences = function(gameState, queues){ { if (!this.fortressStartTime) this.fortressStartTime = gameState.getTimeElapsed(); - queues.defenceBuilding.addItem(new ConstructionPlan(gameState, this.bFort[0], { "base" : 1 })); - debug ("Building a fortress"); + queues.defenceBuilding.addItem(new m.ConstructionPlan(gameState, this.bFort[0], { "base" : 1 })); + m.debug ("Building a fortress"); } } if (gameState.countEntitiesByType(gameState.applyCiv(this.bFort[i]), true) >= 1) { @@ -877,13 +885,13 @@ HQ.prototype.buildDefences = function(gameState, queues){ } }; -HQ.prototype.buildBlacksmith = function(gameState, queues){ - if (gameState.getTimeElapsed() > Config.Military.timeForBlacksmith*1000) { +m.HQ.prototype.buildBlacksmith = function(gameState, queues){ + if (gameState.getTimeElapsed() > this.Config.Military.timeForBlacksmith*1000) { if (queues.militaryBuilding.length() === 0 && gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_blacksmith")) === 0) { var tp = gameState.getTemplate(gameState.applyCiv("structures/{civ}_blacksmith")); if (tp.available(gameState)) - queues.militaryBuilding.addItem(new ConstructionPlan(gameState, "structures/{civ}_blacksmith", { "base" : 1 })); + queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_blacksmith", { "base" : 1 })); } } }; @@ -892,32 +900,32 @@ HQ.prototype.buildBlacksmith = function(gameState, queues){ // They are mostly defined by Config.js. This is unreliable since changes could be done easily. // TODO: We need to determine these dynamically. Also doesn't build fortresses since the above function does that. // TODO: building placement is bad. Choice of buildings is also fairly dumb. -HQ.prototype.constructTrainingBuildings = function(gameState, queues) { +m.HQ.prototype.constructTrainingBuildings = function(gameState, queues) { Engine.ProfileStart("Build buildings"); - var workersNumber = gameState.getOwnEntitiesByRole("worker").filter(Filters.not(Filters.byHasMetadata(PlayerID, "plan"))).length; + var workersNumber = gameState.getOwnEntitiesByRole("worker").filter(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID, "plan"))).length; - if (workersNumber > Config.Military.popForBarracks1) { + if (workersNumber > this.Config.Military.popForBarracks1) { if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0])) + queues.militaryBuilding.length() < 1) { - debug ("Trying to build barracks"); - queues.militaryBuilding.addItem(new ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 })); + m.debug ("Trying to build barracks"); + queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 })); } } - if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0])) < 2 && workersNumber > Config.Military.popForBarracks2) + if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0])) < 2 && workersNumber > this.Config.Military.popForBarracks2) if (queues.militaryBuilding.length() < 1) - queues.militaryBuilding.addItem(new ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 })); + queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 })); if (gameState.countEntitiesByType(gameState.applyCiv(this.bModerate[0]), true) === 2 && gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0])) < 3 && workersNumber > 125) if (queues.militaryBuilding.length() < 1) { - queues.militaryBuilding.addItem(new ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 })); + queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 })); if (gameState.civ() == "gaul" || gameState.civ() == "brit" || gameState.civ() == "iber") { - queues.militaryBuilding.addItem(new ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 })); - queues.militaryBuilding.addItem(new ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 })); + queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 })); + queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 })); } } //build advanced military buildings - if (workersNumber >= Config.Military.popForBarracks2 - 15 && gameState.currentPhase() > 2){ + if (workersNumber >= this.Config.Military.popForBarracks2 - 15 && gameState.currentPhase() > 2){ if (queues.militaryBuilding.length() === 0){ var inConst = 0; for (var i in this.bAdvanced) @@ -925,7 +933,7 @@ HQ.prototype.constructTrainingBuildings = function(gameState, queues) { 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){ - queues.militaryBuilding.addItem(new ConstructionPlan(gameState, this.bAdvanced[i], { "base" : 1 })); + queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bAdvanced[i], { "base" : 1 })); } } } @@ -939,8 +947,8 @@ HQ.prototype.constructTrainingBuildings = function(gameState, queues) { if (inConst == 1) { var i = Math.floor(Math.random() * this.bAdvanced.length); if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bAdvanced[i])) < 1){ - queues.militaryBuilding.addItem(new ConstructionPlan(gameState, this.bAdvanced[i], { "base" : 1 })); - queues.militaryBuilding.addItem(new ConstructionPlan(gameState, this.bAdvanced[i], { "base" : 1 })); + queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bAdvanced[i], { "base" : 1 })); + queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bAdvanced[i], { "base" : 1 })); } } } @@ -949,9 +957,9 @@ HQ.prototype.constructTrainingBuildings = function(gameState, queues) { }; // TODO: use pop(). Currently unused as this is too gameable. -HQ.prototype.garrisonAllFemales = function(gameState) { - var buildings = gameState.getOwnEntities().filter(Filters.byCanGarrison()).toEntityArray(); - var females = gameState.getOwnEntities().filter(Filters.byClass("Support")); +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 cache = {}; @@ -974,16 +982,16 @@ HQ.prototype.garrisonAllFemales = function(gameState) { }); this.hasGarrisonedFemales = true; }; -HQ.prototype.ungarrisonAll = function(gameState) { +m.HQ.prototype.ungarrisonAll = function(gameState) { this.hasGarrisonedFemales = false; - var buildings = gameState.getOwnEntities().filter(Filters.and(Filters.byClass("Structure"),Filters.byCanGarrison())).toEntityArray(); + var buildings = gameState.getOwnEntities().filter(API3.Filters.and(API3.Filters.byClass("Structure"),API3.Filters.byCanGarrison())).toEntityArray(); buildings.forEach( function (struct) { if (struct.garrisoned() && struct.garrisoned().length) struct.unloadAll(); }); }; -HQ.prototype.pausePlan = function(gameState, planName) { +m.HQ.prototype.pausePlan = function(gameState, planName) { for (var attackType in this.upcomingAttacks) { for (var i in this.upcomingAttacks[attackType]) { var attack = this.upcomingAttacks[attackType][i]; @@ -999,7 +1007,7 @@ HQ.prototype.pausePlan = function(gameState, planName) { } } } -HQ.prototype.unpausePlan = function(gameState, planName) { +m.HQ.prototype.unpausePlan = function(gameState, planName) { for (var attackType in this.upcomingAttacks) { for (var i in this.upcomingAttacks[attackType]) { var attack = this.upcomingAttacks[attackType][i]; @@ -1015,7 +1023,7 @@ HQ.prototype.unpausePlan = function(gameState, planName) { } } } -HQ.prototype.pauseAllPlans = function(gameState) { +m.HQ.prototype.pauseAllPlans = function(gameState) { for (var attackType in this.upcomingAttacks) { for (var i in this.upcomingAttacks[attackType]) { var attack = this.upcomingAttacks[attackType][i]; @@ -1029,7 +1037,7 @@ HQ.prototype.pauseAllPlans = function(gameState) { } } } -HQ.prototype.unpauseAllPlans = function(gameState) { +m.HQ.prototype.unpauseAllPlans = function(gameState) { for (var attackType in this.upcomingAttacks) { for (var i in this.upcomingAttacks[attackType]) { var attack = this.upcomingAttacks[attackType][i]; @@ -1047,7 +1055,7 @@ HQ.prototype.unpauseAllPlans = function(gameState) { // Some functions are run every turn // Others once in a while -HQ.prototype.update = function(gameState, queues, events) { +m.HQ.prototype.update = function(gameState, queues, events) { Engine.ProfileStart("Headquarters update"); this.checkEvents(gameState,events,queues); @@ -1057,7 +1065,7 @@ HQ.prototype.update = function(gameState, queues, events) { this.trainMoreWorkers(gameState, queues); // sandbox doesn't expand. - if (Config.difficulty !== 0) + if (this.Config.difficulty !== 0) this.checkBasesRessLevel(gameState, queues); this.buildMoreHouses(gameState,queues); @@ -1065,7 +1073,7 @@ HQ.prototype.update = function(gameState, queues, events) { if (gameState.getTimeElapsed() > this.techStartTime && gameState.currentPhase() > 2) this.tryResearchTechs(gameState,queues); - if (Config.difficulty > 1) + if (this.Config.difficulty > 1) this.tryBartering(gameState); this.buildFarmstead(gameState, queues); @@ -1087,7 +1095,7 @@ HQ.prototype.update = function(gameState, queues, events) { for (i in this.baseManagers) { this.baseManagers[i].checkEvents(gameState, events, queues) - if ( ( (+i + gameState.ai.playedTurn) % (uniqueIDBases - 1)) === 0) + if ( ( (+i + gameState.ai.playedTurn) % (m.playerGlobals[PlayerID].uniqueIDBases - 1)) === 0) this.baseManagers[i].update(gameState, queues, events); } @@ -1113,10 +1121,10 @@ HQ.prototype.update = function(gameState, queues, events) { if (updateStep === 1 || attack.isPaused() ) { // just chillin' } else if (updateStep === 0 || updateStep === 3) { - debug ("Military Manager: " +attack.getType() +" plan " +attack.getName() +" aborted."); + m.debug ("Military Manager: " +attack.getType() +" plan " +attack.getName() +" aborted."); if (updateStep === 3) { this.attackPlansEncounteredWater = true; - debug("No attack path found. Aborting."); + m.debug("No attack path found. Aborting."); } attack.Abort(gameState, this); this.upcomingAttacks[attackType].splice(i--,1); @@ -1130,7 +1138,7 @@ HQ.prototype.update = function(gameState, queues, events) { chatText = "I'm starting an attack against " + gameState.sharedScript.playersData[attack.targetPlayer].name + "."; gameState.ai.chatTeam(chatText); - debug ("Military Manager: Starting " +attack.getType() +" plan " +attack.getName()); + m.debug ("Military Manager: Starting " +attack.getType() +" plan " +attack.getName()); attack.StartAttack(gameState,this); this.startedAttacks[attackType].push(attack); this.upcomingAttacks[attackType].splice(i--,1); @@ -1145,7 +1153,7 @@ HQ.prototype.update = function(gameState, queues, events) { chatText = "I'm starting an attack against " + gameState.sharedScript.playersData[attack.targetPlayer].name + "."; gameState.ai.chatTeam(chatText); - debug ("Military Manager: Starting " +attack.getType() +" plan " +attack.getName()); + m.debug ("Military Manager: Starting " +attack.getType() +" plan " +attack.getName()); this.startedAttacks[attackType].push(attack); this.upcomingAttacks[attackType].splice(i--,1); } @@ -1160,7 +1168,7 @@ HQ.prototype.update = function(gameState, queues, events) { { var remaining = attack.update(gameState,this,events); if (remaining == 0 || remaining == undefined) { - debug ("Military Manager: " +attack.getType() +" plan " +attack.getName() +" is now finished."); + m.debug ("Military Manager: " +attack.getType() +" plan " +attack.getName() +" is now finished."); attack.Abort(gameState); this.startedAttacks[attackType].splice(i--,1); } @@ -1174,14 +1182,14 @@ HQ.prototype.update = function(gameState, queues, events) { if (gameState.ai.strategy === "rush" && this.startedAttacks["CityAttack"].length !== 0) { // and then we revert. gameState.ai.strategy = "normal"; - Config.Economy.femaleRatio = 0.4; + this.Config.Economy.femaleRatio = 0.4; gameState.ai.modules.economy.targetNumWorkers = Math.max(Math.floor(gameState.getPopulationMax()*0.55), 1); } else if (gameState.ai.strategy === "rush" && this.upcomingAttacks["CityAttack"].length === 0) { - Lalala = new CityAttack(gameState, this,this.TotalAttackNumber, -1, "rush") + Lalala = new m.CityAttack(gameState, this, this.Config, this.TotalAttackNumber, -1, "rush") this.TotalAttackNumber++; this.upcomingAttacks["CityAttack"].push(Lalala); - debug ("Starting a little something"); + m.debug ("Starting a little something"); } else if (gameState.ai.strategy !== "rush" && !this.waterMap) { // creating plans after updating because an aborted plan might be reused in that case. @@ -1193,22 +1201,22 @@ HQ.prototype.update = function(gameState, queues, events) { } else { // basically only the first plan, really. if (this.upcomingAttacks["CityAttack"].length == 0 && gameState.getTimeElapsed() < 12*60000) { - var Lalala = new CityAttack(gameState, this,this.TotalAttackNumber, -1); + var Lalala = new m.CityAttack(gameState, this, this.Config, this.TotalAttackNumber, -1); if (Lalala.failed) { this.attackPlansEncounteredWater = true; // hack } else { - debug ("Military Manager: Creating the plan " +this.TotalAttackNumber); + m.debug ("Military Manager: Creating the plan " +this.TotalAttackNumber); this.TotalAttackNumber++; this.upcomingAttacks["CityAttack"].push(Lalala); } - } else if (this.upcomingAttacks["CityAttack"].length == 0 && Config.difficulty !== 0) { - var Lalala = new CityAttack(gameState, this,this.TotalAttackNumber, -1, "superSized"); + } else if (this.upcomingAttacks["CityAttack"].length == 0 && this.Config.difficulty !== 0) { + var Lalala = new m.CityAttack(gameState, this, this.Config, this.TotalAttackNumber, -1, "superSized"); if (Lalala.failed) { this.attackPlansEncounteredWater = true; // hack } else { - debug ("Military Manager: Creating the super sized plan " +this.TotalAttackNumber); + m.debug ("Military Manager: Creating the super sized plan " +this.TotalAttackNumber); this.TotalAttackNumber++; this.upcomingAttacks["CityAttack"].push(Lalala); } @@ -1221,12 +1229,12 @@ HQ.prototype.update = function(gameState, queues, events) { // very old relic. This should be reimplemented someday so the code stays here. if (this.HarassRaiding && this.preparingRaidNumber + this.startedRaidNumber < 1 && gameState.getTimeElapsed() < 780000) { - var Lalala = new CityAttack(gameState, this,this.totalStartedAttackNumber, -1, "harass_raid"); + var Lalala = new m.CityAttack(gameState, this,this.totalStartedAttackNumber, -1, "harass_raid"); if (!Lalala.createSupportPlans(gameState, this, )) { - debug ("Military Manager: harrassing plan not a valid option"); + m.debug ("Military Manager: harrassing plan not a valid option"); this.HarassRaiding = false; } else { - debug ("Military Manager: Creating the harass raid plan " +this.totalStartedAttackNumber); + m.debug ("Military Manager: Creating the harass raid plan " +this.totalStartedAttackNumber); this.totalStartedAttackNumber++; this.preparingRaidNumber++; @@ -1243,7 +1251,7 @@ HQ.prototype.update = function(gameState, queues, events) { this.buildDropsites(gameState, queues); Engine.ProfileStop(); - if (Config.difficulty !== 0) + if (this.Config.difficulty !== 0) this.tryBartering(gameState); this.buildFarmstead(gameState, queues); @@ -1255,3 +1263,7 @@ HQ.prototype.update = function(gameState, queues, events) { */ Engine.ProfileStop(); // Heaquarters update }; + +return m; + +}(AEGIS); 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 9cd8070ffd..a62bce4003 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/map-module.js +++ b/binaries/data/mods/public/simulation/ai/aegis/map-module.js @@ -1,6 +1,10 @@ -// other map functions +var AEGIS = function(m) +{ -Map.createObstructionMap = function(gameState, accessIndex, template){ +// other map functions +m.TERRITORY_PLAYER_MASK = 0x3F; + +m.createObstructionMap = function(gameState, accessIndex, template){ var passabilityMap = gameState.getMap(); var territoryMap = gameState.ai.territoryMap; @@ -31,7 +35,7 @@ Map.createObstructionMap = function(gameState, accessIndex, template){ for (var y = 0; y < passabilityMap.height; ++y) { var i = x + y*passabilityMap.width; - var tilePlayer = (territoryMap.data[i] & TERRITORY_PLAYER_MASK); + var tilePlayer = (territoryMap.data[i] & m.TERRITORY_PLAYER_MASK); if (gameState.ai.myIndex !== gameState.ai.accessibility.landPassMap[i]) { @@ -88,7 +92,7 @@ Map.createObstructionMap = function(gameState, accessIndex, template){ var obstructionTiles = new Uint8Array(passabilityMap.data.length); for (var i = 0; i < passabilityMap.data.length; ++i) { - var tilePlayer = (territoryMap.data[i] & TERRITORY_PLAYER_MASK); + var tilePlayer = (territoryMap.data[i] & m.TERRITORY_PLAYER_MASK); var invalidTerritory = ( (!buildOwn && tilePlayer == playerID) || (!buildAlly && gameState.isPlayerAlly(tilePlayer) && tilePlayer != playerID) || @@ -105,7 +109,7 @@ Map.createObstructionMap = function(gameState, accessIndex, template){ } } - var map = new Map(gameState.sharedScript, obstructionTiles); + var map = new API3.Map(gameState.sharedScript, obstructionTiles); map.setMaxVal(255); if (template && template.buildDistance()){ @@ -126,17 +130,19 @@ Map.createObstructionMap = function(gameState, accessIndex, template){ }; - -Map.createTerritoryMap = function(gameState) { +m.createTerritoryMap = function(gameState) { var map = gameState.ai.territoryMap; - var ret = new Map(gameState.sharedScript, map.data); + var ret = new API3.Map(gameState.sharedScript, map.data); ret.getOwner = function(p) { - return this.point(p) & TERRITORY_PLAYER_MASK; + return this.point(p) & m.TERRITORY_PLAYER_MASK; } ret.getOwnerIndex = function(p) { - return this.map[p] & TERRITORY_PLAYER_MASK; + return this.map[p] & m.TERRITORY_PLAYER_MASK; } return ret; }; + +return m; +}(AEGIS); 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 fb36c4a713..e8f48dbb42 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/naval-manager.js +++ b/binaries/data/mods/public/simulation/ai/aegis/naval-manager.js @@ -1,3 +1,6 @@ +var AEGIS = function(m) +{ + /* Naval Manager Will deal with anything ships. -Basically trade over water (with fleets and goals commissioned by the economy manager) @@ -9,7 +12,7 @@ Does not build them though, that's for the base manager to handle. */ -var NavalManager = function() { +m.NavalManager = function() { // accessibility zones for which we have a dock. // Connexion is described as [landindex] = [seaIndexes]; // technically they also exist for sea zones but I don't care. @@ -32,16 +35,16 @@ var NavalManager = function() { }; // More initialisation for stuff that needs the gameState -NavalManager.prototype.init = function(gameState, events, queues) { +m.NavalManager.prototype.init = function(gameState, events, queues) { // finished docks - this.docks = gameState.getOwnEntities().filter(Filters.and(Filters.byClass("Dock"), Filters.not(Filters.isFoundation()))); + this.docks = gameState.getOwnEntities().filter(API3.Filters.and(API3.Filters.byClass("Dock"), API3.Filters.not(API3.Filters.isFoundation()))); this.docks.allowQuickIter(); this.docks.registerUpdates(); - this.ships = gameState.getOwnEntities().filter(Filters.byClass("Ship")); + this.ships = gameState.getOwnEntities().filter(API3.Filters.byClass("Ship")); // note: those two can overlap (some transport ships are warships too and vice-versa). - this.tpShips = this.ships.filter(Filters.byCanGarrison()); - this.warships = this.ships.filter(Filters.byClass("Warship")); + this.tpShips = this.ships.filter(API3.Filters.byCanGarrison()); + this.warships = this.ships.filter(API3.Filters.byClass("Warship")); this.ships.registerUpdates(); this.tpShips.registerUpdates(); @@ -52,19 +55,19 @@ NavalManager.prototype.init = function(gameState, events, queues) { if (gameState.ai.accessibility.regionType[i] !== "water") { // push dummies - this.seaShips.push(new EntityCollection(gameState.sharedScript)); - this.seaTpShips.push(new EntityCollection(gameState.sharedScript)); - this.seaWarships.push(new EntityCollection(gameState.sharedScript)); + this.seaShips.push(new API3.EntityCollection(gameState.sharedScript)); + this.seaTpShips.push(new API3.EntityCollection(gameState.sharedScript)); + this.seaWarships.push(new API3.EntityCollection(gameState.sharedScript)); this.wantedTpShips.push(0); this.wantedWarships.push(0); } else { - var collec = this.ships.filter(Filters.byStaticMetadata(PlayerID, "sea", i)); + var collec = this.ships.filter(API3.Filters.byStaticMetadata(PlayerID, "sea", i)); collec.registerUpdates(); this.seaShips.push(collec); - collec = this.tpShips.filter(Filters.byStaticMetadata(PlayerID, "sea", i)); + collec = this.tpShips.filter(API3.Filters.byStaticMetadata(PlayerID, "sea", i)); collec.registerUpdates(); this.seaTpShips.push(collec); - var collec = this.warships.filter(Filters.byStaticMetadata(PlayerID, "sea", i)); + var collec = this.warships.filter(API3.Filters.byStaticMetadata(PlayerID, "sea", i)); collec.registerUpdates(); this.seaWarships.push(collec); @@ -76,7 +79,7 @@ NavalManager.prototype.init = function(gameState, events, queues) { } }; -NavalManager.prototype.getUnconnectedSeas = function (gameState, region) { +m.NavalManager.prototype.getUnconnectedSeas = function (gameState, region) { var seas = gameState.ai.accessibility.regionLinks[region] if (seas.length === 0) return []; @@ -89,7 +92,7 @@ NavalManager.prototype.getUnconnectedSeas = function (gameState, region) { }; // returns true if there is a path from A to B and we have docks. -NavalManager.prototype.canReach = function (gameState, regionA, regionB) { +m.NavalManager.prototype.canReach = function (gameState, regionA, regionB) { var path = gameState.ai.accessibility.getTrajectToIndex(regionA, regionB); if (!path) { @@ -100,7 +103,7 @@ NavalManager.prototype.canReach = function (gameState, regionA, regionB) { if (gameState.ai.accessibility.regionType[path[i]] == "land") if (this.accessibleSeas.indexOf(path[i+1]) === -1) { - debug ("cannot reach because of " + path[i+1]); + m.debug ("cannot reach because of " + path[i+1]); return false; // we wn't be able to board on that sea } } @@ -108,7 +111,7 @@ NavalManager.prototype.canReach = function (gameState, regionA, regionB) { }; -NavalManager.prototype.checkEvents = function (gameState, queues, events) { +m.NavalManager.prototype.checkEvents = function (gameState, queues, events) { for (i in events) { if (events[i].type == "Destroy") @@ -137,19 +140,19 @@ NavalManager.prototype.checkEvents = function (gameState, queues, events) { } }; -NavalManager.prototype.addPlan = function(plan) { +m.NavalManager.prototype.addPlan = function(plan) { this.transportPlans.push(plan); }; // will create a plan at the end of the turn. // many units can call this separately and end up in the same plan // which can be useful. -NavalManager.prototype.askForTransport = function(entity, startPos, endPos) { +m.NavalManager.prototype.askForTransport = function(entity, startPos, endPos) { this.askedPlans.push([entity, startPos, endPos]); }; // creates aforementionned plans -NavalManager.prototype.createPlans = function(gameState) { +m.NavalManager.prototype.createPlans = function(gameState) { var startID = {}; for (i in this.askedPlans) @@ -172,13 +175,13 @@ NavalManager.prototype.createPlans = function(gameState) { for (var i in startID) for (var k in startID[i]) { - var tpPlan = new TransportPlan(gameState, startID[i][k].units, startID[i][k].dest, false) + var tpPlan = new m.TransportPlan(gameState, startID[i][k].units, startID[i][k].dest, false) this.transportPlans.push (tpPlan); } }; // TODO: work on this. -NavalManager.prototype.maintainFleet = function(gameState, queues, events) { +m.NavalManager.prototype.maintainFleet = function(gameState, queues, events) { // check if we have enough transport ships. // check per region. for (var i = 0; i < this.seaShips.length; ++i) @@ -188,13 +191,13 @@ NavalManager.prototype.maintainFleet = function(gameState, queues, events) { && tpNb + queues.ships.length() === 0 && gameState.getTemplate(gameState.applyCiv("units/{civ}_ship_bireme")).available(gameState)) { // TODO: check our dock can build the wanted ship types, for Carthage. - queues.ships.addItem(new TrainingPlan(gameState, "units/{civ}_ship_bireme", { "sea" : i }, 1, 0, -1, 1 )); + queues.ships.addItem(new m.TrainingPlan(gameState, "units/{civ}_ship_bireme", { "sea" : i }, 1, 0, -1, 1 )); } } }; // bumps up the number of ships we want if we need more. -NavalManager.prototype.checkLevels = function(gameState, queues) { +m.NavalManager.prototype.checkLevels = function(gameState, queues) { if (queues.ships.length() !== 0) return; for (var i = 0; i < this.transportPlans.length; ++i) @@ -214,7 +217,7 @@ NavalManager.prototype.checkLevels = function(gameState, queues) { }; // assigns free ships to plans that need some -NavalManager.prototype.assignToPlans = function(gameState, queues, events) { +m.NavalManager.prototype.assignToPlans = function(gameState, queues, events) { for (var i = 0; i < this.transportPlans.length; ++i) { var plan = this.transportPlans[i]; @@ -228,7 +231,7 @@ NavalManager.prototype.assignToPlans = function(gameState, queues, events) { { if (!ship.getMetadata(PlayerID, "tpplan")) { - debug ("Assigning ship " + ship.id() + " to plan" + plan.ID); + m.debug ("Assigning ship " + ship.id() + " to plan" + plan.ID); plan.assignShip(gameState, ship); return true; } @@ -239,7 +242,7 @@ NavalManager.prototype.assignToPlans = function(gameState, queues, events) { return false; }; -NavalManager.prototype.checkActivePlan = function(ID) { +m.NavalManager.prototype.checkActivePlan = function(ID) { for (var i = 0; i < this.transportPlans.length; ++i) if (this.transportPlans[i].ID === ID) return true; @@ -249,7 +252,7 @@ NavalManager.prototype.checkActivePlan = function(ID) { // Some functions are run every turn // Others once in a while -NavalManager.prototype.update = function(gameState, queues, events) { +m.NavalManager.prototype.update = function(gameState, queues, events) { Engine.ProfileStart("Naval Manager update"); this.checkEvents(gameState, queues, events); @@ -286,3 +289,6 @@ NavalManager.prototype.update = function(gameState, queues, events) { } Engine.ProfileStop(); }; + +return m; +}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/aegis/plan-transport.js b/binaries/data/mods/public/simulation/ai/aegis/plan-transport.js index 73592956cf..f7800f746d 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/plan-transport.js +++ b/binaries/data/mods/public/simulation/ai/aegis/plan-transport.js @@ -1,3 +1,6 @@ +var AEGIS = function(m) +{ + /* Describes a transport plan Constructor assign units (units is an ID array, or an ID), a destionation (position, ingame), and a wanted escort size. @@ -15,10 +18,10 @@ // TODO: finish the support of multiple accessibility indexes. // TODO: this doesn't check we can actually reach in the init, which we might want? -var TransportPlan = function(gameState, units, destination, allAtOnce, escortSize, onlyIfOK) { +m.TransportPlan = function(gameState, units, destination, allAtOnce, escortSize, onlyIfOK) { var self = this; - this.ID = uniqueIDTPlans++; + this.ID = m.playerGlobals[PlayerID].uniqueIDTPlans++; var unitsID = []; if (units.length !== undefined) @@ -26,7 +29,7 @@ var TransportPlan = function(gameState, units, destination, allAtOnce, escortSiz else unitsID = [units]; - this.units = EntityCollectionFromIds(gameState, unitsID); + this.units = m.EntityCollectionFromIds(gameState, unitsID); this.units.forEach(function (ent) { //}){ ent.setMetadata(PlayerID, "tpplan", self.ID); ent.setMetadata(PlayerID, "formerRole", ent.getMetadata(PlayerID, "role")); @@ -36,8 +39,8 @@ var TransportPlan = function(gameState, units, destination, allAtOnce, escortSiz this.units.freeze(); this.units.registerUpdates(); - debug ("Starting a new plan with ID " + this.ID + " to " + destination); - debug ("units are " + uneval (units)); + m.debug ("Starting a new plan with ID " + this.ID + " to " + destination); + m.debug ("units are " + uneval (units)); this.destination = destination; this.destinationIndex = gameState.ai.accessibility.getAccessValue(destination); @@ -70,7 +73,7 @@ var TransportPlan = function(gameState, units, destination, allAtOnce, escortSiz }; // count available slots -TransportPlan.prototype.countFreeSlots = function(onlyTrulyFree) +m.TransportPlan.prototype.countFreeSlots = function(onlyTrulyFree) { var slots = 0; this.transportShips.forEach(function (ent) { //}){ @@ -80,12 +83,12 @@ TransportPlan.prototype.countFreeSlots = function(onlyTrulyFree) }); } -TransportPlan.prototype.assignShip = function(gameState, ship) +m.TransportPlan.prototype.assignShip = function(gameState, ship) { ship.setMetadata(PlayerID,"tpplan", this.ID); } -TransportPlan.prototype.releaseAll = function(gameState) +m.TransportPlan.prototype.releaseAll = function(gameState) { this.ships.forEach(function (ent) { ent.setMetadata(PlayerID,"tpplan", undefined) }); this.units.forEach(function (ent) { @@ -96,25 +99,25 @@ TransportPlan.prototype.releaseAll = function(gameState) }); } -TransportPlan.prototype.releaseAllShips = function(gameState) +m.TransportPlan.prototype.releaseAllShips = function(gameState) { this.ships.forEach(function (ent) { ent.setMetadata(PlayerID,"tpplan", undefined) }); } -TransportPlan.prototype.needTpShips = function() +m.TransportPlan.prototype.needTpShips = function() { if ((this.allAtOnce && this.countFreeSlots() >= this.units.length) || this.transportShips.length > 0) return false; return true; } -TransportPlan.prototype.needEscortShips = function() +m.TransportPlan.prototype.needEscortShips = function() { return !((this.onlyIfOK && this.escortShips.length < this.escortSize) || !this.onlyIfOK); } // returns the zone for which we are needing our ships -TransportPlan.prototype.neededShipsZone = function() +m.TransportPlan.prototype.neededShipsZone = function() { if (!this.seaZone) return false; @@ -134,7 +137,7 @@ TransportPlan.prototype.neededShipsZone = function() > there is the possibility that we'll be moving units on land, but that's basically a restart too, with more clearing. Grouping Path is basically the same with "grouping" and we never unboard (unless there is a need to) */ -TransportPlan.prototype.carryOn = function(gameState, navalManager) +m.TransportPlan.prototype.carryOn = function(gameState, navalManager) { if (this.state === "unstarted") { @@ -171,7 +174,7 @@ TransportPlan.prototype.carryOn = function(gameState, navalManager) // let's get our index this turn. this.startIndex = unitIndexes[0]; - debug ("plan " + this.ID + " from " + this.startIndex); + m.debug ("plan " + this.ID + " from " + this.startIndex); return true; } @@ -190,7 +193,7 @@ TransportPlan.prototype.carryOn = function(gameState, navalManager) } // we have a path, register the first sea zone. this.seaZone = this.path[1]; - debug ("Plan " + this.ID + " over seazone " + this.seaZone); + m.debug ("Plan " + this.ID + " over seazone " + this.seaZone); } // if we currently have no baoarding spot, try and find one. if (!this.boardingSpot) @@ -206,10 +209,10 @@ TransportPlan.prototype.carryOn = function(gameState, navalManager) var passabilityMap = gameState.getMap(); var territoryMap = gameState.ai.territoryMap; var obstructionMask = gameState.getPassabilityClassMask("foundationObstruction") | gameState.getPassabilityClassMask("building-shore"); - var obstructions = new Map(gameState.sharedScript); + var obstructions = new API3.Map(gameState.sharedScript); // wanted map. - var friendlyTiles = new Map(gameState.sharedScript); + var friendlyTiles = new API3.Map(gameState.sharedScript); for (var j = 0; j < friendlyTiles.length; ++j) { @@ -248,7 +251,7 @@ TransportPlan.prototype.carryOn = function(gameState, navalManager) // we have the spot we want to board at. this.boardingSpot = [x,z]; - debug ("Plan " + this.ID + " new boarding spot is " + this.boardingSpot); + m.debug ("Plan " + this.ID + " new boarding spot is " + this.boardingSpot); } // if all at once we need to be full, else we just need enough escort ships. @@ -262,7 +265,7 @@ TransportPlan.prototype.carryOn = function(gameState, navalManager) this.garrisonShipID = -1; - debug ("Boarding"); + m.debug ("Boarding"); this.state = "boarding"; } return true; @@ -300,12 +303,12 @@ TransportPlan.prototype.carryOn = function(gameState, navalManager) // check if we need to move our units and ships closer together var stillMoving = false; - if (SquareVectorDistance(this.ships.getCentrePosition(),this.boardingSpot) > 1600) + if (API3.SquareVectorDistance(this.ships.getCentrePosition(),this.boardingSpot) > 1600) { this.ships.move(this.boardingSpot[0],this.boardingSpot[1]); stillMoving = true; // wait till ships are in position } - if (SquareVectorDistance(this.units.getCentrePosition(),this.boardingSpot) > 1600) + if (API3.SquareVectorDistance(this.units.getCentrePosition(),this.boardingSpot) > 1600) { this.units.move(this.boardingSpot[0],this.boardingSpot[1]); stillMoving = true; // wait till units are in position @@ -365,10 +368,10 @@ TransportPlan.prototype.carryOn = function(gameState, navalManager) var passabilityMap = gameState.getMap(); var territoryMap = gameState.ai.territoryMap; var obstructionMask = gameState.getPassabilityClassMask("foundationObstruction") | gameState.getPassabilityClassMask("building-shore"); - var obstructions = new Map(gameState.sharedScript); + var obstructions = new API3.Map(gameState.sharedScript); // wanted map. - var friendlyTiles = new Map(gameState.sharedScript); + var friendlyTiles = new API3.Map(gameState.sharedScript); var wantedIndex = -1; @@ -377,7 +380,7 @@ TransportPlan.prototype.carryOn = function(gameState, navalManager) this.path.splice(0,2); wantedIndex = this.path[0]; } else { - debug ("too short at " +uneval(this.path)); + m.debug ("too short at " +uneval(this.path)); return false; // Incomputable } @@ -431,7 +434,7 @@ TransportPlan.prototype.carryOn = function(gameState, navalManager) return false; // check if we need to move ships - if (SquareVectorDistance(this.ships.getCentrePosition(),this.unboardingSpot) > 400) + if (API3.SquareVectorDistance(this.ships.getCentrePosition(),this.unboardingSpot) > 400) { this.ships.move(this.unboardingSpot[0],this.unboardingSpot[1]); } else { @@ -447,7 +450,7 @@ TransportPlan.prototype.carryOn = function(gameState, navalManager) return false; // check if we need to move ships - if (SquareVectorDistance(this.ships.getCentrePosition(),this.unboardingSpot) > 400) + if (API3.SquareVectorDistance(this.ships.getCentrePosition(),this.unboardingSpot) > 400) { this.ships.move(this.unboardingSpot[0],this.unboardingSpot[1]); } else { @@ -455,7 +458,7 @@ TransportPlan.prototype.carryOn = function(gameState, navalManager) // TODO: improve on this. if (this.path.length > 1) { - debug ("plan " + this.ID + " going back for more"); + m.debug ("plan " + this.ID + " going back for more"); // basically reset. delete this.boardingSpot; delete this.unboardingSpot; @@ -463,10 +466,13 @@ TransportPlan.prototype.carryOn = function(gameState, navalManager) this.releaseAllShips(); return true; } - debug ("plan " + this.ID + " is finished"); + m.debug ("plan " + this.ID + " is finished"); return false; } } return true; } + +return m; +}(AEGIS); 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 e40593bc98..b831cef1aa 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/queue-manager.js +++ b/binaries/data/mods/public/simulation/ai/aegis/queue-manager.js @@ -1,3 +1,6 @@ +var AEGIS = function(m) +{ + // This takes the input queues and picks which items to fund with resources until no more resources are left to distribute. // // Currently this manager keeps accounts for each queue, split between the 4 main resources @@ -17,7 +20,8 @@ // // This system should be improved. It's probably not flexible enough. -var QueueManager = function(queues, priorities) { +m.QueueManager = function(Config, queues, priorities) { + this.Config = Config; this.queues = queues; this.priorities = priorities; this.account = {}; @@ -28,7 +32,7 @@ var QueueManager = function(queues, priorities) { this.queueArrays = []; for (var p in this.queues) { this.account[p] = 0; - this.accounts[p] = new Resources(); + this.accounts[p] = new API3.Resources(); this.queueArrays.push([p,this.queues[p]]); } this.queueArrays.sort(function (a,b) { return (self.priorities[b[0]] - self.priorities[a[0]]) }); @@ -36,7 +40,7 @@ var QueueManager = function(queues, priorities) { this.curItemQueue = []; }; -QueueManager.prototype.getAvailableResources = function(gameState, noAccounts) { +m.QueueManager.prototype.getAvailableResources = function(gameState, noAccounts) { var resources = gameState.getResources(); if (noAccounts) return resources; @@ -46,16 +50,16 @@ QueueManager.prototype.getAvailableResources = function(gameState, noAccounts) { return resources; }; -QueueManager.prototype.getTotalAccountedResources = function(gameState) { - var resources = new Resources(); +m.QueueManager.prototype.getTotalAccountedResources = function(gameState) { + var resources = new API3.Resources(); for (var key in this.queues) { resources.add(this.accounts[key]); } return resources; }; -QueueManager.prototype.currentNeeds = function(gameState) { - var needs = new Resources(); +m.QueueManager.prototype.currentNeeds = function(gameState) { + var needs = new API3.Resources(); // get out current resources, not removing accounts. var current = this.getAvailableResources(gameState, true); //queueArrays because it's faster. @@ -82,8 +86,8 @@ QueueManager.prototype.currentNeeds = function(gameState) { }; }; -QueueManager.prototype.futureNeeds = function(gameState) { - var needs = new Resources(); +m.QueueManager.prototype.futureNeeds = function(gameState) { + var needs = new API3.Resources(); // get out current resources, not removing accounts. var current = this.getAvailableResources(gameState, true); //queueArrays because it's faster. @@ -108,7 +112,7 @@ QueueManager.prototype.futureNeeds = function(gameState) { }; // calculate the gather rates we'd want to be able to use all elements in our queues -QueueManager.prototype.wantedGatherRates = function(gameState) { +m.QueueManager.prototype.wantedGatherRates = function(gameState) { var rates = { "food" : 0, "wood" : 0, "stone" : 0, "metal" : 0 }; var qTime = gameState.getTimeElapsed(); var qCosts = { "food" : 0, "wood" : 0, "stone" : 0, "metal" : 0 }; @@ -143,7 +147,9 @@ QueueManager.prototype.wantedGatherRates = function(gameState) { // estimate time based on priority + cost + nb // TODO: work on this. for (type in qCosts) + { qCosts[type] += (cost[type] + Math.min(cost[type],this.priorities[name])); + } qTime += 30000; } else { // TODO: work on this. @@ -165,7 +171,7 @@ QueueManager.prototype.wantedGatherRates = function(gameState) { return rates; }; -/*QueueManager.prototype.logNeeds = function(gameState) { +/*m.QueueManager.prototype.logNeeds = function(gameState) { if (!this.totor) { this.totor = []; @@ -255,34 +261,34 @@ QueueManager.prototype.wantedGatherRates = function(gameState) { }; */ -QueueManager.prototype.printQueues = function(gameState){ - debug("QUEUES"); +m.QueueManager.prototype.printQueues = function(gameState){ + m.debug("QUEUES"); for (var i in this.queues){ var qStr = ""; var q = this.queues[i]; if (q.queue.length > 0) - debug((i + ":")); + m.debug((i + ":")); for (var j in q.queue){ qStr = " " + q.queue[j].type + " "; if (q.queue[j].number) qStr += "x" + q.queue[j].number; - debug (qStr); + m.debug (qStr); } } - debug ("Accounts"); + m.debug ("Accounts"); for (var p in this.accounts) { - debug(p + ": " + uneval(this.accounts[p])); + m.debug(p + ": " + uneval(this.accounts[p])); } - debug("Needed Resources:" + uneval(this.futureNeeds(gameState,false))); - debug ("Wanted Gather Rates:" + uneval(this.wantedGatherRates(gameState))); - debug ("Current Resources:" + uneval(gameState.getResources())); - debug ("Available Resources:" + uneval(this.getAvailableResources(gameState))); + m.debug("Needed Resources:" + uneval(this.futureNeeds(gameState,false))); + m.debug ("Wanted Gather Rates:" + uneval(this.wantedGatherRates(gameState))); + m.debug ("Current Resources:" + uneval(gameState.getResources())); + m.debug ("Available Resources:" + uneval(this.getAvailableResources(gameState))); }; // nice readable HTML version. -QueueManager.prototype.HTMLprintQueues = function(gameState){ - if (!Config.debug) +m.QueueManager.prototype.HTMLprintQueues = function(gameState){ + if (!m.DebugEnabled) return; log(" Aegis Queue Manager "); for (var i in this.queues){ @@ -325,13 +331,13 @@ QueueManager.prototype.HTMLprintQueues = function(gameState){ log(""); }; -QueueManager.prototype.clear = function(){ +m.QueueManager.prototype.clear = function(){ this.curItemQueue = []; for (var i in this.queues) this.queues[i].empty(); }; -QueueManager.prototype.update = function(gameState) { +m.QueueManager.prototype.update = function(gameState) { var self = this; for (var i in this.priorities){ @@ -430,7 +436,7 @@ QueueManager.prototype.update = function(gameState) { this.accounts[queues[under]] -= amnt; this.accounts[queues[over]] += amnt; --over; - debug ("Shifting " + amnt + " from " + queues[under] + " to " +queues[over]); + m.debug ("Shifting " + amnt + " from " + queues[under] + " to " +queues[over]); continue; } else { ++under; @@ -447,8 +453,8 @@ QueueManager.prototype.update = function(gameState) { Engine.ProfileStart("Pick items from queues"); - //debug ("start"); - //debug (uneval(this.accounts)); + //m.debug ("start"); + //m.debug (uneval(this.accounts)); // Start the next item in the queue if we can afford it. for (var i in this.queueArrays) { @@ -457,7 +463,7 @@ QueueManager.prototype.update = function(gameState) { if (queue.length() > 0 && !queue.paused) { var item = queue.getNext(); - var total = new Resources(); + var total = new API3.Resources(); total.add(this.accounts[name]); if (total.canAfford(item.getCost())) { @@ -471,7 +477,7 @@ QueueManager.prototype.update = function(gameState) { this.accounts[name].reset(); } } - //debug (uneval(this.accounts)); + //m.debug (uneval(this.accounts)); Engine.ProfileStop(); @@ -481,7 +487,7 @@ QueueManager.prototype.update = function(gameState) { Engine.ProfileStop(); }; -QueueManager.prototype.pauseQueue = function(queue, scrapAccounts) { +m.QueueManager.prototype.pauseQueue = function(queue, scrapAccounts) { if (this.queues[queue]) { this.queues[queue].paused = true; @@ -490,12 +496,12 @@ QueueManager.prototype.pauseQueue = function(queue, scrapAccounts) { } } -QueueManager.prototype.unpauseQueue = function(queue) { +m.QueueManager.prototype.unpauseQueue = function(queue) { if (this.queues[queue]) this.queues[queue].paused = false; } -QueueManager.prototype.pauseAll = function(scrapAccounts, but) { +m.QueueManager.prototype.pauseAll = function(scrapAccounts, but) { for (var p in this.queues) if (p != but) { @@ -505,28 +511,30 @@ QueueManager.prototype.pauseAll = function(scrapAccounts, but) { } } -QueueManager.prototype.unpauseAll = function(but) { +m.QueueManager.prototype.unpauseAll = function(but) { for (var p in this.queues) if (p != but) this.queues[p].paused = false; } -QueueManager.prototype.addQueue = function(queueName, priority) { +m.QueueManager.prototype.addQueue = function(queueName, priority) { if (this.queues[queueName] == undefined) { - this.queues[queueName] = new Queue(); + this.queues[queueName] = new m.Queue(); this.priorities[queueName] = priority; this.account[queueName] = 0; - this.accounts[queueName] = new Resources(); + this.accounts[queueName] = new API3.Resources(); var self = this; this.queueArrays = []; for (var p in this.queues) + { this.queueArrays.push([p,this.queues[p]]); + } this.queueArrays.sort(function (a,b) { return (self.priorities[b[0]] - self.priorities[a[0]]) }); } } -QueueManager.prototype.removeQueue = function(queueName) { +m.QueueManager.prototype.removeQueue = function(queueName) { if (this.queues[queueName] !== undefined) { if ( this.curItemQueue.indexOf(queueName) !== -1) { this.curItemQueue.splice(this.curItemQueue.indexOf(queueName),1); @@ -539,18 +547,23 @@ QueueManager.prototype.removeQueue = function(queueName) { var self = this; this.queueArrays = []; for (var p in this.queues) + { this.queueArrays.push([p,this.queues[p]]); + } this.queueArrays.sort(function (a,b) { return (self.priorities[b[0]] - self.priorities[a[0]]) }); } } -QueueManager.prototype.changePriority = function(queueName, newPriority) { +m.QueueManager.prototype.changePriority = function(queueName, newPriority) { var self = this; if (this.queues[queueName] !== undefined) this.priorities[queueName] = newPriority; this.queueArrays = []; - for (var p in this.queues) { + for (var p in this.queues) + { this.queueArrays.push([p,this.queues[p]]); } this.queueArrays.sort(function (a,b) { return (self.priorities[b[0]] - self.priorities[a[0]]) }); } +return m; +}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/aegis/queue.js b/binaries/data/mods/public/simulation/ai/aegis/queue.js index 4f5c3e73ae..fd478e8c1a 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/queue.js +++ b/binaries/data/mods/public/simulation/ai/aegis/queue.js @@ -1,17 +1,20 @@ +var AEGIS = function(m) +{ + /* * Holds a list of wanted items to train or construct */ -var Queue = function() { +m.Queue = function() { this.queue = []; this.paused = false; }; -Queue.prototype.empty = function() { +m.Queue.prototype.empty = function() { this.queue = []; }; -Queue.prototype.addItem = function(plan) { +m.Queue.prototype.addItem = function(plan) { for (var i in this.queue) { if (plan.category === "unit" && this.queue[i].type == plan.type && this.queue[i].number + plan.number <= this.queue[i].maxMerge) @@ -23,7 +26,7 @@ Queue.prototype.addItem = function(plan) { this.queue.push(plan); }; -Queue.prototype.getNext = function() { +m.Queue.prototype.getNext = function() { if (this.queue.length > 0) { return this.queue[0]; } else { @@ -31,7 +34,7 @@ Queue.prototype.getNext = function() { } }; -Queue.prototype.startNext = function(gameState) { +m.Queue.prototype.startNext = function(gameState) { if (this.queue.length > 0) { this.queue.shift().start(gameState); return true; @@ -42,8 +45,8 @@ Queue.prototype.startNext = function(gameState) { // returns the maximal account we'll accept for this queue. // Currently 100% of the cost of the first element and 80% of that of the second -Queue.prototype.maxAccountWanted = function(gameState) { - var cost = new Resources(); +m.Queue.prototype.maxAccountWanted = function(gameState) { + var cost = new API3.Resources(); if (this.queue.length > 0 && this.queue[0].isGo(gameState)) cost.add(this.queue[0].getCost()); if (this.queue.length > 1 && this.queue[1].isGo(gameState)) @@ -55,19 +58,19 @@ Queue.prototype.maxAccountWanted = function(gameState) { return cost; }; -Queue.prototype.queueCost = function(){ - var cost = new Resources(); +m.Queue.prototype.queueCost = function(){ + var cost = new API3.Resources(); for (var key in this.queue){ cost.add(this.queue[key].getCost()); } return cost; }; -Queue.prototype.length = function() { +m.Queue.prototype.length = function() { return this.queue.length; }; -Queue.prototype.countQueuedUnits = function(){ +m.Queue.prototype.countQueuedUnits = function(){ var count = 0; for (var i in this.queue){ count += this.queue[i].number; @@ -75,7 +78,7 @@ Queue.prototype.countQueuedUnits = function(){ return count; }; -Queue.prototype.countQueuedUnitsWithClass = function(classe){ +m.Queue.prototype.countQueuedUnitsWithClass = function(classe){ var count = 0; for (var i in this.queue){ if (this.queue[i].template && this.queue[i].template.hasClass(classe)) @@ -83,7 +86,7 @@ Queue.prototype.countQueuedUnitsWithClass = function(classe){ } return count; }; -Queue.prototype.countQueuedUnitsWithMetadata = function(data,value){ +m.Queue.prototype.countQueuedUnitsWithMetadata = function(data,value){ var count = 0; for (var i in this.queue){ if (this.queue[i].metadata[data] && this.queue[i].metadata[data] == value) @@ -92,7 +95,7 @@ Queue.prototype.countQueuedUnitsWithMetadata = function(data,value){ return count; }; -Queue.prototype.countAllByType = function(t){ +m.Queue.prototype.countAllByType = function(t){ var count = 0; for (var i = 0; i < this.queue.length; i++){ @@ -102,3 +105,6 @@ Queue.prototype.countAllByType = function(t){ } return count; }; + +return m; +}(AEGIS); 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 2a8e844cd8..6d197dfb2a 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/queueplan-building.js +++ b/binaries/data/mods/public/simulation/ai/aegis/queueplan-building.js @@ -1,10 +1,13 @@ -var ConstructionPlan = function(gameState, type, metadata, startTime, expectedTime, position) { +var AEGIS = function(m) +{ + +m.ConstructionPlan = function(gameState, type, metadata, startTime, expectedTime, position) { this.type = gameState.applyCiv(type); this.position = position; this.metadata = metadata; - this.ID = uniqueIDBOPlans++; + this.ID = m.playerGlobals[PlayerID].uniqueIDBOPlans++; this.template = gameState.getTemplate(this.type); if (!this.template) { @@ -12,7 +15,7 @@ var ConstructionPlan = function(gameState, type, metadata, startTime, expectedTi } this.category = "building"; - this.cost = new Resources(this.template.cost()); + this.cost = new API3.Resources(this.template.cost()); this.number = 1; // The number of buildings to build if (!startTime) @@ -28,13 +31,13 @@ var ConstructionPlan = function(gameState, type, metadata, startTime, expectedTi }; // return true if we willstart amassing resource for this plan -ConstructionPlan.prototype.isGo = function(gameState) { +m.ConstructionPlan.prototype.isGo = function(gameState) { return (gameState.getTimeElapsed() > this.startTime); }; // checks other than resource ones. // TODO: change this. -ConstructionPlan.prototype.canStart = function(gameState) { +m.ConstructionPlan.prototype.canStart = function(gameState) { if (gameState.buildingsBuilt > 0) return false; @@ -51,7 +54,7 @@ ConstructionPlan.prototype.canStart = function(gameState) { return (builders.length != 0); }; -ConstructionPlan.prototype.start = function(gameState) { +m.ConstructionPlan.prototype.start = function(gameState) { var builders = gameState.findBuilders(this.type).toEntityArray(); @@ -63,11 +66,11 @@ ConstructionPlan.prototype.start = function(gameState) { if (!pos){ if (this.template.hasClass("Naval")) gameState.ai.HQ.dockFailed = true; - debug("No room to place " + this.type); + m.debug("No room to place " + this.type); return; } if (this.template.hasClass("Naval")) - debug (pos); + m.debug (pos); gameState.buildingsBuilt++; if (gameState.getTemplate(this.type).buildCategory() === "Dock") @@ -80,20 +83,20 @@ ConstructionPlan.prototype.start = function(gameState) { builders[0].construct(this.type, pos.x, pos.z, pos.angle, this.metadata); }; -ConstructionPlan.prototype.getCost = function() { - var costs = new Resources(); +m.ConstructionPlan.prototype.getCost = function() { + var costs = new API3.Resources(); costs.add(this.cost); return costs; }; -ConstructionPlan.prototype.findGoodPosition = function(gameState) { +m.ConstructionPlan.prototype.findGoodPosition = function(gameState) { var template = gameState.getTemplate(this.type); var cellSize = gameState.cellSize; // size of each tile // First, find all tiles that are far enough away from obstructions: - var obstructionMap = Map.createObstructionMap(gameState,0, template); + var obstructionMap = m.createObstructionMap(gameState,0, template); //obstructionMap.dumpIm(template.buildCategory() + "_obstructions_pre.png"); @@ -104,7 +107,7 @@ ConstructionPlan.prototype.findGoodPosition = function(gameState) { // Compute each tile's closeness to friendly structures: - var friendlyTiles = new Map(gameState.sharedScript); + var friendlyTiles = new API3.Map(gameState.sharedScript); var alreadyHasHouses = false; @@ -229,3 +232,7 @@ ConstructionPlan.prototype.findGoodPosition = function(gameState) { "angle" : angle }; }; + + +return m; +}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/aegis/queueplan-research.js b/binaries/data/mods/public/simulation/ai/aegis/queueplan-research.js index a6dd26347b..8cefb2c621 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/queueplan-research.js +++ b/binaries/data/mods/public/simulation/ai/aegis/queueplan-research.js @@ -1,14 +1,17 @@ -var ResearchPlan = function(gameState, type, startTime, expectedTime, rush) { +var AEGIS = function(m) +{ + +m.ResearchPlan = function(gameState, type, startTime, expectedTime, rush) { this.type = type; - this.ID = uniqueIDBOPlans++; + this.ID = m.playerGlobals[PlayerID].uniqueIDBOPlans++; this.template = gameState.getTemplate(this.type); if (!this.template || this.template.researchTime === undefined) { return false; } this.category = "technology"; - this.cost = new Resources(this.template.cost(),0); + this.cost = new API3.Resources(this.template.cost(),0); this.number = 1; // Obligatory for compatibility if (!startTime) @@ -30,23 +33,23 @@ var ResearchPlan = function(gameState, type, startTime, expectedTime, rush) { }; // return true if we willstart amassing resource for this plan -ResearchPlan.prototype.isGo = function(gameState) { +m.ResearchPlan.prototype.isGo = function(gameState) { return (gameState.getTimeElapsed() > this.startTime); }; -ResearchPlan.prototype.canStart = function(gameState) { +m.ResearchPlan.prototype.canStart = function(gameState) { // also checks canResearch return (gameState.findResearchers(this.type).length !== 0); }; -ResearchPlan.prototype.start = function(gameState) { +m.ResearchPlan.prototype.start = function(gameState) { var self = this; // TODO: this is special cased for "rush" technologies, ie the town phase // which currently is a 100% focus. gameState.ai.queueManager.unpauseAll(); - //debug ("Starting the research plan for " + this.type); + //m.debug ("Starting the research plan for " + this.type); var trainers = gameState.findResearchers(this.type).toEntityArray(); //for (var i in trainers) @@ -66,9 +69,12 @@ ResearchPlan.prototype.start = function(gameState) { } }; -ResearchPlan.prototype.getCost = function(){ - var costs = new Resources(); +m.ResearchPlan.prototype.getCost = function(){ + var costs = new API3.Resources(); costs.add(this.cost); return costs; }; + +return m; +}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/aegis/queueplan-training.js b/binaries/data/mods/public/simulation/ai/aegis/queueplan-training.js index 23f3875073..d4d3f7a538 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/queueplan-training.js +++ b/binaries/data/mods/public/simulation/ai/aegis/queueplan-training.js @@ -1,15 +1,18 @@ -var TrainingPlan = function(gameState, type, metadata, number, startTime, expectedTime, maxMerge) { +var AEGIS = function(m) +{ + +m.TrainingPlan = function(gameState, type, metadata, number, startTime, expectedTime, maxMerge) { this.type = gameState.applyCiv(type); this.metadata = metadata; - this.ID = uniqueIDBOPlans++; + this.ID = m.playerGlobals[PlayerID].uniqueIDBOPlans++; this.template = gameState.getTemplate(this.type); if (!this.template) return false; this.category = "unit"; - this.cost = new Resources(this.template.cost(), this.template._template.Cost.Population); + this.cost = new API3.Resources(this.template.cost(), this.template._template.Cost.Population); if (!number) this.number = 1; else @@ -33,11 +36,11 @@ var TrainingPlan = function(gameState, type, metadata, number, startTime, expect }; // return true if we willstart amassing resource for this plan -TrainingPlan.prototype.isGo = function(gameState) { +m.TrainingPlan.prototype.isGo = function(gameState) { return (gameState.getTimeElapsed() > this.startTime); }; -TrainingPlan.prototype.canStart = function(gameState) { +m.TrainingPlan.prototype.canStart = function(gameState) { if (this.invalidTemplate) return false; @@ -48,7 +51,7 @@ TrainingPlan.prototype.canStart = function(gameState) { return (trainers.length != 0); }; -TrainingPlan.prototype.start = function(gameState) { +m.TrainingPlan.prototype.start = function(gameState) { //warn("Executing TrainingPlan " + uneval(this)); var self = this; var trainers = gameState.findTrainers(this.type).toEntityArray(); @@ -72,15 +75,19 @@ TrainingPlan.prototype.start = function(gameState) { } }; -TrainingPlan.prototype.getCost = function(){ - var multCost = new Resources(); +m.TrainingPlan.prototype.getCost = function(){ + var multCost = new API3.Resources(); multCost.add(this.cost); multCost.multiply(this.number); return multCost; }; -TrainingPlan.prototype.addItem = function(amount){ +m.TrainingPlan.prototype.addItem = function(amount){ if (amount === undefined) amount = 1; this.number += amount; }; + + +return m; +}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/aegis/template-manager.js b/binaries/data/mods/public/simulation/ai/aegis/template-manager.js index 651e54fe65..affeb586c6 100755 --- a/binaries/data/mods/public/simulation/ai/aegis/template-manager.js +++ b/binaries/data/mods/public/simulation/ai/aegis/template-manager.js @@ -1,9 +1,12 @@ +var AEGIS = function(m) +{ + /* * Used to know which templates I have, which templates I know I can train, things like that. * Mostly unused. */ -var TemplateManager = function(gameState) { +m.TemplateManager = function(gameState) { var self = this; this.knownTemplatesList = []; @@ -23,7 +26,7 @@ var TemplateManager = function(gameState) { this.getTemplateCounters(gameState); }; -TemplateManager.prototype.AcknowledgeTemplates = function(gameState) +m.TemplateManager.prototype.AcknowledgeTemplates = function(gameState) { var self = this; var myEntities = gameState.getOwnEntities(); @@ -39,7 +42,7 @@ TemplateManager.prototype.AcknowledgeTemplates = function(gameState) }); } -TemplateManager.prototype.getBuildableSubtemplates = function(gameState) +m.TemplateManager.prototype.getBuildableSubtemplates = function(gameState) { for each (var templateName in this.knownTemplatesList) { var template = gameState.getTemplate(templateName); @@ -59,7 +62,7 @@ TemplateManager.prototype.getBuildableSubtemplates = function(gameState) } } } -TemplateManager.prototype.getTrainableSubtemplates = function(gameState) +m.TemplateManager.prototype.getTrainableSubtemplates = function(gameState) { for each (var templateName in this.knownTemplatesList) { var template = gameState.getTemplate(templateName); @@ -79,7 +82,7 @@ TemplateManager.prototype.getTrainableSubtemplates = function(gameState) } } } -TemplateManager.prototype.getTemplateCounters = function(gameState) +m.TemplateManager.prototype.getTemplateCounters = function(gameState) { for (var i in this.unitTemplates) { @@ -89,7 +92,7 @@ TemplateManager.prototype.getTemplateCounters = function(gameState) } } // features auto-caching -TemplateManager.prototype.getCountersToClasses = function(gameState,classes,templateName) +m.TemplateManager.prototype.getCountersToClasses = function(gameState,classes,templateName) { if (templateName !== undefined && this.templateCounteredBy[templateName]) return this.templateCounteredBy[templateName]; @@ -114,3 +117,6 @@ TemplateManager.prototype.getCountersToClasses = function(gameState,classes,temp return templates; } + +return m; +}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/aegis/timer.js b/binaries/data/mods/public/simulation/ai/aegis/timer.js index 520d73501b..2d6e9739c7 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/timer.js +++ b/binaries/data/mods/public/simulation/ai/aegis/timer.js @@ -1,3 +1,6 @@ +var AEGIS = function(m) +{ + //The Timer class // The instance of this class is created in the qBot object under the name 'timer' //The methods that are available to call from this instance are: //timer.setTimer : Creates a new timer with the given interval (miliseconds). @@ -11,7 +14,7 @@ //-EmjeR-// Timer class // -var Timer = function() { +m.Timer = function() { ///Private array. var alarmList = []; @@ -94,7 +97,7 @@ var Timer = function() { //-EmjeR-// Alarm class // -function alarm(gameState, id, interval, delay, repeat) { +m.alarm = function(gameState, id, interval, delay, repeat) { this.id = id; this.interval = interval; this.delay = delay; @@ -104,3 +107,6 @@ function alarm(gameState, id, interval, delay, repeat) { this.active = true; this.counter = 0; }; + +return m; +}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/aegis/utils-extend.js b/binaries/data/mods/public/simulation/ai/aegis/utils-extend.js index fc03c213d6..854363590d 100755 --- a/binaries/data/mods/public/simulation/ai/aegis/utils-extend.js +++ b/binaries/data/mods/public/simulation/ai/aegis/utils-extend.js @@ -1,4 +1,7 @@ -function AssocArraytoArray(assocArray) { +var AEGIS = function(m) +{ + +m.AssocArraytoArray = function(assocArray) { var endArray = []; for (var i in assocArray) endArray.push(assocArray[i]); @@ -7,7 +10,7 @@ function AssocArraytoArray(assocArray) { // A is the reference, B must be in "range" of A // this supposes the range is already squared -function inRange(a, b, range)// checks for X distance +m.inRange = function(a, b, range)// checks for X distance { // will avoid unnecessary checking for position in some rare cases... I'm lazy if (a === undefined || b === undefined || range === undefined) @@ -18,9 +21,12 @@ function inRange(a, b, range)// checks for X distance return ((dx*dx + dz*dz ) < range); } // slower than SquareVectorDistance, faster than VectorDistance but not exactly accurate. -function ManhattanDistance(a, b) +m.ManhattanDistance = function(a, b) { var dx = a[0] - b[0]; var dz = a[1] - b[1]; return Math.abs(dx) + Math.abs(dz); } + +return m; +}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/aegis/worker.js b/binaries/data/mods/public/simulation/ai/aegis/worker.js index 9d52db633c..87787e8bea 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/worker.js +++ b/binaries/data/mods/public/simulation/ai/aegis/worker.js @@ -1,15 +1,18 @@ +var AEGIS = function(m) +{ + /** * This class makes a worker do as instructed by the economy manager */ -var Worker = function(ent) { +m.Worker = function(ent) { this.ent = ent; this.maxApproachTime = 45000; this.unsatisfactoryResource = false; // if true we'll reguarly check if we can't have better now. this.baseID = 0; }; -Worker.prototype.update = function(baseManager, gameState) { +m.Worker.prototype.update = function(baseManager, gameState) { this.baseID = baseManager.ID; var subrole = this.ent.getMetadata(PlayerID, "subrole"); @@ -64,7 +67,7 @@ Worker.prototype.update = function(baseManager, gameState) { } Engine.ProfileStop(); } - // debug: show the resource we're gathering from + // m.debug: show the resource we're gathering from //Engine.PostCommand({"type": "set-shading-color", "entities": [this.ent.id()], "rgb": [10,0,0]}); } else if (this.ent.unitAIState().split(".")[1] === "GATHER") { @@ -93,9 +96,9 @@ Worker.prototype.update = function(baseManager, gameState) { && this.ent.unitAIOrderData()[0]["target"]) { var ent = gameState.getEntityById(this.ent.unitAIOrderData()[0]["target"]); - debug ("here " + this.startApproachingResourceAmount + "," + ent.resourceSupplyAmount()); + m.debug ("here " + this.startApproachingResourceAmount + "," + ent.resourceSupplyAmount()); if (ent && this.startApproachingResourceAmount == ent.resourceSupplyAmount() && this.startEnt == ent.id()) { - debug (ent.toString() + " is inaccessible"); + m.debug (ent.toString() + " is inaccessible"); ent.setMetadata(PlayerID, "inaccessible", true); this.ent.flee(ent); this.ent.setMetadata(PlayerID, "subrole", "idle"); @@ -116,7 +119,7 @@ Worker.prototype.update = function(baseManager, gameState) { var ent = gameState.getEntityById(this.gatheringFrom); if ((ent && ent.resourceSupplyAmount() == ent.resourceSupplyMax())) { // if someone gathers from it, it's only that the pathfinder sucks. - debug (ent.toString() + " is inaccessible"); + m.debug (ent.toString() + " is inaccessible"); ent.setMetadata(PlayerID, "inaccessible", true); this.ent.flee(ent); this.ent.setMetadata(PlayerID, "subrole", "idle"); @@ -129,7 +132,7 @@ Worker.prototype.update = function(baseManager, gameState) { var ent = gameState.getEntityById(this.ent.unitAIOrderData()[0].target); if (ent && !ent.isHurt()) { // if someone gathers from it, it's only that the pathfinder sucks. - debug (ent.toString() + " is inaccessible from Combat"); + m.debug (ent.toString() + " is inaccessible from Combat"); ent.setMetadata(PlayerID, "inaccessible", true); this.ent.flee(ent); this.ent.setMetadata(PlayerID, "subrole", "idle"); @@ -186,7 +189,7 @@ Worker.prototype.update = function(baseManager, gameState) { // this can happen in two ways: // -either we were on an unsatisfactory resource last time we started gathering (this.unsatisfactoryResource) // -Or we auto-moved to a bad resource thanks to the great UnitAI. -Worker.prototype.checkUnsatisfactoryResource = function(gameState) { +m.Worker.prototype.checkUnsatisfactoryResource = function(gameState) { if (this.unsatisfactoryResource) return true; if (this.ent.unitAIOrderData().length && this.ent.unitAIState().split(".")[1] === "GATHER" && this.ent.unitAIState().split(".")[2] === "GATHERING" && this.ent.unitAIOrderData()[0]["target"]) @@ -198,7 +201,8 @@ Worker.prototype.checkUnsatisfactoryResource = function(gameState) { return false; }; -Worker.prototype.startGathering = function(baseManager, gameState) { +m.Worker.prototype.startGathering = function(baseManager, gameState) { + var resource = this.ent.getMetadata(PlayerID, "gather-type"); var ent = this.ent; var self = this; @@ -219,10 +223,10 @@ Worker.prototype.startGathering = function(baseManager, gameState) { // TODO: this is a huge part of multi-base support. Count only those in the same base as the worker. var number = 0; - var ourDropsites = EntityCollectionFromIds(gameState,Object.keys(baseManager.dropsites)); + var ourDropsites = m.EntityCollectionFromIds(gameState,Object.keys(baseManager.dropsites)); if (ourDropsites.length === 0) { - debug ("We do not have a dropsite for " + resource + ", aborting"); + m.debug ("We do not have a dropsite for " + resource + ", aborting"); return; } @@ -245,7 +249,7 @@ Worker.prototype.startGathering = function(baseManager, gameState) { return; if (dropsite.position() && (baseManager.dropsites[dropsite.id()][resource][4] > 1000 || (number === 1 && baseManager.dropsites[dropsite.id()][resource][4] > 200) ) && baseManager.dropsites[dropsite.id()][resource][5].length < maxPerDP) { - var dist = SquareVectorDistance(ent.position(), dropsite.position()); + var dist = API3.SquareVectorDistance(ent.position(), dropsite.position()); if (dist < minDropsiteDist){ minDropsiteDist = dist; nearestResources = baseManager.dropsites[dropsite.id()][resource][1]; @@ -254,6 +258,7 @@ Worker.prototype.startGathering = function(baseManager, gameState) { } }); } + // we've found no fitting dropsites close enough from us. // So'll try with far away. if (!nearestResources || nearestResources.length === 0) { @@ -262,7 +267,7 @@ Worker.prototype.startGathering = function(baseManager, gameState) { return; if (dropsite.position() && baseManager.dropsites[dropsite.id()][resource][4] > 400 && baseManager.dropsites[dropsite.id()][resource][5].length < maxPerDP) { - var dist = SquareVectorDistance(ent.position(), dropsite.position()); + var dist = API3.SquareVectorDistance(ent.position(), dropsite.position()); if (dist < minDropsiteDist){ minDropsiteDist = dist; nearestResources = baseManager.dropsites[dropsite.id()][resource][1]; @@ -271,7 +276,7 @@ Worker.prototype.startGathering = function(baseManager, gameState) { } }); } - + if (!nearestResources || nearestResources.length === 0){ if (resource === "food") if (this.buildAnyField(gameState)) @@ -283,7 +288,7 @@ Worker.prototype.startGathering = function(baseManager, gameState) { if (gameState.ai.HQ.switchWorkerBase(gameState, this.ent, resource)) return; - //debug ("No fitting dropsite for " + resource + " found, iterating the map."); + //m.debug ("No fitting dropsite for " + resource + " found, iterating the map."); nearestResources = gameState.getResourceSupplies(resource); this.unsatisfactoryResource = true; // TODO: should try setting up dropsites. @@ -298,20 +303,20 @@ Worker.prototype.startGathering = function(baseManager, gameState) { return; if (gameState.ai.HQ.switchWorkerBase(gameState, this.ent, resource)) return; - debug("No " + resource + " found! (1)"); + m.debug("No " + resource + " found! (1)"); } else { if (gameState.ai.HQ.switchWorkerBase(gameState, this.ent, resource)) return; - debug("No " + resource + " found! (1)"); + m.debug("No " + resource + " found! (1)"); } return; } - //debug("Found " + nearestResources.length + "spots for " + resource); + //m.debug("Found " + nearestResources.length + "spots for " + resource); /*if (!nearestDropsite) { - debug ("No dropsite for " +resource); + m.debug ("No dropsite for " +resource); return; }*/ @@ -324,12 +329,12 @@ Worker.prototype.startGathering = function(baseManager, gameState) { nearestResources.forEach(function(supply) { //}){ // sanity check, perhaps sheep could be garrisoned? if (!supply.position()) { - //debug ("noposition"); + //m.debug ("noposition"); return; } if (supply.getMetadata(PlayerID, "inaccessible") === true) { - //debug ("inaccessible"); + //m.debug ("inaccessible"); return; } @@ -341,19 +346,19 @@ Worker.prototype.startGathering = function(baseManager, gameState) { // Don't gather enemy farms or farms from another base if ((!supply.isOwn(PlayerID) && supply.owner() !== 0) || (supply.isOwn(PlayerID) && supply.getMetadata(PlayerID,"base") !== self.baseID)) { - //debug ("enemy"); + //m.debug ("enemy"); return; } // quickscope accessbility check. if (!gameState.ai.accessibility.pathAvailable(gameState, ent.position(), supply.position())) { - //debug ("nopath"); + //m.debug ("nopath"); return; } // some simple check for chickens: if they're in a square that's inaccessible, we won't gather from them. if (supply.footprintRadius() < 1) { - var fakeMap = new Map(gameState.sharedScript,gameState.getMap().data); + var fakeMap = new API3.Map(gameState.sharedScript,gameState.getMap().data); var id = fakeMap.gamePosToMapPos(supply.position())[0] + fakeMap.width*fakeMap.gamePosToMapPos(supply.position())[1]; if ( (gameState.sharedScript.passabilityClasses["pathfinderObstruction"] & gameState.getMap().data[id]) ) { @@ -363,18 +368,18 @@ Worker.prototype.startGathering = function(baseManager, gameState) { } // measure the distance to the resource (largely irrelevant) - var dist = SquareVectorDistance(supply.position(), ent.position()); + var dist = API3.SquareVectorDistance(supply.position(), ent.position()); if (dist > 4900 && supply.hasClass("Animal")) return; // Add on a factor for the nearest dropsite if one exists if (nearestDropsite !== undefined ){ - dist += 4*SquareVectorDistance(supply.position(), nearestDropsite.position()); + dist += 4*API3.SquareVectorDistance(supply.position(), nearestDropsite.position()); dist /= 5.0; } - var territoryOwner = Map.createTerritoryMap(gameState).getOwner(supply.position()); + var territoryOwner = m.createTerritoryMap(gameState).getOwner(supply.position()); if (territoryOwner != PlayerID && territoryOwner != 0) { dist *= 5.0; //return; @@ -395,7 +400,7 @@ Worker.prototype.startGathering = function(baseManager, gameState) { if (!nearestDropsite) { ourDropsites.forEach(function (dropsite){ //}){ if (dropsite.position()){ - var dist = SquareVectorDistance(pos, dropsite.position()); + var dist = API3.SquareVectorDistance(pos, dropsite.position()); if (dist < minDropsiteDist){ minDropsiteDist = dist; nearestDropsite = dropsite; @@ -404,17 +409,17 @@ Worker.prototype.startGathering = function(baseManager, gameState) { }); if (!nearestDropsite) { - debug ("No dropsite for " +resource); + m.debug ("No dropsite for " +resource); return; } } // if the resource is far away, try to build a farm instead. var tried = false; - if (resource === "food" && SquareVectorDistance(pos,this.ent.position()) > 22500) + if (resource === "food" && API3.SquareVectorDistance(pos,this.ent.position()) > 22500) { tried = this.buildAnyField(gameState); - if (!tried && SquareVectorDistance(pos,this.ent.position()) > 62500) { + if (!tried && API3.SquareVectorDistance(pos,this.ent.position()) > 62500) { // TODO: ought to change behavior here. return; // wait. a farm should appear. } @@ -429,7 +434,7 @@ Worker.prototype.startGathering = function(baseManager, gameState) { else gameState.turnCache["ressGathererNB"][nearestSupply.id()]++; - this.maxApproachTime = Math.max(30000, VectorDistance(pos,this.ent.position()) * 5000); + this.maxApproachTime = Math.max(30000, API3.VectorDistance(pos,this.ent.position()) * 5000); this.startApproachingResourceAmount = ent.resourceSupplyAmount(); this.startEnt = ent.id(); ent.gather(nearestSupply); @@ -443,7 +448,7 @@ Worker.prototype.startGathering = function(baseManager, gameState) { return; if (resource !== "food") - debug("No " + resource + " found! (2)"); + m.debug("No " + resource + " found! (2)"); // If we had a fitting closest dropsite with a lot of resources, mark it as not good. It means it's probably full. Then retry. // it'll be resetted next time it's counted anyway. if (nearestDropsite && nearestDropsite.getMetadata(PlayerID, "resource-quantity-" +resource)+nearestDropsite.getMetadata(PlayerID, "resource-quantity-far-" +resource) > 400) @@ -456,7 +461,7 @@ Worker.prototype.startGathering = function(baseManager, gameState) { }; // Makes the worker deposit the currently carried resources at the closest dropsite -Worker.prototype.returnResources = function(gameState){ +m.Worker.prototype.returnResources = function(gameState){ if (!this.ent.resourceCarrying() || this.ent.resourceCarrying().length === 0){ return true; // assume we're OK. } @@ -472,7 +477,7 @@ Worker.prototype.returnResources = function(gameState){ var dist = Math.min(); gameState.getOwnDropsites(resource).forEach(function(dropsite){ if (dropsite.position()){ - var d = SquareVectorDistance(self.ent.position(), dropsite.position()); + var d = API3.SquareVectorDistance(self.ent.position(), dropsite.position()); if (d < dist){ dist = d; closestDropsite = dropsite; @@ -481,7 +486,7 @@ Worker.prototype.returnResources = function(gameState){ }); if (!closestDropsite){ - debug("No dropsite found to deposit " + resource); + m.debug("No dropsite found to deposit " + resource); return false; } @@ -489,7 +494,7 @@ Worker.prototype.returnResources = function(gameState){ return true; }; -Worker.prototype.startHunting = function(gameState, baseManager){ +m.Worker.prototype.startHunting = function(gameState, baseManager){ var ent = this.ent; if (!ent.position() || !baseManager.isHunting) @@ -500,7 +505,7 @@ Worker.prototype.startHunting = function(gameState, baseManager){ var resources = gameState.getResourceSupplies("food"); if (resources.length === 0){ - debug("No food found to hunt!"); + m.debug("No food found to hunt!"); return; } @@ -522,9 +527,9 @@ Worker.prototype.startHunting = function(gameState, baseManager){ return; // measure the distance to the resource - var dist = SquareVectorDistance(supply.position(), ent.position()); + var dist = API3.SquareVectorDistance(supply.position(), ent.position()); - var territoryOwner = Map.createTerritoryMap(gameState).getOwner(supply.position()); + var territoryOwner = m.createTerritoryMap(gameState).getOwner(supply.position()); if (territoryOwner != PlayerID && territoryOwner != 0) { dist *= 3.0; } @@ -547,7 +552,7 @@ Worker.prototype.startHunting = function(gameState, baseManager){ // find a fitting dropsites in case we haven't already. gameState.getOwnDropsites("food").forEach(function (dropsite){ //}){ if (dropsite.position()){ - var dist = SquareVectorDistance(pos, dropsite.position()); + var dist = API3.SquareVectorDistance(pos, dropsite.position()); if (dist < minDropsiteDist){ minDropsiteDist = dist; nearestDropsite = dropsite; @@ -558,7 +563,7 @@ Worker.prototype.startHunting = function(gameState, baseManager){ { baseManager.isHunting = false; ent.setMetadata(PlayerID, "role", undefined); - debug ("No dropsite for hunting food"); + m.debug ("No dropsite for hunting food"); return; } if (minDropsiteDist > 35000) { @@ -571,11 +576,11 @@ Worker.prototype.startHunting = function(gameState, baseManager){ } else { baseManager.isHunting = false; ent.setMetadata(PlayerID, "role", undefined); - debug("No food found for hunting! (2)"); + m.debug("No food found for hunting! (2)"); } }; -Worker.prototype.getResourceType = function(type){ +m.Worker.prototype.getResourceType = function(type){ if (!type || !type.generic){ return undefined; } @@ -587,7 +592,7 @@ Worker.prototype.getResourceType = function(type){ } }; -Worker.prototype.getGatherRate = function(gameState) { +m.Worker.prototype.getGatherRate = function(gameState) { if (this.ent.getMetadata(PlayerID,"subrole") !== "gatherer") return 0; var rates = this.ent.resourceGatherRates(); @@ -601,7 +606,7 @@ Worker.prototype.getGatherRate = function(gameState) { if (type.generic == "treasure") return 1000; var tstring = type.generic + "." + type.specific; - //debug (+rates[tstring] + " for " + tstring + " for " + this.ent._templateName); + //m.debug (+rates[tstring] + " for " + tstring + " for " + this.ent._templateName); if (rates[tstring]) return rates[tstring]; return 0; @@ -609,10 +614,10 @@ Worker.prototype.getGatherRate = function(gameState) { return 0; }; -Worker.prototype.buildAnyField = function(gameState){ +m.Worker.prototype.buildAnyField = function(gameState){ var self = this; var okay = false; - var foundations = gameState.getOwnFoundations().filter(Filters.byMetadata(PlayerID,"base",this.baseID)); + var foundations = gameState.getOwnFoundations().filter(API3.Filters.byMetadata(PlayerID,"base",this.baseID)); foundations.filterNearest(this.ent.position(), foundations.length); foundations.forEach(function (found) { if (found._template.BuildRestrictions.Category === "Field" && !okay) { @@ -636,3 +641,6 @@ Worker.prototype.buildAnyField = function(gameState){ } return okay; }; + +return m; +}(AEGIS); diff --git a/binaries/data/mods/public/simulation/ai/common-api-v2/base.js b/binaries/data/mods/public/simulation/ai/common-api-v2/base.js index 07ef3e1a71..cc034353c0 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v2/base.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v2/base.js @@ -1,3 +1,5 @@ +var PlayerID = -1; + function BaseAI(settings) { if (!settings) @@ -112,8 +114,9 @@ BaseAI.prototype.GetTemplate = function(name) return null; }; -BaseAI.prototype.HandleMessage = function(state) +BaseAI.prototype.HandleMessage = function(state, playerID) { + PlayerID = playerID; if (!this._entities) { // Do a (shallow) clone of all the initial entity properties (in order @@ -237,7 +240,7 @@ BaseAI.prototype.OnUpdate = function() BaseAI.prototype.chat = function(message) { - Engine.PostCommand({"type": "chat", "message": message}); + Engine.PostCommand(PlayerID, {"type": "chat", "message": message}); }; BaseAI.prototype.registerUpdatingEntityCollection = function(entCollection) diff --git a/binaries/data/mods/public/simulation/ai/common-api-v2/entity.js b/binaries/data/mods/public/simulation/ai/common-api-v2/entity.js index a1c915e78a..1bb7d420ee 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v2/entity.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v2/entity.js @@ -372,40 +372,40 @@ var Entity = Class({ move: function(x, z, queued) { queued = queued || false; - Engine.PostCommand({"type": "walk", "entities": [this.id()], "x": x, "z": z, "queued": queued}); + Engine.PostCommand(PlayerID, {"type": "walk", "entities": [this.id()], "x": x, "z": z, "queued": queued}); return this; }, garrison: function(target) { - Engine.PostCommand({"type": "garrison", "entities": [this.id()], "target": target.id(),"queued": false}); + Engine.PostCommand(PlayerID, {"type": "garrison", "entities": [this.id()], "target": target.id(),"queued": false}); return this; }, attack: function(unitId) { - Engine.PostCommand({"type": "attack", "entities": [this.id()], "target": unitId, "queued": false}); + Engine.PostCommand(PlayerID, {"type": "attack", "entities": [this.id()], "target": unitId, "queued": false}); return this; }, gather: function(target, queued) { queued = queued || false; - Engine.PostCommand({"type": "gather", "entities": [this.id()], "target": target.id(), "queued": queued}); + Engine.PostCommand(PlayerID, {"type": "gather", "entities": [this.id()], "target": target.id(), "queued": queued}); return this; }, repair: function(target, queued) { queued = queued || false; - Engine.PostCommand({"type": "repair", "entities": [this.id()], "target": target.id(), "autocontinue": false, "queued": queued}); + Engine.PostCommand(PlayerID, {"type": "repair", "entities": [this.id()], "target": target.id(), "autocontinue": false, "queued": queued}); return this; }, returnResources: function(target, queued) { queued = queued || false; - Engine.PostCommand({"type": "returnresource", "entities": [this.id()], "target": target.id(), "queued": queued}); + Engine.PostCommand(PlayerID, {"type": "returnresource", "entities": [this.id()], "target": target.id(), "queued": queued}); return this; }, destroy: function() { - Engine.PostCommand({"type": "delete-entities", "entities": [this.id()]}); + Engine.PostCommand(PlayerID, {"type": "delete-entities", "entities": [this.id()]}); return this; }, @@ -423,7 +423,7 @@ var Entity = Class({ return this; } - Engine.PostCommand({ + Engine.PostCommand(PlayerID, { "type": "train", "entities": [this.id()], "template": type, @@ -437,7 +437,7 @@ var Entity = Class({ // TODO: verify this unit can construct this, just for internal // sanity-checking and error reporting - Engine.PostCommand({ + Engine.PostCommand(PlayerID, { "type": "construct", "entities": [this.id()], "template": template, diff --git a/binaries/data/mods/public/simulation/ai/common-api-v2/entitycollection.js b/binaries/data/mods/public/simulation/ai/common-api-v2/entitycollection.js index 4afbb69bc0..f68aa9a59d 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v2/entitycollection.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v2/entitycollection.js @@ -109,13 +109,13 @@ EntityCollection.prototype.forEach = function(callback, thisp) EntityCollection.prototype.move = function(x, z, queued) { queued = queued || false; - Engine.PostCommand({"type": "walk", "entities": this.toIdArray(), "x": x, "z": z, "queued": queued}); + Engine.PostCommand(PlayerID, {"type": "walk", "entities": this.toIdArray(), "x": x, "z": z, "queued": queued}); return this; }; EntityCollection.prototype.destroy = function() { - Engine.PostCommand({"type": "delete-entities", "entities": this.toIdArray()}); + Engine.PostCommand(PlayerID, {"type": "delete-entities", "entities": this.toIdArray()}); return this; }; diff --git a/binaries/data/mods/public/simulation/ai/common-api-v3/baseAI.js b/binaries/data/mods/public/simulation/ai/common-api-v3/baseAI.js index 528b9ce9c9..d5998fe287 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v3/baseAI.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v3/baseAI.js @@ -1,19 +1,22 @@ var PlayerID = -1; -function BaseAI(settings) +var API3 = (function() { + +var m = {}; + +m.BaseAI = function(settings) { if (!settings) return; this.player = settings.player; - PlayerID = this.player; // played turn, in case you don't want the AI to play every turn. this.turn = 0; -} +}; //Return a simple object (using no classes etc) that will be serialized into saved games -BaseAI.prototype.Serialize = function() +m.BaseAI.prototype.Serialize = function() { // TODO: ought to get the AI script subclass to serialize its own state // TODO: actually this is part of a larger reflection on wether AIs should or not. @@ -22,15 +25,16 @@ BaseAI.prototype.Serialize = function() //Called after the constructor when loading a saved game, with 'data' being //whatever Serialize() returned -BaseAI.prototype.Deserialize = function(data, sharedScript) +m.BaseAI.prototype.Deserialize = function(data, sharedScript) { // TODO: ought to get the AI script subclass to deserialize its own state // TODO: actually this is part of a larger reflection on wether AIs should or not. this.isDeserialized = true; }; -BaseAI.prototype.Init = function(state, sharedAI) +m.BaseAI.prototype.Init = function(state, playerID, sharedAI) { + PlayerID = playerID; // define some references this.entities = sharedAI.entities; this.templates = sharedAI.templates; @@ -43,7 +47,7 @@ BaseAI.prototype.Init = function(state, sharedAI) this.techModifications = sharedAI._techModifications[this.player]; this.playerData = sharedAI.playersData[this.player]; - this.gameState = sharedAI.gameState[PlayerID]; + this.gameState = sharedAI.gameState[this.player]; this.gameState.ai = this; this.sharedScript = sharedAI; @@ -52,13 +56,14 @@ BaseAI.prototype.Init = function(state, sharedAI) this.CustomInit(this.gameState, this.sharedScript); } -BaseAI.prototype.CustomInit = function() +m.BaseAI.prototype.CustomInit = function() { // AIs override this function }; -BaseAI.prototype.HandleMessage = function(state, sharedAI) +m.BaseAI.prototype.HandleMessage = function(state, playerID, sharedAI) { this.events = sharedAI.events; + PlayerID = playerID; if (this.isDeserialized && this.turn !== 0) { @@ -70,20 +75,24 @@ BaseAI.prototype.HandleMessage = function(state, sharedAI) this.OnUpdate(sharedAI); }; -BaseAI.prototype.OnUpdate = function() +m.BaseAI.prototype.OnUpdate = function() { // AIs override this function }; -BaseAI.prototype.chat = function(message) +m.BaseAI.prototype.chat = function(message) { - Engine.PostCommand({"type": "chat", "message": message}); + Engine.PostCommand(PlayerID,{"type": "chat", "message": message}); }; -BaseAI.prototype.chatTeam = function(message) +m.BaseAI.prototype.chatTeam = function(message) { - Engine.PostCommand({"type": "chat", "message": "/team " +message}); + Engine.PostCommand(PlayerID,{"type": "chat", "message": "/team " +message}); }; -BaseAI.prototype.chatEnemies = function(message) +m.BaseAI.prototype.chatEnemies = function(message) { - Engine.PostCommand({"type": "chat", "message": "/enemy " +message}); + Engine.PostCommand(PlayerID,{"type": "chat", "message": "/enemy " +message}); }; +return m; + +}()); + diff --git a/binaries/data/mods/public/simulation/ai/common-api-v3/class.js b/binaries/data/mods/public/simulation/ai/common-api-v3/class.js index d2bfdcb133..2767debace 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v3/class.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v3/class.js @@ -1,8 +1,10 @@ +var API3 = function(m) +{ /** * Provides a nicer syntax for defining classes, * with support for OO-style inheritance. */ -function Class(data) +m.Class = function(data) { var ctor; if (data._init) @@ -34,3 +36,7 @@ print((new A).foo+" "+(new A).bar+"\n"); print((new B).foo+" "+(new B).bar+"\n"); print((new C).foo+" "+(new C).bar+"\n"); //*/ + +return m; + +}(API3); diff --git a/binaries/data/mods/public/simulation/ai/common-api-v3/entity.js b/binaries/data/mods/public/simulation/ai/common-api-v3/entity.js index 7cb12bee72..b29eb13eda 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v3/entity.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v3/entity.js @@ -1,4 +1,7 @@ -var EntityTemplate = Class({ +var API3 = function(m) +{ + +m.EntityTemplate = m.Class({ // techModifications should be the tech modifications of only one player. // gamestates handle "GetTemplate" and should push the player's @@ -447,8 +450,8 @@ var EntityTemplate = Class({ -var Entity = Class({ - _super: EntityTemplate, +m.Entity = m.Class({ + _super: m.EntityTemplate, _init: function(sharedAI, entity) { @@ -576,7 +579,7 @@ var Entity = Class({ return this._entity.resourceCarrying; }, - garrisoned: function() { return new EntityCollection(this._ai, this._entity.garrisoned); }, + garrisoned: function() { return new m.EntityCollection(this._ai, this._entity.garrisoned); }, canGarrisonInside: function() { return this._entity.garrisoned.length < this.garrisonMax(); }, @@ -584,32 +587,32 @@ var Entity = Class({ move: function(x, z, queued) { queued = queued || false; - Engine.PostCommand({"type": "walk", "entities": [this.id()], "x": x, "z": z, "queued": queued }); + Engine.PostCommand(PlayerID,{"type": "walk", "entities": [this.id()], "x": x, "z": z, "queued": queued }); return this; }, attackMove: function(x, z, queued) { queued = queued || false; - Engine.PostCommand({"type": "attack-walk", "entities": [this.id()], "x": x, "z": z, "queued": queued }); + Engine.PostCommand(PlayerID,{"type": "attack-walk", "entities": [this.id()], "x": x, "z": z, "queued": queued }); return this; }, // violent, aggressive, defensive, passive, standground setStance: function(stance,queued){ - Engine.PostCommand({"type": "stance", "entities": [this.id()], "name" : stance, "queued": queued }); + Engine.PostCommand(PlayerID,{"type": "stance", "entities": [this.id()], "name" : stance, "queued": queued }); return this; }, // TODO: replace this with the proper "STOP" command stopMoving: function() { if (this.position() !== undefined) - Engine.PostCommand({"type": "walk", "entities": [this.id()], "x": this.position()[0], "z": this.position()[1], "queued": false}); + Engine.PostCommand(PlayerID,{"type": "walk", "entities": [this.id()], "x": this.position()[0], "z": this.position()[1], "queued": false}); }, unload: function(id) { if (!this._template.GarrisonHolder) return undefined; - Engine.PostCommand({"type": "unload", "garrisonHolder": this.id(), "entities": [id]}); + Engine.PostCommand(PlayerID,{"type": "unload", "garrisonHolder": this.id(), "entities": [id]}); return this; }, @@ -617,17 +620,17 @@ var Entity = Class({ unloadAll: function() { if (!this._template.GarrisonHolder) return undefined; - Engine.PostCommand({"type": "unload-all-own", "garrisonHolders": [this.id()]}); + Engine.PostCommand(PlayerID,{"type": "unload-all-own", "garrisonHolders": [this.id()]}); return this; }, garrison: function(target) { - Engine.PostCommand({"type": "garrison", "entities": [this.id()], "target": target.id(),"queued": false}); + Engine.PostCommand(PlayerID,{"type": "garrison", "entities": [this.id()], "target": target.id(),"queued": false}); return this; }, attack: function(unitId) { - Engine.PostCommand({"type": "attack", "entities": [this.id()], "target": unitId, "queued": false}); + Engine.PostCommand(PlayerID,{"type": "attack", "entities": [this.id()], "target": unitId, "queued": false}); return this; }, @@ -635,40 +638,40 @@ var Entity = Class({ flee: function(unitToFleeFrom) { if (this.position() !== undefined && unitToFleeFrom.position() !== undefined) { var FleeDirection = [this.position()[0] - unitToFleeFrom.position()[0],this.position()[1] - unitToFleeFrom.position()[1]]; - var dist = VectorDistance(unitToFleeFrom.position(), this.position() ); + var dist = m.VectorDistance(unitToFleeFrom.position(), this.position() ); FleeDirection[0] = (FleeDirection[0]/dist) * 8; FleeDirection[1] = (FleeDirection[1]/dist) * 8; - Engine.PostCommand({"type": "walk", "entities": [this.id()], "x": this.position()[0] + FleeDirection[0]*5, "z": this.position()[1] + FleeDirection[1]*5, "queued": false}); + Engine.PostCommand(PlayerID,{"type": "walk", "entities": [this.id()], "x": this.position()[0] + FleeDirection[0]*5, "z": this.position()[1] + FleeDirection[1]*5, "queued": false}); } return this; }, gather: function(target, queued) { queued = queued || false; - Engine.PostCommand({"type": "gather", "entities": [this.id()], "target": target.id(), "queued": queued}); + Engine.PostCommand(PlayerID,{"type": "gather", "entities": [this.id()], "target": target.id(), "queued": queued}); return this; }, repair: function(target, queued) { queued = queued || false; - Engine.PostCommand({"type": "repair", "entities": [this.id()], "target": target.id(), "autocontinue": false, "queued": queued}); + Engine.PostCommand(PlayerID,{"type": "repair", "entities": [this.id()], "target": target.id(), "autocontinue": false, "queued": queued}); return this; }, returnResources: function(target, queued) { queued = queued || false; - Engine.PostCommand({"type": "returnresource", "entities": [this.id()], "target": target.id(), "queued": queued}); + Engine.PostCommand(PlayerID,{"type": "returnresource", "entities": [this.id()], "target": target.id(), "queued": queued}); return this; }, destroy: function() { - Engine.PostCommand({"type": "delete-entities", "entities": [this.id()] }); + Engine.PostCommand(PlayerID,{"type": "delete-entities", "entities": [this.id()] }); return this; }, barter: function(buyType, sellType, amount) { - Engine.PostCommand({"type": "barter", "sell" : sellType, "buy" : buyType, "amount" : amount }); + Engine.PostCommand(PlayerID,{"type": "barter", "sell" : sellType, "buy" : buyType, "amount" : amount }); return this; }, @@ -686,7 +689,7 @@ var Entity = Class({ return this; } - Engine.PostCommand({ + Engine.PostCommand(PlayerID,{ "type": "train", "entities": [this.id()], "template": type, @@ -699,8 +702,7 @@ var Entity = Class({ construct: function(template, x, z, angle, metadata) { // TODO: verify this unit can construct this, just for internal // sanity-checking and error reporting - - Engine.PostCommand({ + Engine.PostCommand(PlayerID,{ "type": "construct", "entities": [this.id()], "template": template, @@ -716,12 +718,12 @@ var Entity = Class({ }, research: function(template) { - Engine.PostCommand({ "type": "research", "entity": this.id(), "template": template }); + Engine.PostCommand(PlayerID,{ "type": "research", "entity": this.id(), "template": template }); return this; }, stopProduction: function(id) { - Engine.PostCommand({ "type": "stop-production", "entity": this.id(), "id": id }); + Engine.PostCommand(PlayerID,{ "type": "stop-production", "entity": this.id(), "id": id }); return this; }, @@ -732,9 +734,12 @@ var Entity = Class({ for (var i in queue) { if (queue[i].progress < percentToStopAt) - Engine.PostCommand({ "type": "stop-production", "entity": this.id(), "id": queue[i].id }); + Engine.PostCommand(PlayerID,{ "type": "stop-production", "entity": this.id(), "id": queue[i].id }); } return this; } }); +return m; + +}(API3); diff --git a/binaries/data/mods/public/simulation/ai/common-api-v3/entitycollection.js b/binaries/data/mods/public/simulation/ai/common-api-v3/entitycollection.js index 25adc95f96..af0ea488e5 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v3/entitycollection.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v3/entitycollection.js @@ -1,4 +1,7 @@ -function EntityCollection(sharedAI, entities, filters) +var API3 = function(m) +{ + +m.EntityCollection = function(sharedAI, entities, filters) { this._ai = sharedAI; this._entities = entities || {}; @@ -22,22 +25,22 @@ function EntityCollection(sharedAI, entities, filters) } }); this.frozen = false; -} +}; // If an entitycollection is frozen, it will never automatically add a unit. // But can remove one. // this makes it easy to create entity collection that will auto-remove dead units // but never add new ones. -EntityCollection.prototype.freeze = function() +m.EntityCollection.prototype.freeze = function() { this.frozen = true; }; -EntityCollection.prototype.defreeze = function() +m.EntityCollection.prototype.defreeze = function() { this.frozen = false; }; -EntityCollection.prototype.allowQuickIter = function() +m.EntityCollection.prototype.allowQuickIter = function() { this._quickIter = true; this._entitiesArray = []; @@ -45,7 +48,7 @@ EntityCollection.prototype.allowQuickIter = function() this._entitiesArray.push(ent); }; -EntityCollection.prototype.toIdArray = function() +m.EntityCollection.prototype.toIdArray = function() { var ret = []; for (var id in this._entities) @@ -53,7 +56,7 @@ EntityCollection.prototype.toIdArray = function() return ret; }; -EntityCollection.prototype.toEntityArray = function() +m.EntityCollection.prototype.toEntityArray = function() { if (this._quickIter === true) return this._entitiesArray; @@ -63,7 +66,7 @@ EntityCollection.prototype.toEntityArray = function() return ret; }; -EntityCollection.prototype.toString = function() +m.EntityCollection.prototype.toString = function() { return "[EntityCollection " + this.toEntityArray().join(" ") + "]"; }; @@ -71,7 +74,7 @@ EntityCollection.prototype.toString = function() /** * Returns the (at most) n entities nearest to targetPos. */ -EntityCollection.prototype.filterNearest = function(targetPos, n) +m.EntityCollection.prototype.filterNearest = function(targetPos, n) { // Compute the distance of each entity var data = []; // [id, ent, distance] @@ -82,14 +85,14 @@ EntityCollection.prototype.filterNearest = function(targetPos, n) { var ent = this._entitiesArray[i]; if (ent.position() !== -1) - data.push([ent.id(), ent, SquareVectorDistance(targetPos, ent.position())]); + data.push([ent.id(), ent, m.SquareVectorDistance(targetPos, ent.position())]); } } else { for (var id in this._entities) { var ent = this._entities[id]; if (ent.position() !== -1) - data.push([id, ent, SquareVectorDistance(targetPos, ent.position())]); + data.push([id, ent, m.SquareVectorDistance(targetPos, ent.position())]); } } @@ -102,10 +105,10 @@ EntityCollection.prototype.filterNearest = function(targetPos, n) for (var i = 0; i < length; ++i) ret[data[i][0]] = data[i][1]; - return new EntityCollection(this._ai, ret); + return new m.EntityCollection(this._ai, ret); }; -EntityCollection.prototype.filter = function(filter, thisp) +m.EntityCollection.prototype.filter = function(filter, thisp) { if (typeof(filter) == "function") filter = {"func": filter, "dynamicProperties": []}; @@ -129,10 +132,10 @@ EntityCollection.prototype.filter = function(filter, thisp) } } - return new EntityCollection(this._ai, ret, this._filters.concat([filter])); + return new m.EntityCollection(this._ai, ret, this._filters.concat([filter])); }; -EntityCollection.prototype.filter_raw = function(callback, thisp) +m.EntityCollection.prototype.filter_raw = function(callback, thisp) { var ret = {}; for (var id in this._entities) @@ -142,10 +145,10 @@ EntityCollection.prototype.filter_raw = function(callback, thisp) if (callback.call(thisp, val, id, this)) ret[id] = ent; } - return new EntityCollection(this._ai, ret); + return new m.EntityCollection(this._ai, ret); }; -EntityCollection.prototype.forEach = function(callback) +m.EntityCollection.prototype.forEach = function(callback) { if (this._quickIter === true) { @@ -162,7 +165,7 @@ EntityCollection.prototype.forEach = function(callback) return this; }; -EntityCollection.prototype.filterNearest = function(targetPos, n) +m.EntityCollection.prototype.filterNearest = function(targetPos, n) { // Compute the distance of each entity var data = []; // [ [id, ent, distance], ... ] @@ -170,7 +173,7 @@ EntityCollection.prototype.filterNearest = function(targetPos, n) { var ent = this._entities[id]; if (ent.position()) - data.push([id, ent, SquareVectorDistance(targetPos, ent.position())]); + data.push([id, ent, m.SquareVectorDistance(targetPos, ent.position())]); } // Sort by increasing distance @@ -182,22 +185,22 @@ EntityCollection.prototype.filterNearest = function(targetPos, n) for each (var val in data.slice(0, n)) ret[val[0]] = val[1]; - return new EntityCollection(this._ai, ret); + return new m.EntityCollection(this._ai, ret); }; -EntityCollection.prototype.move = function(x, z, queued) +m.EntityCollection.prototype.move = function(x, z, queued) { queued = queued || false; - Engine.PostCommand({"type": "walk", "entities": this.toIdArray(), "x": x, "z": z, "queued": queued}); + Engine.PostCommand(PlayerID,{"type": "walk", "entities": this.toIdArray(), "x": x, "z": z, "queued": queued}); return this; }; -EntityCollection.prototype.attackMove = function(x, z, queued) +m.EntityCollection.prototype.attackMove = function(x, z, queued) { queued = queued || false; Engine.PostCommand({"type": "attack-walk", "entities": this.toIdArray(), "x": x, "z": z, "queued": queued}); return this; }; -EntityCollection.prototype.moveIndiv = function(x, z, queued) +m.EntityCollection.prototype.moveIndiv = function(x, z, queued) { queued = queued || false; for (var id in this._entities) @@ -206,16 +209,16 @@ EntityCollection.prototype.moveIndiv = function(x, z, queued) // It disables JIT compiling of this loop. Don't remove it unless you are sure that the underlying issue has been resolved! // TODO: Check this again after the SpiderMonkey upgrade. try {} finally {} - Engine.PostCommand({"type": "walk", "entities": [this._entities[id].id()], "x": x, "z": z, "queued": queued}); + Engine.PostCommand(PlayerID,{"type": "walk", "entities": [this._entities[id].id()], "x": x, "z": z, "queued": queued}); } return this; }; -EntityCollection.prototype.destroy = function() +m.EntityCollection.prototype.destroy = function() { - Engine.PostCommand({"type": "delete-entities", "entities": this.toIdArray()}); + Engine.PostCommand(PlayerID,{"type": "delete-entities", "entities": this.toIdArray()}); return this; }; -EntityCollection.prototype.attack = function(unit) +m.EntityCollection.prototype.attack = function(unit) { var unitId; if (typeof(unit) === "Entity") @@ -226,18 +229,18 @@ EntityCollection.prototype.attack = function(unit) { unitId = unit; } - Engine.PostCommand({"type": "attack", "entities": this.toIdArray(), "target": unitId, "queued": false}); + Engine.PostCommand(PlayerID,{"type": "attack", "entities": this.toIdArray(), "target": unitId, "queued": false}); return this; }; // violent, aggressive, defensive, passive, standground -EntityCollection.prototype.setStance = function(stance) +m.EntityCollection.prototype.setStance = function(stance) { - Engine.PostCommand({"type": "stance", "entities": this.toIdArray(), "name" : stance, "queued": false}); + Engine.PostCommand(PlayerID,{"type": "stance", "entities": this.toIdArray(), "name" : stance, "queued": false}); return this; }; // Returns the average position of all units -EntityCollection.prototype.getCentrePosition = function() +m.EntityCollection.prototype.getCentrePosition = function() { var sumPos = [0, 0]; var count = 0; @@ -263,7 +266,7 @@ EntityCollection.prototype.getCentrePosition = function() // returns the average position from the sample first units. // This might be faster for huge collections, but there's // always a risk that it'll be unprecise. -EntityCollection.prototype.getApproximatePosition = function(sample) +m.EntityCollection.prototype.getApproximatePosition = function(sample) { var sumPos = [0, 0]; var i = 0; @@ -291,7 +294,7 @@ EntityCollection.prototype.getApproximatePosition = function(sample) // Removes an entity from the collection, returns true if the entity was a member, false otherwise -EntityCollection.prototype.removeEnt = function(ent) +m.EntityCollection.prototype.removeEnt = function(ent) { if (this._entities[ent.id()]) { @@ -310,7 +313,7 @@ EntityCollection.prototype.removeEnt = function(ent) }; // Adds an entity to the collection, returns true if the entity was not member, false otherwise -EntityCollection.prototype.addEnt = function(ent) +m.EntityCollection.prototype.addEnt = function(ent) { if (this._entities[ent.id()]) { @@ -333,7 +336,7 @@ EntityCollection.prototype.addEnt = function(ent) // Force can add a unit despite a freezing. // If an entitycollection is frozen, it will never automatically add a unit. // But can remove one. -EntityCollection.prototype.updateEnt = function(ent, force) +m.EntityCollection.prototype.updateEnt = function(ent, force) { var passesFilters = true; for each (var filter in this._filters) @@ -353,17 +356,17 @@ EntityCollection.prototype.updateEnt = function(ent, force) } }; -EntityCollection.prototype.registerUpdates = function(noPush) +m.EntityCollection.prototype.registerUpdates = function(noPush) { this._ai.registerUpdatingEntityCollection(this,noPush); }; -EntityCollection.prototype.unregister = function() +m.EntityCollection.prototype.unregister = function() { this._ai.removeUpdatingEntityCollection(this); }; -EntityCollection.prototype.dynamicProperties = function() +m.EntityCollection.prototype.dynamicProperties = function() { var ret = []; for each (var filter in this._filters) @@ -374,12 +377,16 @@ EntityCollection.prototype.dynamicProperties = function() return ret; }; -EntityCollection.prototype.setUID = function(id) +m.EntityCollection.prototype.setUID = function(id) { this._UID = id; }; -EntityCollection.prototype.getUID = function() +m.EntityCollection.prototype.getUID = function() { return this._UID; }; + +return m; + +}(API3); diff --git a/binaries/data/mods/public/simulation/ai/common-api-v3/filters.js b/binaries/data/mods/public/simulation/ai/common-api-v3/filters.js index 2787dc11cd..0119ad3bce 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v3/filters.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v3/filters.js @@ -1,4 +1,7 @@ -var Filters = { +var API3 = function(m) +{ + +m.Filters = { byType: function(type){ return {"func" : function(ent){ return ent.templateName() === type; @@ -169,7 +172,7 @@ var Filters = { if (ent.position() === undefined){ return false; }else{ - return (SquareVectorDistance(startPoint, ent.position()) < dist*dist); + return (m.SquareVectorDistance(startPoint, ent.position()) < dist*dist); } }, "dynamicProperties": ['position']}; @@ -181,7 +184,7 @@ var Filters = { if (!ent.position()){ return false; }else{ - return (SquareVectorDistance(startPoint, ent.position()) < dist*dist); + return (m.SquareVectorDistance(startPoint, ent.position()) < dist*dist); } }, "dynamicProperties": []}; @@ -238,3 +241,8 @@ var Filters = { "dynamicProperties": []}; } }; + +return m; + +}(API3); + diff --git a/binaries/data/mods/public/simulation/ai/common-api-v3/gamestate.js b/binaries/data/mods/public/simulation/ai/common-api-v3/gamestate.js index 150eb8b4dd..7b5c43bb9d 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v3/gamestate.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v3/gamestate.js @@ -1,8 +1,12 @@ +var API3 = function(m) +{ + + /** * Provides an API for the rest of the AI scripts to query the world state at a * higher level than the raw data. */ -var GameState = function() { +m.GameState = function() { this.ai = null; // must be updated by the AIs. this.cellSize = 4.0; // Size of each map tile @@ -10,7 +14,7 @@ var GameState = function() { this.turnCache = {}; }; -GameState.prototype.init = function(SharedScript, state, player) { +m.GameState.prototype.init = function(SharedScript, state, player) { this.sharedScript = SharedScript; this.EntCollecNames = SharedScript._entityCollectionsName; this.EntCollec = SharedScript._entityCollections; @@ -23,7 +27,7 @@ GameState.prototype.init = function(SharedScript, state, player) { this.techModifications = SharedScript._techModifications[this.player]; }; -GameState.prototype.update = function(SharedScript, state) { +m.GameState.prototype.update = function(SharedScript, state) { this.sharedScript = SharedScript; this.EntCollecNames = SharedScript._entityCollectionsName; this.EntCollec = SharedScript._entityCollections; @@ -39,7 +43,7 @@ GameState.prototype.update = function(SharedScript, state) { this.turnCache = {}; }; -GameState.prototype.updatingCollection = function(id, filter, collection, allowQuick){ +m.GameState.prototype.updatingCollection = function(id, filter, collection, allowQuick){ // automatically add the player ID id = this.player + "-" + id; if (!this.EntCollecNames[id]){ @@ -56,7 +60,7 @@ GameState.prototype.updatingCollection = function(id, filter, collection, allowQ return this.EntCollecNames[id]; }; -GameState.prototype.destroyCollection = function(id){ +m.GameState.prototype.destroyCollection = function(id){ // automatically add the player ID id = this.player + "-" + id; @@ -65,7 +69,7 @@ GameState.prototype.destroyCollection = function(id){ delete this.EntCollecNames[id]; } }; -GameState.prototype.getEC = function(id){ +m.GameState.prototype.getEC = function(id){ // automatically add the player ID id = this.player + "-" + id; @@ -74,7 +78,7 @@ GameState.prototype.getEC = function(id){ return undefined; }; -GameState.prototype.updatingGlobalCollection = function(id, filter, collection, allowQuick) { +m.GameState.prototype.updatingGlobalCollection = function(id, filter, collection, allowQuick) { if (!this.EntCollecNames[id]){ if (collection !== undefined) this.EntCollecNames[id] = collection.filter(filter); @@ -88,21 +92,21 @@ GameState.prototype.updatingGlobalCollection = function(id, filter, collection, return this.EntCollecNames[id]; }; -GameState.prototype.destroyGlobalCollection = function(id) +m.GameState.prototype.destroyGlobalCollection = function(id) { if (this.EntCollecNames[id] !== undefined){ this.sharedScript.removeUpdatingEntityCollection(this.EntCollecNames[id]); delete this.EntCollecNames[id]; } }; -GameState.prototype.getGEC = function(id) +m.GameState.prototype.getGEC = function(id) { if (this.EntCollecNames[id] !== undefined) return this.EntCollecNames[id]; return undefined; }; -GameState.prototype.currentPhase = function() +m.GameState.prototype.currentPhase = function() { if (this.isResearched("phase_city")) return 3; @@ -113,30 +117,30 @@ GameState.prototype.currentPhase = function() return 0; }; -GameState.prototype.townPhase = function() +m.GameState.prototype.townPhase = function() { if (this.playerData.civ == "athen") return "phase_town_athen"; return "phase_town_generic"; }; -GameState.prototype.cityPhase = function() +m.GameState.prototype.cityPhase = function() { return "phase_city_generic"; }; -GameState.prototype.isResearched = function(template) +m.GameState.prototype.isResearched = function(template) { return this.playerData.researchedTechs[template] !== undefined; }; // true if started or queued -GameState.prototype.isResearching = function(template) +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. -GameState.prototype.canResearch = function(techTemplateName, noRequirementCheck) +m.GameState.prototype.canResearch = function(techTemplateName, noRequirementCheck) { var template = this.getTemplate(techTemplateName); if (!template) @@ -170,7 +174,7 @@ GameState.prototype.canResearch = function(techTemplateName, noRequirementCheck) // Private function for checking a set of requirements is met // basically copies TechnologyManager's -GameState.prototype.checkTechRequirements = function (reqs) +m.GameState.prototype.checkTechRequirements = function (reqs) { // If there are no requirements then all requirements are met if (!reqs) @@ -229,73 +233,73 @@ GameState.prototype.checkTechRequirements = function (reqs) }; -GameState.prototype.getTimeElapsed = function() +m.GameState.prototype.getTimeElapsed = function() { return this.timeElapsed; }; -GameState.prototype.getTemplate = function(type) +m.GameState.prototype.getTemplate = function(type) { if (this.techTemplates[type] !== undefined) - return new Technology(this.techTemplates, type); + return new m.Technology(this.techTemplates, type); if (!this.templates[type]) return null; - return new EntityTemplate(this.templates[type], this.techModifications); + return new m.EntityTemplate(this.templates[type], this.techModifications); }; -GameState.prototype.applyCiv = function(str) { +m.GameState.prototype.applyCiv = function(str) { return str.replace(/\{civ\}/g, this.playerData.civ); }; -GameState.prototype.civ = function() { +m.GameState.prototype.civ = function() { return this.playerData.civ; }; /** * @returns {Resources} */ -GameState.prototype.getResources = function() { - return new Resources(this.playerData.resourceCounts); +m.GameState.prototype.getResources = function() { + return new m.Resources(this.playerData.resourceCounts); }; -GameState.prototype.getMap = function() { +m.GameState.prototype.getMap = function() { return this.sharedScript.passabilityMap; }; -GameState.prototype.getPopulation = function() { +m.GameState.prototype.getPopulation = function() { return this.playerData.popCount; }; -GameState.prototype.getPopulationLimit = function() { +m.GameState.prototype.getPopulationLimit = function() { return this.playerData.popLimit; }; -GameState.prototype.getPopulationMax = function() { +m.GameState.prototype.getPopulationMax = function() { return this.playerData.popMax; }; -GameState.prototype.getPassabilityClassMask = function(name) { +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]; }; -GameState.prototype.getPlayerID = function() { +m.GameState.prototype.getPlayerID = function() { return this.player; }; -GameState.prototype.isPlayerAlly = function(id) { +m.GameState.prototype.isPlayerAlly = function(id) { return this.playerData.isAlly[id]; }; -GameState.prototype.isPlayerEnemy = function(id) { +m.GameState.prototype.isPlayerEnemy = function(id) { return this.playerData.isEnemy[id]; }; -GameState.prototype.getEnemies = function(){ +m.GameState.prototype.getEnemies = function(){ var ret = []; for (var i in this.playerData.isEnemy){ if (this.playerData.isEnemy[i]){ @@ -305,7 +309,7 @@ GameState.prototype.getEnemies = function(){ return ret; }; -GameState.prototype.isEntityAlly = function(ent) { +m.GameState.prototype.isEntityAlly = function(ent) { if (ent && ent.owner && (typeof ent.owner) === "function"){ return this.playerData.isAlly[ent.owner()]; } else if (ent && ent.owner){ @@ -314,7 +318,7 @@ GameState.prototype.isEntityAlly = function(ent) { return false; }; -GameState.prototype.isEntityEnemy = function(ent) { +m.GameState.prototype.isEntityEnemy = function(ent) { if (ent && ent.owner && (typeof ent.owner) === "function"){ return this.playerData.isEnemy[ent.owner()]; } else if (ent && ent.owner){ @@ -323,7 +327,7 @@ GameState.prototype.isEntityEnemy = function(ent) { return false; }; -GameState.prototype.isEntityOwn = function(ent) { +m.GameState.prototype.isEntityOwn = function(ent) { if (ent && ent.owner && (typeof ent.owner) === "function"){ return ent.owner() == this.player; } else if (ent && ent.owner){ @@ -332,11 +336,11 @@ GameState.prototype.isEntityOwn = function(ent) { return false; }; -GameState.prototype.getOwnEntities = function() { - return this.updatingCollection("own-entities", Filters.byOwner(this.player)); +m.GameState.prototype.getOwnEntities = function() { + return this.updatingCollection("own-entities", m.Filters.byOwner(this.player)); }; -GameState.prototype.getEnemyEntities = function() { +m.GameState.prototype.getEnemyEntities = function() { var diplomacyChange = false; var enemies = this.getEnemies(); if (this.enemies){ @@ -351,17 +355,17 @@ GameState.prototype.getEnemyEntities = function() { } } if (diplomacyChange || !this.enemies){ - return this.updatingCollection("enemy-entities", Filters.byOwners(enemies)); + return this.updatingCollection("enemy-entities", m.Filters.byOwners(enemies)); this.enemies = enemies; } return this.getEC("enemy-entities"); }; -GameState.prototype.getEntities = function() { +m.GameState.prototype.getEntities = function() { return this.entities; }; -GameState.prototype.getEntityById = function(id){ +m.GameState.prototype.getEntityById = function(id){ if (this.entities._entities[id]) { return this.entities._entities[id]; }else{ @@ -370,37 +374,37 @@ GameState.prototype.getEntityById = function(id){ return undefined; }; -GameState.prototype.getOwnEntitiesByMetadata = function(key, value, maintain){ +m.GameState.prototype.getOwnEntitiesByMetadata = function(key, value, maintain){ if (maintain === true) - return this.updatingCollection(key + "-" + value, Filters.byMetadata(this.player, key, value),this.getOwnEntities()); - return this.getOwnEntities().filter(Filters.byMetadata(this.player, key, value)); + 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)); }; -GameState.prototype.getOwnEntitiesByRole = function(role){ +m.GameState.prototype.getOwnEntitiesByRole = function(role){ return this.getOwnEntitiesByMetadata("role", role, true); }; -GameState.prototype.getOwnTrainingFacilities = function(){ - return this.updatingCollection("own-training-facilities", Filters.byTrainingQueue(), this.getOwnEntities(), true); +m.GameState.prototype.getOwnTrainingFacilities = function(){ + return this.updatingCollection("own-training-facilities", m.Filters.byTrainingQueue(), this.getOwnEntities(), true); }; -GameState.prototype.getOwnResearchFacilities = function(){ - return this.updatingCollection("own-research-facilities", Filters.byResearchAvailable(), this.getOwnEntities(), true); +m.GameState.prototype.getOwnResearchFacilities = function(){ + return this.updatingCollection("own-research-facilities", m.Filters.byResearchAvailable(), this.getOwnEntities(), true); }; -GameState.prototype.getOwnEntitiesByType = function(type, maintain){ - var filter = Filters.byType(type); +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.getOwnEntities().filter(filter); }; -GameState.prototype.countEntitiesByType = function(type, maintain) { +m.GameState.prototype.countEntitiesByType = function(type, maintain) { return this.getOwnEntitiesByType(type, maintain).length; }; -GameState.prototype.countEntitiesAndQueuedByType = function(type) { +m.GameState.prototype.countEntitiesAndQueuedByType = function(type) { var count = this.countEntitiesByType(type, true); // Count building foundations @@ -424,7 +428,7 @@ GameState.prototype.countEntitiesAndQueuedByType = function(type) { return count; }; -GameState.prototype.countFoundationsWithType = function(type) { +m.GameState.prototype.countFoundationsWithType = function(type) { var foundationType = "foundation|" + type; var count = 0; this.getOwnEntities().forEach(function(ent) { @@ -435,11 +439,11 @@ GameState.prototype.countFoundationsWithType = function(type) { return count; }; -GameState.prototype.countOwnEntitiesByRole = function(role) { +m.GameState.prototype.countOwnEntitiesByRole = function(role) { return this.getOwnEntitiesByRole(role).length; }; -GameState.prototype.countOwnEntitiesAndQueuedWithRole = function(role) { +m.GameState.prototype.countOwnEntitiesAndQueuedWithRole = function(role) { var count = this.countOwnEntitiesByRole(role); // Count entities in building production queues @@ -452,7 +456,7 @@ GameState.prototype.countOwnEntitiesAndQueuedWithRole = function(role) { return count; }; -GameState.prototype.countOwnQueuedEntitiesWithMetadata = function(data, value) { +m.GameState.prototype.countOwnQueuedEntitiesWithMetadata = function(data, value) { // Count entities in building production queues var count = 0; this.getOwnTrainingFacilities().forEach(function(ent) { @@ -468,7 +472,7 @@ GameState.prototype.countOwnQueuedEntitiesWithMetadata = function(data, value) { * Find buildings that are capable of training the given unit type, and aren't * already too busy. */ -GameState.prototype.findTrainers = function(template) { +m.GameState.prototype.findTrainers = function(template) { var maxQueueLength = 3; // avoid tying up resources in giant training queues return this.getOwnTrainingFacilities().filter(function(ent) { @@ -490,7 +494,7 @@ GameState.prototype.findTrainers = function(template) { /** * Find units that are capable of constructing the given building type. */ -GameState.prototype.findBuilders = function(template) { +m.GameState.prototype.findBuilders = function(template) { return this.getOwnEntities().filter(function(ent) { var buildable = ent.buildableEntities(); @@ -505,7 +509,7 @@ GameState.prototype.findBuilders = function(template) { * Find buildings that are capable of researching the given tech, and aren't * already too busy. */ -GameState.prototype.findResearchers = function(templateName, noRequirementCheck) { +m.GameState.prototype.findResearchers = function(templateName, noRequirementCheck) { // let's check we can research the tech. if (!this.canResearch(templateName, noRequirementCheck)) return []; @@ -532,36 +536,36 @@ GameState.prototype.findResearchers = function(templateName, noRequirementCheck) }); }; -GameState.prototype.getOwnFoundations = function() { - return this.updatingCollection("ownFoundations", Filters.isFoundation(), this.getOwnEntities()); +m.GameState.prototype.getOwnFoundations = function() { + return this.updatingCollection("ownFoundations", m.Filters.isFoundation(), this.getOwnEntities()); }; -GameState.prototype.getOwnDropsites = function(resource){ +m.GameState.prototype.getOwnDropsites = function(resource){ if (resource !== undefined) - return this.updatingCollection("dropsite-own-" + resource, Filters.isDropsite(resource), this.getOwnEntities(), true); - return this.updatingCollection("dropsite-own", Filters.isDropsite(), this.getOwnEntities(), true); + return this.updatingCollection("dropsite-own-" + resource, m.Filters.isDropsite(resource), this.getOwnEntities(), true); + return this.updatingCollection("dropsite-own", m.Filters.isDropsite(), this.getOwnEntities(), true); }; -GameState.prototype.getResourceSupplies = function(resource){ - return this.updatingGlobalCollection("resource-" + resource, Filters.byResource(resource), this.getEntities(), true); +m.GameState.prototype.getResourceSupplies = function(resource){ + return this.updatingGlobalCollection("resource-" + resource, m.Filters.byResource(resource), this.getEntities(), true); }; -GameState.prototype.getEntityLimits = function() { +m.GameState.prototype.getEntityLimits = function() { return this.playerData.entityLimits; }; -GameState.prototype.getEntityCounts = function() { +m.GameState.prototype.getEntityCounts = function() { return this.playerData.entityCounts; }; // Checks whether the maximum number of buildings have been cnstructed for a certain catergory -GameState.prototype.isEntityLimitReached = function(category) { +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]); }; -GameState.prototype.findTrainableUnits = function(classes){ +m.GameState.prototype.findTrainableUnits = function(classes){ var allTrainable = []; this.getOwnEntities().forEach(function(ent) { var trainable = ent.trainableEntities(); @@ -593,7 +597,7 @@ GameState.prototype.findTrainableUnits = function(classes){ // Return all techs which can currently be researched // Does not factor cost. // If there are pairs, both techs are returned. -GameState.prototype.findAvailableTech = function() { +m.GameState.prototype.findAvailableTech = function() { var allResearchable = []; this.getOwnEntities().forEach(function(ent) { @@ -625,16 +629,20 @@ GameState.prototype.findAvailableTech = function() { }; // defcon utilities -GameState.prototype.timeSinceDefconChange = function() { +m.GameState.prototype.timeSinceDefconChange = function() { return this.getTimeElapsed()-this.ai.defconChangeTime; }; -GameState.prototype.setDefcon = function(level,force) { +m.GameState.prototype.setDefcon = function(level,force) { if (this.ai.defcon >= level || force) { this.ai.defcon = level; this.ai.defconChangeTime = this.getTimeElapsed(); } }; -GameState.prototype.defcon = function() { +m.GameState.prototype.defcon = function() { return this.ai.defcon; }; +return m; + +}(API3); + diff --git a/binaries/data/mods/public/simulation/ai/common-api-v3/map-module.js b/binaries/data/mods/public/simulation/ai/common-api-v3/map-module.js index 720e89b435..303edf6e51 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v3/map-module.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v3/map-module.js @@ -1,10 +1,12 @@ +var API3 = function(m) +{ + /* The map module. * Copied with changes from QuantumState's original for qBot, it's a component for storing 8 bit values. */ -const TERRITORY_PLAYER_MASK = 0x3F; - -function Map(sharedScript, originalMap, actualCopy){ +// The function needs to be named too because of the copyConstructor functionality +m.Map = function Map(sharedScript, originalMap, actualCopy){ // get the map to find out the correct dimensions var gameMap = sharedScript.passabilityMap; this.width = gameMap.width; @@ -25,19 +27,19 @@ function Map(sharedScript, originalMap, actualCopy){ this.cellSize = 4; } -Map.prototype.setMaxVal = function(val){ +m.Map.prototype.setMaxVal = function(val){ this.maxVal = val; }; -Map.prototype.gamePosToMapPos = function(p){ +m.Map.prototype.gamePosToMapPos = function(p){ return [Math.floor(p[0]/this.cellSize), Math.floor(p[1]/this.cellSize)]; }; -Map.prototype.point = function(p){ +m.Map.prototype.point = function(p){ var q = this.gamePosToMapPos(p); return this.map[q[0] + this.width * q[1]]; }; -Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type) { +m.Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type) { strength = strength ? +strength : +maxDist; type = type ? type : 'linear'; @@ -90,7 +92,7 @@ Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type) { } }; -Map.prototype.multiplyInfluence = function(cx, cy, maxDist, strength, type) { +m.Map.prototype.multiplyInfluence = function(cx, cy, maxDist, strength, type) { strength = strength ? +strength : +maxDist; type = type ? type : 'constant'; @@ -145,7 +147,7 @@ Map.prototype.multiplyInfluence = function(cx, cy, maxDist, strength, type) { }; // doesn't check for overflow. -Map.prototype.setInfluence = function(cx, cy, maxDist, value) { +m.Map.prototype.setInfluence = function(cx, cy, maxDist, value) { value = value ? value : 0; var x0 = Math.max(0, cx - maxDist); @@ -166,7 +168,7 @@ Map.prototype.setInfluence = function(cx, cy, maxDist, value) { } }; -Map.prototype.sumInfluence = function(cx, cy, radius){ +m.Map.prototype.sumInfluence = function(cx, cy, radius){ var x0 = Math.max(0, cx - radius); var y0 = Math.max(0, cy - radius); var x1 = Math.min(this.width, cx + radius); @@ -192,7 +194,7 @@ Map.prototype.sumInfluence = function(cx, cy, radius){ * neighbours' values. (If the grid is initialised with 0s and 65535s or 255s, the * result of each cell is its Manhattan distance to the nearest 0.) */ -Map.prototype.expandInfluences = function(maximum, map) { +m.Map.prototype.expandInfluences = function(maximum, map) { var grid = this.map; if (map !== undefined) grid = map; @@ -252,7 +254,7 @@ Map.prototype.expandInfluences = function(maximum, map) { } }; -Map.prototype.findBestTile = function(radius, obstructionTiles){ +m.Map.prototype.findBestTile = function(radius, obstructionTiles){ // Find the best non-obstructed tile var bestIdx = 0; var bestVal = -1; @@ -269,14 +271,14 @@ Map.prototype.findBestTile = function(radius, obstructionTiles){ return [bestIdx, bestVal]; }; // Multiplies current map by the parameter map pixelwise -Map.prototype.multiply = function(map, onlyBetter, divider, maxMultiplier){ +m.Map.prototype.multiply = function(map, onlyBetter, divider, maxMultiplier){ for (var i = 0; i < this.length; ++i){ if (map.map[i]/divider > 1) this.map[i] = Math.min(maxMultiplier*this.map[i], this.map[i] * (map.map[i]/divider)); } }; // add to current map by the parameter map pixelwise -Map.prototype.add = function(map){ +m.Map.prototype.add = function(map){ for (var i = 0; i < this.length; ++i) { if (this.map[i] + map.map[i] < 0) this.map[i] = 0; @@ -287,8 +289,12 @@ Map.prototype.add = function(map){ } }; -Map.prototype.dumpIm = function(name, threshold){ +m.Map.prototype.dumpIm = function(name, threshold){ name = name ? name : "default.png"; threshold = threshold ? threshold : this.maxVal; Engine.DumpImage(name, this.map, this.width, this.height, threshold); }; + +return m; + +}(API3); diff --git a/binaries/data/mods/public/simulation/ai/common-api-v3/resources.js b/binaries/data/mods/public/simulation/ai/common-api-v3/resources.js index f47330b4e0..f6978d4fcc 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v3/resources.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v3/resources.js @@ -1,4 +1,7 @@ -function Resources(amounts, population) { +var API3 = function(m) +{ + +m.Resources = function(amounts, population) { if (amounts === undefined) { amounts = { food : 0, @@ -19,9 +22,9 @@ function Resources(amounts, population) { } } -Resources.prototype.types = [ "food", "wood", "stone", "metal" ]; +m.Resources.prototype.types = [ "food", "wood", "stone", "metal" ]; -Resources.prototype.reset = function() { +m.Resources.prototype.reset = function() { for ( var tKey in this.types) { var t = this.types[tKey]; this[t] = 0; @@ -29,7 +32,7 @@ Resources.prototype.reset = function() { this.population = 0; }; -Resources.prototype.canAfford = function(that) { +m.Resources.prototype.canAfford = function(that) { for ( var tKey in this.types) { var t = this.types[tKey]; if (this[t] < that[t]) { @@ -39,7 +42,7 @@ Resources.prototype.canAfford = function(that) { return true; }; -Resources.prototype.add = function(that) { +m.Resources.prototype.add = function(that) { for ( var tKey in this.types) { var t = this.types[tKey]; this[t] += that[t]; @@ -47,7 +50,7 @@ Resources.prototype.add = function(that) { this.population += that.population; }; -Resources.prototype.subtract = function(that) { +m.Resources.prototype.subtract = function(that) { for ( var tKey in this.types) { var t = this.types[tKey]; this[t] -= that[t]; @@ -55,7 +58,7 @@ Resources.prototype.subtract = function(that) { this.population += that.population; }; -Resources.prototype.multiply = function(n) { +m.Resources.prototype.multiply = function(n) { for ( var tKey in this.types) { var t = this.types[tKey]; this[t] *= n; @@ -63,7 +66,7 @@ Resources.prototype.multiply = function(n) { this.population *= n; }; -Resources.prototype.toInt = function() { +m.Resources.prototype.toInt = function() { var sum = 0; for ( var tKey in this.types) { var t = this.types[tKey]; @@ -72,3 +75,7 @@ Resources.prototype.toInt = function() { sum += this.population * 50; // based on typical unit costs return sum; }; + +return m; + +}(API3); diff --git a/binaries/data/mods/public/simulation/ai/common-api-v3/shared.js b/binaries/data/mods/public/simulation/ai/common-api-v3/shared.js index a70d61e481..3c632a3025 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v3/shared.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v3/shared.js @@ -1,5 +1,8 @@ +var API3 = function(m) +{ + // Shared script handling templates and basic terrain analysis -function SharedScript(settings) +m.SharedScript = function(settings) { if (!settings) return; @@ -37,14 +40,14 @@ function SharedScript(settings) //Return a simple object (using no classes etc) that will be serialized //into saved games //TODO: that -SharedScript.prototype.Serialize = function() +m.SharedScript.prototype.Serialize = function() { return { "players" : this._players, "templates" : this._templates, "techTp" : this._techTemplates }; }; // Called after the constructor when loading a saved game, with 'data' being // whatever Serialize() returned -SharedScript.prototype.Deserialize = function(data) +m.SharedScript.prototype.Deserialize = function(data) { this._players = data.players; this._templates = data.templates; @@ -56,7 +59,7 @@ SharedScript.prototype.Deserialize = function(data) // (This is a bit yucky and fragile since it's the inverse of // CCmpTemplateManager::CopyFoundationSubset and only includes components // that our EntityTemplate class currently uses.) -var g_FoundationForbiddenComponents = { +m.g_FoundationForbiddenComponents = { "ProductionQueue": 1, "ResourceSupply": 1, "ResourceDropsite": 1, @@ -65,7 +68,7 @@ var g_FoundationForbiddenComponents = { // Components that will be disabled in resource entity templates. // Roughly the inverse of CCmpTemplateManager::CopyResourceSubset. -var g_ResourceForbiddenComponents = { +m.g_ResourceForbiddenComponents = { "Cost": 1, "Decay": 1, "Health": 1, @@ -74,7 +77,7 @@ var g_ResourceForbiddenComponents = { "Vision": 1 }; -SharedScript.prototype.GetTemplate = function(name) +m.SharedScript.prototype.GetTemplate = function(name) { if (this._templates[name]) return this._templates[name]; @@ -89,7 +92,7 @@ SharedScript.prototype.GetTemplate = function(name) var foundation = {}; for (var key in base) - if (!g_FoundationForbiddenComponents[key]) + if (!m.g_FoundationForbiddenComponents[key]) foundation[key] = base[key]; this._derivedTemplates[name] = foundation; @@ -101,7 +104,7 @@ SharedScript.prototype.GetTemplate = function(name) var resource = {}; for (var key in base) - if (!g_ResourceForbiddenComponents[key]) + if (!m.g_ResourceForbiddenComponents[key]) resource[key] = base[key]; this._derivedTemplates[name] = resource; @@ -115,7 +118,7 @@ SharedScript.prototype.GetTemplate = function(name) // Initialize the shared component. // We need to now the initial state of the game for this, as we will use it. // This is called right at the end of the map generation. -SharedScript.prototype.init = function(state) { +m.SharedScript.prototype.init = function(state) { this.passabilityClasses = state.passabilityClasses; this.passabilityMap = state.passabilityMap; this.players = this._players; @@ -128,15 +131,15 @@ SharedScript.prototype.init = function(state) { this._entities = {}; for (var id in state.entities) - this._entities[id] = new Entity(this, state.entities[id]); + this._entities[id] = new m.Entity(this, state.entities[id]); // entity collection updated on create/destroy event. - this.entities = new EntityCollection(this, this._entities); + this.entities = new m.EntityCollection(this, this._entities); // create the terrain analyzer - this.terrainAnalyzer = new TerrainAnalysis(); + this.terrainAnalyzer = new m.TerrainAnalysis(); this.terrainAnalyzer.init(this, state); - this.accessibility = new Accessibility(); + this.accessibility = new m.Accessibility(); this.accessibility.init(state, this.terrainAnalyzer); // defined in TerrainAnalysis.js @@ -145,14 +148,14 @@ SharedScript.prototype.init = function(state) { this.gameState = {}; for (var i in this._players) { - this.gameState[this._players[i]] = new GameState(); + this.gameState[this._players[i]] = new m.GameState(); this.gameState[this._players[i]].init(this,state,this._players[i]); } }; // General update of the shared script, before each AI's update // applies entity deltas, and each gamestate. -SharedScript.prototype.onUpdate = function(state) +m.SharedScript.prototype.onUpdate = function(state) { if (this.isDeserialized && this.turn !== 0) { @@ -188,7 +191,7 @@ SharedScript.prototype.onUpdate = function(state) Engine.ProfileStop(); }; -SharedScript.prototype.ApplyEntitiesDelta = function(state) +m.SharedScript.prototype.ApplyEntitiesDelta = function(state) { Engine.ProfileStart("Shared ApplyEntitiesDelta"); @@ -202,7 +205,7 @@ SharedScript.prototype.ApplyEntitiesDelta = function(state) { continue; // Sometimes there are things like foundations which get destroyed too fast } - this._entities[evt.msg.entity] = new Entity(this, state.entities[evt.msg.entity]); + this._entities[evt.msg.entity] = new m.Entity(this, state.entities[evt.msg.entity]); this.entities.addEnt(this._entities[evt.msg.entity]); // Update all the entity collections since the create operation affects static properties as well as dynamic @@ -302,7 +305,7 @@ SharedScript.prototype.ApplyEntitiesDelta = function(state) Engine.ProfileStop(); }; -SharedScript.prototype.registerUpdatingEntityCollection = function(entCollection, noPush) +m.SharedScript.prototype.registerUpdatingEntityCollection = function(entCollection, noPush) { if (!noPush) { this._entityCollections.push(entCollection); @@ -316,7 +319,7 @@ SharedScript.prototype.registerUpdatingEntityCollection = function(entCollection this._entityCollectionsUID++; }; -SharedScript.prototype.removeUpdatingEntityCollection = function(entCollection) +m.SharedScript.prototype.removeUpdatingEntityCollection = function(entCollection) { for (var i in this._entityCollections) { @@ -338,7 +341,7 @@ SharedScript.prototype.removeUpdatingEntityCollection = function(entCollection) } }; -SharedScript.prototype.updateEntityCollections = function(property, ent) +m.SharedScript.prototype.updateEntityCollections = function(property, ent) { if (this._entityCollectionsByDynProp[property] !== undefined) { @@ -349,7 +352,7 @@ SharedScript.prototype.updateEntityCollections = function(property, ent) } } -SharedScript.prototype.setMetadata = function(player, ent, key, value) +m.SharedScript.prototype.setMetadata = function(player, ent, key, value) { var metadata = this._entityMetadata[player][ent.id()]; if (!metadata) @@ -359,7 +362,7 @@ SharedScript.prototype.setMetadata = function(player, ent, key, value) this.updateEntityCollections('metadata', ent); this.updateEntityCollections('metadata.' + key, ent); }; -SharedScript.prototype.getMetadata = function(player, ent, key) +m.SharedScript.prototype.getMetadata = function(player, ent, key) { var metadata = this._entityMetadata[player][ent.id()]; @@ -367,7 +370,7 @@ SharedScript.prototype.getMetadata = function(player, ent, key) return undefined; return metadata[key]; }; -SharedScript.prototype.deleteMetadata = function(player, ent, key) +m.SharedScript.prototype.deleteMetadata = function(player, ent, key) { var metadata = this._entityMetadata[player][ent.id()]; @@ -378,7 +381,7 @@ SharedScript.prototype.deleteMetadata = function(player, ent, key) return true; }; -function copyPrototype(descendant, parent) { +m.copyPrototype = function(descendant, parent) { var sConstructor = parent.toString(); var aMatch = sConstructor.match( /\s*function (.*)\(/ ); if ( aMatch != null ) { descendant.prototype[aMatch[1]] = parent; } @@ -387,3 +390,7 @@ function copyPrototype(descendant, parent) { } }; +return m; + +}(API3); + diff --git a/binaries/data/mods/public/simulation/ai/common-api-v3/technology.js b/binaries/data/mods/public/simulation/ai/common-api-v3/technology.js index 96365b6e4d..cf72234627 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v3/technology.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v3/technology.js @@ -1,6 +1,9 @@ +var API3 = function(m) +{ + // Wrapper around a technology template -function Technology(allTemplates, templateName) +m.Technology = function(allTemplates, templateName) { this._templateName = templateName; var template = allTemplates[templateName]; @@ -20,7 +23,7 @@ function Technology(allTemplates, templateName) this._techTemplates = allTemplates; } // returns generic, or specific if civ provided. -Technology.prototype.name = function(civ) +m.Technology.prototype.name = function(civ) { if (civ === undefined) { @@ -34,37 +37,37 @@ Technology.prototype.name = function(civ) } }; -Technology.prototype.pairDef = function() +m.Technology.prototype.pairDef = function() { return this._definesPair; }; // in case this defines a pair only, returns the two paired technologies. -Technology.prototype.getPairedTechs = function() +m.Technology.prototype.getPairedTechs = function() { if (!this._definesPair) return undefined; - var techOne = new Technology(this._techTemplates, this._template.top); - var techTwo = new Technology(this._techTemplates, this._template.bottom); + var techOne = new m.Technology(this._techTemplates, this._template.top); + var techTwo = new m.Technology(this._techTemplates, this._template.bottom); return [techOne,techTwo]; }; -Technology.prototype.pair = function() +m.Technology.prototype.pair = function() { if (!this._isPair) return undefined; return this._template.pair; }; -Technology.prototype.pairedWith = function() +m.Technology.prototype.pairedWith = function() { if (!this._isPair) return undefined; return this._pairedWith; }; -Technology.prototype.cost = function() +m.Technology.prototype.cost = function() { if (!this._template.cost) return undefined; @@ -72,49 +75,49 @@ Technology.prototype.cost = function() }; // seconds -Technology.prototype.researchTime = function() +m.Technology.prototype.researchTime = function() { if (!this._template.researchTime) return undefined; return this._template.researchTime; }; -Technology.prototype.requirements = function() +m.Technology.prototype.requirements = function() { if (!this._template.requirements) return undefined; return this._template.requirements; }; -Technology.prototype.autoResearch = function() +m.Technology.prototype.autoResearch = function() { if (!this._template.autoResearch) return undefined; return this._template.autoResearch; }; -Technology.prototype.supersedes = function() +m.Technology.prototype.supersedes = function() { if (!this._template.supersedes) return undefined; return this._template.supersedes; }; -Technology.prototype.modifications = function() +m.Technology.prototype.modifications = function() { if (!this._template.modifications) return undefined; return this._template.modifications; }; -Technology.prototype.affects = function() +m.Technology.prototype.affects = function() { if (!this._template.affects) return undefined; return this._template.affects; }; -Technology.prototype.isAffected = function(classes) +m.Technology.prototype.isAffected = function(classes) { if (!this._template.affects) return false; @@ -136,3 +139,7 @@ Technology.prototype.isAffected = function(classes) } return false; }; + +return m; + +}(API3); diff --git a/binaries/data/mods/public/simulation/ai/common-api-v3/terrain-analysis-pathfinder.js b/binaries/data/mods/public/simulation/ai/common-api-v3/terrain-analysis-pathfinder.js index dc25d06bf9..e1179e9fa3 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v3/terrain-analysis-pathfinder.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v3/terrain-analysis-pathfinder.js @@ -1,3 +1,6 @@ +var API3 = function(m) +{ + // An implementation of A* as a pathfinder. // It's oversamplable, and has a specific "distance from block" // variable to avoid narrow passages if wanted. @@ -11,7 +14,7 @@ // The initializer creates an expanded influence map for checking. // It's not extraordinarily slow, but it might be. -function aStarPath(gameState, onWater, disregardEntities, targetTerritory) { +m.aStarPath = function(gameState, onWater, disregardEntities, targetTerritory) { var self = this; // get the terrain analyzer map as a reference. @@ -52,10 +55,10 @@ function aStarPath(gameState, onWater, disregardEntities, targetTerritory) { } this.expandInfluences(255,this.widthMap); } -copyPrototype(aStarPath, Map); +m.copyPrototype(m.aStarPath, m.Map); // marks some points of the map as impassable. This can be used to create different paths, or to avoid going through some areas. -aStarPath.prototype.markImpassableArea = function(cx, cy, Distance) { +m.aStarPath.prototype.markImpassableArea = function(cx, cy, Distance) { [cx,cy] = this.gamePosToMapPos([cx,cy]); var x0 = Math.max(0, cx - Distance); var y0 = Math.max(0, cy - Distance); @@ -78,7 +81,7 @@ aStarPath.prototype.markImpassableArea = function(cx, cy, Distance) { // sending gamestate creates a map // (you run the risk of "jumping" over obstacles or weird behavior. -aStarPath.prototype.getPath = function(start, end, Sampling, preferredWidth, iterationLimit, gamestate) +m.aStarPath.prototype.getPath = function(start, end, Sampling, preferredWidth, iterationLimit, gamestate) { this.Sampling = Sampling >= 1 ? Sampling : 1; this.minWidth = 1; @@ -97,7 +100,7 @@ aStarPath.prototype.getPath = function(start, end, Sampling, preferredWidth, ite } if (gamestate !== undefined) { - this.TotorMap = new Map(gamestate); + this.TotorMap = new m.Map(gamestate); this.TotorMap.addInfluence(s[0],s[1],1,200,'constant'); this.TotorMap.addInfluence(e[0],e[1],1,200,'constant'); } @@ -135,7 +138,7 @@ aStarPath.prototype.getPath = function(start, end, Sampling, preferredWidth, ite this.isOpened[this.s] = true; this.openList.push(this.s); - this.fCostArray[this.s] = SquareVectorDistance([this.s%w, Math.floor(this.s/w)], [this.e%w, Math.floor(this.e/w)]); + this.fCostArray[this.s] = m.SquareVectorDistance([this.s%w, Math.floor(this.s/w)], [this.e%w, Math.floor(this.e/w)]); this.gCostArray[this.s] = 0; this.parentSquare[this.s] = this.s; @@ -143,7 +146,7 @@ aStarPath.prototype.getPath = function(start, end, Sampling, preferredWidth, ite } // in case it's not over yet, this can carry on the calculation of a path over multiple turn until it's over -aStarPath.prototype.continuePath = function(gamestate) +m.aStarPath.prototype.continuePath = function(gamestate) { var w = this.width; var h = this.height; @@ -208,7 +211,7 @@ aStarPath.prototype.continuePath = function(gamestate) { this.parentSquare[index] = this.currentSquare; - this.fCostArray[index] = SquareVectorDistance([index%w, Math.floor(index/w)], target);// * cost[i]; + this.fCostArray[index] = m.SquareVectorDistance([index%w, Math.floor(index/w)], target);// * cost[i]; this.gCostArray[index] = this.gCostArray[this.currentSquare] + cost[i] * this.Sampling;// - this.map[index]; if (!this.onWater && this.map[index] === 200) { @@ -231,7 +234,7 @@ aStarPath.prototype.continuePath = function(gamestate) this.openList.push(index); } this.isOpened[index] = true; - if (SquareVectorDistance( [index%w, Math.floor(index/w)] , target) <= this.Sampling*this.Sampling-1) { + if (m.SquareVectorDistance( [index%w, Math.floor(index/w)] , target) <= this.Sampling*this.Sampling-1) { if (this.e != index) this.parentSquare[this.e] = index; found = true; @@ -294,7 +297,7 @@ aStarPath.prototype.continuePath = function(gamestate) if (gamestate !== undefined) this.TotorMap.addInfluence(this.currentSquare % w, Math.floor(this.currentSquare / w),1,50,'constant'); - if (SquareVectorDistance([lastPosx,lastPosy],[this.currentSquare % w, Math.floor(this.currentSquare / w)]) > 300 || changes[this.currentSquare]) + if (m.SquareVectorDistance([lastPosx,lastPosy],[this.currentSquare % w, Math.floor(this.currentSquare / w)]) > 300 || changes[this.currentSquare]) { lastPosx = (this.currentSquare % w); lastPosy = Math.floor(this.currentSquare / w); @@ -325,3 +328,7 @@ aStarPath.prototype.continuePath = function(gamestate) } } + +return m; + +}(API3); diff --git a/binaries/data/mods/public/simulation/ai/common-api-v3/terrain-analysis.js b/binaries/data/mods/public/simulation/ai/common-api-v3/terrain-analysis.js index 9e92e1bb46..b47d34ad65 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v3/terrain-analysis.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v3/terrain-analysis.js @@ -1,3 +1,6 @@ +var API3 = function(m) +{ + /* * TerrainAnalysis, inheriting from the Map Component. * @@ -12,13 +15,13 @@ * But truly separating optimizes. */ -function TerrainAnalysis() { +m.TerrainAnalysis = function() { this.cellSize = 4; } -copyPrototype(TerrainAnalysis, Map); +m.copyPrototype(m.TerrainAnalysis, m.Map); -TerrainAnalysis.prototype.init = function(sharedScript,rawState) { +m.TerrainAnalysis.prototype.init = function(sharedScript,rawState) { var self = this; var passabilityMap = rawState.passabilityMap; @@ -108,7 +111,7 @@ TerrainAnalysis.prototype.init = function(sharedScript,rawState) { }; // Returns the (approximately) closest point which is passable by searching in a spiral pattern -TerrainAnalysis.prototype.findClosestPassablePoint = function(startPoint, onLand, limitDistance, quickscope){ +m.TerrainAnalysis.prototype.findClosestPassablePoint = function(startPoint, onLand, limitDistance, quickscope){ var w = this.width; var p = startPoint; var direction = 1; @@ -150,7 +153,7 @@ TerrainAnalysis.prototype.findClosestPassablePoint = function(startPoint, onLand // Returns an estimate of a tile accessibility. It checks neighboring cells over two levels. // returns a count. It's not integer. About 2 should be fairly accessible already. -TerrainAnalysis.prototype.countConnected = function(startIndex, byLand){ +m.TerrainAnalysis.prototype.countConnected = function(startIndex, byLand){ var count = 0.0; var w = this.width; @@ -181,7 +184,7 @@ TerrainAnalysis.prototype.countConnected = function(startIndex, byLand){ }; // TODO: for now this resets to 255. -TerrainAnalysis.prototype.updateMapWithEvents = function(sharedAI) { +m.TerrainAnalysis.prototype.updateMapWithEvents = function(sharedAI) { var self = this; var events = sharedAI.events; @@ -243,13 +246,13 @@ TerrainAnalysis.prototype.updateMapWithEvents = function(sharedAI) { * so this can use the land regions already. */ -function Accessibility() { +m.Accessibility = function() { } -copyPrototype(Accessibility, TerrainAnalysis); +m.copyPrototype(m.Accessibility, m.TerrainAnalysis); -Accessibility.prototype.init = function(rawState, terrainAnalyser){ +m.Accessibility.prototype.init = function(rawState, terrainAnalyser){ var self = this; this.Map(rawState, terrainAnalyser.map); @@ -322,7 +325,7 @@ Accessibility.prototype.init = function(rawState, terrainAnalyser){ //Engine.DumpImage("NavalPassMap.png", this.navalPassMap, this.width, this.height, 255); } -Accessibility.prototype.getAccessValue = function(position, onWater) { +m.Accessibility.prototype.getAccessValue = function(position, onWater) { var gamePos = this.gamePosToMapPos(position); if (onWater === true) return this.navalPassMap[gamePos[0] + this.width*gamePos[1]]; @@ -343,7 +346,7 @@ Accessibility.prototype.getAccessValue = function(position, onWater) { // Returns true if a point is deemed currently accessible (is not blocked by surrounding trees...) // NB: accessible means that you can reach it from one side, not necessariliy that you can go ON it. -Accessibility.prototype.isAccessible = function(gameState, position, onLand){ +m.Accessibility.prototype.isAccessible = function(gameState, position, onLand){ var gamePos = this.gamePosToMapPos(position); // quick check @@ -356,7 +359,7 @@ Accessibility.prototype.isAccessible = function(gameState, position, onLand){ // Return true if you can go from a point to a point without switching means of transport // Hardcore means is also checks for isAccessible at the end (it checks for either water or land though, beware). // This is a blind check and not a pathfinder: for all it knows there is a huge block of trees in the middle. -Accessibility.prototype.pathAvailable = function(gameState, start, end, onWater, hardcore){ +m.Accessibility.prototype.pathAvailable = function(gameState, start, end, onWater, hardcore){ var pstart = this.gamePosToMapPos(start); var istart = pstart[0] + pstart[1]*this.width; var pend = this.gamePosToMapPos(end); @@ -382,7 +385,7 @@ Accessibility.prototype.pathAvailable = function(gameState, start, end, onWater, return false; }; -Accessibility.prototype.getTrajectTo = function(start, end, noBound) { +m.Accessibility.prototype.getTrajectTo = function(start, end, noBound) { var pstart = this.gamePosToMapPos(start); var istart = pstart[0] + pstart[1]*this.width; var pend = this.gamePosToMapPos(end); @@ -413,7 +416,7 @@ Accessibility.prototype.getTrajectTo = function(start, end, noBound) { // assumes a land unit unless start point is over deep water. // if the path is more complicated than "land->sea->land" (or "sea->land->sea"), it will run A* to try and figure it out // Thus it can handle arbitrarily complicated paths (theoretically). -Accessibility.prototype.getTrajectToIndex = function(istart, iend, noBound){ +m.Accessibility.prototype.getTrajectToIndex = function(istart, iend, noBound){ var startRegion = istart; var currentRegion = istart; @@ -530,7 +533,7 @@ Accessibility.prototype.getTrajectToIndex = function(istart, iend, noBound){ return path; }; -Accessibility.prototype.getRegionSize = function(position, onWater){ +m.Accessibility.prototype.getRegionSize = function(position, onWater){ var pos = this.gamePosToMapPos(position); var index = pos[0] + pos[1]*this.width; var ID = (onWater === true) ? this.navalPassMap[index] : this.landPassMap[index]; @@ -539,7 +542,7 @@ Accessibility.prototype.getRegionSize = function(position, onWater){ return this.regionSize[ID]; }; -Accessibility.prototype.getRegionSizei = function(index, onWater) { +m.Accessibility.prototype.getRegionSizei = function(index, onWater) { if (this.regionSize[this.landPassMap[index]] === undefined && (!onWater || this.regionSize[this.navalPassMap[index]] === undefined)) return 0; if (onWater && this.regionSize[this.navalPassMap[index]] > this.regionSize[this.landPassMap[index]]) @@ -549,7 +552,7 @@ Accessibility.prototype.getRegionSizei = function(index, onWater) { // Implementation of a fast flood fill. Reasonably good performances for JS. // TODO: take big zones of impassable trees into account? -Accessibility.prototype.floodFill = function(startIndex, value, onWater) +m.Accessibility.prototype.floodFill = function(startIndex, value, onWater) { this.s = startIndex; if ((!onWater && this.landPassMap[this.s] !== 0) || (onWater && this.navalPassMap[this.s] !== 0) ) { @@ -668,16 +671,16 @@ Accessibility.prototype.floodFill = function(startIndex, value, onWater) } // creates a map of resource density -SharedScript.prototype.createResourceMaps = function(sharedScript) { +m.SharedScript.prototype.createResourceMaps = function(sharedScript) { for (var resource in this.decreaseFactor){ // if there is no resourceMap create one with an influence for everything with that resource if (! this.resourceMaps[resource]){ // We're creting them 8-bit. Things could go above 255 if there are really tons of resources // But at that point the precision is not really important anyway. And it saves memory. - this.resourceMaps[resource] = new Map(sharedScript, new Uint8Array(sharedScript.passabilityMap.data.length)); + this.resourceMaps[resource] = new m.Map(sharedScript, new Uint8Array(sharedScript.passabilityMap.data.length)); this.resourceMaps[resource].setMaxVal(255); - this.CCResourceMaps[resource] = new Map(sharedScript, new Uint8Array(sharedScript.passabilityMap.data.length)); + this.CCResourceMaps[resource] = new m.Map(sharedScript, new Uint8Array(sharedScript.passabilityMap.data.length)); this.CCResourceMaps[resource].setMaxVal(255); } } @@ -711,16 +714,16 @@ SharedScript.prototype.createResourceMaps = function(sharedScript) { // creates and maintains a map of unused resource density // this also takes dropsites into account. // resources that are "part" of a dropsite are not counted. -SharedScript.prototype.updateResourceMaps = function(sharedScript, events) { +m.SharedScript.prototype.updateResourceMaps = function(sharedScript, events) { for (var resource in this.decreaseFactor){ // if there is no resourceMap create one with an influence for everything with that resource if (! this.resourceMaps[resource]){ // We're creting them 8-bit. Things could go above 255 if there are really tons of resources // But at that point the precision is not really important anyway. And it saves memory. - this.resourceMaps[resource] = new Map(sharedScript, new Uint8Array(sharedScript.passabilityMap.data.length)); + this.resourceMaps[resource] = new m.Map(sharedScript, new Uint8Array(sharedScript.passabilityMap.data.length)); this.resourceMaps[resource].setMaxVal(255); - this.CCResourceMaps[resource] = new Map(sharedScript, new Uint8Array(sharedScript.passabilityMap.data.length)); + this.CCResourceMaps[resource] = new m.Map(sharedScript, new Uint8Array(sharedScript.passabilityMap.data.length)); this.CCResourceMaps[resource].setMaxVal(255); } } @@ -780,3 +783,7 @@ SharedScript.prototype.updateResourceMaps = function(sharedScript, events) { } } }; + +return m; + +}(API3); diff --git a/binaries/data/mods/public/simulation/ai/common-api-v3/utils.js b/binaries/data/mods/public/simulation/ai/common-api-v3/utils.js index e0c2cb3f4c..3e9e76ccf7 100644 --- a/binaries/data/mods/public/simulation/ai/common-api-v3/utils.js +++ b/binaries/data/mods/public/simulation/ai/common-api-v3/utils.js @@ -1,11 +1,14 @@ -function VectorDistance(a, b) +var API3 = function(m) +{ + +m.VectorDistance = function(a, b) { var dx = a[0] - b[0]; var dz = a[1] - b[1]; return Math.sqrt(dx*dx + dz*dz); } -function SquareVectorDistance(a, b) +m.SquareVectorDistance = function(a, b) { var dx = a[0] - b[0]; var dz = a[1] - b[1]; @@ -13,7 +16,7 @@ function SquareVectorDistance(a, b) } // A is the reference, B must be in "range" of A // this supposes the range is already squared -function inRange(a, b, range)// checks for X distance +m.inRange = function(a, b, range)// checks for X distance { // will avoid unnecessary checking for position in some rare cases... I'm lazy if (a === undefined || b === undefined || range === undefined) @@ -24,26 +27,26 @@ function inRange(a, b, range)// checks for X distance return ((dx*dx + dz*dz ) < range); } // slower than SquareVectorDistance, faster than VectorDistance but not exactly accurate. -function ManhattanDistance(a, b) +m.ManhattanDistance = function(a, b) { var dx = a[0] - b[0]; var dz = a[1] - b[1]; return Math.abs(dx) + Math.abs(dz); } -function AssocArraytoArray(assocArray) { +m.AssocArraytoArray = function(assocArray) { var endArray = []; for (var i in assocArray) endArray.push(assocArray[i]); return endArray; }; -function MemoizeInit(obj) +m.MemoizeInit = function(obj) { obj._memoizeCache = {}; } -function Memoize(funcname, func) +m.Memoize = function(funcname, func) { return function() { var args = funcname + '|' + Array.prototype.join.call(arguments, '|'); @@ -56,7 +59,7 @@ function Memoize(funcname, func) }; } -function ShallowClone(obj) +m.ShallowClone = function(obj) { var ret = {}; for (var k in obj) @@ -65,7 +68,7 @@ function ShallowClone(obj) } // Picks a random element from an array -function PickRandom(list){ +m.PickRandom = function(list){ if (list.length === 0) { return undefined; @@ -75,3 +78,7 @@ function PickRandom(list){ return list[Math.floor(Math.random()*list.length)]; } } + +return m; + +}(API3); diff --git a/binaries/data/mods/public/simulation/ai/qbot/attackMoveToLocation.js b/binaries/data/mods/public/simulation/ai/qbot/attackMoveToLocation.js index c38c75d974..abc66f19ad 100644 --- a/binaries/data/mods/public/simulation/ai/qbot/attackMoveToLocation.js +++ b/binaries/data/mods/public/simulation/ai/qbot/attackMoveToLocation.js @@ -1,4 +1,6 @@ -function AttackMoveToLocation(gameState, militaryManager, minAttackSize, maxAttackSize, targetFinder){ +function AttackMoveToLocation(gameState, Config, militaryManager, minAttackSize, maxAttackSize, targetFinder){ + + this.Config = Config; this.minAttackSize = minAttackSize || Config.attack.minAttackSize; this.maxAttackSize = maxAttackSize || Config.attack.maxAttackSize; this.idList=[]; @@ -17,7 +19,7 @@ AttackMoveToLocation.prototype.canExecute = function(gameState, militaryManager) var enemyCount = militaryManager.measureEnemyCount(gameState); // We require our army to be >= this strength - var targetStrength = enemyStrength * Config.attack.enemyRatio; + var targetStrength = enemyStrength * this.Config.attack.enemyRatio; var availableCount = militaryManager.countAvailableUnits(); var availableStrength = militaryManager.measureAvailableStrength(); diff --git a/binaries/data/mods/public/simulation/ai/qbot/config.js b/binaries/data/mods/public/simulation/ai/qbot/config.js index 779863f40e..91c610da24 100644 --- a/binaries/data/mods/public/simulation/ai/qbot/config.js +++ b/binaries/data/mods/public/simulation/ai/qbot/config.js @@ -1,23 +1,25 @@ -var baseConfig = { - "attack" : { +function Config() { + this.debug = false + + this.attack = { "minAttackSize" : 20, // attackMoveToLocation "maxAttackSize" : 60, // attackMoveToLocation "enemyRatio" : 1.5, // attackMoveToLocation "groupSize" : 10 // military - }, + }; // defence - "defence" : { + this.defence = { "acquireDistance" : 220, "releaseDistance" : 250, "groupRadius" : 20, "groupBreakRadius" : 40, "groupMergeRadius" : 10, "defenderRatio" : 2 - }, + }; // military - "buildings" : { + this.buildings = { "moderate" : { "default" : [ "structures/{civ}_barracks" ] }, @@ -37,10 +39,10 @@ var baseConfig = { "default" : [ "structures/{civ}_fortress" ], "celt" : [ "structures/{civ}_fortress_b", "structures/{civ}_fortress_g" ] } - }, + }; // qbot - "priorities" : { // Note these are dynamic, you are only setting the initial values + this.priorities = { // Note these are dynamic, you are only setting the initial values "house" : 500, "citizenSoldier" : 100, "villager" : 100, @@ -51,13 +53,6 @@ var baseConfig = { "militaryBuilding" : 50, "defenceBuilding" : 17, "civilCentre" : 1000 - }, - - "debug" : false + }; }; -var Config = { - "debug": false -}; - -Config.__proto__ = baseConfig; \ No newline at end of file diff --git a/binaries/data/mods/public/simulation/ai/qbot/defence.js b/binaries/data/mods/public/simulation/ai/qbot/defence.js index b7e0fbf8c6..97ee747b6d 100644 --- a/binaries/data/mods/public/simulation/ai/qbot/defence.js +++ b/binaries/data/mods/public/simulation/ai/qbot/defence.js @@ -1,4 +1,4 @@ -function Defence(){ +function Defence(Config){ this.ACQUIRE_DIST = Config.defence.acquireDistance; this.RELEASE_DIST = Config.defence.releaseDistance; diff --git a/binaries/data/mods/public/simulation/ai/qbot/economy.js b/binaries/data/mods/public/simulation/ai/qbot/economy.js index befeef3d46..9081efdf33 100644 --- a/binaries/data/mods/public/simulation/ai/qbot/economy.js +++ b/binaries/data/mods/public/simulation/ai/qbot/economy.js @@ -357,17 +357,17 @@ EconomyManager.prototype.updateNearbyResources = function(gameState){ // Make resources glow wildly if (resource == "food"){ ent.getMetadata("nearby-resources-" + resource).forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [10,0,0]}); + Engine.PostCommand(PlayerID, {"type": "set-shading-color", "entities": [ent.id()], "rgb": [10,0,0]}); }); } if (resource == "wood"){ ent.getMetadata("nearby-resources-" + resource).forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,10,0]}); + Engine.PostCommand(PlayerID, {"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,10,0]}); }); } if (resource == "metal"){ ent.getMetadata("nearby-resources-" + resource).forEach(function(ent){ - Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,0,10]}); + Engine.PostCommand(PlayerID, {"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,0,10]}); }); }*/ }); diff --git a/binaries/data/mods/public/simulation/ai/qbot/entity-extend.js b/binaries/data/mods/public/simulation/ai/qbot/entity-extend.js index 904ccdad9b..b358426867 100644 --- a/binaries/data/mods/public/simulation/ai/qbot/entity-extend.js +++ b/binaries/data/mods/public/simulation/ai/qbot/entity-extend.js @@ -3,12 +3,12 @@ Entity.prototype.deleteMetadata = function(id) { }; Entity.prototype.garrison = function(target) { - Engine.PostCommand({"type": "garrison", "entities": [this.id()], "target": target.id(),"queued": false}); + Engine.PostCommand(PlayerID, {"type": "garrison", "entities": [this.id()], "target": target.id(),"queued": false}); return this; }; Entity.prototype.attack = function(unitId) { - Engine.PostCommand({"type": "attack", "entities": [this.id()], "target": unitId, "queued": false}); + Engine.PostCommand(PlayerID, {"type": "attack", "entities": [this.id()], "target": unitId, "queued": false}); return this; -}; \ No newline at end of file +}; diff --git a/binaries/data/mods/public/simulation/ai/qbot/entitycollection-extend.js b/binaries/data/mods/public/simulation/ai/qbot/entitycollection-extend.js index f966daa8a2..732c81ed68 100644 --- a/binaries/data/mods/public/simulation/ai/qbot/entitycollection-extend.js +++ b/binaries/data/mods/public/simulation/ai/qbot/entitycollection-extend.js @@ -7,7 +7,7 @@ EntityCollection.prototype.attack = function(unit) unitId = unit; } - Engine.PostCommand({"type": "attack", "entities": this.toIdArray(), "target": unitId, "queued": false}); + Engine.PostCommand(PlayerID, {"type": "attack", "entities": this.toIdArray(), "target": unitId, "queued": false}); return this; }; @@ -37,4 +37,4 @@ EntityCollection.prototype.getCentrePosition = function(){ }else{ return [sumPos[0]/count, sumPos[1]/count]; } -}; \ No newline at end of file +}; diff --git a/binaries/data/mods/public/simulation/ai/qbot/military.js b/binaries/data/mods/public/simulation/ai/qbot/military.js index 62cdb83148..f9e8e959f0 100755 --- a/binaries/data/mods/public/simulation/ai/qbot/military.js +++ b/binaries/data/mods/public/simulation/ai/qbot/military.js @@ -6,7 +6,9 @@ * */ -var MilitaryAttackManager = function() { +var MilitaryAttackManager = function(Config) { + + this.Config = Config // these use the structure soldiers[unitId] = true|false to register the units this.attackManagers = [AttackMoveToLocation]; this.availableAttacks = []; @@ -16,7 +18,7 @@ var MilitaryAttackManager = function() { this.attackCount = 0; this.lastAttackTime = 0; - this.defenceManager = new Defence(); + this.defenceManager = new Defence(Config); }; MilitaryAttackManager.prototype.init = function(gameState) { @@ -24,22 +26,22 @@ MilitaryAttackManager.prototype.init = function(gameState) { // load units and buildings from the config files - if (civ in Config.buildings.moderate){ - this.bModerate = Config.buildings.moderate[civ]; + if (civ in this.Config.buildings.moderate){ + this.bModerate = this.Config.buildings.moderate[civ]; }else{ - this.bModerate = Config.buildings.moderate['default']; + this.bModerate = this.Config.buildings.moderate['default']; } - if (civ in Config.buildings.advanced){ - this.bAdvanced = Config.buildings.advanced[civ]; + if (civ in this.Config.buildings.advanced){ + this.bAdvanced = this.Config.buildings.advanced[civ]; }else{ - this.bAdvanced = Config.buildings.advanced['default']; + this.bAdvanced = this.Config.buildings.advanced['default']; } - if (civ in Config.buildings.fort){ - this.bFort = Config.buildings.fort[civ]; + if (civ in this.Config.buildings.fort){ + this.bFort = this.Config.buildings.fort[civ]; }else{ - this.bFort = Config.buildings.fort['default']; + this.bFort = this.Config.buildings.fort['default']; } for (var i in this.bAdvanced){ @@ -54,7 +56,7 @@ MilitaryAttackManager.prototype.init = function(gameState) { }; // TODO: figure out how to make this generic for (var i in this.attackManagers){ - this.availableAttacks[i] = new this.attackManagers[i](gameState, this); + this.availableAttacks[i] = new this.attackManagers[i](gameState, this.Config, this); } var enemies = gameState.getEnemyEntities(); @@ -435,7 +437,7 @@ MilitaryAttackManager.prototype.update = function(gameState, queues, events) { this.currentAttacks.push(this.availableAttacks[i]); //debug("Attacking!"); } - this.availableAttacks.splice(i, 1, new this.attackManagers[i](gameState, this)); + this.availableAttacks.splice(i, 1, new this.attackManagers[i](gameState, this.Config, this)); } Engine.ProfileStop(); diff --git a/binaries/data/mods/public/simulation/ai/qbot/qbot.js b/binaries/data/mods/public/simulation/ai/qbot/qbot.js index ec361a5d2d..3b31180798 100644 --- a/binaries/data/mods/public/simulation/ai/qbot/qbot.js +++ b/binaries/data/mods/public/simulation/ai/qbot/qbot.js @@ -1,15 +1,19 @@ +var g_debugEnabled = false; function QBotAI(settings) { BaseAI.call(this, settings); this.turn = 0; + this.Config = new Config(); + this.modules = { "economy": new EconomyManager(), - "military": new MilitaryAttackManager(), + "military": new MilitaryAttackManager(this.Config), "housing": new HousingManager() }; + // this.queues cannot be modified past initialisation or queue-manager will break this.queues = { house : new Queue(), @@ -26,7 +30,7 @@ function QBotAI(settings) { this.productionQueues = []; - this.priorities = Config.priorities; + this.priorities = this.Config.priorities; this.queueManager = new QueueManager(this.queues, this.priorities); @@ -161,7 +165,7 @@ QBotAI.prototype.Serialize = function() }; function debug(output){ - if (Config.debug){ + if (g_debugEnabled){ if (typeof output === "string"){ warn(output); }else{ diff --git a/binaries/data/mods/public/simulation/ai/qbot/worker.js b/binaries/data/mods/public/simulation/ai/qbot/worker.js index e8c48f377f..2bb4fc0f87 100644 --- a/binaries/data/mods/public/simulation/ai/qbot/worker.js +++ b/binaries/data/mods/public/simulation/ai/qbot/worker.js @@ -34,7 +34,7 @@ Worker.prototype.update = function(gameState) { this.startApproachingResourceTime = gameState.getTimeElapsed(); - //Engine.PostCommand({"type": "set-shading-color", "entities": [this.ent.id()], "rgb": [10,0,0]}); + //Engine.PostCommand(PlayerID, {"type": "set-shading-color", "entities": [this.ent.id()], "rgb": [10,0,0]}); }else{ // If we haven't reached the resource in 2 minutes twice in a row and none of the resource has been // gathered then mark it as inaccessible. @@ -61,7 +61,7 @@ Worker.prototype.update = function(gameState) { this.ent.repair(target); } - //Engine.PostCommand({"type": "set-shading-color", "entities": [this.ent.id()], "rgb": [0,10,0]}); + //Engine.PostCommand(PlayerID, {"type": "set-shading-color", "entities": [this.ent.id()], "rgb": [0,10,0]}); } Engine.ProfileStart("Update Gatherer Counts"); @@ -248,4 +248,4 @@ Worker.prototype.getResourceType = function(type){ }else{ return type.generic; } -}; \ No newline at end of file +}; diff --git a/source/simulation2/components/CCmpAIManager.cpp b/source/simulation2/components/CCmpAIManager.cpp index 97982b2512..2be8aff550 100644 --- a/source/simulation2/components/CCmpAIManager.cpp +++ b/source/simulation2/components/CCmpAIManager.cpp @@ -78,22 +78,9 @@ private: NONCOPYABLE(CAIPlayer); public: CAIPlayer(CAIWorker& worker, const std::wstring& aiName, player_id_t player, uint8_t difficulty, - const shared_ptr& runtime, boost::rand48& rng) : - m_Worker(worker), m_AIName(aiName), m_Player(player), m_Difficulty(difficulty), m_ScriptInterface("Engine", "AI", runtime) + shared_ptr scriptInterface) : + m_Worker(worker), m_AIName(aiName), m_Player(player), m_Difficulty(difficulty), m_ScriptInterface(scriptInterface) { - m_ScriptInterface.SetCallbackData(static_cast (this)); - - m_ScriptInterface.ReplaceNondeterministicRNG(rng); - m_ScriptInterface.LoadGlobalScripts(); - - m_ScriptInterface.RegisterFunction("IncludeModule"); - m_ScriptInterface.RegisterFunction("DumpHeap"); - m_ScriptInterface.RegisterFunction("ForceGC"); - m_ScriptInterface.RegisterFunction("PostCommand"); - - m_ScriptInterface.RegisterFunction, u32, u32, u32, CAIPlayer::DumpImage>("DumpImage"); - - m_ScriptInterface.RegisterFunction("RegisterSerializablePrototype"); } ~CAIPlayer() @@ -103,112 +90,10 @@ private: m_Commands.clear(); } - static void IncludeModule(void* cbdata, std::wstring name) - { - CAIPlayer* self = static_cast (cbdata); - - self->LoadScripts(name); - } - static void DumpHeap(void* cbdata) - { - CAIPlayer* self = static_cast (cbdata); - - //std::cout << JS_GetGCParameter(self->m_ScriptInterface.GetRuntime(), JSGC_BYTES) << std::endl; - self->m_ScriptInterface.DumpHeap(); - } - static void ForceGC(void* cbdata) - { - CAIPlayer* self = static_cast (cbdata); - - JS_GC(self->m_ScriptInterface.GetContext()); - } - static void PostCommand(void* cbdata, CScriptValRooted cmd) - { - CAIPlayer* self = static_cast (cbdata); - - self->m_Commands.push_back(self->m_ScriptInterface.WriteStructuredClone(cmd.get())); - } - - /** - * Debug function for AI scripts to dump 2D array data (e.g. terrain tile weights). - * TODO: check if this needs to be here too. - */ - static void DumpImage(void* UNUSED(cbdata), std::wstring name, std::vector data, u32 w, u32 h, u32 max) - { - // TODO: this is totally not threadsafe. - - VfsPath filename = L"screenshots/aidump/" + name; - - if (data.size() != w*h) - { - debug_warn(L"DumpImage: data size doesn't match w*h"); - return; - } - - if (max == 0) - { - debug_warn(L"DumpImage: max must not be 0"); - return; - } - - const size_t bpp = 8; - int flags = TEX_BOTTOM_UP|TEX_GREY; - - const size_t img_size = w * h * bpp/8; - const size_t hdr_size = tex_hdr_size(filename); - shared_ptr buf; - AllocateAligned(buf, hdr_size+img_size, maxSectorSize); - Tex t; - if (tex_wrap(w, h, bpp, flags, buf, hdr_size, &t) < 0) - return; - - u8* img = buf.get() + hdr_size; - for (size_t i = 0; i < data.size(); ++i) - img[i] = (u8)((data[i] * 255) / max); - - tex_write(&t, filename); - tex_free(&t); - } - - static void RegisterSerializablePrototype(void* cbdata, std::wstring name, CScriptVal proto) - { - CAIPlayer* self = static_cast (cbdata); - // Add our player number to avoid name conflicts with other prototypes - // TODO: it would be better if serializable prototypes were stored in ScriptInterfaces - // and then each serializer would access those matching its own context, but that's - // not possible with AIs sharing data across contexts - std::wstringstream protoID; - protoID << self->m_Player << L"." << name.c_str(); - self->m_Worker.RegisterSerializablePrototype(protoID.str(), proto); - } - - bool LoadScripts(const std::wstring& moduleName) - { - // Ignore modules that are already loaded - if (m_LoadedModules.find(moduleName) != m_LoadedModules.end()) - return true; - - // Mark this as loaded, to prevent it recursively loading itself - m_LoadedModules.insert(moduleName); - - // Load and execute *.js - VfsPaths pathnames; - vfs::GetPathnames(g_VFS, L"simulation/ai/" + moduleName + L"/", L"*.js", pathnames); - for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it) - { - if (!m_ScriptInterface.LoadGlobalScriptFile(*it)) - { - LOGERROR(L"Failed to load script %ls", it->string().c_str()); - return false; - } - } - - return true; - } - bool Initialise(bool callConstructor) { - if (!LoadScripts(m_AIName)) + // LoadScripts will only load each script once even though we call it for each player + if (!m_Worker.LoadScripts(m_AIName)) return false; OsPath path = L"simulation/ai/" + m_AIName + L"/data.json"; @@ -220,23 +105,40 @@ private: } // Get the constructor name from the metadata + // If the AI doesn't use modules, we look for the constructor in the global object + // TODO: All AIs should use modules. Remove the condition if this requirement is met. + std::string moduleName; std::string constructor; - if (!m_ScriptInterface.GetProperty(metadata.get(), "constructor", constructor)) + CScriptVal objectWithConstructor; // object that should contain the constructor function + CScriptVal ctor; + if (!m_ScriptInterface->HasProperty(metadata.get(), "moduleName")) + { + objectWithConstructor = m_ScriptInterface->GetGlobalObject(); + } + else + { + m_ScriptInterface->GetProperty(metadata.get(), "moduleName", moduleName); + if(!m_ScriptInterface->GetProperty(m_ScriptInterface->GetGlobalObject(), moduleName.c_str(), objectWithConstructor) || objectWithConstructor.undefined()) + { + LOGERROR(L"Failed to create AI player: %ls: can't find the module that should contain the constructor: '%hs'", path.string().c_str(), moduleName.c_str()); + return false; + } + } + if (!m_ScriptInterface->GetProperty(metadata.get(), "constructor", constructor)) { LOGERROR(L"Failed to create AI player: %ls: missing 'constructor'", path.string().c_str()); return false; } // Get the constructor function from the loaded scripts - CScriptVal ctor; - if (!m_ScriptInterface.GetProperty(m_ScriptInterface.GetGlobalObject(), constructor.c_str(), ctor) + if (!m_ScriptInterface->GetProperty(objectWithConstructor.get(), constructor.c_str(), ctor) || ctor.undefined()) { LOGERROR(L"Failed to create AI player: %ls: can't find constructor '%hs'", path.string().c_str(), constructor.c_str()); return false; } - m_ScriptInterface.GetProperty(metadata.get(), "useShared", m_UseSharedComponent); + m_ScriptInterface->GetProperty(metadata.get(), "useShared", m_UseSharedComponent); CScriptVal obj; @@ -244,20 +146,20 @@ private: { // Set up the data to pass as the constructor argument CScriptVal settings; - m_ScriptInterface.Eval(L"({})", settings); - m_ScriptInterface.SetProperty(settings.get(), "player", m_Player, false); - m_ScriptInterface.SetProperty(settings.get(), "difficulty", m_Difficulty, false); + m_ScriptInterface->Eval(L"({})", settings); + m_ScriptInterface->SetProperty(settings.get(), "player", m_Player, false); + m_ScriptInterface->SetProperty(settings.get(), "difficulty", m_Difficulty, false); ENSURE(m_Worker.m_HasLoadedEntityTemplates); - m_ScriptInterface.SetProperty(settings.get(), "templates", m_Worker.m_EntityTemplates, false); + m_ScriptInterface->SetProperty(settings.get(), "templates", m_Worker.m_EntityTemplates, false); - obj = m_ScriptInterface.CallConstructor(ctor.get(), settings.get()); + obj = m_ScriptInterface->CallConstructor(ctor.get(), settings.get()); } else { // For deserialization, we want to create the object with the correct prototype // but don't want to actually run the constructor again // XXX: actually we don't currently use this path for deserialization - maybe delete it? - obj = m_ScriptInterface.NewObjectFromConstructor(ctor.get()); + obj = m_ScriptInterface->NewObjectFromConstructor(ctor.get()); } if (obj.undefined()) @@ -266,26 +168,26 @@ private: return false; } - m_Obj = CScriptValRooted(m_ScriptInterface.GetContext(), obj); + m_Obj = CScriptValRooted(m_ScriptInterface->GetContext(), obj); return true; } - void Run(CScriptVal state) + void Run(CScriptVal state, int playerID) { m_Commands.clear(); - m_ScriptInterface.CallFunctionVoid(m_Obj.get(), "HandleMessage", state); + m_ScriptInterface->CallFunctionVoid(m_Obj.get(), "HandleMessage", state, playerID); } // overloaded with a sharedAI part. // javascript can handle both natively on the same function. - void Run(CScriptVal state, CScriptValRooted SharedAI) + void Run(CScriptVal state, int playerID, CScriptValRooted SharedAI) { m_Commands.clear(); - m_ScriptInterface.CallFunctionVoid(m_Obj.get(), "HandleMessage", state, SharedAI); + m_ScriptInterface->CallFunctionVoid(m_Obj.get(), "HandleMessage", state, playerID, SharedAI); } void InitAI(CScriptVal state, CScriptValRooted SharedAI) { m_Commands.clear(); - m_ScriptInterface.CallFunctionVoid(m_Obj.get(), "Init", state, SharedAI); + m_ScriptInterface->CallFunctionVoid(m_Obj.get(), "Init", state, m_Player, SharedAI); } CAIWorker& m_Worker; @@ -294,10 +196,9 @@ private: uint8_t m_Difficulty; bool m_UseSharedComponent; - ScriptInterface m_ScriptInterface; + shared_ptr m_ScriptInterface; CScriptValRooted m_Obj; std::vector > m_Commands; - std::set m_LoadedModules; }; public: @@ -313,7 +214,7 @@ public: // removed as soon whenever the new pathfinder is committed // And the AIs can stop relying on their own little hands. m_ScriptRuntime(ScriptInterface::CreateRuntime(33554432)), - m_ScriptInterface("Engine", "AI", m_ScriptRuntime), + m_ScriptInterface(new ScriptInterface("Engine", "AI", m_ScriptRuntime)), m_TurnNum(0), m_CommandsComputed(true), m_HasLoadedEntityTemplates(false), @@ -321,16 +222,17 @@ public: { // TODO: ought to seed the RNG (in a network-synchronised way) before we use it - m_ScriptInterface.ReplaceNondeterministicRNG(m_RNG); - m_ScriptInterface.LoadGlobalScripts(); + m_ScriptInterface->ReplaceNondeterministicRNG(m_RNG); + m_ScriptInterface->LoadGlobalScripts(); - m_ScriptInterface.SetCallbackData(NULL); + m_ScriptInterface->SetCallbackData(static_cast (this)); - m_ScriptInterface.RegisterFunction("PostCommand"); - m_ScriptInterface.RegisterFunction("DumpHeap"); - m_ScriptInterface.RegisterFunction("ForceGC"); + m_ScriptInterface->RegisterFunction("PostCommand"); + m_ScriptInterface->RegisterFunction("IncludeModule"); + m_ScriptInterface->RegisterFunction("DumpHeap"); + m_ScriptInterface->RegisterFunction("ForceGC"); - m_ScriptInterface.RegisterFunction, u32, u32, u32, CAIWorker::DumpImage>("DumpImage"); + m_ScriptInterface->RegisterFunction, u32, u32, u32, CAIWorker::DumpImage>("DumpImage"); } ~CAIWorker() @@ -343,18 +245,55 @@ public: m_PassabilityMapVal = CScriptValRooted(); m_TerritoryMapVal = CScriptValRooted(); } - - // This is called by AIs if they use the v3 API. - // If the AIs originate the call, cbdata is not NULL. - // If the shared component does, it is, so it must not be taken into account. - static void PostCommand(void* cbdata, CScriptValRooted cmd) + + bool LoadScripts(const std::wstring& moduleName) { - if (cbdata == NULL) { - debug_warn(L"Warning: the shared component has tried to push an engine command. Ignoring."); - return; + // Ignore modules that are already loaded + if (m_LoadedModules.find(moduleName) != m_LoadedModules.end()) + return true; + + // Mark this as loaded, to prevent it recursively loading itself + m_LoadedModules.insert(moduleName); + + // Load and execute *.js + VfsPaths pathnames; + vfs::GetPathnames(g_VFS, L"simulation/ai/" + moduleName + L"/", L"*.js", pathnames); + for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it) + { + if (!m_ScriptInterface->LoadGlobalScriptFile(*it)) + { + LOGERROR(L"Failed to load script %ls", it->string().c_str()); + return false; + } } - CAIPlayer* self = static_cast (cbdata); - self->m_Commands.push_back(self->m_ScriptInterface.WriteStructuredClone(cmd.get())); + + return true; + } + + static void IncludeModule(void* cbdata, std::wstring name) + { + CAIWorker* self = static_cast (cbdata); + self->LoadScripts(name); + } + + static void PostCommand(void* cbdata, int playerid, CScriptValRooted cmd) + { + CAIWorker* self = static_cast (cbdata); + self->PostCommand(playerid, cmd); + } + + void PostCommand(int playerid, CScriptValRooted cmd) + { + for (size_t i=0; im_Player == playerid) + { + m_Players[i]->m_Commands.push_back(m_ScriptInterface->WriteStructuredClone(cmd.get())); + return; + } + } + + LOGERROR(L"Invalid playerid in PostCommand!"); } // The next two ought to be implmeneted someday but for now as it returns "null" it can't static void DumpHeap(void* cbdata) @@ -364,7 +303,7 @@ public: return; } CAIWorker* self = static_cast (cbdata); - self->m_ScriptInterface.DumpHeap(); + self->m_ScriptInterface->DumpHeap(); } static void ForceGC(void* cbdata) { @@ -374,7 +313,7 @@ public: } CAIWorker* self = static_cast (cbdata); PROFILE3("AI compute GC"); - JS_GC(self->m_ScriptInterface.GetContext()); + JS_GC(self->m_ScriptInterface->GetContext()); } /** @@ -424,28 +363,26 @@ public: // reset the value so it can be used to determine if we actually initialized it. m_HasSharedComponent = false; - - VfsPaths sharedPathnames; - // Check for "shared" module. - vfs::GetPathnames(g_VFS, L"simulation/ai/common-api-v3/", L"*.js", sharedPathnames); - for (VfsPaths::iterator it = sharedPathnames.begin(); it != sharedPathnames.end(); ++it) - { - if (!m_ScriptInterface.LoadGlobalScriptFile(*it)) - { - LOGERROR(L"Failed to load shared script %ls", it->string().c_str()); - return false; - } - m_HasSharedComponent = true; - } - if (!m_HasSharedComponent) - return false; + if (LoadScripts(L"common-api-v3")) + m_HasSharedComponent = true; + else + return false; + // mainly here for the error messages OsPath path = L"simulation/ai/common-api-v2/"; - // Constructor name is SharedScript + // Constructor name is SharedScript, it's in the module API3 + // TODO: Hardcoding this is bad, we need a smarter way. + CScriptVal AIModule; CScriptVal ctor; - if (!m_ScriptInterface.GetProperty(m_ScriptInterface.GetGlobalObject(), "SharedScript", ctor) + if (!m_ScriptInterface->GetProperty(m_ScriptInterface->GetGlobalObject(), "API3", AIModule) || AIModule.undefined()) + { + LOGERROR(L"Failed to create shared AI component: %ls: can't find module '%hs'", path.string().c_str(), "API3"); + return false; + } + + if (!m_ScriptInterface->GetProperty(AIModule.get(), "SharedScript", ctor) || ctor.undefined()) { LOGERROR(L"Failed to create shared AI component: %ls: can't find constructor '%hs'", path.string().c_str(), "SharedScript"); @@ -454,32 +391,32 @@ public: // Set up the data to pass as the constructor argument CScriptVal settings; - m_ScriptInterface.Eval(L"({})", settings); + m_ScriptInterface->Eval(L"({})", settings); CScriptVal playersID; - m_ScriptInterface.Eval(L"({})", playersID); + m_ScriptInterface->Eval(L"({})", playersID); for (size_t i = 0; i < m_Players.size(); ++i) { - jsval val = m_ScriptInterface.ToJSVal(m_ScriptInterface.GetContext(), m_Players[i]->m_Player); - m_ScriptInterface.SetPropertyInt(playersID.get(), i, CScriptVal(val), true); + jsval val = m_ScriptInterface->ToJSVal(m_ScriptInterface->GetContext(), m_Players[i]->m_Player); + m_ScriptInterface->SetPropertyInt(playersID.get(), i, CScriptVal(val), true); } - m_ScriptInterface.SetProperty(settings.get(), "players", playersID); + m_ScriptInterface->SetProperty(settings.get(), "players", playersID); ENSURE(m_HasLoadedEntityTemplates); - m_ScriptInterface.SetProperty(settings.get(), "templates", m_EntityTemplates, false); + m_ScriptInterface->SetProperty(settings.get(), "templates", m_EntityTemplates, false); if (hasTechs) { - m_ScriptInterface.SetProperty(settings.get(), "techTemplates", m_TechTemplates, false); + m_ScriptInterface->SetProperty(settings.get(), "techTemplates", m_TechTemplates, false); } else { // won't get the tech templates directly. CScriptVal fakeTech; - m_ScriptInterface.Eval("({})", fakeTech); - m_ScriptInterface.SetProperty(settings.get(), "techTemplates", fakeTech, false); + m_ScriptInterface->Eval("({})", fakeTech); + m_ScriptInterface->SetProperty(settings.get(), "techTemplates", fakeTech, false); } - m_SharedAIObj = CScriptValRooted(m_ScriptInterface.GetContext(),m_ScriptInterface.CallConstructor(ctor.get(), settings.get())); + m_SharedAIObj = CScriptValRooted(m_ScriptInterface->GetContext(),m_ScriptInterface->CallConstructor(ctor.get(), settings.get())); if (m_SharedAIObj.undefined()) @@ -493,7 +430,7 @@ public: bool AddPlayer(const std::wstring& aiName, player_id_t player, uint8_t difficulty, bool callConstructor) { - shared_ptr ai(new CAIPlayer(*this, aiName, player, difficulty, m_ScriptRuntime, m_RNG)); + shared_ptr ai(new CAIPlayer(*this, aiName, player, difficulty, m_ScriptInterface)); if (!ai->Initialise(callConstructor)) return false; @@ -501,7 +438,7 @@ public: if (!m_HasSharedComponent) m_HasSharedComponent = ai->m_UseSharedComponent; - m_ScriptInterface.MaybeGC(); + m_ScriptInterface->MaybeGC(); m_Players.push_back(ai); @@ -513,18 +450,18 @@ public: // this will be run last by InitGame.Js, passing the full game representation. // For now it will run for the shared Component. // This is NOT run during deserialization. - CScriptVal state = m_ScriptInterface.ReadStructuredClone(gameState); - JSContext* cx = m_ScriptInterface.GetContext(); + CScriptVal state = m_ScriptInterface->ReadStructuredClone(gameState); + JSContext* cx = m_ScriptInterface->GetContext(); m_PassabilityMapVal = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, passabilityMap)); m_TerritoryMapVal = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, territoryMap)); if (m_HasSharedComponent) { - m_ScriptInterface.SetProperty(state.get(), "passabilityMap", m_PassabilityMapVal, true); - m_ScriptInterface.SetProperty(state.get(), "territoryMap", m_TerritoryMapVal, true); + m_ScriptInterface->SetProperty(state.get(), "passabilityMap", m_PassabilityMapVal, true); + m_ScriptInterface->SetProperty(state.get(), "territoryMap", m_TerritoryMapVal, true); - m_ScriptInterface.CallFunctionVoid(m_SharedAIObj.get(), "init", state); - m_ScriptInterface.MaybeGC(); + m_ScriptInterface->CallFunctionVoid(m_SharedAIObj.get(), "init", state); + m_ScriptInterface->MaybeGC(); for (size_t i = 0; i < m_Players.size(); ++i) { @@ -545,7 +482,7 @@ public: { m_PassabilityMap = passabilityMap; - JSContext* cx = m_ScriptInterface.GetContext(); + JSContext* cx = m_ScriptInterface->GetContext(); m_PassabilityMapVal = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, m_PassabilityMap)); } @@ -553,7 +490,7 @@ public: { m_TerritoryMap = territoryMap; - JSContext* cx = m_ScriptInterface.GetContext(); + JSContext* cx = m_ScriptInterface->GetContext(); m_TerritoryMapVal = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, m_TerritoryMap)); } @@ -583,25 +520,25 @@ public: } void RegisterTechTemplates(const shared_ptr& techTemplates) { - JSContext* cx = m_ScriptInterface.GetContext(); - m_TechTemplates = CScriptValRooted(cx, m_ScriptInterface.ReadStructuredClone(techTemplates)); + JSContext* cx = m_ScriptInterface->GetContext(); + m_TechTemplates = CScriptValRooted(cx, m_ScriptInterface->ReadStructuredClone(techTemplates)); } void LoadEntityTemplates(const std::vector >& templates) { m_HasLoadedEntityTemplates = true; - m_ScriptInterface.Eval("({})", m_EntityTemplates); + m_ScriptInterface->Eval("({})", m_EntityTemplates); for (size_t i = 0; i < templates.size(); ++i) { - jsval val = templates[i].second->ToJSVal(m_ScriptInterface.GetContext(), false); - m_ScriptInterface.SetProperty(m_EntityTemplates.get(), templates[i].first.c_str(), CScriptVal(val), true); + jsval val = templates[i].second->ToJSVal(m_ScriptInterface->GetContext(), false); + m_ScriptInterface->SetProperty(m_EntityTemplates.get(), templates[i].first.c_str(), CScriptVal(val), true); } // Since the template data is shared between AI players, freeze it // to stop any of them changing it and confusing the other players - m_ScriptInterface.FreezeObject(m_EntityTemplates.get(), true); + m_ScriptInterface->FreezeObject(m_EntityTemplates.get(), true); } void Serialize(std::ostream& stream, bool isDebug) @@ -610,13 +547,13 @@ public: if (isDebug) { - CDebugSerializer serializer(m_ScriptInterface, stream); + CDebugSerializer serializer(*m_ScriptInterface, stream); serializer.Indent(4); SerializeState(serializer); } else { - CStdSerializer serializer(m_ScriptInterface, stream); + CStdSerializer serializer(*m_ScriptInterface, stream); // TODO: see comment in Deserialize() serializer.SetSerializablePrototypes(m_SerializablePrototypes); SerializeState(serializer); @@ -637,7 +574,7 @@ public: if (m_HasSharedComponent) { CScriptVal sharedData; - if (!m_ScriptInterface.CallFunction(m_SharedAIObj.get(), "Serialize", sharedData)) + if (!m_ScriptInterface->CallFunction(m_SharedAIObj.get(), "Serialize", sharedData)) LOGERROR(L"AI shared script Serialize call failed"); serializer.ScriptVal("sharedData", sharedData); } @@ -650,15 +587,15 @@ public: serializer.NumberU32_Unbounded("num commands", (u32)m_Players[i]->m_Commands.size()); for (size_t j = 0; j < m_Players[i]->m_Commands.size(); ++j) { - CScriptVal val = m_ScriptInterface.ReadStructuredClone(m_Players[i]->m_Commands[j]); + CScriptVal val = m_ScriptInterface->ReadStructuredClone(m_Players[i]->m_Commands[j]); serializer.ScriptVal("command", val); } - bool hasCustomSerialize = m_ScriptInterface.HasProperty(m_Players[i]->m_Obj.get(), "Serialize"); + bool hasCustomSerialize = m_ScriptInterface->HasProperty(m_Players[i]->m_Obj.get(), "Serialize"); if (hasCustomSerialize) { CScriptVal scriptData; - if (!m_ScriptInterface.CallFunction(m_Players[i]->m_Obj.get(), "Serialize", scriptData)) + if (!m_ScriptInterface->CallFunction(m_Players[i]->m_Obj.get(), "Serialize", scriptData)) LOGERROR(L"AI script Serialize call failed"); serializer.ScriptVal("data", scriptData); } @@ -673,7 +610,7 @@ public: { ENSURE(m_CommandsComputed); // deserializing while we're still actively computing would be bad - CStdDeserializer deserializer(m_ScriptInterface, stream); + CStdDeserializer deserializer(*m_ScriptInterface, stream); m_PlayerMetadata.clear(); m_Players.clear(); @@ -695,7 +632,7 @@ public: { CScriptVal sharedData; deserializer.ScriptVal("sharedData", sharedData); - if (!m_ScriptInterface.CallFunctionVoid(m_SharedAIObj.get(), "Deserialize", sharedData)) + if (!m_ScriptInterface->CallFunctionVoid(m_SharedAIObj.get(), "Deserialize", sharedData)) LOGERROR(L"AI shared script Deserialize call failed"); } @@ -717,7 +654,7 @@ public: { CScriptVal val; deserializer.ScriptVal("command", val); - m_Players.back()->m_Commands.push_back(m_ScriptInterface.WriteStructuredClone(val.get())); + m_Players.back()->m_Commands.push_back(m_ScriptInterface->WriteStructuredClone(val.get())); } // TODO: this is yucky but necessary while the AIs are sharing data between contexts; @@ -726,17 +663,17 @@ public: // prototypes could be stored in their ScriptInterface deserializer.SetSerializablePrototypes(m_DeserializablePrototypes); - bool hasCustomDeserialize = m_ScriptInterface.HasProperty(m_Players.back()->m_Obj.get(), "Deserialize"); + bool hasCustomDeserialize = m_ScriptInterface->HasProperty(m_Players.back()->m_Obj.get(), "Deserialize"); if (hasCustomDeserialize) { CScriptVal scriptData; deserializer.ScriptVal("data", scriptData); if (m_Players[i]->m_UseSharedComponent) { - if (!m_ScriptInterface.CallFunctionVoid(m_Players.back()->m_Obj.get(), "Deserialize", scriptData, m_SharedAIObj)) + if (!m_ScriptInterface->CallFunctionVoid(m_Players.back()->m_Obj.get(), "Deserialize", scriptData, m_SharedAIObj)) LOGERROR(L"AI script Deserialize call failed"); } - else if (!m_ScriptInterface.CallFunctionVoid(m_Players.back()->m_Obj.get(), "Deserialize", scriptData)) + else if (!m_ScriptInterface->CallFunctionVoid(m_Players.back()->m_Obj.get(), "Deserialize", scriptData)) { LOGERROR(L"AI script deserialize() call failed"); } @@ -770,7 +707,7 @@ private: if (m_PlayerMetadata.find(path) == m_PlayerMetadata.end()) { // Load and cache the AI player metadata - m_PlayerMetadata[path] = m_ScriptInterface.ReadJSONFile(path); + m_PlayerMetadata[path] = m_ScriptInterface->ReadJSONFile(path); } return m_PlayerMetadata[path]; @@ -786,7 +723,7 @@ private: if (m_TurnNum++ % 50 == 0) { PROFILE3("AI compute GC"); - m_ScriptInterface.MaybeGC(); + m_ScriptInterface->MaybeGC(); } return; } @@ -795,13 +732,13 @@ private: CScriptVal state; { PROFILE3("AI compute read state"); - state = m_ScriptInterface.ReadStructuredClone(m_GameState); - m_ScriptInterface.SetProperty(state.get(), "passabilityMap", m_PassabilityMapVal, true); - m_ScriptInterface.SetProperty(state.get(), "territoryMap", m_TerritoryMapVal, true); + state = m_ScriptInterface->ReadStructuredClone(m_GameState); + m_ScriptInterface->SetProperty(state.get(), "passabilityMap", m_PassabilityMapVal, true); + m_ScriptInterface->SetProperty(state.get(), "territoryMap", m_TerritoryMapVal, true); } // It would be nice to do - // m_ScriptInterface.FreezeObject(state.get(), true); + // m_ScriptInterface->FreezeObject(state.get(), true); // to prevent AI scripts accidentally modifying the state and // affecting other AI scripts they share it with. But the performance // cost is far too high, so we won't do that. @@ -810,7 +747,7 @@ private: if (m_HasSharedComponent) { PROFILE3("AI run shared component"); - m_ScriptInterface.CallFunctionVoid(m_SharedAIObj.get(), "onUpdate", state); + m_ScriptInterface->CallFunctionVoid(m_SharedAIObj.get(), "onUpdate", state); } for (size_t i = 0; i < m_Players.size(); ++i) @@ -818,18 +755,19 @@ private: PROFILE3("AI script"); PROFILE2_ATTR("player: %d", m_Players[i]->m_Player); PROFILE2_ATTR("script: %ls", m_Players[i]->m_AIName.c_str()); + if (m_HasSharedComponent && m_Players[i]->m_UseSharedComponent) - m_Players[i]->Run(state,m_SharedAIObj); + m_Players[i]->Run(state, m_Players[i]->m_Player, m_SharedAIObj); else - m_Players[i]->Run(state); + m_Players[i]->Run(state, m_Players[i]->m_Player); } // Run GC if we are about to overflow - if (JS_GetGCParameter(m_ScriptInterface.GetRuntime(), JSGC_BYTES) > 33000000) + if (JS_GetGCParameter(m_ScriptInterface->GetRuntime(), JSGC_BYTES) > 33000000) { PROFILE3("AI compute GC"); - JS_GC(m_ScriptInterface.GetContext()); + JS_GC(m_ScriptInterface->GetContext()); } // Run the GC every so often. @@ -838,12 +776,12 @@ private: /*if (m_TurnNum++ % 20 == 0) { PROFILE3("AI compute GC"); - m_ScriptInterface.MaybeGC(); + m_ScriptInterface->MaybeGC(); }*/ } shared_ptr m_ScriptRuntime; - ScriptInterface m_ScriptInterface; + shared_ptr m_ScriptInterface; boost::rand48 m_RNG; u32 m_TurnNum; @@ -858,6 +796,8 @@ private: CScriptValRooted m_SharedAIObj; std::vector m_Commands; + std::set m_LoadedModules; + shared_ptr m_GameState; Grid m_PassabilityMap; CScriptValRooted m_PassabilityMapVal;
Aegis Build Order