forked from mirrors/0ad
Major update for qBot-xp, with slight changes to unitAI (that broke qBot, and fix for that).
This was SVN commit r12343.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
// basically an attack plan. The name is an artifact.
|
// basically an attack plan. The name is an artifact.
|
||||||
function CityAttack(gameState, militaryManager, uniqueID, targetEnemy, type , targetFinder){
|
function CityAttack(gameState, militaryManager, uniqueID, targetEnemy, type , targetFinder) {
|
||||||
|
|
||||||
//This is the list of IDs of the units in the plan
|
//This is the list of IDs of the units in the plan
|
||||||
this.idList=[];
|
this.idList=[];
|
||||||
@@ -35,12 +35,33 @@ function CityAttack(gameState, militaryManager, uniqueID, targetEnemy, type , ta
|
|||||||
|
|
||||||
this.onArrivalReaction = "proceedOnTargets";
|
this.onArrivalReaction = "proceedOnTargets";
|
||||||
|
|
||||||
|
// priority is relative. If all are 0, the only relevant criteria is "currentsize/targetsize".
|
||||||
|
// if not, this is a "bonus". The higher the priority, the more this unit will get built.
|
||||||
|
// Should really be clamped to [0.1-1.5] (assuming 1 is default/the norm)
|
||||||
|
// Eg: if all are priority 1, and the siege is 0.5, the siege units will get built
|
||||||
|
// only once every other category is at least 50% of its target size.
|
||||||
this.unitStat = {};
|
this.unitStat = {};
|
||||||
this.unitStat["RangedInfantry"] = { "priority" : 1, "minSize" : 4, "targetSize" : 10, "batchSize" : 5, "classes" : ["Infantry","Ranged"], "templates" : [] };
|
this.unitStat["RangedInfantry"] = { "priority" : 1, "minSize" : 4, "targetSize" : 10, "batchSize" : 5, "classes" : ["Infantry","Ranged"], "templates" : [] };
|
||||||
this.unitStat["MeleeInfantry"] = { "priority" : 1, "minSize" : 4, "targetSize" : 10, "batchSize" : 5, "classes" : ["Infantry","Melee"], "templates" : [] };
|
this.unitStat["MeleeInfantry"] = { "priority" : 1, "minSize" : 4, "targetSize" : 10, "batchSize" : 5, "classes" : ["Infantry","Melee"], "templates" : [] };
|
||||||
this.unitStat["MeleeCavalry"] = { "priority" : 1, "minSize" : 3, "targetSize" : 8 , "batchSize" : 3, "classes" : ["Cavalry","Melee"], "templates" : [] };
|
this.unitStat["MeleeCavalry"] = { "priority" : 1, "minSize" : 3, "targetSize" : 8 , "batchSize" : 3, "classes" : ["Cavalry","Melee"], "templates" : [] };
|
||||||
this.unitStat["RangedCavalry"] = { "priority" : 1, "minSize" : 3, "targetSize" : 8 , "batchSize" : 3, "classes" : ["Cavalry","Ranged"], "templates" : [] };
|
this.unitStat["RangedCavalry"] = { "priority" : 1, "minSize" : 3, "targetSize" : 8 , "batchSize" : 3, "classes" : ["Cavalry","Ranged"], "templates" : [] };
|
||||||
this.unitStat["Siege"] = { "priority" : 1, "minSize" : 0, "targetSize" : 3 , "batchSize" : 1, "classes" : ["Siege"], "templates" : [] };
|
this.unitStat["Siege"] = { "priority" : 0.5, "minSize" : 0, "targetSize" : 3 , "batchSize" : 1, "classes" : ["Siege"], "templates" : [] };
|
||||||
|
|
||||||
|
|
||||||
|
if (type === "superSized") {
|
||||||
|
this.unitStat["RangedInfantry"] = { "priority" : 1, "minSize" : 5, "targetSize" : 18, "batchSize" : 5, "classes" : ["Infantry","Ranged"], "templates" : [] };
|
||||||
|
this.unitStat["MeleeInfantry"] = { "priority" : 1, "minSize" : 6, "targetSize" : 24, "batchSize" : 5, "classes" : ["Infantry","Melee"], "templates" : [] };
|
||||||
|
this.unitStat["MeleeCavalry"] = { "priority" : 1, "minSize" : 4, "targetSize" : 12 , "batchSize" : 5, "classes" : ["Cavalry","Melee"], "templates" : [] };
|
||||||
|
this.unitStat["RangedCavalry"] = { "priority" : 1, "minSize" : 4, "targetSize" : 12 , "batchSize" : 5, "classes" : ["Cavalry","Ranged"], "templates" : [] };
|
||||||
|
this.unitStat["Siege"] = { "priority" : 0.5, "minSize" : 3, "targetSize" : 6 , "batchSize" : 3, "classes" : ["Siege"], "templates" : [] };
|
||||||
|
this.maxPreparationTime = 450*1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
this.unitStat["Siege"]["filter"] = function (ent) {
|
||||||
|
var strength = [ent.attackStrengths("Melee")["crush"],ent.attackStrengths("Ranged")["crush"]];
|
||||||
|
return (strength[0] > 15 || strength[1] > 15);
|
||||||
|
};*/
|
||||||
|
|
||||||
var filter = Filters.and(Filters.byMetadata("plan",this.name),Filters.byOwner(gameState.player));
|
var filter = Filters.and(Filters.byMetadata("plan",this.name),Filters.byOwner(gameState.player));
|
||||||
this.unitCollection = gameState.getOwnEntities().filter(filter);
|
this.unitCollection = gameState.getOwnEntities().filter(filter);
|
||||||
@@ -58,7 +79,7 @@ function CityAttack(gameState, militaryManager, uniqueID, targetEnemy, type , ta
|
|||||||
var cat = unitCat;
|
var cat = unitCat;
|
||||||
var Unit = this.unitStat[cat];
|
var Unit = this.unitStat[cat];
|
||||||
|
|
||||||
var filter = Filters.and(Filters.byClassesAnd(Unit["classes"]),Filters.and(Filters.byMetadata("plan",this.name),Filters.byOwner(gameState.player)));
|
filter = Filters.and(Filters.byClassesAnd(Unit["classes"]),Filters.and(Filters.byMetadata("plan",this.name),Filters.byOwner(gameState.player)));
|
||||||
this.unit[cat] = gameState.getOwnEntities().filter(filter);
|
this.unit[cat] = gameState.getOwnEntities().filter(filter);
|
||||||
this.unit[cat].registerUpdates();
|
this.unit[cat].registerUpdates();
|
||||||
this.unit[cat].length;
|
this.unit[cat].length;
|
||||||
@@ -110,15 +131,22 @@ function CityAttack(gameState, militaryManager, uniqueID, targetEnemy, type , ta
|
|||||||
// taking this so that fortresses won't crash it for now. TODO: change the rally point if it becomes invalid
|
// taking this so that fortresses won't crash it for now. TODO: change the rally point if it becomes invalid
|
||||||
if(gameState.ai.pathsToMe.length > 1)
|
if(gameState.ai.pathsToMe.length > 1)
|
||||||
var position = [(gameState.ai.pathsToMe[0][0]+gameState.ai.pathsToMe[1][0])/2.0,(gameState.ai.pathsToMe[0][1]+gameState.ai.pathsToMe[1][1])/2.0];
|
var position = [(gameState.ai.pathsToMe[0][0]+gameState.ai.pathsToMe[1][0])/2.0,(gameState.ai.pathsToMe[0][1]+gameState.ai.pathsToMe[1][1])/2.0];
|
||||||
else
|
else if (gameState.ai.pathsToMe.length !== 0)
|
||||||
var position = [gameState.ai.pathsToMe[0][0],gameState.ai.pathsToMe[0][1]];
|
var position = [gameState.ai.pathsToMe[0][0],gameState.ai.pathsToMe[0][1]];
|
||||||
|
else
|
||||||
|
var position = [-1,-1];
|
||||||
|
|
||||||
var CCs = gameState.getOwnEntities().filter(Filters.byClass("CivCentre"));
|
var CCs = gameState.getOwnEntities().filter(Filters.byClass("CivCentre"));
|
||||||
var nearestCCArray = CCs.filterNearest(position, 1).toEntityArray();
|
var nearestCCArray = CCs.filterNearest(position, 1).toEntityArray();
|
||||||
var CCpos = nearestCCArray[0].position();
|
var CCpos = nearestCCArray[0].position();
|
||||||
this.rallyPoint = [0,0];
|
this.rallyPoint = [0,0];
|
||||||
this.rallyPoint[0] = (position[0]*3 + CCpos[0]) / 4.0;
|
if (position[0] !== -1) {
|
||||||
this.rallyPoint[1] = (position[1]*3 + CCpos[1]) / 4.0;
|
this.rallyPoint[0] = (position[0]*3 + CCpos[0]) / 4.0;
|
||||||
|
this.rallyPoint[1] = (position[1]*3 + CCpos[1]) / 4.0;
|
||||||
|
} else {
|
||||||
|
this.rallyPoint[0] = CCpos[0];
|
||||||
|
this.rallyPoint[1] = CCpos[1];
|
||||||
|
}
|
||||||
if (type == 'harass_raid')
|
if (type == 'harass_raid')
|
||||||
{
|
{
|
||||||
this.rallyPoint[0] = (position[0]*3.9 + 0.1 * CCpos[0]) / 4.0;
|
this.rallyPoint[0] = (position[0]*3.9 + 0.1 * CCpos[0]) / 4.0;
|
||||||
@@ -132,12 +160,13 @@ function CityAttack(gameState, militaryManager, uniqueID, targetEnemy, type , ta
|
|||||||
this.threatList = []; // sounds so FBI
|
this.threatList = []; // sounds so FBI
|
||||||
this.tactics = undefined;
|
this.tactics = undefined;
|
||||||
|
|
||||||
gameState.ai.queueManager.addQueue("plan_" + this.name, 130); // high priority: some may gather anyway
|
gameState.ai.queueManager.addQueue("plan_" + this.name, 100); // high priority: some may gather anyway
|
||||||
this.queue = gameState.ai.queues["plan_" + this.name];
|
this.queue = gameState.ai.queues["plan_" + this.name];
|
||||||
|
|
||||||
|
|
||||||
this.assignUnits(gameState);
|
this.assignUnits(gameState);
|
||||||
|
|
||||||
|
// get a good path to an estimated target.
|
||||||
|
this.pathFinder = new aStarPath(gameState,false);
|
||||||
};
|
};
|
||||||
|
|
||||||
CityAttack.prototype.getName = function(){
|
CityAttack.prototype.getName = function(){
|
||||||
@@ -160,6 +189,8 @@ CityAttack.prototype.canStart = function(gameState){
|
|||||||
// TODO: check if our target is valid and a few other stuffs (good moment to attack?)
|
// TODO: check if our target is valid and a few other stuffs (good moment to attack?)
|
||||||
};
|
};
|
||||||
CityAttack.prototype.isStarted = function(){
|
CityAttack.prototype.isStarted = function(){
|
||||||
|
if ((this.state !== "unexecuted"))
|
||||||
|
debug ("Attack plan already started");
|
||||||
return !(this.state == "unexecuted");
|
return !(this.state == "unexecuted");
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -178,6 +209,8 @@ CityAttack.prototype.setPaused = function(gameState, boolValue){
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
CityAttack.prototype.mustStart = function(gameState){
|
CityAttack.prototype.mustStart = function(gameState){
|
||||||
|
if (this.isPaused())
|
||||||
|
return false;
|
||||||
var MaxReachedEverywhere = true;
|
var MaxReachedEverywhere = true;
|
||||||
for (unitCat in this.unitStat) {
|
for (unitCat in this.unitStat) {
|
||||||
var Unit = this.unitStat[unitCat];
|
var Unit = this.unitStat[unitCat];
|
||||||
@@ -191,74 +224,152 @@ CityAttack.prototype.mustStart = function(gameState){
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Three returns possible: 1 is "keep going", 0 is "failed plan", 2 is "start"
|
// 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, militaryManager,events) {
|
CityAttack.prototype.updatePreparation = function(gameState, militaryManager,events) {
|
||||||
if (this.isPaused())
|
var self = this;
|
||||||
return 1; // continue
|
|
||||||
|
if (this.path == undefined || this.target == undefined) {
|
||||||
|
// find our target
|
||||||
|
var targets = this.targetFinder(gameState, militaryManager);
|
||||||
|
if (targets.length === 0){
|
||||||
|
targets = this.defaultTargetFinder(gameState, militaryManager);
|
||||||
|
}
|
||||||
|
if (targets.length) {
|
||||||
|
var rand = Math.floor((Math.random()*targets.length));
|
||||||
|
this.targetPos = undefined;
|
||||||
|
var count = 0;
|
||||||
|
while (!this.targetPos){
|
||||||
|
this.target = targets.toEntityArray()[rand];
|
||||||
|
this.targetPos = this.target.position();
|
||||||
|
count++;
|
||||||
|
if (count > 1000){
|
||||||
|
debug("No target with a valid position found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.path = this.pathFinder.getPath(this.rallyPoint,this.targetPos, false, 2);
|
||||||
|
if (this.path === undefined || this.path[1] === true) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
this.path = this.path[0];
|
||||||
|
} else if (targets.length == 0 ) {
|
||||||
|
gameState.ai.gameFinished = true;
|
||||||
|
debug ("I do not have any target. So I'll just assume I won the game.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Engine.ProfileStart("Update Preparation");
|
Engine.ProfileStart("Update Preparation");
|
||||||
|
|
||||||
// let's sort by training advancement, ie 'current size / target size'
|
// keep on while the units finish being trained.
|
||||||
this.buildOrder.sort(function (a,b) {
|
if (this.mustStart(gameState) && gameState.countOwnQueuedEntitiesWithMetadata("plan", +this.name) ) {
|
||||||
a[0] = a[2].length/a[3]["targetSize"];
|
this.assignUnits(gameState);
|
||||||
b[0] = b[2].length/b[3]["targetSize"];
|
if ( (gameState.ai.turn + gameState.ai.player) % 40 == 0) {
|
||||||
return (a[0]) - (b[0]);
|
this.AllToRallyPoint(gameState, true); // gain some time, start regrouping
|
||||||
});
|
this.unitCollection.forEach(function (entity) { entity.setMetadata("role","attack"); });
|
||||||
|
}
|
||||||
this.assignUnits(gameState);
|
Engine.ProfileStop();
|
||||||
|
return 1;
|
||||||
if ( (gameState.ai.turn + gameState.ai.player) % 40 == 0)
|
} else if (!this.mustStart(gameState)) {
|
||||||
this.AllToRallyPoint(gameState, false);
|
// We still have time left to recruit units and do stuffs.
|
||||||
|
|
||||||
var canstart = this.canStart(gameState);
|
// let's sort by training advancement, ie 'current size / target size'
|
||||||
|
// count the number of queued units too.
|
||||||
Engine.ProfileStart("Creating units and looking through events");
|
// substract priority.
|
||||||
|
this.buildOrder.sort(function (a,b) { //}) {
|
||||||
// gets the number in training of the same kind as the first one.
|
|
||||||
var specialData = "Plan_"+this.name+"_"+this.buildOrder[0][4];
|
var aQueued = gameState.countOwnQueuedEntitiesWithMetadata("special","Plan_"+self.name+"_"+self.buildOrder[0][4]);
|
||||||
var inTraining = gameState.countOwnQueuedEntitiesWithMetadata("special",specialData);
|
aQueued += self.queue.countTotalQueuedUnitsWithMetadata("special","Plan_"+self.name+"_"+self.buildOrder[0][4]);
|
||||||
if (this.queue.countTotalQueuedUnits() + inTraining + this.buildOrder[0][2].length < Math.min(15,this.buildOrder[0][3]["targetSize"]) ) {
|
a[0] = (a[2].length + aQueued)/a[3]["targetSize"];
|
||||||
if (this.buildOrder[0][0] < 1 && this.queue.countTotalQueuedUnits() < 5) {
|
|
||||||
|
var bQueued = gameState.countOwnQueuedEntitiesWithMetadata("special","Plan_"+self.name+"_"+self.buildOrder[0][4]);
|
||||||
var template = militaryManager.findBestTrainableUnit(gameState, this.buildOrder[0][1], [ ["strength",1], ["cost",1] ] );
|
bQueued += self.queue.countTotalQueuedUnitsWithMetadata("special","Plan_"+self.name+"_"+self.buildOrder[0][4]);
|
||||||
//debug ("tried " + uneval(this.buildOrder[0][1]) +", and " + template);
|
b[0] = (b[2].length + bQueued)/b[3]["targetSize"];
|
||||||
// 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) {
|
a[0] -= a[3]["priority"];
|
||||||
delete this.unitStat[this.buildOrder[0][4]]; // deleting the associated unitstat.
|
b[0] -= b[3]["priority"];
|
||||||
this.buildOrder.splice(0,1);
|
return (a[0]) - (b[0]);
|
||||||
|
});
|
||||||
} else {
|
|
||||||
if (gameState.getTemplate(template).hasClasses(["CitizenSoldier", "Infantry"]))
|
if (!this.isPaused()) {
|
||||||
this.queue.addItem( new UnitTrainingPlan(gameState,template, { "role" : "worker", "plan" : this.name, "special" : specialData },this.buildOrder[0][3]["batchSize"] ) );
|
this.assignUnits(gameState);
|
||||||
else
|
|
||||||
this.queue.addItem( new UnitTrainingPlan(gameState,template, { "role" : "attack", "plan" : this.name, "special" : specialData },this.buildOrder[0][3]["batchSize"] ) );
|
if ( (gameState.ai.turn + gameState.ai.player) % 40 == 0) {
|
||||||
|
this.AllToRallyPoint(gameState, false);
|
||||||
|
this.unitCollection.setStance("defensive"); // make sure units won't disperse out of control
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// can happen for now
|
Engine.ProfileStart("Creating units.");
|
||||||
if (this.buildOrder.length === 0) {
|
|
||||||
debug ("Ending plan: no build orders");
|
// gets the number in training of the same kind as the first one.
|
||||||
return 0; // will abort the plan, should return something else
|
var specialData = "Plan_"+this.name+"_"+this.buildOrder[0][4];
|
||||||
}
|
var inTraining = gameState.countOwnQueuedEntitiesWithMetadata("special",specialData);
|
||||||
|
if (this.queue.countTotalQueuedUnits() + inTraining + this.buildOrder[0][2].length < Math.min(15,this.buildOrder[0][3]["targetSize"]) ) {
|
||||||
for (var key in events){
|
if (this.buildOrder[0][0] < 1 && this.queue.length() < 4) {
|
||||||
var e = events[key];
|
|
||||||
if (e.type === "Attacked" && e.msg){
|
var template = militaryManager.findBestTrainableUnit(gameState, this.buildOrder[0][1], [ ["strength",1], ["cost",1] ] );
|
||||||
if (this.unitCollection.toIdArray().indexOf(e.msg.target) !== -1){
|
//debug ("tried " + uneval(this.buildOrder[0][1]) +", and " + template);
|
||||||
var attacker = gameState.getEntityById(e.msg.attacker);
|
// HACK (TODO replace) : if we have no trainable template... Then we'll simply remove the buildOrder, effectively removing the unit from the plan.
|
||||||
if (attacker && attacker.position()) {
|
if (template === undefined) {
|
||||||
this.unitCollection.attack(e.msg.attacker);
|
delete this.unitStat[this.buildOrder[0][4]]; // deleting the associated unitstat.
|
||||||
break;
|
this.buildOrder.splice(0,1);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (gameState.getTemplate(template).hasClasses(["CitizenSoldier", "Infantry"]))
|
||||||
|
this.queue.addItem( new UnitTrainingPlan(gameState,template, { "role" : "worker", "plan" : this.name, "special" : specialData },this.buildOrder[0][3]["batchSize"] ) );
|
||||||
|
else
|
||||||
|
this.queue.addItem( new UnitTrainingPlan(gameState,template, { "role" : "attack", "plan" : this.name, "special" : specialData },this.buildOrder[0][3]["batchSize"] ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
if (!this.startedPathing && this.path === undefined) {
|
||||||
|
|
||||||
|
// find our target
|
||||||
|
var targets = this.targetFinder(gameState, militaryManager);
|
||||||
|
if (targets.length === 0){
|
||||||
|
targets = this.defaultTargetFinder(gameState, militaryManager);
|
||||||
|
}
|
||||||
|
if (targets.length) {
|
||||||
|
var rand = Math.floor((Math.random()*targets.length));
|
||||||
|
this.targetPos = undefined;
|
||||||
|
var count = 0;
|
||||||
|
while (!this.targetPos){
|
||||||
|
var target = targets.toEntityArray()[rand];
|
||||||
|
this.targetPos = target.position();
|
||||||
|
count++;
|
||||||
|
if (count > 1000){
|
||||||
|
debug("No target with a valid position found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.startedPathing = true;
|
||||||
|
// Start pathfinding using the optimized version, with a minimal sampling of 2
|
||||||
|
this.pathFinder.getPath(this.rallyPoint,this.targetPos, false, 2, gameState);
|
||||||
|
}
|
||||||
|
} else if (this.startedPathing) {
|
||||||
|
var path = this.pathFinder.continuePath(gameState);
|
||||||
|
if (path !== "toBeContinued") {
|
||||||
|
this.startedPathing = false;
|
||||||
|
this.path = path;
|
||||||
|
debug("Pathing ended");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// can happen for now
|
||||||
|
if (this.buildOrder.length === 0) {
|
||||||
|
debug ("Ending plan: no build orders");
|
||||||
|
return 0; // will abort the plan, should return something else
|
||||||
|
}
|
||||||
|
Engine.ProfileStop();
|
||||||
|
Engine.ProfileStop();
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
Engine.ProfileStop();
|
Engine.ProfileStop();
|
||||||
// we count our units by triggering "canStart"
|
// if we're here, it means we must start (and have no units in training left).
|
||||||
// returns false if we can no longer have time and cannot start.
|
// if we can, do, else, abort.
|
||||||
// returns 0 if I must start and can't, returns 1 if I don't have to start, and returns 2 if I must start and can
|
if (this.canStart(gameState))
|
||||||
if (!this.mustStart(gameState))
|
|
||||||
return 1;
|
|
||||||
else if (canstart)
|
|
||||||
return 2;
|
return 2;
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
@@ -268,21 +379,13 @@ CityAttack.prototype.assignUnits = function(gameState){
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
// TODO: assign myself units that fit only, right now I'm getting anything.
|
// TODO: assign myself units that fit only, right now I'm getting anything.
|
||||||
|
|
||||||
/*
|
|
||||||
// I'll take any unit set to "Defense" that has no subrole (ie is set to be a defensive unit, but has no particular task)
|
|
||||||
// I assign it to myself, and then it's mine, the entity collection will detect it.
|
|
||||||
var Defenders = gameState.getOwnEntitiesByRole("defence");
|
|
||||||
Defenders.forEach(function(ent) {
|
|
||||||
if (ent.getMetadata("subrole") == "idle" || !ent.getMetadata("subrole")) {
|
|
||||||
ent.setMetadata("role", "attack");
|
|
||||||
ent.setMetadata("plan", self.name);
|
|
||||||
}
|
|
||||||
});*/
|
|
||||||
// Assign all no-roles that fit (after a plan aborts, for example).
|
// Assign all no-roles that fit (after a plan aborts, for example).
|
||||||
var NoRole = gameState.getOwnEntitiesByRole(undefined);
|
var NoRole = gameState.getOwnEntitiesByRole(undefined);
|
||||||
NoRole.forEach(function(ent) {
|
NoRole.forEach(function(ent) {
|
||||||
ent.setMetadata("role", "attack");
|
if (ent.hasClasses(["CitizenSoldier", "Infantry"]))
|
||||||
|
ent.setMetadata("role", "worker");
|
||||||
|
else
|
||||||
|
ent.setMetadata("role", "attack");
|
||||||
ent.setMetadata("plan", self.name);
|
ent.setMetadata("plan", self.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -314,7 +417,10 @@ CityAttack.prototype.AllToRallyPoint = function(gameState, evenWorkers) {
|
|||||||
CityAttack.prototype.defaultTargetFinder = function(gameState, militaryManager){
|
CityAttack.prototype.defaultTargetFinder = function(gameState, militaryManager){
|
||||||
var targets = undefined;
|
var targets = undefined;
|
||||||
|
|
||||||
targets = militaryManager.enemyWatchers[this.targetPlayer].getEnemyBuildings("ConquestCritical");
|
targets = militaryManager.enemyWatchers[this.targetPlayer].getEnemyBuildings("CivCentre");
|
||||||
|
if (targets.length == 0) {
|
||||||
|
targets = militaryManager.enemyWatchers[this.targetPlayer].getEnemyBuildings("ConquestCritical");
|
||||||
|
}
|
||||||
// If there's nothing, attack anything else that's less critical
|
// If there's nothing, attack anything else that's less critical
|
||||||
if (targets.length == 0) {
|
if (targets.length == 0) {
|
||||||
targets = militaryManager.enemyWatchers[this.targetPlayer].getEnemyBuildings("Town");
|
targets = militaryManager.enemyWatchers[this.targetPlayer].getEnemyBuildings("Town");
|
||||||
@@ -322,6 +428,10 @@ CityAttack.prototype.defaultTargetFinder = function(gameState, militaryManager){
|
|||||||
if (targets.length == 0) {
|
if (targets.length == 0) {
|
||||||
targets = militaryManager.enemyWatchers[this.targetPlayer].getEnemyBuildings("Village");
|
targets = militaryManager.enemyWatchers[this.targetPlayer].getEnemyBuildings("Village");
|
||||||
}
|
}
|
||||||
|
// no buildings, attack anything conquest critical, even units (it's assuming it won't move).
|
||||||
|
if (targets.length == 0) {
|
||||||
|
targets = gameState.getEnemyEntities().filter(Filters.byClass("ConquestCritical"));
|
||||||
|
}
|
||||||
return targets;
|
return targets;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -355,59 +465,21 @@ CityAttack.prototype.raidingTargetFinder = function(gameState, militaryManager,
|
|||||||
// If we're here, it's because we have in our IDlist enough units.
|
// If we're here, it's because we have in our IDlist enough units.
|
||||||
// now the IDlist units are treated turn by turn
|
// now the IDlist units are treated turn by turn
|
||||||
CityAttack.prototype.StartAttack = function(gameState, militaryManager){
|
CityAttack.prototype.StartAttack = function(gameState, militaryManager){
|
||||||
|
|
||||||
var targets = [];
|
|
||||||
if (this.type === "harass_raid")
|
|
||||||
targets = this.targetFinder(gameState, militaryManager, "villager");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
targets = this.targetFinder(gameState, militaryManager);
|
|
||||||
|
|
||||||
if (targets.length === 0){
|
// check we have a target and a path.
|
||||||
targets = this.defaultTargetFinder(gameState, militaryManager);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have a target, move to it
|
if (this.targetPos && this.path !== undefined) {
|
||||||
if (targets.length) {
|
// erase our queue. This will stop any leftover unit from being trained.
|
||||||
|
gameState.ai.queueManager.removeQueue("plan_" + this.name);
|
||||||
|
|
||||||
var curPos = this.unitCollection.getCentrePosition();
|
var curPos = this.unitCollection.getCentrePosition();
|
||||||
|
|
||||||
// pick a random target from the list
|
|
||||||
var rand = Math.floor((Math.random()*targets.length));
|
|
||||||
this.targetPos = undefined;
|
|
||||||
var count = 0;
|
|
||||||
while (!this.targetPos){
|
|
||||||
var target = targets.toEntityArray()[rand];
|
|
||||||
this.targetPos = target.position();
|
|
||||||
count++;
|
|
||||||
if (count > 1000){
|
|
||||||
warn("No target with a valid position found");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find possible distinct paths to the enemy
|
|
||||||
var pathFinder = new PathFinder(gameState);
|
|
||||||
var pathsToEnemy = pathFinder.getPaths(curPos, this.targetPos);
|
|
||||||
if (! pathsToEnemy){
|
|
||||||
pathsToEnemy = [[this.targetPos]];
|
|
||||||
}
|
|
||||||
this.path = [];
|
|
||||||
|
|
||||||
if (this.type !== "harass_raid")
|
|
||||||
{
|
|
||||||
var rand = Math.floor(Math.random() * pathsToEnemy.length);
|
|
||||||
this.path = pathsToEnemy[rand];
|
|
||||||
} else {
|
|
||||||
this.path = pathsToEnemy[Math.min(2,pathsToEnemy.length-1)];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.unitCollection.forEach(function(ent) { ent.setMetadata("subrole", "attacking"); ent.setMetadata("role", "attack") ;});
|
this.unitCollection.forEach(function(ent) { ent.setMetadata("subrole", "attacking"); ent.setMetadata("role", "attack") ;});
|
||||||
|
|
||||||
// filtering by those that started to attack only
|
// filtering by those that started to attack only
|
||||||
var filter = Filters.byMetadata("subrole","attacking");
|
var filter = Filters.byMetadata("subrole","attacking");
|
||||||
this.unitCollection = this.unitCollection.filter(filter);
|
this.unitCollection = this.unitCollection.filter(filter);
|
||||||
//this.unitCollection.registerUpdates();
|
this.unitCollection.registerUpdates();
|
||||||
//this.unitCollection.length;
|
//this.unitCollection.length;
|
||||||
|
|
||||||
for (unitCat in this.unitStat) {
|
for (unitCat in this.unitStat) {
|
||||||
@@ -418,7 +490,7 @@ CityAttack.prototype.StartAttack = function(gameState, militaryManager){
|
|||||||
this.unitCollection.move(this.path[0][0], this.path[0][1]);
|
this.unitCollection.move(this.path[0][0], this.path[0][1]);
|
||||||
debug ("Started to attack with the plan " + this.name);
|
debug ("Started to attack with the plan " + this.name);
|
||||||
this.state = "walking";
|
this.state = "walking";
|
||||||
} else if (targets.length == 0 ) {
|
} else {
|
||||||
gameState.ai.gameFinished = true;
|
gameState.ai.gameFinished = true;
|
||||||
debug ("I do not have any target. So I'll just assume I won the game.");
|
debug ("I do not have any target. So I'll just assume I won the game.");
|
||||||
return true;
|
return true;
|
||||||
@@ -428,6 +500,7 @@ CityAttack.prototype.StartAttack = function(gameState, militaryManager){
|
|||||||
|
|
||||||
// Runs every turn after the attack is executed
|
// Runs every turn after the attack is executed
|
||||||
CityAttack.prototype.update = function(gameState, militaryManager, events){
|
CityAttack.prototype.update = function(gameState, militaryManager, events){
|
||||||
|
var self = this;
|
||||||
Engine.ProfileStart("Update Attack");
|
Engine.ProfileStart("Update Attack");
|
||||||
|
|
||||||
// we're marching towards the target
|
// we're marching towards the target
|
||||||
@@ -545,10 +618,10 @@ CityAttack.prototype.update = function(gameState, militaryManager, events){
|
|||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
if (this.state === "walking"){
|
if (this.state === "walking"){
|
||||||
if (SquareVectorDistance(this.position, this.lastPosition) < 400 && this.path.length > 0) {
|
if (SquareVectorDistance(this.position, this.lastPosition) < 20 && this.path.length > 0) {
|
||||||
this.unitCollection.move(this.path[0][0], this.path[0][1]);
|
this.unitCollection.move(this.path[0][0], this.path[0][1]);
|
||||||
}
|
}
|
||||||
if (SquareVectorDistance(this.unitCollection.getCentrePosition(), this.path[0]) < 400){
|
if (SquareVectorDistance(this.unitCollection.getCentrePosition(), this.path[0]) < 900){
|
||||||
this.path.shift();
|
this.path.shift();
|
||||||
if (this.path.length > 0){
|
if (this.path.length > 0){
|
||||||
this.unitCollection.move(this.path[0][0], this.path[0][1]);
|
this.unitCollection.move(this.path[0][0], this.path[0][1]);
|
||||||
@@ -564,31 +637,10 @@ CityAttack.prototype.update = function(gameState, militaryManager, events){
|
|||||||
// let's proceed on with whatever happens now.
|
// let's proceed on with whatever happens now.
|
||||||
// There's a ton of TODOs on this part.
|
// There's a ton of TODOs on this part.
|
||||||
if (this.onArrivalReaction == "proceedOnTargets") {
|
if (this.onArrivalReaction == "proceedOnTargets") {
|
||||||
// Each unit will randomly pick a target and attack it and then they'll do what they feel like doing for now. TODO
|
|
||||||
// only the targeted enemy. I've seen the units attack gazelles otherwise.
|
|
||||||
var enemyUnits = gameState.getEnemyEntities().filter(Filters.and(Filters.byStaticDistance(this.unitCollection.getCentrePosition(), 100), Filters.byClass("Unit")));
|
|
||||||
enemyUnits = enemyUnits.filter(Filters.byOwner(this.targetEnemy));
|
|
||||||
var enemyStructures = gameState.getEnemyEntities().filter(Filters.and(Filters.byStaticDistance(this.unitCollection.getCentrePosition(), 150), Filters.byClass("Structure")));
|
|
||||||
enemyStructures = enemyStructures.filter(Filters.byOwner(this.targetEnemy));
|
|
||||||
enemyUnits = enemyUnits.toEntityArray();
|
|
||||||
enemyStructures = enemyStructures.toEntityArray();
|
|
||||||
|
|
||||||
this.unitCollection.forEach( function (ent) { //}) {
|
|
||||||
if (ent.hasClass("Siege")) {
|
|
||||||
if (enemyStructures.length !== 0) {
|
|
||||||
var rand = Math.floor(Math.random() * enemyStructures.length*0.99);
|
|
||||||
ent.attack(enemyStructures[+rand].id());
|
|
||||||
} else
|
|
||||||
ent.stopMoving();
|
|
||||||
} else {
|
|
||||||
if (enemyUnits.length !== 0) {
|
|
||||||
var rand = Math.floor(Math.random() * enemyUnits.length*0.99);
|
|
||||||
ent.attack(enemyUnits[(+rand)].id());
|
|
||||||
} else
|
|
||||||
ent.stopMoving();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.state = "";
|
this.state = "";
|
||||||
|
this.unitCollection.forEach( function (ent) { //}) {
|
||||||
|
ent.stopMoving();
|
||||||
|
});
|
||||||
} else if (this.onArrivalReaction == "huntVillagers") {
|
} else if (this.onArrivalReaction == "huntVillagers") {
|
||||||
// let's get any villager and target them with a tactics manager
|
// let's get any villager and target them with a tactics manager
|
||||||
var enemyCitizens = gameState.entities.filter(function(ent) {
|
var enemyCitizens = gameState.entities.filter(function(ent) {
|
||||||
@@ -608,30 +660,56 @@ CityAttack.prototype.update = function(gameState, militaryManager, events){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.state === ""){
|
|
||||||
|
if (this.state === "" && gameState.ai.playedTurn % 3 === 0) {
|
||||||
// Each unit will randomly pick a target and attack it and then they'll do what they feel like doing for now. TODO
|
// Each unit will randomly pick a target and attack it and then they'll do what they feel like doing for now. TODO
|
||||||
// only the targeted enemy. I've seen the units attack gazelles otherwise.
|
// only the targeted enemy. I've seen the units attack gazelles otherwise.
|
||||||
var enemyUnits = gameState.getEnemyEntities().filter(Filters.and(Filters.byStaticDistance(this.unitCollection.getCentrePosition(), 200), Filters.byClass("Unit")));
|
var enemyUnits = gameState.getEnemyEntities().filter(Filters.and(Filters.byOwner(this.targetPlayer), Filters.byClass("Unit")));
|
||||||
enemyUnits = enemyUnits.filter(Filters.byOwner(this.targetEnemy));
|
var enemyStructures = gameState.getEnemyEntities().filter(Filters.and(Filters.byOwner(this.targetPlayer), Filters.byClass("Structure")));
|
||||||
var enemyStructures = gameState.getEnemyEntities().filter(Filters.and(Filters.byStaticDistance(this.unitCollection.getCentrePosition(), 250), Filters.byClass("Structure")));
|
|
||||||
enemyStructures = enemyStructures.filter(Filters.byOwner(this.targetEnemy));
|
|
||||||
enemyUnits = enemyUnits.toEntityArray();
|
|
||||||
enemyStructures = enemyStructures.toEntityArray();
|
|
||||||
|
|
||||||
this.unitCollection.forEach( function (ent) { //}) {
|
this.unitCollection.forEach( function (ent) { //}) {
|
||||||
if (ent.isIdle()) {
|
if (ent.isIdle()) {
|
||||||
|
var mStruct = enemyStructures.filter(function (enemy) {// }){
|
||||||
|
if (!enemy.position()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (SquareVectorDistance(enemy.position(),ent.position()) > ent.visionRange()*ent.visionRange() + 100) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var mUnit = enemyUnits.filter(function (enemy) {// }){
|
||||||
|
if (!enemy.position()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (SquareVectorDistance(enemy.position(),ent.position()) > ent.visionRange()*ent.visionRange() + 100) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
mUnit = mUnit.toEntityArray();
|
||||||
|
mStruct = mStruct.toEntityArray();
|
||||||
if (ent.hasClass("Siege")) {
|
if (ent.hasClass("Siege")) {
|
||||||
if (enemyStructures.length !== 0) {
|
if (mStruct.length !== 0) {
|
||||||
var rand = Math.floor(Math.random() * enemyStructures.length*0.99);
|
var rand = Math.floor(Math.random() * mStruct.length*0.99);
|
||||||
ent.attack(enemyStructures[+rand].id());
|
ent.attack(mStruct[+rand].id());
|
||||||
} else
|
//debug ("Siege units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName());
|
||||||
ent.stopMoving();
|
} else if (SquareVectorDistance(self.targetPos, ent.position()) > 900 ){
|
||||||
|
//debug ("Siege units moving to " + uneval(self.targetPos));
|
||||||
|
ent.move((self.targetPos[0] + ent.position()[0])/2,(self.targetPos[1] + ent.position()[1])/2);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (enemyUnits.length !== 0) {
|
if (mUnit.length !== 0) {
|
||||||
var rand = Math.floor(Math.random() * enemyUnits.length*0.99);
|
var rand = Math.floor(Math.random() * mUnit.length*0.99);
|
||||||
ent.attack(enemyUnits[(+rand)].id());
|
ent.attack(mUnit[(+rand)].id());
|
||||||
} else
|
//debug ("Units attacking a unit from " +mUnit[+rand].owner() + " , " +mUnit[+rand].templateName());
|
||||||
ent.stopMoving();
|
} else if (mStruct.length !== 0) {
|
||||||
|
var rand = Math.floor(Math.random() * mStruct.length*0.99);
|
||||||
|
ent.attack(mStruct[+rand].id());
|
||||||
|
//debug ("Units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName());
|
||||||
|
} else if (SquareVectorDistance(self.targetPos, ent.position()) > 900 ){
|
||||||
|
//debug ("Units moving to " + uneval(self.targetPos));
|
||||||
|
ent.move((self.targetPos[0] + ent.position()[0])/2,(self.targetPos[1] + ent.position()[1])/2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -654,6 +732,7 @@ CityAttack.prototype.update = function(gameState, militaryManager, events){
|
|||||||
}*/
|
}*/
|
||||||
this.lastPosition = this.position;
|
this.lastPosition = this.position;
|
||||||
Engine.ProfileStop();
|
Engine.ProfileStop();
|
||||||
|
|
||||||
return this.unitCollection.length;
|
return this.unitCollection.length;
|
||||||
};
|
};
|
||||||
CityAttack.prototype.totalCountUnits = function(gameState){
|
CityAttack.prototype.totalCountUnits = function(gameState){
|
||||||
|
|||||||
@@ -43,13 +43,13 @@ var baseConfig = {
|
|||||||
// qbot
|
// qbot
|
||||||
"priorities" : { // Note these are dynamic, you are only setting the initial values
|
"priorities" : { // Note these are dynamic, you are only setting the initial values
|
||||||
"house" : 500,
|
"house" : 500,
|
||||||
"citizenSoldier" : 100,
|
"citizenSoldier" : 65,
|
||||||
"villager" : 150,
|
"villager" : 95,
|
||||||
"economicBuilding" : 50,
|
"economicBuilding" : 80,
|
||||||
"field" : 20,
|
"field" : 20,
|
||||||
"advancedSoldier" : 30,
|
"advancedSoldier" : 30,
|
||||||
"siege" : 10,
|
"siege" : 10,
|
||||||
"militaryBuilding" : 80,
|
"militaryBuilding" : 90,
|
||||||
"defenceBuilding" : 17,
|
"defenceBuilding" : 17,
|
||||||
"civilCentre" : 1000
|
"civilCentre" : 1000
|
||||||
},
|
},
|
||||||
@@ -58,7 +58,7 @@ var baseConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var Config = {
|
var Config = {
|
||||||
"debug": false
|
"debug": true
|
||||||
};
|
};
|
||||||
|
|
||||||
Config.__proto__ = baseConfig;
|
Config.__proto__ = baseConfig;
|
||||||
@@ -28,12 +28,11 @@ function Defence(){
|
|||||||
// This allows the Defence manager to take units from the plans for Defence.
|
// This allows the Defence manager to take units from the plans for Defence.
|
||||||
|
|
||||||
// Defcon levels
|
// Defcon levels
|
||||||
// 6 (or more): no danger whatsoever detected
|
// 5: no danger whatsoever detected
|
||||||
// 5: local zones of danger (ie a tower somewhere, things like that)
|
// 4: a few enemy units are being dealt with, but nothing too dangerous.
|
||||||
// 4: a few enemy units inbound, like a scout or something. (local danger). Usually seen as the last level before a true "attack"
|
// 3: A reasonnably sized enemy army is being dealt with, but it should not be a problem.
|
||||||
// 3: reasonnably sized enemy army inbound, local danger
|
// 2: A big enemy army is in the base, but we are not outnumbered
|
||||||
// 2: well sized enemy army inbound, general danger
|
// 1: Huge army in the base, outnumbering us.
|
||||||
// 1: Sizable enemy army inside of my base, general danger.
|
|
||||||
|
|
||||||
|
|
||||||
Defence.prototype.update = function(gameState, events, militaryManager){
|
Defence.prototype.update = function(gameState, events, militaryManager){
|
||||||
@@ -150,6 +149,7 @@ Defence.prototype.defendFromEnemyArmies = function(gameState, events, militaryMa
|
|||||||
|
|
||||||
var nbOfAttackers = 0;
|
var nbOfAttackers = 0;
|
||||||
|
|
||||||
|
var newEnemies = [];
|
||||||
// clean up before adding new units (slight speeding up, since new units can't already be dead)
|
// clean up before adding new units (slight speeding up, since new units can't already be dead)
|
||||||
for (i in this.listOfEnemies) {
|
for (i in this.listOfEnemies) {
|
||||||
if (this.listOfEnemies[i].length === 0) {
|
if (this.listOfEnemies[i].length === 0) {
|
||||||
@@ -164,6 +164,9 @@ Defence.prototype.defendFromEnemyArmies = function(gameState, events, militaryMa
|
|||||||
var enemyWatcher = militaryManager.enemyWatchers[unit.owner()];
|
var enemyWatcher = militaryManager.enemyWatchers[unit.owner()];
|
||||||
if (enemyWatcher.isPartOfDangerousArmy(unit.id())) {
|
if (enemyWatcher.isPartOfDangerousArmy(unit.id())) {
|
||||||
nbOfAttackers++;
|
nbOfAttackers++;
|
||||||
|
if (this.attackerCache[unit.id()].length == 0) {
|
||||||
|
newEnemies.push(unit);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// if we had defined the attackerCache, ie if we had tried to attack this unit.
|
// if we had defined the attackerCache, ie if we had tried to attack this unit.
|
||||||
if (this.attackerCache[unit.id()] != undefined) {
|
if (this.attackerCache[unit.id()] != undefined) {
|
||||||
@@ -175,7 +178,6 @@ Defence.prototype.defendFromEnemyArmies = function(gameState, events, militaryMa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// okay so now, for every dangerous armies, we loop.
|
// okay so now, for every dangerous armies, we loop.
|
||||||
for (armyID in dangerArmies) {
|
for (armyID in dangerArmies) {
|
||||||
// looping through army units
|
// looping through army units
|
||||||
@@ -196,6 +198,7 @@ Defence.prototype.defendFromEnemyArmies = function(gameState, events, militaryMa
|
|||||||
self.attackerCache[ent.id()] = self.myUnits.filter(filter);
|
self.attackerCache[ent.id()] = self.myUnits.filter(filter);
|
||||||
self.attackerCache[ent.id()].registerUpdates();
|
self.attackerCache[ent.id()].registerUpdates();
|
||||||
nbOfAttackers++;
|
nbOfAttackers++;
|
||||||
|
newEnemies.push(ent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -214,46 +217,77 @@ Defence.prototype.defendFromEnemyArmies = function(gameState, events, militaryMa
|
|||||||
militaryManager.unpauseAllPlans(gameState);
|
militaryManager.unpauseAllPlans(gameState);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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).
|
// 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.
|
// now I'll list my idle defenders, then my idle soldiers that could defend.
|
||||||
// and then I'll assign my units.
|
// and then I'll assign my units.
|
||||||
// and then rock on.
|
// and then rock on.
|
||||||
/*
|
|
||||||
if (nbOfAttackers === 0) {
|
if (nbOfAttackers < 10){
|
||||||
return;
|
gameState.setDefcon(4); // few local units
|
||||||
} else if (nbOfAttackers < 5){
|
} else if (nbOfAttackers >= 10){
|
||||||
gameState.upDefcon(4); // few local units
|
gameState.setDefcon(3);
|
||||||
} else if (nbOfAttackers >= 5){
|
|
||||||
gameState.upDefcon(3); // local attack, dangerous but not hugely threatening for my survival
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.idleDefs.length < nbOfAttackers) {
|
var nonDefenders = this.myUnits.filter(Filters.or( Filters.not(Filters.byMetadata("role","defence")),Filters.isIdle()));
|
||||||
gameState.upDefcon(2); // general danger
|
nonDefenders = nonDefenders.filter(Filters.not(Filters.byClass("Female")));
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// todo: improve the logic against attackers.
|
var defenceRatio = this.defenceRatio;
|
||||||
|
if (newEnemies.length * defenceRatio> nonDefenders.length) {
|
||||||
|
defenceRatio = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each enemy, we'll pick two units.
|
||||||
|
for each (enemy in newEnemies) {
|
||||||
|
if (nonDefenders.length === 0)
|
||||||
|
break;
|
||||||
|
|
||||||
// reupdate the existing defenders.
|
var assigned = self.attackerCache[enemy.id()].length;
|
||||||
|
if (assigned >= defenceRatio)
|
||||||
this.idleDefs.forEach(function(ent) {
|
return;
|
||||||
ent.setMetadata("subrole","newdefender");
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// let's check for a counter.
|
||||||
|
//debug ("Enemy is a " + uneval(enemy._template.Identity.Classes._string) );
|
||||||
nbOfAttackers *= this.defenceRatio;
|
var potCounters = gameState.ai.templateManager.getCountersToClasses(gameState,enemy.classes(),enemy.templateName());
|
||||||
// Assume those taken care of.
|
//debug ("Counters are" +uneval(potCounters));
|
||||||
nbOfAttackers -= +(this.defenders.length);
|
var counters = [];
|
||||||
|
for (o in potCounters) {
|
||||||
// need new units?
|
var counter = nonDefenders.filter(Filters.and(Filters.byType(potCounters[o][0]), Filters.byStaticDistance(enemy.position(), 150) )).toEntityArray();
|
||||||
if (nbOfAttackers <= 0)
|
if (counter.length !== 0)
|
||||||
return;
|
for (unit in counter)
|
||||||
|
counters.push(counter[unit]);
|
||||||
|
}
|
||||||
|
//debug ("I have " +counters.length +"countering units");
|
||||||
|
for (var i = 0; i < defenceRatio && i < counters.length; i++) {
|
||||||
|
if (counters[i].getMetadata("plan") !== undefined)
|
||||||
|
militaryManager.pausePlan(gameState,counters[i].getMetadata("plan"));
|
||||||
|
counters[i].setMetadata("formerrole", counters[i].getMetadata("role"));
|
||||||
|
counters[i].setMetadata("role","defence");
|
||||||
|
counters[i].setMetadata("subrole","defending");
|
||||||
|
counters[i].attack(+enemy.id());
|
||||||
|
nonDefenders.updateEnt(counters[i]);
|
||||||
|
assigned++;
|
||||||
|
//debug ("Sending a " +counters[i].templateName() +" to counter a " + enemy.templateName());
|
||||||
|
}
|
||||||
|
if (assigned !== defenceRatio) {
|
||||||
|
// take closest units
|
||||||
|
nonDefenders.filter(Filters.byClass("CitizenSoldier")).filterNearest(enemy.position(),defenceRatio-assigned).forEach(function (defender) { //}){
|
||||||
|
if (defender.getMetadata("plan") !== undefined)
|
||||||
|
militaryManager.pausePlan(gameState,defender.getMetadata("plan"));
|
||||||
|
defender.setMetadata("formerrole", defender.getMetadata("role"));
|
||||||
|
defender.setMetadata("role","defence");
|
||||||
|
defender.setMetadata("subrole","defending");
|
||||||
|
defender.attack(+enemy.id());
|
||||||
|
nonDefenders.updateEnt(defender);
|
||||||
|
assigned++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
// yes. We'll pick new units (pretty randomly for now, todo)
|
// yes. We'll pick new units (pretty randomly for now, todo)
|
||||||
// first from attack plans, then from workers.
|
// first from attack plans, then from workers.
|
||||||
var newSoldiers = gameState.getOwnEntities().filter(function (ent) {
|
var newSoldiers = gameState.getOwnEntities().filter(function (ent) {
|
||||||
if (ent.getMetadata("plan") != undefined)
|
if (ent.getMetadata("plan") != undefined && ent.getMetadata("role") != "defence")
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
@@ -287,6 +321,15 @@ Defence.prototype.defendFromEnemyArmies = function(gameState, events, militaryMa
|
|||||||
}
|
}
|
||||||
// okay
|
// okay
|
||||||
newSoldiers = gameState.getOwnEntitiesByMetadata("subrole","newdefender");
|
newSoldiers = gameState.getOwnEntitiesByMetadata("subrole","newdefender");
|
||||||
|
|
||||||
|
// we're okay, but there's a big amount of units
|
||||||
|
// todo: check against total number of soldiers
|
||||||
|
if (nbOfAttackers <= 0 && newSoldiers.length > 35)
|
||||||
|
gameState.setDefcon(2);
|
||||||
|
else if (nbOfAttackers > 0) {
|
||||||
|
// we are actually lacking units
|
||||||
|
gameState.setDefcon(1);
|
||||||
|
}
|
||||||
// TODO. For now, each unit will pick the closest unit that is attacked by only one/zero guy, or any if there is none.
|
// TODO. For now, each unit will pick the closest unit that is attacked by only one/zero guy, or any if there is none.
|
||||||
// ought to regroup them first for optimization.
|
// ought to regroup them first for optimization.
|
||||||
|
|
||||||
@@ -304,7 +347,7 @@ Defence.prototype.defendFromEnemyArmies = function(gameState, events, militaryMa
|
|||||||
ent.setMetadata("subrole","defending");
|
ent.setMetadata("subrole","defending");
|
||||||
ent.attack(+target);
|
ent.attack(+target);
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,6 +367,11 @@ Defence.prototype.MessageProcess = function(gameState,events, militaryManager) {
|
|||||||
// if we're not on enemy territory
|
// if we're not on enemy territory
|
||||||
var territory = +this.territoryMap.point(attacker.position()) - 64;
|
var territory = +this.territoryMap.point(attacker.position()) - 64;
|
||||||
|
|
||||||
|
// we do not consider units that are defenders, and we do not consider units that are part of an attacking attack plan
|
||||||
|
// (attacking attacking plans are dealing with threats on their own).
|
||||||
|
if (ourUnit !== undefined && (ourUnit.getMetadata("role") == "defence" || ourUnit.getMetadata("subrole") == "attacking"))
|
||||||
|
continue;
|
||||||
|
|
||||||
// let's check for animals
|
// let's check for animals
|
||||||
if (attacker.owner() == 0) {
|
if (attacker.owner() == 0) {
|
||||||
// if our unit is still alive, we make it react
|
// if our unit is still alive, we make it react
|
||||||
@@ -371,7 +419,7 @@ Defence.prototype.MessageProcess = function(gameState,events, militaryManager) {
|
|||||||
this.WantedUnitsAttacker[attacker.id()].registerUpdates();
|
this.WantedUnitsAttacker[attacker.id()].registerUpdates();
|
||||||
this.WantedUnitsAttacker[attacker.id()].length;
|
this.WantedUnitsAttacker[attacker.id()].length;
|
||||||
}
|
}
|
||||||
if (ourUnit && ourUnit.hasClass("Unit") && ourUnit.getMetadata("role") != "attack") {
|
if (ourUnit && ourUnit.hasClass("Unit")) {
|
||||||
if (ourUnit.hasClass("Support")) {
|
if (ourUnit.hasClass("Support")) {
|
||||||
// TODO: it's a villager. Garrison it.
|
// TODO: it's a villager. Garrison it.
|
||||||
// TODO: make other neighboring villagers garrison
|
// TODO: make other neighboring villagers garrison
|
||||||
@@ -393,7 +441,7 @@ Defence.prototype.MessageProcess = function(gameState,events, militaryManager) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// At most, this will put defcon to 5
|
// At most, this will put defcon to 4
|
||||||
Defence.prototype.DealWithWantedUnits = function(gameState, events, militaryManager) {
|
Defence.prototype.DealWithWantedUnits = function(gameState, events, militaryManager) {
|
||||||
//if (gameState.defcon() < 3)
|
//if (gameState.defcon() < 3)
|
||||||
// return;
|
// return;
|
||||||
@@ -424,8 +472,8 @@ Defence.prototype.DealWithWantedUnits = function(gameState, events, militaryMana
|
|||||||
if (nbOfAttackers === 0)
|
if (nbOfAttackers === 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// at most, we'll deal with two enemies at once.
|
// at most, we'll deal with 3 enemies at once.
|
||||||
if (nbOfDealtWith >= 2)
|
if (nbOfDealtWith >= 3)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// dynamic properties are not updated nearly fast enough here so a little caching
|
// dynamic properties are not updated nearly fast enough here so a little caching
|
||||||
@@ -434,11 +482,14 @@ Defence.prototype.DealWithWantedUnits = function(gameState, events, militaryMana
|
|||||||
// we send 3 units to each target just to be sure. TODO refine.
|
// we send 3 units to each target just to be sure. TODO refine.
|
||||||
// we do not use plan units
|
// we do not use plan units
|
||||||
this.idleDefs.forEach(function(ent) {
|
this.idleDefs.forEach(function(ent) {
|
||||||
if (nbOfDealtWith < 2 && nbOfAttackers > 0 && ent.getMetadata("plan") == undefined)
|
if (nbOfDealtWith < 3 && nbOfAttackers > 0 && ent.getMetadata("plan") == undefined)
|
||||||
for (o in self.listOfWantedUnits) {
|
for (o in self.listOfWantedUnits) {
|
||||||
if ( (addedto[o] == undefined && self.WantedUnitsAttacker[o].length < 3) || (addedto[o] && self.WantedUnitsAttacker[o].length + addedto[o] < 3)) {
|
if ( (addedto[o] == undefined && self.WantedUnitsAttacker[o].length < 3) || (addedto[o] && self.WantedUnitsAttacker[o].length + addedto[o] < 3)) {
|
||||||
if (self.WantedUnitsAttacker[o].length === 0)
|
if (self.WantedUnitsAttacker[o].length === 0)
|
||||||
nbOfDealtWith++;
|
nbOfDealtWith++;
|
||||||
|
|
||||||
|
ent.setMetadata("formerrole", ent.getMetadata("role"));
|
||||||
|
ent.setMetadata("role","defence");
|
||||||
ent.setMetadata("subrole", "defending");
|
ent.setMetadata("subrole", "defending");
|
||||||
ent.attack(+o);
|
ent.attack(+o);
|
||||||
if (addedto[o])
|
if (addedto[o])
|
||||||
@@ -455,17 +506,19 @@ Defence.prototype.DealWithWantedUnits = function(gameState, events, militaryMana
|
|||||||
// still some undealt with attackers, recruit citizen soldiers
|
// still some undealt with attackers, recruit citizen soldiers
|
||||||
if (nbOfAttackers > 0 && nbOfDealtWith < 2) {
|
if (nbOfAttackers > 0 && nbOfDealtWith < 2) {
|
||||||
|
|
||||||
//gameState.upDefcon(5);
|
gameState.setDefcon(4);
|
||||||
|
|
||||||
var newSoldiers = gameState.getOwnEntitiesByRole("worker");
|
var newSoldiers = gameState.getOwnEntitiesByRole("worker");
|
||||||
newSoldiers.forEach(function(ent) {
|
newSoldiers.forEach(function(ent) {
|
||||||
// If we're not female, we attack
|
// If we're not female, we attack
|
||||||
if (ent.hasClass("CitizenSoldier"))
|
if (ent.hasClass("CitizenSoldier"))
|
||||||
if (nbOfDealtWith < 2 && nbOfAttackers > 0)
|
if (nbOfDealtWith < 3 && nbOfAttackers > 0)
|
||||||
for (o in self.listOfWantedUnits) {
|
for (o in self.listOfWantedUnits) {
|
||||||
if ( (addedto[o] == undefined && self.WantedUnitsAttacker[o].length < 3) || (addedto[o] && self.WantedUnitsAttacker[o].length + addedto[o] < 3)) {
|
if ( (addedto[o] == undefined && self.WantedUnitsAttacker[o].length < 3) || (addedto[o] && self.WantedUnitsAttacker[o].length + addedto[o] < 3)) {
|
||||||
if (self.WantedUnitsAttacker[o].length === 0)
|
if (self.WantedUnitsAttacker[o].length === 0)
|
||||||
nbOfDealtWith++;
|
nbOfDealtWith++;
|
||||||
|
ent.setMetadata("formerrole", ent.getMetadata("role"));
|
||||||
|
ent.setMetadata("role","defence");
|
||||||
ent.setMetadata("subrole", "defending");
|
ent.setMetadata("subrole", "defending");
|
||||||
ent.attack(+o);
|
ent.attack(+o);
|
||||||
if (addedto[o])
|
if (addedto[o])
|
||||||
|
|||||||
@@ -7,15 +7,27 @@ var EconomyManager = function() {
|
|||||||
this.setCount = 0; //stops villagers being reassigned to other resources too frequently, count a set number of
|
this.setCount = 0; //stops villagers being reassigned to other resources too frequently, count a set number of
|
||||||
//turns before trying to reassign them.
|
//turns before trying to reassign them.
|
||||||
|
|
||||||
|
// this means we'll have about a big third of women, and thus we can maximize resource gathering rates.
|
||||||
this.femaleRatio = 0.4;
|
this.femaleRatio = 0.4;
|
||||||
|
|
||||||
this.farmingFields = false;
|
this.farmingFields = false;
|
||||||
|
|
||||||
this.dropsiteNumbers = {wood: 2, stone: 1, metal: 1};
|
this.dropsiteNumbers = {"wood": 1, "stone": 0.5, "metal": 0.5};
|
||||||
};
|
};
|
||||||
// More initialisation for stuff that needs the gameState
|
// More initialisation for stuff that needs the gameState
|
||||||
EconomyManager.prototype.init = function(gameState){
|
EconomyManager.prototype.init = function(gameState){
|
||||||
this.targetNumWorkers = Math.max(Math.floor(gameState.getPopulationMax()/2.5), 1);
|
this.targetNumWorkers = Math.max(Math.floor(gameState.getPopulationMax()*0.55), 1);
|
||||||
|
|
||||||
|
// initialize once all the resource maps.
|
||||||
|
this.updateResourceMaps(gameState, ["food","wood","stone","metal"]);
|
||||||
|
this.updateResourceConcentrations(gameState,"food");
|
||||||
|
this.updateResourceConcentrations(gameState,"wood");
|
||||||
|
this.updateResourceConcentrations(gameState,"stone");
|
||||||
|
this.updateResourceConcentrations(gameState,"metal");
|
||||||
|
this.updateNearbyResources(gameState, "food");
|
||||||
|
this.updateNearbyResources(gameState, "wood");
|
||||||
|
this.updateNearbyResources(gameState, "stone");
|
||||||
|
this.updateNearbyResources(gameState, "metal");
|
||||||
};
|
};
|
||||||
|
|
||||||
// okay, so here we'll create both females and male workers.
|
// okay, so here we'll create both females and male workers.
|
||||||
@@ -29,26 +41,45 @@ EconomyManager.prototype.trainMoreWorkers = function(gameState, queues) {
|
|||||||
// Count the workers in the world and in progress
|
// Count the workers in the world and in progress
|
||||||
var numFemales = gameState.countEntitiesAndQueuedByType(gameState.applyCiv("units/{civ}_support_female_citizen"));
|
var numFemales = gameState.countEntitiesAndQueuedByType(gameState.applyCiv("units/{civ}_support_female_citizen"));
|
||||||
numFemales += queues.villager.countTotalQueuedUnits();
|
numFemales += queues.villager.countTotalQueuedUnits();
|
||||||
var numWorkers = gameState.countOwnEntitiesAndQueuedWithRole("worker");
|
|
||||||
numWorkers += queues.citizenSoldier.countTotalQueuedUnits();
|
|
||||||
var numTotal = numWorkers + queues.villager.countTotalQueuedUnits() + queues.citizenSoldier.countTotalQueuedUnits();
|
|
||||||
|
|
||||||
|
// counting the workers that aren't part of a plan
|
||||||
|
var numWorkers = 0;
|
||||||
|
gameState.getOwnEntities().forEach (function (ent) {
|
||||||
|
if (ent.getMetadata("role") == "worker" && ent.getMetadata("plan") == undefined)
|
||||||
|
numWorkers++;
|
||||||
|
});
|
||||||
|
gameState.getOwnTrainingFacilities().forEach(function(ent) {
|
||||||
|
ent.trainingQueue().forEach(function(item) {
|
||||||
|
if (item.metadata && item.metadata.role == "worker" && item.metadata.plan == undefined)
|
||||||
|
numWorkers += item.count;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var numQueued = queues.villager.countTotalQueuedUnits() + queues.citizenSoldier.countTotalQueuedUnits();
|
||||||
|
var numTotal = numWorkers + numQueued;
|
||||||
|
|
||||||
|
this.targetNumFields = numFemales/15;
|
||||||
|
if ((gameState.ai.playedTurn+2) % 3 === 0) {
|
||||||
|
this.dropsiteNumbers = {"wood": Math.ceil((numWorkers)/25)/2, "stone": Math.ceil((numWorkers)/40)/2, "metal": Math.ceil((numWorkers)/30)/2};
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug (numTotal + "/" +this.targetNumWorkers + ", " +numFemales +"/" +numTotal);
|
||||||
|
|
||||||
// If we have too few, train more
|
// If we have too few, train more
|
||||||
if (numTotal < this.targetNumWorkers && (queues.villager.countTotalQueuedUnits() < 10 || queues.citizenSoldier.countTotalQueuedUnits() < 10) ) {
|
// should plan enough to always have females…
|
||||||
var template = "units/{civ}_support_female_citizen";
|
if (numTotal < this.targetNumWorkers && numQueued < 20) {
|
||||||
var size = 1;
|
var template = gameState.applyCiv("units/{civ}_support_female_citizen");
|
||||||
if (numFemales/numWorkers > this.femaleRatio && gameState.getTimeElapsed() > 60*1000) {
|
var size = Math.min(Math.ceil(gameState.getTimeElapsed() / 240000),5);
|
||||||
|
if (numFemales/numTotal > this.femaleRatio && gameState.getTimeElapsed() > 60*1000) {
|
||||||
|
var size = Math.min(Math.ceil(gameState.getTimeElapsed() / 120000),5);
|
||||||
template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], [ ["cost",1], ["speed",0.5]]);
|
template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], [ ["cost",1], ["speed",0.5]]);
|
||||||
size = 5;
|
|
||||||
if (!template) {
|
if (!template) {
|
||||||
template = "units/{civ}_support_female_citizen";
|
template = gameState.applyCiv("units/{civ}_support_female_citizen");
|
||||||
size = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (size == 5)
|
if (template === gameState.applyCiv("units/{civ}_support_female_citizen"))
|
||||||
queues.citizenSoldier.addItem(new UnitTrainingPlan(gameState, template, { "role" : "worker" },size ));
|
|
||||||
else
|
|
||||||
queues.villager.addItem(new UnitTrainingPlan(gameState, template, { "role" : "worker" },size ));
|
queues.villager.addItem(new UnitTrainingPlan(gameState, template, { "role" : "worker" },size ));
|
||||||
|
else
|
||||||
|
queues.citizenSoldier.addItem(new UnitTrainingPlan(gameState, template, { "role" : "worker" },size ));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// picks the best template based on parameters and classes
|
// picks the best template based on parameters and classes
|
||||||
@@ -101,7 +132,7 @@ EconomyManager.prototype.pickMostNeededResources = function(gameState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var numGatherers = {};
|
var numGatherers = {};
|
||||||
for ( var type in this.gatherWeights){
|
for (type in this.gatherWeights){
|
||||||
numGatherers[type] = gameState.updatingCollection("workers-gathering-" + type,
|
numGatherers[type] = gameState.updatingCollection("workers-gathering-" + type,
|
||||||
Filters.byMetadata("gather-type", type), gameState.getOwnEntitiesByRole("worker")).length;
|
Filters.byMetadata("gather-type", type), gameState.getOwnEntitiesByRole("worker")).length;
|
||||||
}
|
}
|
||||||
@@ -200,44 +231,77 @@ EconomyManager.prototype.assignToFoundations = function(gameState) {
|
|||||||
// If we have some foundations, and we don't have enough
|
// If we have some foundations, and we don't have enough
|
||||||
// builder-workers,
|
// builder-workers,
|
||||||
// try reassigning some other workers who are nearby
|
// try reassigning some other workers who are nearby
|
||||||
|
|
||||||
|
// up to 2.5 buildings at once (that is 3, but one won't be complete).
|
||||||
|
|
||||||
var foundations = gameState.getOwnFoundations();
|
var foundations = gameState.getOwnFoundations().toEntityArray();
|
||||||
|
var damagedBuildings = gameState.getOwnEntities().filter(function (ent) { if (ent.needsRepair() && ent.getMetadata("plan") == undefined) { return true; } return false; }).toEntityArray();
|
||||||
|
|
||||||
// Check if nothing to build
|
// Check if nothing to build
|
||||||
if (!foundations.length){
|
if (!foundations.length && !damagedBuildings.length){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var workers = gameState.getOwnEntitiesByRole("worker");
|
var workers = gameState.getOwnEntitiesByRole("worker");
|
||||||
|
|
||||||
var builderWorkers = this.workersBySubrole(gameState, "builder");
|
var builderWorkers = this.workersBySubrole(gameState, "builder");
|
||||||
|
|
||||||
// Check if enough builders
|
var addedWorkers = 0;
|
||||||
var extraNeeded = this.targetNumBuilders*foundations.length - builderWorkers.length;
|
|
||||||
if (extraNeeded <= 0){
|
for (i in foundations) {
|
||||||
return;
|
var target = foundations[i];
|
||||||
|
if (target._template.BuildRestrictions.Category === "Field")
|
||||||
|
continue; // we do not build fields
|
||||||
|
var assigned = gameState.getOwnEntitiesByMetadata("target-foundation", target).length;
|
||||||
|
if (assigned < this.targetNumBuilders) {
|
||||||
|
if (builderWorkers.length + addedWorkers < this.targetNumBuilders*Math.min(2.5,gameState.getTimeElapsed()/60000)) {
|
||||||
|
var nonBuilderWorkers = workers.filter(function(ent) { return (ent.getMetadata("subrole") !== "builder" && ent.getMetadata("gather-type") !== "food" && ent.position() !== undefined); });
|
||||||
|
var nearestNonBuilders = nonBuilderWorkers.filterNearest(target.position(), this.targetNumBuilders - assigned);
|
||||||
|
|
||||||
|
nearestNonBuilders.forEach(function(ent) {
|
||||||
|
addedWorkers++;
|
||||||
|
ent.setMetadata("subrole", "builder");
|
||||||
|
ent.setMetadata("target-foundation", target);
|
||||||
|
});
|
||||||
|
if (this.targetNumBuilders - assigned - nearestNonBuilders.length > 0) {
|
||||||
|
var nonBuilderWorkers = workers.filter(function(ent) { return (ent.getMetadata("subrole") !== "builder" && ent.position() !== undefined); });
|
||||||
|
var nearestNonBuilders = nonBuilderWorkers.filterNearest(target.position(), this.targetNumBuilders - assigned);
|
||||||
|
nearestNonBuilders.forEach(function(ent) {
|
||||||
|
addedWorkers++;
|
||||||
|
ent.setMetadata("subrole", "builder");
|
||||||
|
ent.setMetadata("target-foundation", target);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// don't repair if we're still under attack, unless it's like a vital (civcentre or wall) building that's getting destroyed.
|
||||||
|
for (i in damagedBuildings) {
|
||||||
|
var target = damagedBuildings[i];
|
||||||
|
if (gameState.defcon() < 5) {
|
||||||
|
if (target.healthLevel() > 0.5 || !target.hasClass("CivCentre") || !target.hasClass("StoneWall")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var assigned = gameState.getOwnEntitiesByMetadata("target-foundation", target).length;
|
||||||
|
if (assigned < this.targetNumBuilders) {
|
||||||
|
if (builderWorkers.length + addedWorkers < this.targetNumBuilders*2.5) {
|
||||||
|
|
||||||
|
var nonBuilderWorkers = workers.filter(function(ent) { return (ent.getMetadata("subrole") !== "builder" && ent.position() !== undefined); });
|
||||||
|
if (gameState.defcon() < 5)
|
||||||
|
nonBuilderWorkers = workers.filter(function(ent) { return (ent.getMetadata("subrole") !== "builder" && ent.hasClass("Female") && ent.position() !== undefined); });
|
||||||
|
var nearestNonBuilders = nonBuilderWorkers.filterNearest(target.position(), this.targetNumBuilders - assigned);
|
||||||
|
|
||||||
|
nearestNonBuilders.forEach(function(ent) {
|
||||||
|
addedWorkers++;
|
||||||
|
ent.setMetadata("subrole", "builder");
|
||||||
|
ent.setMetadata("target-foundation", target);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick non-builders who are closest to the first foundation,
|
|
||||||
// and tell them to start building it
|
|
||||||
|
|
||||||
var target = foundations.toEntityArray()[0];
|
|
||||||
|
|
||||||
var nonBuilderWorkers = workers.filter(function(ent) {
|
|
||||||
// check position so garrisoned units aren't tasked
|
|
||||||
return (ent.getMetadata("subrole") !== "builder" && ent.position() !== undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
var nearestNonBuilders = nonBuilderWorkers.filterNearest(target.position(), extraNeeded);
|
|
||||||
|
|
||||||
// Order each builder individually, not as a formation
|
|
||||||
nearestNonBuilders.forEach(function(ent) {
|
|
||||||
ent.setMetadata("subrole", "builder");
|
|
||||||
ent.setMetadata("target-foundation", target);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
EconomyManager.prototype.buildMoreFields = function(gameState, queues) {
|
EconomyManager.prototype.buildMoreFields = function(gameState, queues) {
|
||||||
if (this.farmingFields) {
|
if (this.farmingFields === true) {
|
||||||
var numFarms = gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_field"));
|
var numFarms = gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_field"));
|
||||||
numFarms += queues.field.countTotalQueuedUnits();
|
numFarms += queues.field.countTotalQueuedUnits();
|
||||||
|
|
||||||
@@ -245,12 +309,13 @@ EconomyManager.prototype.buildMoreFields = function(gameState, queues) {
|
|||||||
queues.field.addItem(new BuildingConstructionPlan(gameState, "structures/{civ}_field"));
|
queues.field.addItem(new BuildingConstructionPlan(gameState, "structures/{civ}_field"));
|
||||||
} else {
|
} else {
|
||||||
var foodAmount = 0;
|
var foodAmount = 0;
|
||||||
gameState.updatingCollection("active-dropsite-food", Filters.byMetadata("active-dropsite-food", true),
|
gameState.getOwnDropsites("food").forEach( function (ent) { //}){
|
||||||
gameState.getOwnDropsites("food")).forEach(function (dropsite){
|
if (ent.getMetadata("resource-quantity-food") != undefined) {
|
||||||
dropsite.getMetadata("nearby-resources-food").forEach(function (supply) {
|
foodAmount += ent.getMetadata("resource-quantity-food");
|
||||||
foodAmount += supply.resourceSupplyAmount();
|
} else {
|
||||||
});
|
foodAmount = 300; // wait till we initialize
|
||||||
});
|
}
|
||||||
|
});
|
||||||
if (foodAmount < 300)
|
if (foodAmount < 300)
|
||||||
this.farmingFields = true;
|
this.farmingFields = true;
|
||||||
}
|
}
|
||||||
@@ -266,13 +331,22 @@ EconomyManager.prototype.buildNewCC= function(gameState, queues) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//creates and maintains a map of tree density
|
// creates and maintains a map of unused resource density
|
||||||
EconomyManager.prototype.updateResourceMaps = function(gameState, events){
|
// this also takes dropsites into account.
|
||||||
// The weight of the influence function is amountOfResource/decreaseFactor
|
// resources that are "part" of a dropsite are not counted.
|
||||||
var decreaseFactor = {'wood': 15, 'stone': 100, 'metal': 100, 'food': 20};
|
EconomyManager.prototype.updateResourceMaps = function(gameState, events) {
|
||||||
// This is the maximum radius of the influence
|
|
||||||
var radius = {'wood':9, 'stone': 10, 'metal': 10, 'food': 12};
|
|
||||||
|
|
||||||
|
// TODO: centralize with that other function that uses the same variables
|
||||||
|
// The weight of the influence function is amountOfResource/decreaseFactor
|
||||||
|
var decreaseFactor = {'wood': 12.0, 'stone': 10.0, 'metal': 10.0, 'food': 20.0};
|
||||||
|
// This is the maximum radius of the influence
|
||||||
|
var radius = {'wood':25.0, 'stone': 24.0, 'metal': 24.0, 'food': 24.0};
|
||||||
|
// smallRadius is the distance necessary to mark a resource as linked to a dropsite.
|
||||||
|
var smallRadius = { 'food':70*70,'wood':120*120,'stone':60*60,'metal':60*60 };
|
||||||
|
// bigRadius is the distance for a weak link (resources are considered when building other dropsites)
|
||||||
|
// and their resource amount is divided by 3 when checking for dropsite resource level.
|
||||||
|
var bigRadius = { 'food':100*100,'wood':180*180,'stone':120*120,'metal':120*120 };
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
for (var resource in radius){
|
for (var resource in radius){
|
||||||
@@ -291,25 +365,84 @@ EconomyManager.prototype.updateResourceMaps = function(gameState, events){
|
|||||||
self.resourceMaps[resource].addInfluence(x, z, radius[resource], strength);
|
self.resourceMaps[resource].addInfluence(x, z, radius[resource], strength);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// TODO: fix for treasure and move out of loop
|
}
|
||||||
// Look for destroy events and subtract the entities original influence from the resourceMap
|
// Look for destroy events and subtract the entities original influence from the resourceMap
|
||||||
for (var i in events) {
|
// also look for dropsite destruction and add the associated entities (along with unmarking them)
|
||||||
var e = events[i];
|
for (var i in events) {
|
||||||
|
var e = events[i];
|
||||||
if (e.type === "Destroy") {
|
if (e.type === "Destroy") {
|
||||||
if (e.msg.entityObj){
|
|
||||||
var ent = e.msg.entityObj;
|
if (e.msg.entityObj){
|
||||||
if (ent && ent.position() && ent.resourceSupplyType() && ent.resourceSupplyType().generic === resource){
|
var ent = e.msg.entityObj;
|
||||||
|
if (ent && ent.position() && ent.resourceSupplyType() && ent.resourceSupplyType().generic !== "treasure") {
|
||||||
|
if (e.msg.metadata[gameState.getPlayerID()] && !e.msg.metadata[gameState.getPlayerID()]["linked-dropsite"]) {
|
||||||
|
var resource = ent.resourceSupplyType().generic;
|
||||||
var x = Math.round(ent.position()[0] / gameState.cellSize);
|
var x = Math.round(ent.position()[0] / gameState.cellSize);
|
||||||
var z = Math.round(ent.position()[1] / gameState.cellSize);
|
var z = Math.round(ent.position()[1] / gameState.cellSize);
|
||||||
var strength = Math.round(ent.resourceSupplyMax()/decreaseFactor[resource]);
|
var strength = Math.round(ent.resourceSupplyMax()/decreaseFactor[resource]);
|
||||||
this.resourceMaps[resource].addInfluence(x, z, radius[resource], -strength);
|
this.resourceMaps[resource].addInfluence(x, z, radius[resource], -strength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else if (e.type === "Create") {
|
if (ent && ent.owner() == gameState.player && ent.resourceDropsiteTypes() !== undefined) {
|
||||||
if (e.msg.entityObj){
|
var resources = ent.resourceDropsiteTypes();
|
||||||
var ent = e.msg.entityObj;
|
for (i in resources) {
|
||||||
if (ent && ent.position() && ent.resourceSupplyType() && ent.resourceSupplyType().generic === resource){
|
var resource = resources[i];
|
||||||
|
// loop through all dropsites to see if the resources of his entity collection could
|
||||||
|
// be taken over by another dropsite
|
||||||
|
var dropsites = gameState.getOwnDropsites(resource);
|
||||||
|
var metadata = e.msg.metadata[gameState.getPlayerID()];
|
||||||
|
metadata["linked-resources-" + resource].filter( function (supply) { //}){
|
||||||
|
var takenOver = false;
|
||||||
|
dropsites.forEach( function (otherDropsite) { //}) {
|
||||||
|
var distance = SquareVectorDistance(supply.position(), otherDropsite.position());
|
||||||
|
if (supply.getMetadata("linked-dropsite") == undefined || supply.getMetadata("linked-dropsite-dist") > distance) {
|
||||||
|
if (distance < bigRadius[resource]) {
|
||||||
|
supply.setMetadata("linked-dropsite", otherDropsite.id() );
|
||||||
|
supply.setMetadata("linked-dropsite-dist", +distance);
|
||||||
|
if (distance < smallRadius[resource]) {
|
||||||
|
takenOver = true;
|
||||||
|
supply.setMetadata("linked-dropsite-nearby", true );
|
||||||
|
} else {
|
||||||
|
supply.setMetadata("linked-dropsite-nearby", false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!takenOver) {
|
||||||
|
var x = Math.round(supply.position()[0] / gameState.cellSize);
|
||||||
|
var z = Math.round(supply.position()[1] / gameState.cellSize);
|
||||||
|
var strength = Math.round(supply.resourceSupplyMax()/decreaseFactor[resource]);
|
||||||
|
self.resourceMaps[resource].addInfluence(x, z, radius[resource], strength);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (e.type === "Create") {
|
||||||
|
if (e.msg.entityObj){
|
||||||
|
var ent = e.msg.entityObj;
|
||||||
|
if (ent && ent.position() && ent.resourceSupplyType() && ent.resourceSupplyType().generic !== "treasure"){
|
||||||
|
var resource = ent.resourceSupplyType().generic;
|
||||||
|
|
||||||
|
var addToMap = true;
|
||||||
|
var dropsites = gameState.getOwnDropsites(resource);
|
||||||
|
dropsites.forEach( function (otherDropsite) { //}) {
|
||||||
|
var distance = SquareVectorDistance(ent.position(), otherDropsite.position());
|
||||||
|
if (ent.getMetadata("linked-dropsite") == undefined || ent.getMetadata("linked-dropsite-dist") > distance) {
|
||||||
|
if (distance < bigRadius[resource]) {
|
||||||
|
if (distance < smallRadius[resource]) {
|
||||||
|
if (ent.getMetadata("linked-dropsite") == undefined)
|
||||||
|
addToMap = false;
|
||||||
|
ent.setMetadata("linked-dropsite-nearby", true );
|
||||||
|
} else {
|
||||||
|
ent.setMetadata("linked-dropsite-nearby", false );
|
||||||
|
}
|
||||||
|
ent.setMetadata("linked-dropsite", otherDropsite.id() );
|
||||||
|
ent.setMetadata("linked-dropsite-dist", +distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (addToMap) {
|
||||||
var x = Math.round(ent.position()[0] / gameState.cellSize);
|
var x = Math.round(ent.position()[0] / gameState.cellSize);
|
||||||
var z = Math.round(ent.position()[1] / gameState.cellSize);
|
var z = Math.round(ent.position()[1] / gameState.cellSize);
|
||||||
var strength = Math.round(ent.resourceSupplyMax()/decreaseFactor[resource]);
|
var strength = Math.round(ent.resourceSupplyMax()/decreaseFactor[resource]);
|
||||||
@@ -318,140 +451,197 @@ EconomyManager.prototype.updateResourceMaps = function(gameState, events){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//this.resourceMaps['wood'].dumpIm("tree_density.png");
|
//this.resourceMaps['wood'].dumpIm("tree_density.png");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns the position of the best place to build a new dropsite for the specified resource
|
// Returns the position of the best place to build a new dropsite for the specified resource
|
||||||
EconomyManager.prototype.getBestResourceBuildSpot = function(gameState, resource){
|
EconomyManager.prototype.getBestResourceBuildSpot = function(gameState, resource){
|
||||||
// A map which gives a positive weight for all CCs and adds a negative weight near all dropsites
|
|
||||||
var friendlyTiles = new Map(gameState);
|
var friendlyTiles = new Map(gameState);
|
||||||
gameState.getOwnEntities().forEach(function(ent) {
|
|
||||||
// We want to build near a CC of ours
|
|
||||||
if (ent.hasClass("CivCentre")){
|
|
||||||
var infl = 200;
|
|
||||||
|
|
||||||
|
friendlyTiles.add(this.resourceMaps[resource]);
|
||||||
|
|
||||||
|
for (i in this.resourceMaps)
|
||||||
|
if (i !== "food")
|
||||||
|
friendlyTiles.multiply(this.resourceMaps[i],true,100,1.5);
|
||||||
|
|
||||||
|
//friendlyTiles.dumpIm(gameState.getTimeElapsed() + "_" + resource + "_density_fade_base.png", 65000);
|
||||||
|
|
||||||
|
var territory = Map.createTerritoryMap(gameState);
|
||||||
|
friendlyTiles.multiplyTerritory(gameState,territory);
|
||||||
|
|
||||||
|
var resources = ["wood","stone","metal"];
|
||||||
|
for (i in resources) {
|
||||||
|
gameState.getOwnDropsites(resources[i]).forEach(function(ent) { //)){
|
||||||
|
// We don't want multiple dropsites at one spot so set to zero if too close.
|
||||||
var pos = ent.position();
|
var pos = ent.position();
|
||||||
var x = Math.round(pos[0] / gameState.cellSize);
|
var x = Math.round(pos[0] / gameState.cellSize);
|
||||||
var z = Math.round(pos[1] / gameState.cellSize);
|
var z = Math.round(pos[1] / gameState.cellSize);
|
||||||
friendlyTiles.addInfluence(x, z, infl, 0.1 * infl);
|
friendlyTiles.setInfluence(x, z, 17, 0);
|
||||||
friendlyTiles.addInfluence(x, z, infl/2, 0.1 * infl);
|
});
|
||||||
}
|
}
|
||||||
// We don't want multiple dropsites at one spot so add a negative for all dropsites
|
//friendlyTiles.dumpIm(gameState.getTimeElapsed() + "_" + resource + "_density_fade_final.png", 10000);
|
||||||
if (ent.resourceDropsiteTypes() && ent.resourceDropsiteTypes().indexOf(resource) !== -1){
|
friendlyTiles.multiply(gameState.ai.distanceFromMeMap,true,gameState.ai.distanceFromMeMap.width/3,2);
|
||||||
var infl = 20;
|
//friendlyTiles.dumpIm(gameState.getTimeElapsed() + "_" + resource + "_density_fade_final2.png", 10000);
|
||||||
|
|
||||||
var pos = ent.position();
|
|
||||||
var x = Math.round(pos[0] / gameState.cellSize);
|
|
||||||
var z = Math.round(pos[1] / gameState.cellSize);
|
|
||||||
|
|
||||||
friendlyTiles.addInfluence(x, z, infl, -50, 'quadratic');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Multiply by tree density to get a combination of the two maps
|
|
||||||
friendlyTiles.multiply(this.resourceMaps[resource]);
|
|
||||||
|
|
||||||
//friendlyTiles.dumpIm(resource + "_density_fade.png", 10000);
|
|
||||||
|
|
||||||
var obstructions = Map.createObstructionMap(gameState);
|
var obstructions = Map.createObstructionMap(gameState);
|
||||||
obstructions.expandInfluences();
|
obstructions.expandInfluences();
|
||||||
|
|
||||||
var bestIdx = friendlyTiles.findBestTile(4, obstructions)[0];
|
var bestIdx = friendlyTiles.findBestTile(2, obstructions)[0];
|
||||||
|
|
||||||
// Convert from 1d map pixel coordinates to game engine coordinates
|
|
||||||
var x = ((bestIdx % friendlyTiles.width) + 0.5) * gameState.cellSize;
|
var x = ((bestIdx % friendlyTiles.width) + 0.5) * gameState.cellSize;
|
||||||
var z = (Math.floor(bestIdx / friendlyTiles.width) + 0.5) * gameState.cellSize;
|
var z = (Math.floor(bestIdx / friendlyTiles.width) + 0.5) * gameState.cellSize;
|
||||||
return [x,z];
|
|
||||||
};
|
|
||||||
|
|
||||||
EconomyManager.prototype.updateResourceConcentrations = function(gameState){
|
if (territory.getOwner([x,z]) === 0) {
|
||||||
var self = this;
|
bestIdx = friendlyTiles.findBestTile(4, obstructions)[0];
|
||||||
var resources = ["food", "wood", "stone", "metal"];
|
x = ((bestIdx % friendlyTiles.width) + 0.5) * gameState.cellSize;
|
||||||
for (var key in resources){
|
z = (Math.floor(bestIdx / friendlyTiles.width) + 0.5) * gameState.cellSize;
|
||||||
var resource = resources[key];
|
return [true, [x,z]];
|
||||||
gameState.getOwnEntities().forEach(function(ent) {
|
|
||||||
if (ent.resourceDropsiteTypes() && ent.resourceDropsiteTypes().indexOf(resource) !== -1){
|
|
||||||
var radius = 14;
|
|
||||||
|
|
||||||
var pos = ent.position();
|
|
||||||
var x = Math.round(pos[0] / gameState.cellSize);
|
|
||||||
var z = Math.round(pos[1] / gameState.cellSize);
|
|
||||||
|
|
||||||
var quantity = self.resourceMaps[resource].sumInfluence(x, z, radius);
|
|
||||||
|
|
||||||
ent.setMetadata("resourceQuantity_" + resource, quantity);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
return [false, [x,z]];
|
||||||
|
};
|
||||||
|
EconomyManager.prototype.updateResourceConcentrations = function(gameState, resource){
|
||||||
|
var self = this;
|
||||||
|
gameState.getOwnDropsites(resource).forEach(function(dropsite) { //}){
|
||||||
|
var amount = 0;
|
||||||
|
var amountFar = 0;
|
||||||
|
if (dropsite.getMetadata("linked-resources-" + resource) == undefined)
|
||||||
|
return;
|
||||||
|
dropsite.getMetadata("linked-resources-" + resource).forEach(function(supply){ //}){
|
||||||
|
if (supply.getMetadata("full") == true)
|
||||||
|
return;
|
||||||
|
if (supply.getMetadata("linked-dropsite-nearby") == true)
|
||||||
|
amount += supply.resourceSupplyAmount();
|
||||||
|
else
|
||||||
|
amountFar += supply.resourceSupplyAmount();
|
||||||
|
supply.setMetadata("dp-update-value",supply.resourceSupplyAmount());
|
||||||
|
});
|
||||||
|
dropsite.setMetadata("resource-quantity-" + resource, amount);
|
||||||
|
dropsite.setMetadata("resource-quantity-far-" + resource, amountFar);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Stores lists of nearby resources
|
// Stores lists of nearby resources
|
||||||
EconomyManager.prototype.updateNearbyResources = function(gameState){
|
EconomyManager.prototype.updateNearbyResources = function(gameState,resource){
|
||||||
var self = this;
|
var self = this;
|
||||||
var resources = ["food", "wood", "stone", "metal"];
|
var resources = ["food", "wood", "stone", "metal"];
|
||||||
var resourceSupplies;
|
var resourceSupplies;
|
||||||
var radius = 100;
|
|
||||||
for (var key in resources){
|
// TODO: centralize with that other function that uses the same variables
|
||||||
var resource = resources[key];
|
// The weight of the influence function is amountOfResource/decreaseFactor
|
||||||
|
var decreaseFactor = {'wood': 12.0, 'stone': 10.0, 'metal': 10.0, 'food': 20.0};
|
||||||
|
// This is the maximum radius of the influence
|
||||||
|
var radius = {'wood':25.0, 'stone': 24.0, 'metal': 24.0, 'food': 24.0};
|
||||||
|
// smallRadius is the distance necessary to mark a resource as linked to a dropsite.
|
||||||
|
var smallRadius = { 'food':80*80,'wood':60*60,'stone':70*70,'metal':70*70 };
|
||||||
|
// bigRadius is the distance for a weak link (resources are considered when building other dropsites)
|
||||||
|
// and their resource amount is divided by 3 when checking for dropsite resource level.
|
||||||
|
var bigRadius = { 'food':140*140,'wood':140*140,'stone':140*140,'metal':140*140 };
|
||||||
|
|
||||||
|
gameState.getOwnDropsites(resource).forEach(function(ent) { //}){
|
||||||
|
|
||||||
gameState.getOwnDropsites(resource).forEach(function(ent) {
|
if (ent.getMetadata("nearby-resources-" + resource) === undefined){
|
||||||
if (ent.getMetadata("nearby-resources-" + resource) === undefined){
|
// let's defined the entity collections (by metadata)
|
||||||
var filterPos = Filters.byStaticDistance(ent.position(), radius);
|
gameState.getResourceSupplies(resource).filter( function (supply) { //}){
|
||||||
|
var distance = SquareVectorDistance(supply.position(), ent.position());
|
||||||
|
// if we're close than the current linked-dropsite, or if it's not linked
|
||||||
|
// TODO: change when actualy resource counting is implemented.
|
||||||
|
|
||||||
var collection = gameState.getResourceSupplies(resource).filter(filterPos);
|
if (supply.getMetadata("linked-dropsite") == undefined || supply.getMetadata("linked-dropsite-dist") > distance) {
|
||||||
collection.registerUpdates();
|
if (distance < bigRadius[resource]) {
|
||||||
|
if (distance < smallRadius[resource]) {
|
||||||
ent.setMetadata("nearby-resources-" + resource, collection);
|
// it's new to the game, remove it from the resource maps
|
||||||
ent.setMetadata("active-dropsite-" + resource, true);
|
if (supply.getMetadata("linked-dropsite") == undefined || supply.getMetadata("linked-dropsite-nearby") == false) {
|
||||||
}
|
var x = Math.round(supply.position()[0] / gameState.cellSize);
|
||||||
|
var z = Math.round(supply.position()[1] / gameState.cellSize);
|
||||||
|
var strength = Math.round(supply.resourceSupplyMax()/decreaseFactor[resource]);
|
||||||
|
self.resourceMaps[resource].addInfluence(x, z, radius[resource], -strength);
|
||||||
|
}
|
||||||
|
supply.setMetadata("linked-dropsite-nearby", true );
|
||||||
|
} else {
|
||||||
|
supply.setMetadata("linked-dropsite-nearby", false );
|
||||||
|
}
|
||||||
|
supply.setMetadata("linked-dropsite", ent.id() );
|
||||||
|
supply.setMetadata("linked-dropsite-dist", +distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// This one is both for the nearby and the linked
|
||||||
|
var filter = Filters.byMetadata("linked-dropsite", ent.id());
|
||||||
|
var collection = gameState.getResourceSupplies(resource).filter(filter);
|
||||||
|
collection.registerUpdates();
|
||||||
|
ent.setMetadata("linked-resources-" + resource, collection);
|
||||||
|
|
||||||
if (ent.getMetadata("nearby-resources-" + resource).length === 0){
|
filter = Filters.byMetadata("linked-dropsite-nearby",true);
|
||||||
ent.setMetadata("active-dropsite-" + resource, false);
|
var collection2 = collection.filter(filter);
|
||||||
}else{
|
collection2.registerUpdates();
|
||||||
ent.setMetadata("active-dropsite-" + resource, true);
|
ent.setMetadata("nearby-resources-" + resource, collection2);
|
||||||
}
|
|
||||||
/*
|
}
|
||||||
// Make resources glow wildly
|
|
||||||
if (resource == "food"){
|
/*
|
||||||
ent.getMetadata("nearby-resources-" + resource).forEach(function(ent){
|
// Make resources glow wildly
|
||||||
Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [10,0,0]});
|
if (resource == "food"){
|
||||||
});
|
ent.getMetadata("linked-resources-" + resource).forEach(function(ent){
|
||||||
}
|
Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [1,0,0]});
|
||||||
if (resource == "wood"){
|
});
|
||||||
ent.getMetadata("nearby-resources-" + resource).forEach(function(ent){
|
ent.getMetadata("nearby-resources-" + resource).forEach(function(ent){
|
||||||
Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,10,0]});
|
Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [10,0,0]});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (resource == "metal"){
|
if (resource == "wood"){
|
||||||
ent.getMetadata("nearby-resources-" + resource).forEach(function(ent){
|
ent.getMetadata("linked-resources-" + resource).forEach(function(ent){
|
||||||
Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,0,10]});
|
Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,1,0]});
|
||||||
});
|
});
|
||||||
}*/
|
ent.getMetadata("nearby-resources-" + resource).forEach(function(ent){
|
||||||
});
|
Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,10,0]});
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
if (resource == "metal"){
|
||||||
|
ent.getMetadata("linked-resources-" + resource).forEach(function(ent){
|
||||||
|
Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,0,1]});
|
||||||
|
});
|
||||||
|
ent.getMetadata("nearby-resources-" + resource).forEach(function(ent){
|
||||||
|
Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,0,10]});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (resource == "stone"){
|
||||||
|
ent.getMetadata("linked-resources-" + resource).forEach(function(ent){
|
||||||
|
Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,0.5,1]});
|
||||||
|
});
|
||||||
|
ent.getMetadata("nearby-resources-" + resource).forEach(function(ent){
|
||||||
|
Engine.PostCommand({"type": "set-shading-color", "entities": [ent.id()], "rgb": [0,5,10]});
|
||||||
|
});
|
||||||
|
}*/
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
//return the number of resource dropsites with an acceptable amount of the resource nearby
|
//return the number of resource dropsites with an acceptable amount of the resource nearby
|
||||||
EconomyManager.prototype.checkResourceConcentrations = function(gameState, resource){
|
EconomyManager.prototype.checkResourceConcentrations = function(gameState, resource){
|
||||||
//TODO: make these values adaptive
|
//TODO: make these values adaptive
|
||||||
var requiredInfluence = {wood: 1400, stone: 200, metal: 200};
|
var requiredInfluence = {"wood": 2500, "stone": 600, "metal": 600};
|
||||||
var count = 0;
|
var count = 0;
|
||||||
gameState.getOwnEntities().forEach(function(ent) {
|
gameState.getOwnDropsites(resource).forEach(function(ent) { //}){
|
||||||
if (ent.resourceDropsiteTypes() && ent.resourceDropsiteTypes().indexOf(resource) !== -1){
|
if (ent.getMetadata("resource-quantity-" + resource) == undefined || typeof(ent.getMetadata("resource-quantity-" + resource)) !== "number") {
|
||||||
var quantity = ent.getMetadata("resourceQuantity_" + resource);
|
count++; // assume it's OK if we don't know.
|
||||||
|
return;
|
||||||
if (quantity >= requiredInfluence[resource]){
|
}
|
||||||
count ++;
|
var quantity = +ent.getMetadata("resource-quantity-" + resource);
|
||||||
}
|
var quantityFar = +ent.getMetadata("resource-quantity-far-" + resource);
|
||||||
|
|
||||||
|
if (quantity >= requiredInfluence[resource]) {
|
||||||
|
count++;
|
||||||
|
} else if (quantity + quantityFar >= requiredInfluence[resource]) {
|
||||||
|
count += 0.5 + (quantity/requiredInfluence[resource])/2;
|
||||||
|
} else {
|
||||||
|
count += ((quantity + quantityFar)/requiredInfluence[resource])/2;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return count;
|
return count;
|
||||||
};
|
};
|
||||||
|
|
||||||
EconomyManager.prototype.buildMarket = function(gameState, queues){
|
EconomyManager.prototype.buildMarket = function(gameState, queues){
|
||||||
if (gameState.getTimeElapsed() > 360 * 1000){
|
if (gameState.getTimeElapsed() > 620 * 1000){
|
||||||
if (queues.economicBuilding.countTotalQueuedUnitsWithClass("BarterMarket") === 0 &&
|
if (queues.economicBuilding.countTotalQueuedUnitsWithClass("BarterMarket") === 0 &&
|
||||||
gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_market")) === 0){
|
gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_market")) === 0){
|
||||||
//only ever build one mill/CC/market at a time
|
//only ever build one mill/CC/market at a time
|
||||||
@@ -485,29 +675,19 @@ EconomyManager.prototype.tryBartering = function(gameState){
|
|||||||
};
|
};
|
||||||
// so this always try to build dropsites.
|
// so this always try to build dropsites.
|
||||||
EconomyManager.prototype.buildDropsites = function(gameState, queues){
|
EconomyManager.prototype.buildDropsites = function(gameState, queues){
|
||||||
if (queues.economicBuilding.totalLength() === 0 &&
|
if (queues.economicBuilding.totalLength() === 0 && gameState.countFoundationsWithType(gameState.applyCiv("structures/{civ}_mill")) === 0 &&
|
||||||
gameState.countFoundationsWithType(gameState.applyCiv("structures/{civ}_mill")) === 0 &&
|
|
||||||
gameState.countFoundationsWithType(gameState.applyCiv("structures/{civ}_civil_centre")) === 0){
|
gameState.countFoundationsWithType(gameState.applyCiv("structures/{civ}_civil_centre")) === 0){
|
||||||
//only ever build one mill/CC/market at a time
|
//only ever build one mill/CC/market at a time
|
||||||
if (gameState.getTimeElapsed() > 30 * 1000){
|
if (gameState.getTimeElapsed() > 30 * 1000){
|
||||||
for (var resource in this.dropsiteNumbers){
|
for (var resource in this.dropsiteNumbers){
|
||||||
if (this.checkResourceConcentrations(gameState, resource) < this.dropsiteNumbers[resource]){
|
if (this.checkResourceConcentrations(gameState, resource) < this.dropsiteNumbers[resource]){
|
||||||
|
|
||||||
var spot = this.getBestResourceBuildSpot(gameState, resource);
|
var spot = this.getBestResourceBuildSpot(gameState, resource);
|
||||||
|
|
||||||
var myCivCentres = gameState.getOwnEntities().filter(function(ent) {
|
if (spot[0] === true){
|
||||||
if (!ent.hasClass("CivCentre") || ent.position() === undefined){
|
queues.economicBuilding.addItem(new BuildingConstructionPlan(gameState, "structures/{civ}_civil_centre", spot[1]));
|
||||||
return false;
|
} else {
|
||||||
}
|
queues.economicBuilding.addItem(new BuildingConstructionPlan(gameState, "structures/{civ}_mill", spot[1]));
|
||||||
var dx = (spot[0]-ent.position()[0]);
|
|
||||||
var dy = (spot[1]-ent.position()[1]);
|
|
||||||
var dist2 = dx*dx + dy*dy;
|
|
||||||
return (ent.hasClass("CivCentre") && dist2 < 180*180);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (myCivCentres.length === 0){
|
|
||||||
queues.economicBuilding.addItem(new BuildingConstructionPlan(gameState, "structures/{civ}_civil_centre", spot));
|
|
||||||
}else{
|
|
||||||
queues.economicBuilding.addItem(new BuildingConstructionPlan(gameState, "structures/{civ}_mill", spot));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -524,38 +704,38 @@ EconomyManager.prototype.update = function(gameState, queues, events) {
|
|||||||
|
|
||||||
this.buildNewCC(gameState,queues);
|
this.buildNewCC(gameState,queues);
|
||||||
|
|
||||||
|
// this function also deals with a few things that are number-of-workers related
|
||||||
Engine.ProfileStart("Train workers and build farms");
|
Engine.ProfileStart("Train workers and build farms");
|
||||||
this.trainMoreWorkers(gameState, queues);
|
this.trainMoreWorkers(gameState, queues);
|
||||||
|
|
||||||
if (gameState.getTimeElapsed() > 5000)
|
if ((gameState.ai.playedTurn+1) % 3 === 0)
|
||||||
this.buildMoreFields(gameState,queues);
|
this.buildMoreFields(gameState,queues);
|
||||||
|
|
||||||
Engine.ProfileStop();
|
Engine.ProfileStop();
|
||||||
|
|
||||||
//Later in the game we want to build stuff faster.
|
//Later in the game we want to build stuff faster.
|
||||||
if (gameState.countEntitiesByType(gameState.applyCiv("units/{civ}_support_female_citizen")) > this.targetNumWorkers * 0.5) {
|
if (gameState.getTimeElapsed() > 15*60*1000) {
|
||||||
this.targetNumBuilders = 6;
|
this.targetNumBuilders = 6;
|
||||||
}else{
|
}else{
|
||||||
this.targetNumBuilders = 3;
|
this.targetNumBuilders = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gameState.getTimeElapsed() > 20*60*1000) {
|
|
||||||
this.dropsiteNumbers = {wood: 3, stone: 2, metal: 2};
|
|
||||||
}else{
|
|
||||||
this.dropsiteNumbers = {wood: 2, stone: 1, metal: 1};
|
|
||||||
}
|
|
||||||
|
|
||||||
Engine.ProfileStart("Update Resource Maps and Concentrations");
|
Engine.ProfileStart("Update Resource Maps and Concentrations");
|
||||||
|
this.updateResourceMaps(gameState, events);
|
||||||
if (gameState.ai.playedTurn % 2 === 0) {
|
if (gameState.ai.playedTurn % 2 === 0) {
|
||||||
this.updateResourceMaps(gameState, events);
|
var resources = ["food", "wood", "stone", "metal"];
|
||||||
this.updateResourceConcentrations(gameState);
|
this.updateNearbyResources(gameState, resources[(gameState.ai.playedTurn % 8)/2]);
|
||||||
this.updateNearbyResources(gameState);
|
} else if (gameState.ai.playedTurn % 2 === 1) {
|
||||||
|
var resources = ["food", "wood", "stone", "metal"];
|
||||||
|
this.updateResourceConcentrations(gameState, resources[((gameState.ai.playedTurn+1) % 8)/2]);
|
||||||
}
|
}
|
||||||
Engine.ProfileStop();
|
Engine.ProfileStop();
|
||||||
|
|
||||||
Engine.ProfileStart("Build new Dropsites");
|
if (gameState.ai.playedTurn % 8 === 0) {
|
||||||
this.buildDropsites(gameState, queues);
|
Engine.ProfileStart("Build new Dropsites");
|
||||||
Engine.ProfileStop();
|
this.buildDropsites(gameState, queues);
|
||||||
|
Engine.ProfileStop();
|
||||||
|
}
|
||||||
this.tryBartering(gameState);
|
this.tryBartering(gameState);
|
||||||
this.buildMarket(gameState, queues);
|
this.buildMarket(gameState, queues);
|
||||||
|
|
||||||
@@ -575,32 +755,33 @@ EconomyManager.prototype.update = function(gameState, queues, events) {
|
|||||||
this.reassignIdleWorkers(gameState);
|
this.reassignIdleWorkers(gameState);
|
||||||
Engine.ProfileStop();
|
Engine.ProfileStop();
|
||||||
|
|
||||||
Engine.ProfileStart("Swap Workers");
|
// this is pretty slow, run it once in a while
|
||||||
var gathererGroups = {};
|
if (gameState.ai.playedTurn % 4 === 0) {
|
||||||
gameState.getOwnEntitiesByRole("worker").forEach(function(ent){
|
Engine.ProfileStart("Swap Workers");
|
||||||
var key = uneval(ent.resourceGatherRates());
|
var gathererGroups = {};
|
||||||
if (!gathererGroups[key]){
|
gameState.getOwnEntitiesByRole("worker").forEach(function(ent){
|
||||||
gathererGroups[key] = {"food": [], "wood": [], "metal": [], "stone": []};
|
var key = uneval(ent.resourceGatherRates());
|
||||||
}
|
if (!gathererGroups[key]){
|
||||||
if (ent.getMetadata("gather-type") in gathererGroups[key]){
|
gathererGroups[key] = {"food": [], "wood": [], "metal": [], "stone": []};
|
||||||
gathererGroups[key][ent.getMetadata("gather-type")].push(ent);
|
}
|
||||||
}
|
if (ent.getMetadata("gather-type") in gathererGroups[key]){
|
||||||
});
|
gathererGroups[key][ent.getMetadata("gather-type")].push(ent);
|
||||||
|
}
|
||||||
for (var i in gathererGroups){
|
});
|
||||||
for (var j in gathererGroups){
|
for (var i in gathererGroups){
|
||||||
var a = eval(i);
|
for (var j in gathererGroups){
|
||||||
var b = eval(j);
|
var a = eval(i);
|
||||||
if (a["food.grain"]/b["food.grain"] > a["wood.tree"]/b["wood.tree"] && gathererGroups[i]["wood"].length > 0 && gathererGroups[j]["food"].length > 0){
|
var b = eval(j);
|
||||||
for (var k = 0; k < Math.min(gathererGroups[i]["wood"].length, gathererGroups[j]["food"].length); k++){
|
if (a["food.grain"]/b["food.grain"] > a["wood.tree"]/b["wood.tree"] && gathererGroups[i]["wood"].length > 0 && gathererGroups[j]["food"].length > 0){
|
||||||
gathererGroups[i]["wood"][k].setMetadata("gather-type", "food");
|
for (var k = 0; k < Math.min(gathererGroups[i]["wood"].length, gathererGroups[j]["food"].length); k++){
|
||||||
gathererGroups[j]["food"][k].setMetadata("gather-type", "wood");
|
gathererGroups[i]["wood"][k].setMetadata("gather-type", "food");
|
||||||
|
gathererGroups[j]["food"][k].setMetadata("gather-type", "wood");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Engine.ProfileStop();
|
||||||
}
|
}
|
||||||
Engine.ProfileStop();
|
|
||||||
|
|
||||||
Engine.ProfileStart("Run Workers");
|
Engine.ProfileStart("Run Workers");
|
||||||
gameState.getOwnEntitiesByRole("worker").forEach(function(ent){
|
gameState.getOwnEntitiesByRole("worker").forEach(function(ent){
|
||||||
if (!ent.getMetadata("worker-object")){
|
if (!ent.getMetadata("worker-object")){
|
||||||
|
|||||||
@@ -177,6 +177,9 @@ enemyWatcher.prototype.splitArmies = function(gameState){
|
|||||||
army.forEach( function (enemy) {
|
army.forEach( function (enemy) {
|
||||||
if (enemy.position() == undefined)
|
if (enemy.position() == undefined)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// debug ("entity " +enemy.templateName() + " is currently " +enemy.visibility(gameState.player));
|
||||||
|
|
||||||
if (!inRange(enemy.position(),centre, 3500) ) {
|
if (!inRange(enemy.position(),centre, 3500) ) {
|
||||||
var newArmyID = uneval( gameState.player + "" + self.totalNBofArmies);
|
var newArmyID = uneval( gameState.player + "" + self.totalNBofArmies);
|
||||||
if (self.dangerousArmies.indexOf(armyID) !== -1)
|
if (self.dangerousArmies.indexOf(armyID) !== -1)
|
||||||
|
|||||||
@@ -44,6 +44,23 @@ EntityTemplate.prototype.hasClasses = function(array) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// returns the classes this counters:
|
||||||
|
// each countered class is an array specifying what is required (even if only one) and the Multiplier [ ["whatever","other whatever"] , 0 ].
|
||||||
|
EntityTemplate.prototype.getCounteredClasses = function() {
|
||||||
|
if (!this._template.Attack)
|
||||||
|
return undefined;
|
||||||
|
|
||||||
|
var Classes = [];
|
||||||
|
for (i in this._template.Attack) {
|
||||||
|
if (!this._template.Attack[i].Bonuses)
|
||||||
|
continue;
|
||||||
|
for (o in this._template.Attack[i].Bonuses) {
|
||||||
|
Classes.push([this._template.Attack[i].Bonuses[o].Classes.split(" "), +this._template.Attack[i].Bonuses[o].Multiplier]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Classes;
|
||||||
|
};
|
||||||
|
|
||||||
EntityTemplate.prototype.getMaxStrength = function()
|
EntityTemplate.prototype.getMaxStrength = function()
|
||||||
{
|
{
|
||||||
var strength = 0.0;
|
var strength = 0.0;
|
||||||
@@ -113,11 +130,18 @@ EntityTemplate.prototype.costSum = function() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Entity.prototype.deleteMetadata = function(id) {
|
Entity.prototype.deleteMetadata = function(id) {
|
||||||
delete this._ai._entityMetadata[this.id()];
|
delete this._ai._entityMetadata[this.id()];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Entity.prototype.healthLevel = function() {
|
||||||
|
return (this.hitpoints() / this.maxHitpoints());
|
||||||
|
};
|
||||||
|
|
||||||
|
Entity.prototype.visibility = function(player) {
|
||||||
|
return this._entity.visibility[player-1];
|
||||||
|
};
|
||||||
|
|
||||||
Entity.prototype.unload = function(id) {
|
Entity.prototype.unload = function(id) {
|
||||||
if (!this._template.GarrisonHolder)
|
if (!this._template.GarrisonHolder)
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -158,3 +182,4 @@ Entity.prototype.barter = function(buyType, sellType, amount) {
|
|||||||
Engine.PostCommand({"type": "barter", "sell" : sellType, "buy" : buyType, "amount" : amount });
|
Engine.PostCommand({"type": "barter", "sell" : sellType, "buy" : buyType, "amount" : amount });
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,12 @@ EntityCollection.prototype.attack = function(unit)
|
|||||||
Engine.PostCommand({"type": "attack", "entities": this.toIdArray(), "target": unitId, "queued": false});
|
Engine.PostCommand({"type": "attack", "entities": this.toIdArray(), "target": unitId, "queued": false});
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
// violent, aggressive, defensive, passive, standground
|
||||||
EntityCollection.prototype.attackMove = function(x, z){
|
EntityCollection.prototype.setStance = function(stance){
|
||||||
Engine.PostCommand({"type": "attack-move", "entities": this.toIdArray(), "x": x, "z": z, "queued": false});
|
Engine.PostCommand({"type": "stance", "entities": this.toIdArray(), "name" : stance, "queued": false});
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function EntityCollectionFromIds(gameState, idList){
|
function EntityCollectionFromIds(gameState, idList){
|
||||||
var ents = {};
|
var ents = {};
|
||||||
for (var i in idList){
|
for (var i in idList){
|
||||||
|
|||||||
@@ -30,4 +30,13 @@ Filters["byTerritory"] = function(Map, territoryIndex){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dynamicProperties": ['position']};
|
"dynamicProperties": ['position']};
|
||||||
};
|
};
|
||||||
|
Filters["isDropsite"] = function(resourceType){
|
||||||
|
return {"func": function(ent){
|
||||||
|
return (ent.resourceDropsiteTypes() && ent.resourceDropsiteTypes().indexOf(resourceType) !== -1
|
||||||
|
&& ent.foundationProgress() === undefined);
|
||||||
|
},
|
||||||
|
"dynamicProperties": []};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ GameState.prototype.countOwnQueuedEntitiesWithMetadata = function(data, value) {
|
|||||||
var count = 0;
|
var count = 0;
|
||||||
this.getOwnTrainingFacilities().forEach(function(ent) {
|
this.getOwnTrainingFacilities().forEach(function(ent) {
|
||||||
ent.trainingQueue().forEach(function(item) {
|
ent.trainingQueue().forEach(function(item) {
|
||||||
if (item.metadata && item.metadata.data && item.metadata.data == value)
|
if (item.metadata && item.metadata[data] && item.metadata[data] == value)
|
||||||
count += item.count;
|
count += item.count;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -344,15 +344,29 @@ GameState.prototype.findTrainableUnits = function(classes){
|
|||||||
for (var i in allTrainable) {
|
for (var i in allTrainable) {
|
||||||
var template = this.getTemplate(allTrainable[i]);
|
var template = this.getTemplate(allTrainable[i]);
|
||||||
var okay = true;
|
var okay = true;
|
||||||
|
|
||||||
for (o in classes)
|
for (o in classes)
|
||||||
if (!template.hasClass(classes[o]))
|
if (!template.hasClass(classes[o]))
|
||||||
okay = false;
|
okay = false;
|
||||||
|
|
||||||
if (template.hasClass("Hero")) // disabling heroes for now
|
if (template.hasClass("Hero")) // disabling heroes for now
|
||||||
okay = false;
|
okay = false;
|
||||||
|
|
||||||
if (okay)
|
if (okay)
|
||||||
ret.push( [allTrainable[i], template] );
|
ret.push( [allTrainable[i], template] );
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
// defcon utilities
|
||||||
|
GameState.prototype.timeSinceDefconChange = function() {
|
||||||
|
return this.getTimeElapsed()-this.ai.defconChangeTime;
|
||||||
|
};
|
||||||
|
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() {
|
||||||
|
return this.ai.defcon;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
const TERRITORY_PLAYER_MASK = 0x3F;
|
const TERRITORY_PLAYER_MASK = 0x3F;
|
||||||
|
|
||||||
//TODO: Make this cope with negative cell values
|
//TODO: Make this cope with negative cell values
|
||||||
function Map(gameState, originalMap){
|
function Map(gameState, originalMap, actualCopy){
|
||||||
// get the map to find out the correct dimensions
|
// get the map to find out the correct dimensions
|
||||||
var gameMap = gameState.getMap();
|
var gameMap = gameState.getMap();
|
||||||
this.width = gameMap.width;
|
this.width = gameMap.width;
|
||||||
this.height = gameMap.height;
|
this.height = gameMap.height;
|
||||||
this.length = gameMap.data.length;
|
this.length = gameMap.data.length;
|
||||||
if (originalMap){
|
|
||||||
|
if (originalMap && actualCopy){
|
||||||
|
this.map = new Uint16Array(this.length);
|
||||||
|
for (var i = 0; i < originalMap.length; ++i)
|
||||||
|
this.map[i] = originalMap[i];
|
||||||
|
} else if (originalMap) {
|
||||||
this.map = originalMap;
|
this.map = originalMap;
|
||||||
}else{
|
} else {
|
||||||
this.map = new Uint16Array(this.length);
|
this.map = new Uint16Array(this.length);
|
||||||
}
|
}
|
||||||
this.cellSize = gameState.cellSize;
|
this.cellSize = gameState.cellSize;
|
||||||
@@ -99,12 +104,28 @@ Map.createTerritoryMap = function(gameState) {
|
|||||||
ret.getOwner = function(p) {
|
ret.getOwner = function(p) {
|
||||||
return this.point(p) & TERRITORY_PLAYER_MASK;
|
return this.point(p) & TERRITORY_PLAYER_MASK;
|
||||||
}
|
}
|
||||||
|
ret.getOwnerIndex = function(p) {
|
||||||
|
return this.map[p] & TERRITORY_PLAYER_MASK;
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
Map.prototype.drawDistance = function(gameState, elements) {
|
||||||
|
for ( var y = 0; y < this.height; ++y) {
|
||||||
|
for ( var x = 0; x < this.width; ++x) {
|
||||||
|
var minDist = 500000;
|
||||||
|
for (i in elements) {
|
||||||
|
var px = elements[i].position()[0]/gameState.cellSize;
|
||||||
|
var py = elements[i].position()[1]/gameState.cellSize;
|
||||||
|
var dist = VectorDistance([px,py], [x,y]);
|
||||||
|
if (dist < minDist)
|
||||||
|
minDist = dist;
|
||||||
|
}
|
||||||
|
this.map[x + y*this.width] = Math.max(1,this.width - minDist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type) {
|
Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type) {
|
||||||
strength = strength ? strength : maxDist;
|
strength = strength ? +strength : +maxDist;
|
||||||
type = type ? type : 'linear';
|
type = type ? type : 'linear';
|
||||||
|
|
||||||
var x0 = Math.max(0, cx - maxDist);
|
var x0 = Math.max(0, cx - maxDist);
|
||||||
@@ -113,19 +134,18 @@ Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type) {
|
|||||||
var y1 = Math.min(this.height, cy + maxDist);
|
var y1 = Math.min(this.height, cy + maxDist);
|
||||||
var maxDist2 = maxDist * maxDist;
|
var maxDist2 = maxDist * maxDist;
|
||||||
|
|
||||||
var str = 0;
|
var str = 0.0;
|
||||||
switch (type){
|
switch (type){
|
||||||
case 'linear':
|
case 'linear':
|
||||||
str = strength / maxDist;
|
str = +strength / +maxDist;
|
||||||
break;
|
break;
|
||||||
case 'quadratic':
|
case 'quadratic':
|
||||||
str = strength / maxDist2;
|
str = +strength / +maxDist2;
|
||||||
break;
|
break;
|
||||||
case 'constant':
|
case 'constant':
|
||||||
str = strength;
|
str = +strength;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( var y = y0; y < y1; ++y) {
|
for ( var y = y0; y < y1; ++y) {
|
||||||
for ( var x = x0; x < x1; ++x) {
|
for ( var x = x0; x < x1; ++x) {
|
||||||
var dx = x - cx;
|
var dx = x - cx;
|
||||||
@@ -145,7 +165,6 @@ Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type) {
|
|||||||
quant = str;
|
quant = str;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-1 * quant > this.map[x + y * this.width]){
|
if (-1 * quant > this.map[x + y * this.width]){
|
||||||
this.map[x + y * this.width] = 0; //set anything which would have gone negative to 0
|
this.map[x + y * this.width] = 0; //set anything which would have gone negative to 0
|
||||||
}else{
|
}else{
|
||||||
@@ -156,6 +175,79 @@ Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Map.prototype.multiplyInfluence = function(cx, cy, maxDist, strength, type) {
|
||||||
|
strength = strength ? +strength : +maxDist;
|
||||||
|
type = type ? type : 'constant';
|
||||||
|
|
||||||
|
var x0 = Math.max(0, cx - maxDist);
|
||||||
|
var y0 = Math.max(0, cy - maxDist);
|
||||||
|
var x1 = Math.min(this.width, cx + maxDist);
|
||||||
|
var y1 = Math.min(this.height, cy + maxDist);
|
||||||
|
var maxDist2 = maxDist * maxDist;
|
||||||
|
|
||||||
|
var str = 0.0;
|
||||||
|
switch (type){
|
||||||
|
case 'linear':
|
||||||
|
str = strength / maxDist;
|
||||||
|
break;
|
||||||
|
case 'quadratic':
|
||||||
|
str = strength / maxDist2;
|
||||||
|
break;
|
||||||
|
case 'constant':
|
||||||
|
str = strength;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( var y = y0; y < y1; ++y) {
|
||||||
|
for ( var x = x0; x < x1; ++x) {
|
||||||
|
var dx = x - cx;
|
||||||
|
var dy = y - cy;
|
||||||
|
var r2 = dx*dx + dy*dy;
|
||||||
|
if (r2 < maxDist2){
|
||||||
|
var quant = 0;
|
||||||
|
switch (type){
|
||||||
|
case 'linear':
|
||||||
|
var r = Math.sqrt(r2);
|
||||||
|
quant = str * (maxDist - r);
|
||||||
|
break;
|
||||||
|
case 'quadratic':
|
||||||
|
quant = str * (maxDist2 - r2);
|
||||||
|
break;
|
||||||
|
case 'constant':
|
||||||
|
quant = str;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var machin = this.map[x + y * this.width] * quant;
|
||||||
|
if (machin <= 0){
|
||||||
|
this.map[x + y * this.width] = 0; //set anything which would have gone negative to 0
|
||||||
|
}else{
|
||||||
|
this.map[x + y * this.width] = machin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Map.prototype.setInfluence = function(cx, cy, maxDist, value) {
|
||||||
|
value = value ? value : 0;
|
||||||
|
|
||||||
|
var x0 = Math.max(0, cx - maxDist);
|
||||||
|
var y0 = Math.max(0, cy - maxDist);
|
||||||
|
var x1 = Math.min(this.width, cx + maxDist);
|
||||||
|
var y1 = Math.min(this.height, cy + maxDist);
|
||||||
|
var maxDist2 = maxDist * maxDist;
|
||||||
|
|
||||||
|
for ( var y = y0; y < y1; ++y) {
|
||||||
|
for ( var x = x0; x < x1; ++x) {
|
||||||
|
var dx = x - cx;
|
||||||
|
var dy = y - cy;
|
||||||
|
var r2 = dx*dx + dy*dy;
|
||||||
|
if (r2 < maxDist2){
|
||||||
|
this.map[x + y * this.width] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Map.prototype.sumInfluence = function(cx, cy, radius){
|
Map.prototype.sumInfluence = function(cx, cy, radius){
|
||||||
var x0 = Math.max(0, cx - radius);
|
var x0 = Math.max(0, cx - radius);
|
||||||
var y0 = Math.max(0, cy - radius);
|
var y0 = Math.max(0, cy - radius);
|
||||||
@@ -249,15 +341,37 @@ Map.prototype.findBestTile = function(radius, obstructionTiles){
|
|||||||
return [bestIdx, bestVal];
|
return [bestIdx, bestVal];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Multiplies current map by the parameter map pixelwise
|
// Multiplies current map by 3 if in my territory
|
||||||
Map.prototype.multiply = function(map){
|
Map.prototype.multiplyTerritory = function(gameState,map){
|
||||||
for (var i = 0; i < this.length; i++){
|
for (var i = 0; i < this.length; ++i){
|
||||||
this.map[i] *= map.map[i];
|
if (map.getOwnerIndex(i) === gameState.player)
|
||||||
|
this.map[i] *= 2.5;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Multiplies current map by the parameter map pixelwise
|
||||||
|
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){
|
||||||
|
for (var i = 0; i < this.length; ++i){
|
||||||
|
this.map[i] += +map.map[i];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// add to current map by the parameter map pixelwise
|
||||||
|
Map.prototype.subtract = function(map){
|
||||||
|
for (var i = 0; i < this.length; ++i){
|
||||||
|
this.map[i] += map.map[i];
|
||||||
|
if (this.map[i] <= 0)
|
||||||
|
this.map[i] = 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Map.prototype.dumpIm = function(name, threshold){
|
Map.prototype.dumpIm = function(name, threshold){
|
||||||
name = name ? name : "default.png";
|
name = name ? name : "default.png";
|
||||||
threshold = threshold ? threshold : 256;
|
threshold = threshold ? threshold : 65500;
|
||||||
Engine.DumpImage(name, this.map, this.width, this.height, threshold);
|
Engine.DumpImage(name, this.map, this.width, this.height, threshold);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,16 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
var MilitaryAttackManager = function() {
|
var MilitaryAttackManager = function() {
|
||||||
// these use the structure soldiers[unitId] = true|false to register the units
|
|
||||||
this.attackManagers = [AttackMoveToLocation];
|
|
||||||
this.availableAttacks = [];
|
|
||||||
this.currentAttacks = [];
|
|
||||||
|
|
||||||
// Counts how many attacks we have sent at the enemy.
|
|
||||||
this.attackCount = 0;
|
|
||||||
this.lastAttackTime = 0;
|
|
||||||
|
|
||||||
this.defenceManager = new Defence();
|
this.defenceManager = new Defence();
|
||||||
|
|
||||||
this.TotalAttackNumber = 0;
|
this.TotalAttackNumber = 0;
|
||||||
this.upcomingAttacks = { "CityAttack" : [] };
|
this.upcomingAttacks = { "CityAttack" : [] };
|
||||||
this.startedAttacks = { "CityAttack" : [] };
|
this.startedAttacks = { "CityAttack" : [] };
|
||||||
@@ -144,9 +136,11 @@ MilitaryAttackManager.prototype.findBestNewUnit = function(gameState, queue, sol
|
|||||||
// picks the best template based on parameters and classes
|
// picks the best template based on parameters and classes
|
||||||
MilitaryAttackManager.prototype.findBestTrainableUnit = function(gameState, classes, parameters) {
|
MilitaryAttackManager.prototype.findBestTrainableUnit = function(gameState, classes, parameters) {
|
||||||
var units = gameState.findTrainableUnits(classes);
|
var units = gameState.findTrainableUnits(classes);
|
||||||
|
|
||||||
if (units.length === 0)
|
if (units.length === 0)
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
||||||
|
|
||||||
units.sort(function(a, b) { //}) {
|
units.sort(function(a, b) { //}) {
|
||||||
var aDivParam = 0, bDivParam = 0;
|
var aDivParam = 0, bDivParam = 0;
|
||||||
var aTopParam = 0, bTopParam = 0;
|
var aTopParam = 0, bTopParam = 0;
|
||||||
@@ -407,7 +401,7 @@ MilitaryAttackManager.prototype.constructTrainingBuildings = function(gameState,
|
|||||||
// Build more military buildings
|
// Build more military buildings
|
||||||
// TODO: make military building better
|
// TODO: make military building better
|
||||||
Engine.ProfileStart("Build buildings");
|
Engine.ProfileStart("Build buildings");
|
||||||
if (gameState.countEntitiesByType(gameState.applyCiv("units/{civ}_support_female_citizen")) > 35) {
|
if (gameState.countEntitiesByType(gameState.applyCiv("units/{civ}_support_female_citizen")) > 25) {
|
||||||
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0]))
|
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0]))
|
||||||
+ queues.militaryBuilding.totalLength() < 1) {
|
+ queues.militaryBuilding.totalLength() < 1) {
|
||||||
queues.militaryBuilding.addItem(new BuildingConstructionPlan(gameState, this.bModerate[0]));
|
queues.militaryBuilding.addItem(new BuildingConstructionPlan(gameState, this.bModerate[0]));
|
||||||
@@ -543,37 +537,36 @@ MilitaryAttackManager.prototype.update = function(gameState, queues, events) {
|
|||||||
|
|
||||||
var attack = this.upcomingAttacks[attackType][i];
|
var attack = this.upcomingAttacks[attackType][i];
|
||||||
|
|
||||||
if (!attack.isPaused()) {
|
// okay so we'll get the support plan
|
||||||
// okay so we'll get the support plan
|
if (!attack.isStarted()) {
|
||||||
if (!attack.isStarted()) {
|
var updateStep = attack.updatePreparation(gameState, this,events);
|
||||||
if (1) { //gameState.ai.status["underAttack"] == false) {
|
|
||||||
var updateStep = attack.updatePreparation(gameState, this,events);
|
// now we're gonna check if the preparation time is over
|
||||||
|
if (updateStep === 1 || attack.isPaused() ) {
|
||||||
// now we're gonna check if the preparation time is over
|
// just chillin'
|
||||||
if (updateStep === 1) {
|
} else if (updateStep === 0 || updateStep === 3) {
|
||||||
// just chillin'
|
debug ("Military Manager: " +attack.getType() +" plan " +attack.getName() +" aborted.");
|
||||||
} else if (updateStep === 0) {
|
if (updateStep === 3) {
|
||||||
debug ("Military Manager: " +attack.getType() +" plan " +attack.getName() +" aborted.");
|
this.attackPlansEncounteredWater = true;
|
||||||
attack.Abort(gameState, this);
|
debug("I dare not wet my feet");
|
||||||
|
|
||||||
//this.abortedAttacks.push(attack);
|
|
||||||
|
|
||||||
this.upcomingAttacks[attackType].splice(i,1);
|
|
||||||
i--;
|
|
||||||
} else if (updateStep === 2) {
|
|
||||||
debug ("Military Manager: Starting " +attack.getType() +" plan " +attack.getName());
|
|
||||||
attack.StartAttack(gameState,this);
|
|
||||||
this.startedAttacks[attackType].push(attack);
|
|
||||||
this.upcomingAttacks[attackType].splice(i,1);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
attack.Abort(gameState, this);
|
||||||
debug ("Military Manager: Starting " +attack.getType() +" plan " +attack.getName());
|
//this.abortedAttacks.push(attack);
|
||||||
this.startedAttacks[attackType].push(attack);
|
|
||||||
this.upcomingAttacks[attackType].splice(i,1);
|
|
||||||
i--;
|
i--;
|
||||||
|
this.upcomingAttacks[attackType].splice(i,1);
|
||||||
|
} else if (updateStep === 2) {
|
||||||
|
debug ("Military Manager: Starting " +attack.getType() +" plan " +attack.getName());
|
||||||
|
attack.StartAttack(gameState,this);
|
||||||
|
this.startedAttacks[attackType].push(attack);
|
||||||
|
i--;
|
||||||
|
this.upcomingAttacks[attackType].splice(i-1,1);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
debug ("Military Manager: Starting " +attack.getType() +" plan " +attack.getName());
|
||||||
|
this.startedAttacks[attackType].push(attack);
|
||||||
|
i--;
|
||||||
|
this.upcomingAttacks[attackType].splice(i-1,1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -596,12 +589,17 @@ MilitaryAttackManager.prototype.update = function(gameState, queues, events) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// creating plans after updating because an aborted plan might be reused in that case.
|
// creating plans after updating because an aborted plan might be reused in that case.
|
||||||
if (gameState.countEntitiesByType(gameState.applyCiv(this.bModerate[0])) >= 1) {
|
if (gameState.countEntitiesByType(gameState.applyCiv(this.bModerate[0])) >= 1 && !this.attackPlansEncounteredWater) {
|
||||||
if (this.upcomingAttacks["CityAttack"].length == 0) {
|
if (this.upcomingAttacks["CityAttack"].length == 0 && gameState.getTimeElapsed() < 25*60000) {
|
||||||
var Lalala = new CityAttack(gameState, this,this.TotalAttackNumber, -1);
|
var Lalala = new CityAttack(gameState, this,this.TotalAttackNumber, -1);
|
||||||
debug ("Military Manager: Creating the plan " +this.TotalAttackNumber);
|
debug ("Military Manager: Creating the plan " +this.TotalAttackNumber);
|
||||||
this.TotalAttackNumber++;
|
this.TotalAttackNumber++;
|
||||||
this.upcomingAttacks["CityAttack"].push(Lalala);
|
this.upcomingAttacks["CityAttack"].push(Lalala);
|
||||||
|
} else if (this.upcomingAttacks["CityAttack"].length == 0) {
|
||||||
|
var Lalala = new CityAttack(gameState, this,this.TotalAttackNumber, -1, "superSized");
|
||||||
|
debug ("Military Manager: Creating the super sized plan " +this.TotalAttackNumber);
|
||||||
|
this.TotalAttackNumber++;
|
||||||
|
this.upcomingAttacks["CityAttack"].push(Lalala);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ BuildingConstructionPlan.prototype.findGoodPosition = function(gameState) {
|
|||||||
|
|
||||||
var obstructionMap = Map.createObstructionMap(gameState,template);
|
var obstructionMap = Map.createObstructionMap(gameState,template);
|
||||||
|
|
||||||
//obstructionMap.dumpIm("obstructions.png");
|
///obstructionMap.dumpIm("obstructions.png");
|
||||||
|
|
||||||
obstructionMap.expandInfluences();
|
obstructionMap.expandInfluences();
|
||||||
|
|
||||||
@@ -64,12 +64,13 @@ BuildingConstructionPlan.prototype.findGoodPosition = function(gameState) {
|
|||||||
|
|
||||||
var friendlyTiles = new Map(gameState);
|
var friendlyTiles = new Map(gameState);
|
||||||
|
|
||||||
|
var alreadyHasHouses = false;
|
||||||
|
|
||||||
// If a position was specified then place the building as close to it as possible
|
// If a position was specified then place the building as close to it as possible
|
||||||
if (this.position){
|
if (this.position){
|
||||||
var x = Math.round(this.position[0] / cellSize);
|
var x = Math.round(this.position[0] / cellSize);
|
||||||
var z = Math.round(this.position[1] / cellSize);
|
var z = Math.round(this.position[1] / cellSize);
|
||||||
friendlyTiles.addInfluence(x, z, 200);
|
friendlyTiles.addInfluence(x, z, 200);
|
||||||
//friendlyTiles.dumpIm("pos.png", 200);
|
|
||||||
}else{
|
}else{
|
||||||
// No position was specified so try and find a sensible place to build
|
// No position was specified so try and find a sensible place to build
|
||||||
gameState.getOwnEntities().forEach(function(ent) {
|
gameState.getOwnEntities().forEach(function(ent) {
|
||||||
@@ -96,9 +97,10 @@ BuildingConstructionPlan.prototype.findGoodPosition = function(gameState) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if (template.genericName() == "House" && ent.genericName() == "House")
|
if (template.genericName() == "House" && ent.genericName() == "House") {
|
||||||
friendlyTiles.addInfluence(x, z, infl*2.0); // houses are close to other houses
|
friendlyTiles.addInfluence(x, z, 15.0,20,'linear'); // houses are close to other houses
|
||||||
else if (template.genericName() == "House") {
|
alreadyHasHouses = true;
|
||||||
|
} else if (template.genericName() == "House") {
|
||||||
friendlyTiles.addInfluence(x, z, Math.ceil(infl/2.0),infl); // houses are farther away from other buildings but houses
|
friendlyTiles.addInfluence(x, z, Math.ceil(infl/2.0),infl); // houses are farther away from other buildings but houses
|
||||||
friendlyTiles.addInfluence(x, z, Math.ceil(infl/4.0),-infl/2.0); // houses are farther away from other buildings but houses
|
friendlyTiles.addInfluence(x, z, Math.ceil(infl/4.0),-infl/2.0); // houses are farther away from other buildings but houses
|
||||||
} else if (ent.genericName() != "House") // houses have no influence on other buildings
|
} else if (ent.genericName() != "House") // houses have no influence on other buildings
|
||||||
@@ -108,14 +110,17 @@ BuildingConstructionPlan.prototype.findGoodPosition = function(gameState) {
|
|||||||
if (ent.hasClass("CivCentre") && template.genericName() != "House"){
|
if (ent.hasClass("CivCentre") && template.genericName() != "House"){
|
||||||
friendlyTiles.addInfluence(x, z, Math.floor(infl/8), Math.floor(-infl/2));
|
friendlyTiles.addInfluence(x, z, Math.floor(infl/8), Math.floor(-infl/2));
|
||||||
} else if (ent.hasClass("CivCentre")) {
|
} else if (ent.hasClass("CivCentre")) {
|
||||||
friendlyTiles.addInfluence(x, z, Math.floor(infl/3.0), infl + 1);
|
friendlyTiles.addInfluence(x, z, infl/3.0, infl + 1);
|
||||||
friendlyTiles.addInfluence(x, z, Math.floor(infl/4), -Math.floor(infl));
|
friendlyTiles.addInfluence(x, z, Math.ceil(infl/5.0), -(infl/2.0), 'linear');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//friendlyTiles.dumpIm("Building " +gameState.getTimeElapsed() + ".png", 200);
|
||||||
|
|
||||||
|
|
||||||
// Find target building's approximate obstruction radius, and expand by a bit to make sure we're not too close, this
|
// Find target building's approximate obstruction radius, and expand by a bit to make sure we're not too close, this
|
||||||
// allows room for units to walk between buildings.
|
// allows room for units to walk between buildings.
|
||||||
// note: not for houses and dropsites who ought to be closer to either each other or a resource.
|
// note: not for houses and dropsites who ought to be closer to either each other or a resource.
|
||||||
@@ -125,18 +130,26 @@ BuildingConstructionPlan.prototype.findGoodPosition = function(gameState) {
|
|||||||
else if (template.buildCategory() === "Dock")
|
else if (template.buildCategory() === "Dock")
|
||||||
var radius = 0;
|
var radius = 0;
|
||||||
else if (template.genericName() != "House" && !template.hasClass("DropsiteWood") && !template.hasClass("DropsiteStone") && !template.hasClass("DropsiteMetal"))
|
else if (template.genericName() != "House" && !template.hasClass("DropsiteWood") && !template.hasClass("DropsiteStone") && !template.hasClass("DropsiteMetal"))
|
||||||
var radius = Math.ceil(template.obstructionRadius() / cellSize) + 2;
|
var radius = Math.ceil(template.obstructionRadius() / cellSize) + 1;
|
||||||
else
|
else
|
||||||
var radius = Math.ceil(template.obstructionRadius() / cellSize);
|
var radius = Math.ceil(template.obstructionRadius() / cellSize);
|
||||||
|
|
||||||
|
// further contract cause walls
|
||||||
if (gameState.playerData.civ == "iber")
|
if (gameState.playerData.civ == "iber")
|
||||||
radius *= 0.95;
|
radius *= 0.95;
|
||||||
|
|
||||||
// Find the best non-obstructed tile
|
// Find the best non-obstructed
|
||||||
var bestTile = friendlyTiles.findBestTile(radius, obstructionMap);
|
if (template.genericName() == "House" && !alreadyHasHouses) {
|
||||||
var bestIdx = bestTile[0];
|
// try to get some space first
|
||||||
var bestVal = bestTile[1];
|
var bestTile = friendlyTiles.findBestTile(10, obstructionMap);
|
||||||
|
var bestIdx = bestTile[0];
|
||||||
|
var bestVal = bestTile[1];
|
||||||
|
}
|
||||||
|
if (bestVal === undefined || bestVal === -1) {
|
||||||
|
var bestTile = friendlyTiles.findBestTile(radius, obstructionMap);
|
||||||
|
var bestIdx = bestTile[0];
|
||||||
|
var bestVal = bestTile[1];
|
||||||
|
}
|
||||||
if (bestVal === -1){
|
if (bestVal === -1){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ UnitTrainingPlan.prototype.canExecute = function(gameState) {
|
|||||||
|
|
||||||
UnitTrainingPlan.prototype.execute = function(gameState) {
|
UnitTrainingPlan.prototype.execute = function(gameState) {
|
||||||
//warn("Executing UnitTrainingPlan " + uneval(this));
|
//warn("Executing UnitTrainingPlan " + uneval(this));
|
||||||
|
var self = this;
|
||||||
var trainers = gameState.findTrainers(this.type).toEntityArray();
|
var trainers = gameState.findTrainers(this.type).toEntityArray();
|
||||||
|
|
||||||
// Prefer training buildings with short queues
|
// Prefer training buildings with short queues
|
||||||
@@ -38,6 +38,16 @@ UnitTrainingPlan.prototype.execute = function(gameState) {
|
|||||||
// plans that have already been executed this turn)
|
// plans that have already been executed this turn)
|
||||||
if (trainers.length > 0){
|
if (trainers.length > 0){
|
||||||
trainers.sort(function(a, b) {
|
trainers.sort(function(a, b) {
|
||||||
|
|
||||||
|
if (self.metadata["plan"] !== undefined) {
|
||||||
|
var aa = a.trainingQueueTime();
|
||||||
|
var bb = b.trainingQueueTime();
|
||||||
|
if (a.hasClass("Civic"))
|
||||||
|
aa += 20;
|
||||||
|
if (b.hasClass("Civic"))
|
||||||
|
bb += 20;
|
||||||
|
return (a.trainingQueueTime() - b.trainingQueueTime());
|
||||||
|
}
|
||||||
return a.trainingQueueTime() - b.trainingQueueTime();
|
return a.trainingQueueTime() - b.trainingQueueTime();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ function QBotAI(settings) {
|
|||||||
this.firstTime = true;
|
this.firstTime = true;
|
||||||
|
|
||||||
this.savedEvents = [];
|
this.savedEvents = [];
|
||||||
|
|
||||||
|
this.defcon = 5;
|
||||||
|
this.defconChangeTime = -10000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
QBotAI.prototype = new BaseAI();
|
QBotAI.prototype = new BaseAI();
|
||||||
@@ -78,8 +81,9 @@ QBotAI.prototype.runInit = function(gameState){
|
|||||||
|
|
||||||
this.templateManager = new TemplateManager(gameState);
|
this.templateManager = new TemplateManager(gameState);
|
||||||
|
|
||||||
|
this.distanceFromMeMap = new Map(gameState);
|
||||||
|
this.distanceFromMeMap.drawDistance(gameState,myKeyEntities.toEntityArray());
|
||||||
|
//this.distanceFromMeMap.dumpIm("dumping.png", this.distanceFromMeMap.width*1.5);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -92,6 +96,12 @@ QBotAI.prototype.OnUpdate = function() {
|
|||||||
this.savedEvents = this.savedEvents.concat(this.events);
|
this.savedEvents = this.savedEvents.concat(this.events);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.turn == 0) {
|
||||||
|
debug ("Initializing");
|
||||||
|
var gameState = new GameState(this);
|
||||||
|
this.runInit(gameState);
|
||||||
|
}
|
||||||
|
|
||||||
// Run the update every n turns, offset depending on player ID to balance
|
// Run the update every n turns, offset depending on player ID to balance
|
||||||
// the load
|
// the load
|
||||||
if ((this.turn + this.player) % 10 == 0) {
|
if ((this.turn + this.player) % 10 == 0) {
|
||||||
@@ -105,13 +115,17 @@ QBotAI.prototype.OnUpdate = function() {
|
|||||||
return; // With no entities to control the AI cannot do anything
|
return; // With no entities to control the AI cannot do anything
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// defcon cooldown
|
||||||
|
if (this.defcon < 5 && gameState.timeSinceDefconChange() > 20000)
|
||||||
|
this.defcon++;
|
||||||
|
|
||||||
this.runInit(gameState);
|
this.runInit(gameState);
|
||||||
|
|
||||||
for (var i in this.modules){
|
for (var i in this.modules){
|
||||||
this.modules[i].update(gameState, this.queues, this.savedEvents);
|
this.modules[i].update(gameState, this.queues, this.savedEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateDynamicPriorities(gameState, this.queues);
|
//this.updateDynamicPriorities(gameState, this.queues);
|
||||||
|
|
||||||
this.queueManager.update(gameState);
|
this.queueManager.update(gameState);
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,18 @@ Queue.prototype.countTotalQueuedUnitsWithClass = function(classe){
|
|||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
};
|
};
|
||||||
|
Queue.prototype.countTotalQueuedUnitsWithMetadata = function(data,value){
|
||||||
|
var count = 0;
|
||||||
|
for (var i in this.queue){
|
||||||
|
if (this.queue[i].metadata[data] && this.queue[i].metadata[data] == value)
|
||||||
|
count += this.queue[i].number;
|
||||||
|
}
|
||||||
|
for (var i in this.outQueue){
|
||||||
|
if (this.outQueue[i].metadata[data] && this.outQueue[i].metadata[data] == value)
|
||||||
|
count += this.outQueue[i].number;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
Queue.prototype.totalLength = function(){
|
Queue.prototype.totalLength = function(){
|
||||||
return this.queue.length + this.outQueue.length;
|
return this.queue.length + this.outQueue.length;
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ var TemplateManager = function(gameState) {
|
|||||||
this.knownTemplatesList = [];
|
this.knownTemplatesList = [];
|
||||||
this.buildingTemplates = [];
|
this.buildingTemplates = [];
|
||||||
this.unitTemplates = [];
|
this.unitTemplates = [];
|
||||||
|
this.templateCounters = {};
|
||||||
|
this.templateCounteredBy = {};
|
||||||
|
|
||||||
// this will store templates that exist
|
// this will store templates that exist
|
||||||
this.AcknowledgeTemplates(gameState);
|
this.AcknowledgeTemplates(gameState);
|
||||||
this.getBuildableSubtemplates(gameState);
|
this.getBuildableSubtemplates(gameState);
|
||||||
@@ -17,6 +19,8 @@ var TemplateManager = function(gameState) {
|
|||||||
this.getTrainableSubtemplates(gameState);
|
this.getTrainableSubtemplates(gameState);
|
||||||
// should be enough in 100% of the cases.
|
// should be enough in 100% of the cases.
|
||||||
|
|
||||||
|
this.getTemplateCounters(gameState);
|
||||||
|
|
||||||
};
|
};
|
||||||
TemplateManager.prototype.AcknowledgeTemplates = function(gameState)
|
TemplateManager.prototype.AcknowledgeTemplates = function(gameState)
|
||||||
{
|
{
|
||||||
@@ -74,3 +78,38 @@ TemplateManager.prototype.getTrainableSubtemplates = function(gameState)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TemplateManager.prototype.getTemplateCounters = function(gameState)
|
||||||
|
{
|
||||||
|
for (i in this.unitTemplates)
|
||||||
|
{
|
||||||
|
var tp = gameState.getTemplate(this.unitTemplates[i]);
|
||||||
|
var tpname = this.unitTemplates[i];
|
||||||
|
this.templateCounters[tpname] = tp.getCounteredClasses();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// features auto-caching
|
||||||
|
TemplateManager.prototype.getCountersToClasses = function(gameState,classes,templateName)
|
||||||
|
{
|
||||||
|
if (templateName !== undefined && this.templateCounteredBy[templateName])
|
||||||
|
return this.templateCounteredBy[templateName];
|
||||||
|
|
||||||
|
var templates = [];
|
||||||
|
for (i in this.templateCounters) {
|
||||||
|
var okay = false;
|
||||||
|
for each (ticket in this.templateCounters[i]) {
|
||||||
|
var okaya = true;
|
||||||
|
for (a in ticket[0]) {
|
||||||
|
if (classes.indexOf(ticket[0][a]) === -1)
|
||||||
|
okaya = false;
|
||||||
|
}
|
||||||
|
if (okaya && templates.indexOf(i) === -1)
|
||||||
|
templates.push([i, ticket[1]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templates.sort (function (a,b) { return -a[1] + b[1]; });
|
||||||
|
|
||||||
|
if (templateName !== undefined)
|
||||||
|
this.templateCounteredBy[templateName] = templates;
|
||||||
|
return templates;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,10 @@ PathFinder.prototype.getPaths = function(start, end, mode){
|
|||||||
}
|
}
|
||||||
|
|
||||||
var paths = [];
|
var paths = [];
|
||||||
|
var i = 0;
|
||||||
while (true){
|
while (true){
|
||||||
|
i++;
|
||||||
|
//this.dumpIm("terrainanalysis_"+i+".png", 511);
|
||||||
this.makeGradient(s,e);
|
this.makeGradient(s,e);
|
||||||
var curPath = this.walkGradient(e, mode);
|
var curPath = this.walkGradient(e, mode);
|
||||||
|
|
||||||
@@ -134,7 +137,7 @@ PathFinder.prototype.getPaths = function(start, end, mode){
|
|||||||
if (paths.length > 0){
|
if (paths.length > 0){
|
||||||
return paths;
|
return paths;
|
||||||
}else{
|
}else{
|
||||||
return undefined;
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -292,7 +295,7 @@ function Accessibility(gameState, location){
|
|||||||
start = this.findClosestPassablePoint(this.gamePosToMapPos(location));
|
start = this.findClosestPassablePoint(this.gamePosToMapPos(location));
|
||||||
iterations += 1;
|
iterations += 1;
|
||||||
}
|
}
|
||||||
|
//this.dumpIm("accessibility.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
copyPrototype(Accessibility, TerrainAnalysis);
|
copyPrototype(Accessibility, TerrainAnalysis);
|
||||||
@@ -347,4 +350,362 @@ Accessibility.prototype.floodFill = function(start){
|
|||||||
newStack = [];
|
newStack = [];
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Some different take on the idea of Quantumstate... What I'll do is make a list of any terrain obstruction...
|
||||||
|
|
||||||
|
function aStarPath(gameState, onWater){
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.passabilityMap = gameState.getMap();
|
||||||
|
|
||||||
|
var obstructionMaskLand = gameState.getPassabilityClassMask("default");
|
||||||
|
var obstructionMaskWater = gameState.getPassabilityClassMask("ship");
|
||||||
|
|
||||||
|
var obstructionTiles = new Uint16Array(this.passabilityMap.data.length);
|
||||||
|
for (var i = 0; i < this.passabilityMap.data.length; ++i)
|
||||||
|
{
|
||||||
|
if (onWater) {
|
||||||
|
obstructionTiles[i] = (this.passabilityMap.data[i] & obstructionMaskWater) ? 0 : 255;
|
||||||
|
} else {
|
||||||
|
obstructionTiles[i] = (this.passabilityMap.data[i] & obstructionMaskLand) ? 0 : 255;
|
||||||
|
// We allow water, but we set it at a different index.
|
||||||
|
if (!(this.passabilityMap.data[i] & obstructionMaskWater) && obstructionTiles[i] === 0)
|
||||||
|
obstructionTiles[i] = 200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (onWater)
|
||||||
|
this.onWater = true;
|
||||||
|
else
|
||||||
|
this.onWater = false;
|
||||||
|
this.pathRequiresWater = this.onWater;
|
||||||
|
|
||||||
|
this.cellSize = gameState.cellSize;
|
||||||
|
|
||||||
|
this.Map(gameState, obstructionTiles);
|
||||||
|
this.passabilityMap = new Map(gameState, obstructionTiles, true);
|
||||||
|
|
||||||
|
var type = ["wood","stone", "metal"];
|
||||||
|
if (onWater) // trees can perhaps be put into water, I'd doubt so about the rest.
|
||||||
|
type = ["wood"];
|
||||||
|
for (o in type) {
|
||||||
|
var entities = gameState.getResourceSupplies(type[o]);
|
||||||
|
entities.forEach(function (supply) { //}){
|
||||||
|
var radius = Math.floor(supply.obstructionRadius() / self.cellSize);
|
||||||
|
if (type[o] === "wood") {
|
||||||
|
for (var xx = -1; xx <= 1;xx++)
|
||||||
|
for (var yy = -1; yy <= 1;yy++)
|
||||||
|
{
|
||||||
|
var x = self.gamePosToMapPos(supply.position())[0];
|
||||||
|
var y = self.gamePosToMapPos(supply.position())[1];
|
||||||
|
if (x+xx >= 0 && x+xx < self.width && y+yy >= 0 && y+yy < self.height)
|
||||||
|
{
|
||||||
|
self.passabilityMap.map[x+xx + (y+yy)*self.width] = 100; // tree
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.map[x + y*self.width] = 0;
|
||||||
|
self.passabilityMap.map[x + y*self.width] = 0;
|
||||||
|
} else {
|
||||||
|
for (var xx = -radius; xx <= radius;xx++)
|
||||||
|
for (var yy = -radius; yy <= radius;yy++)
|
||||||
|
{
|
||||||
|
var x = self.gamePosToMapPos(supply.position())[0];
|
||||||
|
var y = self.gamePosToMapPos(supply.position())[1];
|
||||||
|
if (x+xx >= 0 && x+xx < self.width && y+yy >= 0 && y+yy < self.height)
|
||||||
|
{
|
||||||
|
self.map[x+xx + (y+yy)*self.width] = 0;
|
||||||
|
self.passabilityMap.map[x+xx + (y+yy)*self.width] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//this.dumpIm("Non-Expanded Obstructions.png",255);
|
||||||
|
this.expandInfluences();
|
||||||
|
//this.dumpIm("Expanded Obstructions.png",10);
|
||||||
|
//this.BluringRadius = 10;
|
||||||
|
//this.Blur(this.BluringRadius); // first steop of bluring
|
||||||
|
}
|
||||||
|
copyPrototype(aStarPath, TerrainAnalysis);
|
||||||
|
|
||||||
|
aStarPath.prototype.getPath = function(start,end,optimized, minSampling, iterationLimit , gamestate)
|
||||||
|
{
|
||||||
|
if (minSampling === undefined)
|
||||||
|
this.minSampling = 2;
|
||||||
|
else this.minSampling = minSampling;
|
||||||
|
|
||||||
|
if (start[0] < 0 || this.gamePosToMapPos(start)[0] >= this.width || start[1] < 0 || this.gamePosToMapPos(start)[1] >= this.height)
|
||||||
|
return undefined;
|
||||||
|
|
||||||
|
var s = this.findClosestPassablePoint(this.gamePosToMapPos(start));
|
||||||
|
var e = this.findClosestPassablePoint(this.gamePosToMapPos(end));
|
||||||
|
|
||||||
|
if (!s || !e){
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
var w = this.width;
|
||||||
|
var h = this.height;
|
||||||
|
|
||||||
|
this.optimized = optimized;
|
||||||
|
if (this.minSampling < 1)
|
||||||
|
this.minSampling = 1;
|
||||||
|
|
||||||
|
if (gamestate !== undefined)
|
||||||
|
{
|
||||||
|
this.TotorMap = new Map(gamestate);
|
||||||
|
this.TotorMap.addInfluence(s[0],s[1],1,200,'constant');
|
||||||
|
this.TotorMap.addInfluence(e[0],e[1],1,200,'constant');
|
||||||
|
}
|
||||||
|
this.iterationLimit = 65500;
|
||||||
|
if (iterationLimit !== undefined)
|
||||||
|
this.iterationLimit = iterationLimit;
|
||||||
|
|
||||||
|
this.s = s[0] + w*s[1];
|
||||||
|
this.e = e[0] + w*e[1];
|
||||||
|
|
||||||
|
// I was using incredibly slow associative arrays before…
|
||||||
|
this.openList = [];
|
||||||
|
this.parentSquare = new Uint32Array(this.map.length);
|
||||||
|
this.isOpened = new Boolean(this.map.length);
|
||||||
|
this.fCostArray = new Uint32Array(this.map.length);
|
||||||
|
this.gCostArray = new Uint32Array(this.map.length);
|
||||||
|
this.currentSquare = this.s;
|
||||||
|
|
||||||
|
this.totalIteration = 0;
|
||||||
|
|
||||||
|
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.gCostArray[this.s] = 0;
|
||||||
|
this.parentSquare[this.s] = this.s;
|
||||||
|
//debug ("Initialized okay");
|
||||||
|
return this.continuePath(gamestate);
|
||||||
|
|
||||||
|
}
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
var w = this.width;
|
||||||
|
var h = this.height;
|
||||||
|
var positions = [[0,1], [0,-1], [1,0], [-1,0], [1,1], [-1,-1], [1,-1], [-1,1]];
|
||||||
|
var cost = [100,100,100,100,150,150,150,150];
|
||||||
|
var invCost = [1,1,1,1,0.8,0.8,0.8,0.8];
|
||||||
|
//creation of variables used in the loop
|
||||||
|
var found = false;
|
||||||
|
var nouveau = false;
|
||||||
|
var shortcut = false;
|
||||||
|
var Sampling = this.minSampling;
|
||||||
|
var closeToEnd = false;
|
||||||
|
var infinity = Math.min();
|
||||||
|
var currentDist = infinity;
|
||||||
|
var e = this.e;
|
||||||
|
var s = this.s;
|
||||||
|
|
||||||
|
var iteration = 0;
|
||||||
|
// on to A*
|
||||||
|
while (found === false && this.openList.length !== 0 && iteration < this.iterationLimit){
|
||||||
|
currentDist = infinity;
|
||||||
|
|
||||||
|
if (shortcut === true) {
|
||||||
|
this.currentSquare = this.openList.shift();
|
||||||
|
} else {
|
||||||
|
for (i in this.openList)
|
||||||
|
{
|
||||||
|
var sum = this.fCostArray[this.openList[i]] + this.gCostArray[this.openList[i]];
|
||||||
|
if (sum < currentDist)
|
||||||
|
{
|
||||||
|
this.currentSquare = this.openList[i];
|
||||||
|
currentDist = sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.openList.splice(this.openList.indexOf(this.currentSquare),1);
|
||||||
|
}
|
||||||
|
if (!this.onWater && this.passabilityMap.map[this.currentSquare] === 200) {
|
||||||
|
this.onWater = true;
|
||||||
|
this.pathRequiresWater = true;
|
||||||
|
} else if (this.onWater && this.passabilityMap.map[this.currentSquare] !== 200)
|
||||||
|
this.onWater = false;
|
||||||
|
|
||||||
|
shortcut = false;
|
||||||
|
this.isOpened[this.currentSquare] = false;
|
||||||
|
|
||||||
|
// optimizaiton: can make huge jumps if I know there's nothing in the way
|
||||||
|
Sampling = this.minSampling;
|
||||||
|
if (this.optimized === true) {
|
||||||
|
Sampling = Math.floor( (+this.map[this.currentSquare]-this.minSampling)/Sampling )*Sampling;
|
||||||
|
if (Sampling < this.minSampling)
|
||||||
|
Sampling = this.minSampling;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
var diagSampling = Math.floor(Sampling / 1.5);
|
||||||
|
if (diagSampling < this.minSampling)
|
||||||
|
diagSampling = this.minSampling;
|
||||||
|
*/
|
||||||
|
var target = [this.e%w, Math.floor(this.e/w)];
|
||||||
|
closeToEnd = false;
|
||||||
|
if (SquareVectorDistance([this.currentSquare%w, Math.floor(this.currentSquare/w)], target) <= Sampling*Sampling)
|
||||||
|
{
|
||||||
|
closeToEnd = true;
|
||||||
|
Sampling = 1;
|
||||||
|
}
|
||||||
|
if (gamestate !== undefined)
|
||||||
|
this.TotorMap.addInfluence(this.currentSquare % w, Math.floor(this.currentSquare / w),1,40,'constant');
|
||||||
|
|
||||||
|
for (i in positions)
|
||||||
|
{
|
||||||
|
//var hereSampling = cost[i] == 1 ? Sampling : diagSampling;
|
||||||
|
var index = 0 + this.currentSquare +positions[i][0]*Sampling +w*Sampling*positions[i][1];
|
||||||
|
if (this.map[index] >= Sampling)
|
||||||
|
{
|
||||||
|
if(this.isOpened[index] === undefined)
|
||||||
|
{
|
||||||
|
this.parentSquare[index] = this.currentSquare;
|
||||||
|
|
||||||
|
this.fCostArray[index] = SquareVectorDistance([index%w, Math.floor(index/w)], target);// * cost[i];
|
||||||
|
this.gCostArray[index] = this.gCostArray[this.currentSquare] + cost[i] * Sampling - this.map[index];
|
||||||
|
|
||||||
|
if (!this.onWater && this.passabilityMap.map[index] === 200) {
|
||||||
|
this.gCostArray[index] += this.fCostArray[index]*2;
|
||||||
|
} else if (this.onWater && this.passabilityMap.map[index] !== 200) {
|
||||||
|
this.gCostArray[index] += this.fCostArray[index]*2;
|
||||||
|
} else if (!this.onWater && this.passabilityMap.map[index] === 100) {
|
||||||
|
this.gCostArray[index] += 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.openList[0] !== undefined && this.fCostArray[this.openList[0]] + this.gCostArray[this.openList[0]] > this.fCostArray[index] + this.gCostArray[index])
|
||||||
|
{
|
||||||
|
this.openList.unshift(index);
|
||||||
|
shortcut = true;
|
||||||
|
} else {
|
||||||
|
this.openList.push(index);
|
||||||
|
}
|
||||||
|
this.isOpened[index] = true;
|
||||||
|
if (closeToEnd === true && (index === e || index - 1 === e || index + 1 === e || index - w === e || index + w === e
|
||||||
|
|| index + 1 + w === e || index + 1 - w === e || index - 1 + w === e|| index - 1 - w === e)) {
|
||||||
|
this.parentSquare[this.e] = this.currentSquare;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var addCost = 0;
|
||||||
|
if (!this.onWater && this.passabilityMap.map[index] === 200) {
|
||||||
|
addCost = this.fCostArray[index]*2;
|
||||||
|
} else if (this.onWater && this.passabilityMap.map[index] !== 200) {
|
||||||
|
addCost = this.fCostArray[index]*2;
|
||||||
|
} else if (!this.onWater && this.passabilityMap.map[index] === 100) {
|
||||||
|
addCost += 100;
|
||||||
|
}
|
||||||
|
addCost -= this.map[index];
|
||||||
|
// already on the Open or closed list
|
||||||
|
if (this.gCostArray[index] > cost[i] * Sampling + addCost + this.gCostArray[this.currentSquare])
|
||||||
|
{
|
||||||
|
this.parentSquare[index] = this.currentSquare;
|
||||||
|
this.gCostArray[index] = cost[i] * Sampling + addCost + this.gCostArray[this.currentSquare];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iteration++;
|
||||||
|
}
|
||||||
|
this.totalIteration += iteration;
|
||||||
|
if (iteration === this.iterationLimit && found === false && this.openList.length !== 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
// we've got to assume that we stopped because we reached the upper limit of iterations
|
||||||
|
return "toBeContinued";
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug (this.totalIteration);
|
||||||
|
var paths = [];
|
||||||
|
if (found) {
|
||||||
|
this.currentSquare = e;
|
||||||
|
var lastPos = [0,0];
|
||||||
|
while (this.parentSquare[this.currentSquare] !== s)
|
||||||
|
{
|
||||||
|
this.currentSquare = this.parentSquare[this.currentSquare];
|
||||||
|
if (gamestate !== undefined)
|
||||||
|
this.TotorMap.addInfluence(this.currentSquare % w, Math.floor(this.currentSquare / w),1,50,'constant');
|
||||||
|
if (SquareVectorDistance(lastPos,[this.currentSquare % w, Math.floor(this.currentSquare / w)]) > 300)
|
||||||
|
{
|
||||||
|
lastPos = [ (this.currentSquare % w) * this.cellSize, Math.floor(this.currentSquare / w) * this.cellSize];
|
||||||
|
paths.push(lastPos);
|
||||||
|
if (gamestate !== undefined)
|
||||||
|
this.TotorMap.addInfluence(this.currentSquare % w, Math.floor(this.currentSquare / w),1,100,'constant');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gamestate !== undefined)
|
||||||
|
this.TotorMap.dumpIm("Path From " +s +" to " +e +".png",255);
|
||||||
|
|
||||||
|
if (paths.length > 0) {
|
||||||
|
return [paths, this.pathRequiresWater];
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make each cell's 8-bit value at least one greater than each of its
|
||||||
|
* neighbours' values. (If the grid is initialised with 0s and things high enough (> 100 on most maps), the
|
||||||
|
* result of each cell is its Manhattan distance to the nearest 0.)
|
||||||
|
*/
|
||||||
|
aStarPath.prototype.expandInfluences = function() {
|
||||||
|
var w = this.width;
|
||||||
|
var h = this.height;
|
||||||
|
var grid = this.map;
|
||||||
|
for ( var y = 0; y < h; ++y) {
|
||||||
|
var min = 8;
|
||||||
|
for ( var x = 0; x < w; ++x) {
|
||||||
|
var g = grid[x + y * w];
|
||||||
|
if (g > min)
|
||||||
|
grid[x + y * w] = min;
|
||||||
|
else if (g < min)
|
||||||
|
min = g;
|
||||||
|
++min;
|
||||||
|
if (min > 8)
|
||||||
|
min = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( var x = w - 2; x >= 0; --x) {
|
||||||
|
var g = grid[x + y * w];
|
||||||
|
if (g > min)
|
||||||
|
grid[x + y * w] = min;
|
||||||
|
else if (g < min)
|
||||||
|
min = g;
|
||||||
|
++min;
|
||||||
|
if (min > 8)
|
||||||
|
min = 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( var x = 0; x < w; ++x) {
|
||||||
|
var min = 8;
|
||||||
|
for ( var y = 0; y < h; ++y) {
|
||||||
|
var g = grid[x + y * w];
|
||||||
|
if (g > min)
|
||||||
|
grid[x + y * w] = min;
|
||||||
|
else if (g < min)
|
||||||
|
min = g;
|
||||||
|
++min;
|
||||||
|
if (min > 8)
|
||||||
|
min = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( var y = h - 2; y >= 0; --y) {
|
||||||
|
var g = grid[x + y * w];
|
||||||
|
if (g > min)
|
||||||
|
grid[x + y * w] = min;
|
||||||
|
else if (g < min)
|
||||||
|
min = g;
|
||||||
|
++min;
|
||||||
|
if (min > 8)
|
||||||
|
min = 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ Worker.prototype.update = function(gameState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (subrole === "gatherer"){
|
if (subrole === "gatherer"){
|
||||||
if (!(this.ent.unitAIState().split(".")[1] === "GATHER" && this.ent.unitAIOrderData().type
|
if (!(this.ent.unitAIState().split(".")[1] === "GATHER" && this.ent.unitAIOrderData()[0].type
|
||||||
&& this.getResourceType(this.ent.unitAIOrderData().type) === this.ent.getMetadata("gather-type"))
|
&& this.getResourceType(this.ent.unitAIOrderData()[0].type) === this.ent.getMetadata("gather-type"))
|
||||||
&& !(this.ent.unitAIState().split(".")[1] === "RETURNRESOURCE")){
|
&& !(this.ent.unitAIState().split(".")[1] === "RETURNRESOURCE")){
|
||||||
// TODO: handle combat for hunting animals
|
// TODO: handle combat for hunting animals
|
||||||
if (!this.ent.resourceCarrying() || this.ent.resourceCarrying().length === 0 ||
|
if (!this.ent.resourceCarrying() || this.ent.resourceCarrying().length === 0 ||
|
||||||
@@ -42,9 +42,11 @@ Worker.prototype.update = function(gameState) {
|
|||||||
if (this.gatheringFrom){
|
if (this.gatheringFrom){
|
||||||
var ent = gameState.getEntityById(this.gatheringFrom);
|
var ent = gameState.getEntityById(this.gatheringFrom);
|
||||||
if (ent && ent.resourceSupplyAmount() == ent.resourceSupplyMax()){
|
if (ent && ent.resourceSupplyAmount() == ent.resourceSupplyMax()){
|
||||||
if (this.approachCount > 0){
|
// if someone gathers from it, it's only that the pathfinder sucks.
|
||||||
|
if (this.approachCount > 0 && ent.getMetadata("gatherer-count") <= 2){
|
||||||
ent.setMetadata("inaccessible", true);
|
ent.setMetadata("inaccessible", true);
|
||||||
this.ent.setMetadata("subrole", "idle");
|
this.ent.setMetadata("subrole", "idle");
|
||||||
|
this.ent.flee(ent);
|
||||||
}
|
}
|
||||||
this.approachCount++;
|
this.approachCount++;
|
||||||
}else{
|
}else{
|
||||||
@@ -55,7 +57,7 @@ Worker.prototype.update = function(gameState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else if(subrole === "builder"){
|
} else if(subrole === "builder") {
|
||||||
if (this.ent.unitAIState().split(".")[1] !== "REPAIR"){
|
if (this.ent.unitAIState().split(".")[1] !== "REPAIR"){
|
||||||
var target = this.ent.getMetadata("target-foundation");
|
var target = this.ent.getMetadata("target-foundation");
|
||||||
this.ent.repair(target);
|
this.ent.repair(target);
|
||||||
@@ -72,44 +74,89 @@ Worker.prototype.update = function(gameState) {
|
|||||||
Worker.prototype.updateGathererCounts = function(gameState, dead){
|
Worker.prototype.updateGathererCounts = function(gameState, dead){
|
||||||
// update gatherer counts for the resources
|
// update gatherer counts for the resources
|
||||||
if (this.ent.unitAIState().split(".")[2] === "GATHERING" && !dead){
|
if (this.ent.unitAIState().split(".")[2] === "GATHERING" && !dead){
|
||||||
if (this.gatheringFrom !== this.ent.unitAIOrderData().target){
|
if (this.gatheringFrom !== this.ent.unitAIOrderData()[0].target){
|
||||||
if (this.gatheringFrom){
|
if (this.gatheringFrom){
|
||||||
var ent = gameState.getEntityById(this.gatheringFrom);
|
var ent = gameState.getEntityById(this.gatheringFrom);
|
||||||
if (ent){
|
if (ent && ent.resourceSupplyType()){
|
||||||
ent.setMetadata("gatherer-count", ent.getMetadata("gatherer-count") - 1);
|
ent.setMetadata("gatherer-count", ent.getMetadata("gatherer-count") - 1);
|
||||||
this.markFull(ent);
|
this.markFull(gameState,ent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.gatheringFrom = this.ent.unitAIOrderData().target;
|
this.gatheringFrom = this.ent.unitAIOrderData()[0].target;
|
||||||
if (this.gatheringFrom){
|
if (this.gatheringFrom){
|
||||||
var ent = gameState.getEntityById(this.gatheringFrom);
|
var ent = gameState.getEntityById(this.gatheringFrom);
|
||||||
if (ent){
|
if (ent && ent.resourceSupplyType()){
|
||||||
ent.setMetadata("gatherer-count", (ent.getMetadata("gatherer-count") || 0) + 1);
|
ent.setMetadata("gatherer-count", (ent.getMetadata("gatherer-count") || 0) + 1);
|
||||||
this.markFull(ent);
|
this.markFull(gameState,ent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
} else if (this.ent.unitAIState().split(".")[2] === "RETURNRESOURCE" && !dead) {
|
||||||
|
// We remove us from the counting is we have no following order or its not "return to collected resource".
|
||||||
|
if (this.ent.unitAIOrderData().length === 1) {
|
||||||
|
var ent = gameState.getEntityById(this.gatheringFrom);
|
||||||
|
if (ent && ent.resourceSupplyType()){
|
||||||
|
ent.setMetadata("gatherer-count", ent.getMetadata("gatherer-count") - 1);
|
||||||
|
this.markFull(gameState,ent);
|
||||||
|
}
|
||||||
|
this.gatheringFrom = undefined;
|
||||||
|
}
|
||||||
|
if (!this.ent.unitAIOrderData()[1].target || this.gatheringFrom !== this.ent.unitAIOrderData()[1].target){
|
||||||
|
if (this.gatheringFrom){
|
||||||
|
var ent = gameState.getEntityById(this.gatheringFrom);
|
||||||
|
if (ent && ent.resourceSupplyType()){
|
||||||
|
ent.setMetadata("gatherer-count", ent.getMetadata("gatherer-count") - 1);
|
||||||
|
this.markFull(gameState,ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.gatheringFrom = undefined;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (this.gatheringFrom){
|
if (this.gatheringFrom){
|
||||||
var ent = gameState.getEntityById(this.gatheringFrom);
|
var ent = gameState.getEntityById(this.gatheringFrom);
|
||||||
if (ent){
|
if (ent && ent.resourceSupplyType()){
|
||||||
ent.setMetadata("gatherer-count", ent.getMetadata("gatherer-count") - 1);
|
ent.setMetadata("gatherer-count", ent.getMetadata("gatherer-count") - 1);
|
||||||
this.markFull(ent);
|
this.markFull(gameState,ent);
|
||||||
}
|
}
|
||||||
this.gatheringFrom = undefined;
|
this.gatheringFrom = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Worker.prototype.markFull = function(ent){
|
Worker.prototype.markFull = function(gameState,ent){
|
||||||
var maxCounts = {"food": 15, "wood": 5, "metal": 15, "stone": 15, "treasure": 1};
|
var maxCounts = {"food": 15, "wood": 6, "metal": 15, "stone": 15, "treasure": 1};
|
||||||
if (ent.resourceSupplyType() && ent.getMetadata("gatherer-count") >= maxCounts[ent.resourceSupplyType().generic]){
|
var resource = ent.resourceSupplyType().generic;
|
||||||
|
if (ent.resourceSupplyType() && ent.getMetadata("gatherer-count") >= maxCounts[resource]){
|
||||||
if (!ent.getMetadata("full")){
|
if (!ent.getMetadata("full")){
|
||||||
ent.setMetadata("full", true);
|
ent.setMetadata("full", true);
|
||||||
|
// update the dropsite
|
||||||
|
var dropsite = gameState.getEntityById(ent.getMetadata("linked-dropsite"));
|
||||||
|
if (dropsite == undefined || dropsite.getMetadata("linked-resources-" + resource) === undefined)
|
||||||
|
return;
|
||||||
|
if (ent.getMetadata("linked-dropsite-nearby") == true) {
|
||||||
|
dropsite.setMetadata("resource-quantity-" + resource, +dropsite.getMetadata("resource-quantity-" + resource) - (+ent.getMetadata("dp-update-value")));
|
||||||
|
dropsite.getMetadata("linked-resources-" + resource).updateEnt(ent);
|
||||||
|
dropsite.getMetadata("nearby-resources-" + resource).updateEnt(ent);
|
||||||
|
} else {
|
||||||
|
dropsite.setMetadata("resource-quantity-far-" + resource, +dropsite.getMetadata("resource-quantity-" + resource) - (+ent.getMetadata("dp-update-value")));
|
||||||
|
dropsite.getMetadata("linked-resources-" + resource).updateEnt(ent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if (ent.getMetadata("full")){
|
if (ent.getMetadata("full")){
|
||||||
ent.setMetadata("full", false);
|
ent.setMetadata("full", false);
|
||||||
|
// update the dropsite
|
||||||
|
var dropsite = gameState.getEntityById(ent.getMetadata("linked-dropsite"));
|
||||||
|
if (dropsite == undefined || dropsite.getMetadata("linked-resources-" + resource) === undefined)
|
||||||
|
return;
|
||||||
|
if (ent.getMetadata("linked-dropsite-nearby") == true) {
|
||||||
|
dropsite.setMetadata("resource-quantity-" + resource, +dropsite.getMetadata("resource-quantity-" + resource) + ent.resourceSupplyAmount());
|
||||||
|
dropsite.getMetadata("linked-resources-" + resource).updateEnt(ent);
|
||||||
|
dropsite.getMetadata("nearby-resources-" + resource).updateEnt(ent);
|
||||||
|
} else {
|
||||||
|
dropsite.setMetadata("resource-quantity-far-" + resource, +dropsite.getMetadata("resource-quantity-" + resource) + ent.resourceSupplyAmount());
|
||||||
|
dropsite.getMetadata("linked-resources-" + resource).updateEnt(ent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -123,23 +170,45 @@ Worker.prototype.startGathering = function(gameState){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: this is not necessarily optimal.
|
||||||
|
|
||||||
// find closest dropsite which has nearby resources of the correct type
|
// find closest dropsite which has nearby resources of the correct type
|
||||||
var minDropsiteDist = Math.min(); // set to infinity initially
|
var minDropsiteDist = Math.min(); // set to infinity initially
|
||||||
var nearestResources = undefined;
|
var nearestResources = undefined;
|
||||||
var nearestDropsite = undefined;
|
var nearestDropsite = undefined;
|
||||||
|
|
||||||
gameState.updatingCollection("active-dropsite-" + resource, Filters.byMetadata("active-dropsite-" + resource, true),
|
// first, look for nearby resources.
|
||||||
gameState.getOwnDropsites(resource)).forEach(function (dropsite){
|
var number = 0;
|
||||||
if (dropsite.position()){
|
gameState.getOwnDropsites(resource).forEach(function (dropsite){ if (dropsite.getMetadata("linked-resources-" +resource) !== undefined
|
||||||
|
&& dropsite.getMetadata("linked-resources-" +resource).length > 3) { number++; } });
|
||||||
|
|
||||||
|
gameState.getOwnDropsites(resource).forEach(function (dropsite){ //}){
|
||||||
|
if (dropsite.getMetadata("resource-quantity-" +resource) == undefined)
|
||||||
|
return;
|
||||||
|
if (dropsite.position() && (dropsite.getMetadata("resource-quantity-" +resource) > 10 || number <= 1) ) {
|
||||||
var dist = SquareVectorDistance(ent.position(), dropsite.position());
|
var dist = SquareVectorDistance(ent.position(), dropsite.position());
|
||||||
if (dist < minDropsiteDist){
|
if (dist < minDropsiteDist){
|
||||||
minDropsiteDist = dist;
|
minDropsiteDist = dist;
|
||||||
nearestResources = dropsite.getMetadata("nearby-resources-" + resource);
|
nearestResources = dropsite.getMetadata("linked-resources-" + resource);
|
||||||
nearestDropsite = dropsite;
|
nearestDropsite = dropsite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// none, check even low level of resources and far away
|
||||||
|
if (!nearestResources || nearestResources.length === 0){
|
||||||
|
gameState.getOwnDropsites(resource).forEach(function (dropsite){ //}){
|
||||||
|
if (dropsite.position() &&
|
||||||
|
(dropsite.getMetadata("resource-quantity-" +resource)+dropsite.getMetadata("resource-quantity-far-" +resource) > 10 || number <= 1)) {
|
||||||
|
var dist = SquareVectorDistance(ent.position(), dropsite.position());
|
||||||
|
if (dist < minDropsiteDist){
|
||||||
|
minDropsiteDist = dist;
|
||||||
|
nearestResources = dropsite.getMetadata("linked-resources-" + resource);
|
||||||
|
nearestDropsite = dropsite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// else, just get the closest to our closest dropsite.
|
||||||
if (!nearestResources || nearestResources.length === 0){
|
if (!nearestResources || nearestResources.length === 0){
|
||||||
nearestResources = gameState.getResourceSupplies(resource);
|
nearestResources = gameState.getResourceSupplies(resource);
|
||||||
gameState.getOwnDropsites(resource).forEach(function (dropsite){
|
gameState.getOwnDropsites(resource).forEach(function (dropsite){
|
||||||
@@ -161,28 +230,32 @@ Worker.prototype.startGathering = function(gameState){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!nearestDropsite) {
|
||||||
|
debug ("No dropsite for " +resource);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var supplies = [];
|
var supplies = [];
|
||||||
var nearestSupplyDist = Math.min();
|
var nearestSupplyDist = Math.min();
|
||||||
var nearestSupply = undefined;
|
var nearestSupply = undefined;
|
||||||
|
|
||||||
nearestResources.forEach(function(supply) {
|
nearestResources.forEach(function(supply) { //}){
|
||||||
// TODO: handle enemy territories
|
// TODO: handle enemy territories
|
||||||
|
|
||||||
if (!supply.position()){
|
if (!supply.position()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (supply.getMetadata("full") == true) {
|
// measure the distance to the resource (largely irrelevant)
|
||||||
return;
|
var dist = SquareVectorDistance(supply.position(), ent.position());
|
||||||
}
|
|
||||||
// measure the distance to the resource
|
|
||||||
var dist = VectorDistance(supply.position(), ent.position());
|
|
||||||
// Add on a factor for the nearest dropsite if one exists
|
// Add on a factor for the nearest dropsite if one exists
|
||||||
if (nearestDropsite){
|
if (supply.getMetadata("linked-dropsite") !== undefined){
|
||||||
dist += 5 * VectorDistance(supply.position(), nearestDropsite.position());
|
dist += 4*SquareVectorDistance(supply.position(), nearestDropsite.position());
|
||||||
|
dist /= 5.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go for treasure as a priority
|
// Go for treasure as a priority
|
||||||
if (dist < 1000 && supply.resourceSupplyType().generic == "treasure"){
|
if (dist < 40000 && supply.resourceSupplyType().generic == "treasure"){
|
||||||
dist /= 1000;
|
dist /= 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,8 +270,11 @@ Worker.prototype.startGathering = function(gameState){
|
|||||||
|
|
||||||
// if the resource is far away, try to build a farm instead.
|
// if the resource is far away, try to build a farm instead.
|
||||||
var tried = false;
|
var tried = false;
|
||||||
if (resource === "food" && SquareVectorDistance(pos,this.ent.position()) > 50000)
|
if (resource === "food" && SquareVectorDistance(pos,this.ent.position()) > 22500)
|
||||||
tried = this.buildAnyField(gameState);
|
tried = this.buildAnyField(gameState);
|
||||||
|
if (!tried && SquareVectorDistance(pos,this.ent.position()) > 62500) {
|
||||||
|
return; // wait. a farm should appear.
|
||||||
|
}
|
||||||
if (!tried) {
|
if (!tried) {
|
||||||
var territoryOwner = gameState.getTerritoryMap().getOwner(pos);
|
var territoryOwner = gameState.getTerritoryMap().getOwner(pos);
|
||||||
if (!gameState.ai.accessibility.isAccessible(pos) ||
|
if (!gameState.ai.accessibility.isAccessible(pos) ||
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ Worker.prototype.update = function(gameState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (subrole === "gatherer"){
|
if (subrole === "gatherer"){
|
||||||
if (!(this.ent.unitAIState().split(".")[1] === "GATHER" && this.ent.unitAIOrderData().type
|
if (!(this.ent.unitAIState().split(".")[1] === "GATHER" && this.ent.unitAIOrderData()[0].type
|
||||||
&& this.getResourceType(this.ent.unitAIOrderData().type) === this.ent.getMetadata("gather-type"))
|
&& this.getResourceType(this.ent.unitAIOrderData()[0].type) === this.ent.getMetadata("gather-type"))
|
||||||
&& !(this.ent.unitAIState().split(".")[1] === "RETURNRESOURCE")){
|
&& !(this.ent.unitAIState().split(".")[1] === "RETURNRESOURCE")){
|
||||||
// TODO: handle combat for hunting animals
|
// TODO: handle combat for hunting animals
|
||||||
if (!this.ent.resourceCarrying() || this.ent.resourceCarrying().length === 0 ||
|
if (!this.ent.resourceCarrying() || this.ent.resourceCarrying().length === 0 ||
|
||||||
@@ -72,7 +72,7 @@ Worker.prototype.update = function(gameState) {
|
|||||||
Worker.prototype.updateGathererCounts = function(gameState, dead){
|
Worker.prototype.updateGathererCounts = function(gameState, dead){
|
||||||
// update gatherer counts for the resources
|
// update gatherer counts for the resources
|
||||||
if (this.ent.unitAIState().split(".")[2] === "GATHERING" && !dead){
|
if (this.ent.unitAIState().split(".")[2] === "GATHERING" && !dead){
|
||||||
if (this.gatheringFrom !== this.ent.unitAIOrderData().target){
|
if (this.gatheringFrom !== this.ent.unitAIOrderData()[0].target){
|
||||||
if (this.gatheringFrom){
|
if (this.gatheringFrom){
|
||||||
var ent = gameState.getEntityById(this.gatheringFrom);
|
var ent = gameState.getEntityById(this.gatheringFrom);
|
||||||
if (ent){
|
if (ent){
|
||||||
@@ -80,7 +80,7 @@ Worker.prototype.updateGathererCounts = function(gameState, dead){
|
|||||||
this.markFull(ent);
|
this.markFull(ent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.gatheringFrom = this.ent.unitAIOrderData().target;
|
this.gatheringFrom = this.ent.unitAIOrderData()[0].target;
|
||||||
if (this.gatheringFrom){
|
if (this.gatheringFrom){
|
||||||
var ent = gameState.getEntityById(this.gatheringFrom);
|
var ent = gameState.getEntityById(this.gatheringFrom);
|
||||||
if (ent){
|
if (ent){
|
||||||
|
|||||||
@@ -2171,10 +2171,12 @@ UnitAI.prototype.AddOrders = function(orders)
|
|||||||
|
|
||||||
UnitAI.prototype.GetOrderData = function()
|
UnitAI.prototype.GetOrderData = function()
|
||||||
{
|
{
|
||||||
if (this.order && this.order.data)
|
var orders = [];
|
||||||
return deepcopy(this.order.data);
|
for (i in this.orderQueue) {
|
||||||
else
|
if (this.orderQueue[i].data)
|
||||||
return undefined;
|
orders.push(deepcopy(this.orderQueue[i].data));
|
||||||
|
}
|
||||||
|
return orders;
|
||||||
};
|
};
|
||||||
|
|
||||||
UnitAI.prototype.TimerHandler = function(data, lateness)
|
UnitAI.prototype.TimerHandler = function(data, lateness)
|
||||||
|
|||||||
Reference in New Issue
Block a user