Moves AI players to one global using the module pattern.

This avoids wrapping overhead that would otherwise be required because
multiple globals per compartment aren't supported anymore in newer
versions of SpiderMonkey.

Check the ticket for a detailed explanation.

Closes #2322
Refs #2241
Refs #1886

This was SVN commit r14441.
This commit is contained in:
Yves 2013-12-30 10:04:59 +00:00
parent 9a8d5312d4
commit 3362c591f5
48 changed files with 1452 additions and 1260 deletions

View File

@ -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;
}());

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\"> <html> <head> <title>Aegis Queue Manager</title> <link rel=\"stylesheet\" href=\"table.css\"> </head> <body> <table> <caption>Aegis Build Order</caption> ");
for (var i in this.queues){
@ -325,13 +331,13 @@ QueueManager.prototype.HTMLprintQueues = function(gameState){
log("</body></html>");
};
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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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,

View File

@ -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;
};

View File

@ -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;
}());

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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();

View File

@ -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;

View File

@ -1,4 +1,4 @@
function Defence(){
function Defence(Config){
this.ACQUIRE_DIST = Config.defence.acquireDistance;
this.RELEASE_DIST = Config.defence.releaseDistance;

View File

@ -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]});
});
}*/
});

View File

@ -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;
};
};

View File

@ -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];
}
};
};

View File

@ -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();

View File

@ -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{

View File

@ -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;
}
};
};

View File

@ -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<ScriptRuntime>& runtime, boost::rand48& rng) :
m_Worker(worker), m_AIName(aiName), m_Player(player), m_Difficulty(difficulty), m_ScriptInterface("Engine", "AI", runtime)
shared_ptr<ScriptInterface> scriptInterface) :
m_Worker(worker), m_AIName(aiName), m_Player(player), m_Difficulty(difficulty), m_ScriptInterface(scriptInterface)
{
m_ScriptInterface.SetCallbackData(static_cast<void*> (this));
m_ScriptInterface.ReplaceNondeterministicRNG(rng);
m_ScriptInterface.LoadGlobalScripts();
m_ScriptInterface.RegisterFunction<void, std::wstring, CAIPlayer::IncludeModule>("IncludeModule");
m_ScriptInterface.RegisterFunction<void, CAIPlayer::DumpHeap>("DumpHeap");
m_ScriptInterface.RegisterFunction<void, CAIPlayer::ForceGC>("ForceGC");
m_ScriptInterface.RegisterFunction<void, CScriptValRooted, CAIPlayer::PostCommand>("PostCommand");
m_ScriptInterface.RegisterFunction<void, std::wstring, std::vector<u32>, u32, u32, u32, CAIPlayer::DumpImage>("DumpImage");
m_ScriptInterface.RegisterFunction<void, std::wstring, CScriptVal, CAIPlayer::RegisterSerializablePrototype>("RegisterSerializablePrototype");
}
~CAIPlayer()
@ -103,112 +90,10 @@ private:
m_Commands.clear();
}
static void IncludeModule(void* cbdata, std::wstring name)
{
CAIPlayer* self = static_cast<CAIPlayer*> (cbdata);
self->LoadScripts(name);
}
static void DumpHeap(void* cbdata)
{
CAIPlayer* self = static_cast<CAIPlayer*> (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<CAIPlayer*> (cbdata);
JS_GC(self->m_ScriptInterface.GetContext());
}
static void PostCommand(void* cbdata, CScriptValRooted cmd)
{
CAIPlayer* self = static_cast<CAIPlayer*> (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<u32> 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<u8> 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<CAIPlayer*> (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<ScriptInterface> m_ScriptInterface;
CScriptValRooted m_Obj;
std::vector<shared_ptr<ScriptInterface::StructuredClone> > m_Commands;
std::set<std::wstring> 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<void*> (this));
m_ScriptInterface.RegisterFunction<void, CScriptValRooted, CAIWorker::PostCommand>("PostCommand");
m_ScriptInterface.RegisterFunction<void, CAIWorker::DumpHeap>("DumpHeap");
m_ScriptInterface.RegisterFunction<void, CAIWorker::ForceGC>("ForceGC");
m_ScriptInterface->RegisterFunction<void, int, CScriptValRooted, CAIWorker::PostCommand>("PostCommand");
m_ScriptInterface->RegisterFunction<void, std::wstring, CAIWorker::IncludeModule>("IncludeModule");
m_ScriptInterface->RegisterFunction<void, CAIWorker::DumpHeap>("DumpHeap");
m_ScriptInterface->RegisterFunction<void, CAIWorker::ForceGC>("ForceGC");
m_ScriptInterface.RegisterFunction<void, std::wstring, std::vector<u32>, u32, u32, u32, CAIWorker::DumpImage>("DumpImage");
m_ScriptInterface->RegisterFunction<void, std::wstring, std::vector<u32>, 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<CAIPlayer*> (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<CAIWorker*> (cbdata);
self->LoadScripts(name);
}
static void PostCommand(void* cbdata, int playerid, CScriptValRooted cmd)
{
CAIWorker* self = static_cast<CAIWorker*> (cbdata);
self->PostCommand(playerid, cmd);
}
void PostCommand(int playerid, CScriptValRooted cmd)
{
for (size_t i=0; i<m_Players.size(); i++)
{
if (m_Players[i]->m_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<CAIWorker*> (cbdata);
self->m_ScriptInterface.DumpHeap();
self->m_ScriptInterface->DumpHeap();
}
static void ForceGC(void* cbdata)
{
@ -374,7 +313,7 @@ public:
}
CAIWorker* self = static_cast<CAIWorker*> (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<CAIPlayer> ai(new CAIPlayer(*this, aiName, player, difficulty, m_ScriptRuntime, m_RNG));
shared_ptr<CAIPlayer> 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<ScriptInterface::StructuredClone>& 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<std::pair<std::string, const CParamNode*> >& 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<ScriptRuntime> m_ScriptRuntime;
ScriptInterface m_ScriptInterface;
shared_ptr<ScriptInterface> m_ScriptInterface;
boost::rand48 m_RNG;
u32 m_TurnNum;
@ -858,6 +796,8 @@ private:
CScriptValRooted m_SharedAIObj;
std::vector<SCommandSets> m_Commands;
std::set<std::wstring> m_LoadedModules;
shared_ptr<ScriptInterface::StructuredClone> m_GameState;
Grid<u16> m_PassabilityMap;
CScriptValRooted m_PassabilityMapVal;