mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
Various improvements to AI's early game. Simplify some code and improve on other. Add a few different strategies (Rush/normal/boom, quite basic for now, refs #2344).
Aegis should mostly respect tech limitations so I'll ref #1964. I'm going to go with #2364 is fixed, the AI should be more efficient in early-game, and late-game is a known problem. Fixes #2274 and fixes #2379. Refs #2372 as it should fix several of those warnings for AIs. Fixes #2256 with a new bartering system, in parts taken from mimo's patch. This was SVN commit r14582.
This commit is contained in:
parent
e98478b68c
commit
d23b7deb98
20 changed files with 603 additions and 618 deletions
|
|
@ -8,24 +8,12 @@ m.playerGlobals = [];
|
|||
m.AegisBot = function AegisBot(settings) {
|
||||
API3.BaseAI.call(this, settings);
|
||||
|
||||
this.Config = new m.Config();
|
||||
|
||||
this.Config.updateDifficulty(settings.difficulty);
|
||||
|
||||
this.turn = 0;
|
||||
|
||||
this.playedTurn = 0;
|
||||
|
||||
this.priorities = this.Config.priorities;
|
||||
|
||||
// this.queues can only be modified by the queue manager or things will go awry.
|
||||
this.queues = {};
|
||||
for (var i in this.priorities)
|
||||
this.queues[i] = new m.Queue();
|
||||
|
||||
this.queueManager = new m.QueueManager(this.Config, this.queues, this.priorities);
|
||||
|
||||
this.HQ = new m.HQ(this.Config);
|
||||
this.Config = new m.Config();
|
||||
this.Config.updateDifficulty(settings.difficulty);
|
||||
this.Config.personality = settings.personality;
|
||||
|
||||
this.firstTime = true;
|
||||
|
||||
|
|
@ -38,6 +26,19 @@ m.AegisBot = function AegisBot(settings) {
|
|||
m.AegisBot.prototype = new API3.BaseAI();
|
||||
|
||||
m.AegisBot.prototype.CustomInit = function(gameState, sharedScript) {
|
||||
|
||||
this.initPersonality(this.gameState);
|
||||
|
||||
this.priorities = this.Config.priorities;
|
||||
// this.queues can only be modified by the queue manager or things will go awry.
|
||||
this.queues = {};
|
||||
for (var i in this.priorities)
|
||||
this.queues[i] = new m.Queue();
|
||||
|
||||
this.queueManager = new m.QueueManager(this.Config, this.queues, this.priorities);
|
||||
|
||||
this.HQ = new m.HQ(this.Config);
|
||||
gameState.Config = this.Config;
|
||||
|
||||
m.playerGlobals[PlayerID] = {};
|
||||
m.playerGlobals[PlayerID].uniqueIDBOPlans = 0; // training/building/research plans
|
||||
|
|
@ -85,8 +86,6 @@ m.AegisBot.prototype.CustomInit = function(gameState, sharedScript) {
|
|||
}
|
||||
|
||||
this.pathInfo.angle += Math.PI/3.0;
|
||||
|
||||
this.chooseRandomStrategy();
|
||||
}
|
||||
|
||||
m.AegisBot.prototype.OnUpdate = function(sharedScript) {
|
||||
|
|
@ -142,19 +141,30 @@ m.AegisBot.prototype.OnUpdate = function(sharedScript) {
|
|||
|
||||
var townPhase = this.gameState.townPhase();
|
||||
var cityPhase = this.gameState.cityPhase();
|
||||
|
||||
// try going up phases.
|
||||
// TODO: softcode this.
|
||||
if (this.gameState.canResearch(townPhase,true) && this.gameState.getTimeElapsed() > (this.Config.Economy.townPhase*1000) && this.gameState.getPopulation() > 40
|
||||
&& this.gameState.findResearchers(townPhase,true).length != 0 && this.queues.majorTech.length() === 0
|
||||
&& this.gameState.getOwnStructures().filter(API3.Filters.byClass("Village")).length > 5)
|
||||
// TODO: softcode this more
|
||||
if (this.gameState.canResearch(townPhase,true) && this.gameState.getPopulation() >= this.Config.Economy.villagePopCap - 10
|
||||
&& this.gameState.findResearchers(townPhase,true).length != 0 && this.queues.majorTech.length() === 0)
|
||||
{
|
||||
this.queueManager.pauseQueue("villager", true);
|
||||
this.queueManager.pauseQueue("citizenSoldier", true);
|
||||
this.queueManager.pauseQueue("house", true);
|
||||
this.queues.majorTech.addItem(new m.ResearchPlan(this.gameState, townPhase,0,-1,true)); // we rush the town phase.
|
||||
m.debug ("Trying to reach town phase");
|
||||
}
|
||||
else if (this.gameState.canResearch(cityPhase,true) && this.gameState.getTimeElapsed() > (this.Config.Economy.cityPhase*1000)
|
||||
var plan = new m.ResearchPlan(this.gameState, townPhase, true);
|
||||
plan.lastIsGo = false;
|
||||
plan.onStart = function (gameState) { gameState.ai.HQ.econState = "growth"; gameState.ai.HQ.OnTownPhase(gameState) };
|
||||
plan.isGo = function (gameState) {
|
||||
var ret = gameState.getPopulation() >= gameState.Config.Economy.villagePopCap
|
||||
if (ret && !this.lastIsGo)
|
||||
this.onGo(gameState);
|
||||
else if (!ret && this.lastIsGo)
|
||||
this.onNotGo(gameState);
|
||||
this.lastIsGo = ret;
|
||||
return ret;
|
||||
};
|
||||
plan.onGo = function (gameState) { gameState.ai.HQ.econState = "townPhasing"; m.debug ("Trying to reach TownPhase"); };
|
||||
plan.onNotGo = function (gameState) { gameState.ai.HQ.econState = "growth"; };
|
||||
|
||||
this.queues.majorTech.addItem(plan);
|
||||
m.debug ("Planning Town Phase");
|
||||
} else if (this.gameState.canResearch(cityPhase,true) && this.gameState.getTimeElapsed() > (this.Config.Economy.cityPhase*1000)
|
||||
&& this.gameState.getOwnEntitiesByRole("worker", true).length > 85
|
||||
&& this.gameState.findResearchers(cityPhase, true).length != 0 && this.queues.majorTech.length() === 0) {
|
||||
m.debug ("Trying to reach city phase");
|
||||
|
|
@ -222,20 +232,43 @@ m.AegisBot.prototype.OnUpdate = function(sharedScript) {
|
|||
this.turn++;
|
||||
};
|
||||
|
||||
m.AegisBot.prototype.chooseRandomStrategy = function()
|
||||
// defines our core components strategy-wise.
|
||||
// TODO: the sky's the limit here.
|
||||
m.AegisBot.prototype.initPersonality = function(gameState)
|
||||
{
|
||||
// deactivated for now.
|
||||
this.strategy = "normal";
|
||||
// rarely and if we can assume it's not a water map.
|
||||
if (!this.pathInfo.needboat && 0)//Math.random() < 0.2 && this.Config.difficulty == 2)
|
||||
this.aggressiveness = 0.5; // I'll try to keep this as a percent but it's basically arbitrary.
|
||||
if (this.Config.difficulty >= 2)
|
||||
this.aggressiveness = Math.random();
|
||||
|
||||
var agrThrsh = 0.8; // treshold for aggressiveness.
|
||||
if (gameState.civ() == "athen")
|
||||
agrThrsh = 0.6; // works very well with athens
|
||||
|
||||
if (this.aggressiveness > agrThrsh)
|
||||
{
|
||||
this.strategy = "rush";
|
||||
// going to rush.
|
||||
this.HQ.targetNumWorkers = 0;
|
||||
this.Config.Economy.townPhase = 480;
|
||||
m.debug("Going the Rush route");
|
||||
this.aggressiveness = 1.0;
|
||||
// we'll try to pull in an attack at village phase.
|
||||
this.Config.Military.defenceBuildingTime = 900;
|
||||
this.Config.Military.popForBarracks1 = 0;
|
||||
this.Config.Economy.villagePopCap = 75;
|
||||
this.Config.Economy.cityPhase = 900;
|
||||
this.Config.Economy.farmsteadStartTime = 600;
|
||||
this.Config.Economy.femaleRatio = 0; // raise it since we'll want to rush age 2.
|
||||
this.Config.Economy.popForMarket = 80;
|
||||
this.Config.Economy.popForFarmstead = 50;
|
||||
this.Config.Economy.targetNumBuilders = 2;
|
||||
this.Config.Economy.femaleRatio = 0.6;
|
||||
this.Config.Defence.prudence = 0.5;
|
||||
} else if (this.aggressiveness < 0.15) {
|
||||
m.debug("Going the Boom route");
|
||||
// Now and then Superboom
|
||||
this.Config.Military.defenceBuildingTime = 600;
|
||||
this.Config.Economy.cityPhase = 1000;
|
||||
this.Config.Military.attackPlansStartTime = 1000;
|
||||
this.Config.Military.popForBarracks1 = 39;
|
||||
this.Config.Economy.villagePopCap = 50;
|
||||
this.Config.Economy.femaleRatio = 1.0;
|
||||
this.Config.Economy.popForMarket = 55;
|
||||
this.Config.Economy.popForFarmstead = 55;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ m.CityAttack = function CityAttack(gameState, HQ, Config, uniqueID, targetEnemy,
|
|||
this.targetPlayer = targetEnemy;
|
||||
if (this.targetPlayer === -1 || this.targetPlayer === undefined) {
|
||||
// let's find our prefered target, basically counting our enemies units.
|
||||
// TODO: improve this.
|
||||
var enemyCount = {};
|
||||
for (var i = 1; i <=8; i++)
|
||||
enemyCount[i] = 0;
|
||||
|
|
@ -53,9 +54,6 @@ m.CityAttack = function CityAttack(gameState, HQ, Config, uniqueID, targetEnemy,
|
|||
this.timeOfPlanStart = gameState.getTimeElapsed(); // we get the time at which we decided to start the attack
|
||||
|
||||
this.maxPreparationTime = 210*1000;
|
||||
// in this case we want to have the attack ready by the 13th minute. Countdown. Minimum 2 minutes.
|
||||
if (type !== "superSized" && this.Config.difficulty >= 1)
|
||||
this.maxPreparationTime = 780000 - gameState.getTimeElapsed() < 120000 ? 120000 : 780000 - gameState.getTimeElapsed();
|
||||
|
||||
this.pausingStart = 0;
|
||||
this.totalPausingTime = 0;
|
||||
|
|
@ -63,6 +61,9 @@ m.CityAttack = function CityAttack(gameState, HQ, Config, uniqueID, targetEnemy,
|
|||
|
||||
this.onArrivalReaction = "proceedOnTargets";
|
||||
|
||||
// priority of the queues we'll create.
|
||||
var priority = 70;
|
||||
|
||||
// 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 faster this unit will get built.
|
||||
// Should really be clamped to [0.1-1.5] (assuming 1 is default/the norm)
|
||||
|
|
@ -70,53 +71,42 @@ m.CityAttack = function CityAttack(gameState, HQ, Config, uniqueID, targetEnemy,
|
|||
// only once every other category is at least 50% of its target size.
|
||||
// note: siege build order is currently added by the military manager if a fortress is there.
|
||||
this.unitStat = {};
|
||||
this.unitStat["RangedInfantry"] = { "priority" : 1, "minSize" : 4, "targetSize" : 10, "batchSize" : 5, "classes" : ["Infantry","Ranged"],
|
||||
"interests" : [ ["canGather", 2], ["strength",2], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["MeleeInfantry"] = { "priority" : 1, "minSize" : 4, "targetSize" : 10, "batchSize" : 5, "classes" : ["Infantry","Melee"],
|
||||
"interests" : [ ["canGather", 2], ["strength",2], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["MeleeCavalry"] = { "priority" : 1, "minSize" : 3, "targetSize" : 8 , "batchSize" : 3, "classes" : ["Cavalry","Melee"],
|
||||
"interests" : [ ["strength",2], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["RangedCavalry"] = { "priority" : 1, "minSize" : 3, "targetSize" : 8 , "batchSize" : 3, "classes" : ["Cavalry","Ranged"],
|
||||
"interests" : [ ["strength",2], ["cost",1] ], "templates" : [] };
|
||||
var priority = 50;
|
||||
this.unitStat["RangedInfantry"] = { "priority" : 1, "minSize" : 6, "targetSize" : 18, "batchSize" : 3, "classes" : ["Infantry","Ranged"], "interests" : [ ["canGather", 1], ["strength",1.6], ["cost",1.5], ["costsResource", 0.3, "stone"], ["costsResource", 0.3, "metal"] ], "templates" : [] };
|
||||
this.unitStat["MeleeInfantry"] = { "priority" : 1, "minSize" : 6, "targetSize" : 18, "batchSize" : 3, "classes" : ["Infantry","Melee"], "interests" : [ ["canGather", 1], ["strength",1.6], ["cost",1.5], ["costsResource", 0.3, "stone"], ["costsResource", 0.3, "metal"] ], "templates" : [] };
|
||||
|
||||
var ats = Math.random() * 15000 - 15000; // attack time shuffle: move the exact attack time around a bit.
|
||||
|
||||
if (type === "rush") {
|
||||
// in this case we want to have the attack ready by the 14th minute. Countdown. Minimum 2 minutes.
|
||||
if (this.Config.difficulty >= 1)
|
||||
this.maxPreparationTime = (800000+ats) - gameState.getTimeElapsed() < 120000 ? 120000 : 800000 + ats - gameState.getTimeElapsed();
|
||||
|
||||
if (type === "Rush") {
|
||||
// we have 3 minutes to train infantry.
|
||||
delete this.unitStat["RangedInfantry"];
|
||||
delete this.unitStat["MeleeInfantry"];
|
||||
delete this.unitStat["MeleeCavalry"];
|
||||
delete this.unitStat["RangedCavalry"];
|
||||
this.unitStat["Infantry"] = { "priority" : 1, "minSize" : 10, "targetSize" : 30, "batchSize" : 1, "classes" : ["Infantry"], "interests" : [ ["strength",1], ["cost",1] ], "templates" : [] };
|
||||
this.maxPreparationTime = 150*1000;
|
||||
priority = 120;
|
||||
this.unitStat["Infantry"] = { "priority" : 1, "minSize" : 10, "targetSize" : 30, "batchSize" : 2, "classes" : ["Infantry"], "interests" : [ ["strength",1], ["cost",1], ["costsResource", 0.5, "stone"], ["costsResource", 0.6, "metal"] ], "templates" : [] };
|
||||
this.maxPreparationTime = (540000+ats) - gameState.getTimeElapsed() < 120000 ? 120000 : 540000 + ats - gameState.getTimeElapsed();
|
||||
priority = 250;
|
||||
} else if (type === "superSized") {
|
||||
// our first attack has started worst case at the 14th minute, we want to attack another time by the 21th minute, so we rock 6.5 minutes
|
||||
this.maxPreparationTime = 480000;
|
||||
this.maxPreparationTime = 480000; // 8 minutes
|
||||
// basically we want a mix of citizen soldiers so our barracks have a purpose, and champion units.
|
||||
this.unitStat["RangedInfantry"] = { "priority" : 1, "minSize" : 5, "targetSize" : 20, "batchSize" : 5, "classes" : ["Infantry","Ranged", "CitizenSoldier"],
|
||||
"interests" : [["strength",3], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["MeleeInfantry"] = { "priority" : 1, "minSize" : 5, "targetSize" : 20, "batchSize" : 5, "classes" : ["Infantry","Melee", "CitizenSoldier" ],
|
||||
"interests" : [ ["strength",3], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["ChampRangedInfantry"] = { "priority" : 1, "minSize" : 5, "targetSize" : 15, "batchSize" : 5, "classes" : ["Infantry","Ranged", "Champion"],
|
||||
"interests" : [["strength",3], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["ChampMeleeInfantry"] = { "priority" : 1, "minSize" : 5, "targetSize" : 15, "batchSize" : 5, "classes" : ["Infantry","Melee", "Champion" ],
|
||||
"interests" : [ ["strength",3], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["MeleeCavalry"] = { "priority" : 1, "minSize" : 3, "targetSize" : 18, "batchSize" : 3, "classes" : ["Cavalry","Melee", "CitizenSoldier" ],
|
||||
"interests" : [ ["strength",2], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["RangedCavalry"] = { "priority" : 1, "minSize" : 3, "targetSize" : 18 , "batchSize" : 3, "classes" : ["Cavalry","Ranged", "CitizenSoldier"],
|
||||
"interests" : [ ["strength",2], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["ChampMeleeInfantry"] = { "priority" : 0.8, "minSize" : 3, "targetSize" : 12, "batchSize" : 3, "classes" : ["Infantry","Melee", "Champion" ],
|
||||
"interests" : [ ["strength",3], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["ChampMeleeCavalry"] = { "priority" : 0.8, "minSize" : 3, "targetSize" : 12, "batchSize" : 3, "classes" : ["Cavalry","Melee", "Champion" ],
|
||||
"interests" : [ ["strength",2], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["RangedInfantry"] = { "priority" : 0.7, "minSize" : 5, "targetSize" : 15, "batchSize" : 5, "classes" : ["Infantry","Ranged", "CitizenSoldier"], "interests" : [["strength",3], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["MeleeInfantry"] = { "priority" : 0.7, "minSize" : 5, "targetSize" : 15, "batchSize" : 5, "classes" : ["Infantry","Melee", "CitizenSoldier" ], "interests" : [ ["strength",3], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["ChampRangedInfantry"] = { "priority" : 1, "minSize" : 5, "targetSize" : 25, "batchSize" : 5, "classes" : ["Infantry","Ranged", "Champion"], "interests" : [["strength",3], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["ChampMeleeInfantry"] = { "priority" : 1, "minSize" : 5, "targetSize" : 20, "batchSize" : 5, "classes" : ["Infantry","Melee", "Champion" ], "interests" : [ ["strength",3], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["MeleeCavalry"] = { "priority" : 0.7, "minSize" : 3, "targetSize" : 15, "batchSize" : 3, "classes" : ["Cavalry","Melee", "CitizenSoldier" ], "interests" : [ ["strength",2], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["RangedCavalry"] = { "priority" : 0.7, "minSize" : 3, "targetSize" : 15, "batchSize" : 3, "classes" : ["Cavalry","Ranged", "CitizenSoldier"], "interests" : [ ["strength",2], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["ChampMeleeInfantry"] = { "priority" : 1, "minSize" : 3, "targetSize" : 18, "batchSize" : 3, "classes" : ["Infantry","Melee", "Champion" ], "interests" : [ ["strength",3], ["cost",1] ], "templates" : [] };
|
||||
this.unitStat["ChampMeleeCavalry"] = { "priority" : 1, "minSize" : 3, "targetSize" : 18, "batchSize" : 3, "classes" : ["Cavalry","Melee", "Champion" ], "interests" : [ ["strength",2], ["cost",1] ], "templates" : [] };
|
||||
|
||||
priority = 70;
|
||||
priority = 90;
|
||||
}
|
||||
|
||||
// TODO: there should probably be one queue per type of training building
|
||||
gameState.ai.queueManager.addQueue("plan_" + this.name, priority);
|
||||
this.queue = gameState.ai.queues["plan_" + this.name];
|
||||
gameState.ai.queueManager.addQueue("plan_" + this.name +"_champ", priority);
|
||||
gameState.ai.queueManager.addQueue("plan_" + this.name +"_champ", priority+1);
|
||||
this.queueChamp = gameState.ai.queues["plan_" + this.name +"_champ"];
|
||||
/*
|
||||
this.unitStat["Siege"]["filter"] = function (ent) {
|
||||
|
|
@ -271,7 +261,10 @@ m.CityAttack.prototype.canStart = function(gameState){
|
|||
for (var unitCat in this.unitStat) {
|
||||
var Unit = this.unitStat[unitCat];
|
||||
if (this.unit[unitCat].length < Unit["minSize"])
|
||||
{
|
||||
m.debug(unitCat + " doesn't have enough units : " + this.unit[unitCat].length);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
|
|
@ -514,7 +507,7 @@ m.CityAttack.prototype.updatePreparation = function(gameState, HQ,events) {
|
|||
queue = this.queueChamp;
|
||||
|
||||
if (this.buildOrder[0][0] < 1 && queue.length() <= 5) {
|
||||
var template = HQ.findBestTrainableSoldier(gameState, this.buildOrder[0][1], this.buildOrder[0][3]["interests"] );
|
||||
var template = HQ.findBestTrainableUnit(gameState, this.buildOrder[0][1], this.buildOrder[0][3]["interests"] );
|
||||
//m.debug ("tried " + uneval(this.buildOrder[0][1]) +", and " + template);
|
||||
// HACK (TODO replace) : if we have no trainable template... Then we'll simply remove the buildOrder, effectively removing the unit from the plan.
|
||||
if (template === undefined) {
|
||||
|
|
@ -738,7 +731,6 @@ m.CityAttack.prototype.update = function(gameState, HQ, events){
|
|||
|
||||
// this actually doesn't do anything right now.
|
||||
if (this.state === "walking") {
|
||||
|
||||
var attackedNB = 0;
|
||||
|
||||
var toProcess = {};
|
||||
|
|
@ -754,12 +746,7 @@ m.CityAttack.prototype.update = function(gameState, HQ, events){
|
|||
var ourUnit = gameState.getEntityById(e.target);
|
||||
|
||||
if (attacker && attacker.position() && attacker.hasClass("Unit") && attacker.owner() != 0 && attacker.owner() != PlayerID) {
|
||||
|
||||
var territoryMap = m.createTerritoryMap(gameState);
|
||||
if ( +territoryMap.point(attacker.position()) - 64 === +this.targetPlayer)
|
||||
{
|
||||
attackedNB++;
|
||||
}
|
||||
attackedNB++;
|
||||
//if (HQ.enemyWatchers[attacker.owner()]) {
|
||||
//toProcess[attacker.id()] = attacker;
|
||||
//var armyID = HQ.enemyWatchers[attacker.owner()].getArmyFromMember(attacker.id());
|
||||
|
|
@ -773,7 +760,8 @@ m.CityAttack.prototype.update = function(gameState, HQ, events){
|
|||
|
||||
}
|
||||
}
|
||||
if (attackedNB > 4) {
|
||||
var territoryMap = m.createTerritoryMap(gameState);
|
||||
if ((territoryMap.getOwner(this.position) === this.targetPlayer && attackedNB > 1) || attackedNB > 4) {
|
||||
m.debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination.");
|
||||
// we must assume we've arrived at the end of the trail.
|
||||
this.state = "arrived";
|
||||
|
|
@ -851,7 +839,6 @@ m.CityAttack.prototype.update = function(gameState, HQ, events){
|
|||
// basically haven't moved an inch: very likely stuck)
|
||||
if (API3.SquareVectorDistance(this.position, this.position5TurnsAgo) < 10 && this.path.length > 0 && gameState.ai.playedTurn % 5 === 0) {
|
||||
// check for stuck siege units
|
||||
|
||||
var sieges = this.unitCollection.filter(API3.Filters.byClass("Siege"));
|
||||
var farthest = 0;
|
||||
var farthestEnt = -1;
|
||||
|
|
@ -889,17 +876,17 @@ m.CityAttack.prototype.update = function(gameState, HQ, events){
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check if our land units are close enough from the next waypoint.
|
||||
if (API3.SquareVectorDistance(this.unitCollection.getCentrePosition(), this.targetPos) < 7500 ||
|
||||
API3.SquareVectorDistance(this.unitCollection.getCentrePosition(), this.path[0][0]) < 650) {
|
||||
if (API3.SquareVectorDistance(this.position, this.targetPos) < 9000 ||
|
||||
API3.SquareVectorDistance(this.position, this.path[0][0]) < 650) {
|
||||
if (this.unitCollection.filter(API3.Filters.byClass("Siege")).length !== 0
|
||||
&& API3.SquareVectorDistance(this.unitCollection.getCentrePosition(), this.targetPos) >= 7500
|
||||
&& API3.SquareVectorDistance(this.position, this.targetPos) >= 9000
|
||||
&& API3.SquareVectorDistance(this.unitCollection.filter(API3.Filters.byClass("Siege")).getCentrePosition(), this.path[0][0]) >= 650)
|
||||
{
|
||||
} else {
|
||||
// okay so here basically two cases. The first one is "we need a boat at this point".
|
||||
// the second one is "we need to unload at this point". The third is "normal".
|
||||
// okay so here basically two cases. First case is "we've arrived"
|
||||
// Second case is "either we need a boat, or we need to unload"
|
||||
if (this.path[0][1] !== true)
|
||||
{
|
||||
this.path.shift();
|
||||
|
|
@ -975,8 +962,10 @@ m.CityAttack.prototype.update = function(gameState, HQ, events){
|
|||
}
|
||||
}
|
||||
|
||||
// basic state of attacking.
|
||||
if (this.state === "") {
|
||||
// Units attacked will target their attacker unless they're siege. Then we take another non-siege unit to attack them.
|
||||
|
||||
// events watch: if siege units are attacked, we'll send some units to deal with enemies.
|
||||
var attackedEvents = events["Attacked"];
|
||||
for (var key in attackedEvents) {
|
||||
var e = attackedEvents[key];
|
||||
|
|
@ -984,31 +973,20 @@ m.CityAttack.prototype.update = function(gameState, HQ, events){
|
|||
var attacker = gameState.getEntityById(e.attacker);
|
||||
var ourUnit = gameState.getEntityById(e.target);
|
||||
|
||||
if (attacker && attacker.position() && attacker.hasClass("Unit") && attacker.owner() != 0 && attacker.owner() != PlayerID) {
|
||||
if (ourUnit.hasClass("Siege"))
|
||||
{
|
||||
var collec = this.unitCollection.filterNearest(ourUnit.position(), 8).filter(Filters.not(Filters.byClass("Siege"))).toEntityArray();
|
||||
if (collec.length !== 0)
|
||||
{
|
||||
collec[0].attack(attacker.id());
|
||||
if (collec.length !== 1)
|
||||
{
|
||||
collec[1].attack(attacker.id());
|
||||
if (collec.length !== 2)
|
||||
{
|
||||
collec[2].attack(attacker.id());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ourUnit.attack(attacker.id());
|
||||
}
|
||||
}
|
||||
if (!attacker || !attacker.position() || !attacker.hasClass("Unit") || attacker.owner() === 0 || attacker.owner() === PlayerID)
|
||||
continue;
|
||||
|
||||
if (!ourUnit.hasClass("Siege"))
|
||||
continue;
|
||||
var collec = this.unitCollection.filter(Filters.not(Filters.byClass("Siege"))).filterNearest(ourUnit.position(), 5).toEntityArray();
|
||||
if (collec.length === 0)
|
||||
continue;
|
||||
collec.attack(attacker.id())
|
||||
}
|
||||
}
|
||||
|
||||
var enemyUnits = gameState.getGEC("player-" +this.targetPlayer + "-units");
|
||||
var enemyStructures = gameState.getGEC("player-" +this.targetPlayer + "-structures");
|
||||
var enemyUnits = gameState.getEnemyUnits(this.targetPlayer);
|
||||
var enemyStructures = gameState.getEnemyStructures(this.targetPlayer);
|
||||
|
||||
if (this.unitCollUpdateArray === undefined || this.unitCollUpdateArray.length === 0)
|
||||
{
|
||||
|
|
@ -1025,69 +1003,108 @@ m.CityAttack.prototype.update = function(gameState, HQ, events){
|
|||
var ent = gameState.getEntityById(this.unitCollUpdateArray[0]);
|
||||
if (!ent)
|
||||
continue;
|
||||
|
||||
// if the unit is in my territory, make it move towards the target.
|
||||
if (territoryMap.point(ent.position()) - 64 === PlayerID) {
|
||||
ent.move(this.targetPos[0],this.targetPos[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
var orderData = ent.unitAIOrderData();
|
||||
if (orderData.length !== 0)
|
||||
orderData = orderData[0];
|
||||
else
|
||||
orderData = undefined;
|
||||
|
||||
// if the unit is in my territory, make it move.
|
||||
if (territoryMap.point(ent.position()) - 64 === PlayerID)
|
||||
ent.move(this.targetPos[0],this.targetPos[1]);
|
||||
|
||||
// update it.
|
||||
var needsUpdate = false;
|
||||
if (ent.isIdle())
|
||||
needsUpdate = true;
|
||||
if (ent.hasClass("Siege") && (!orderData || !orderData["target"] || !gameState.getEntityById(orderData["target"]) || !gameState.getEntityById(orderData["target"]).hasClass("ConquestCritical")) )
|
||||
else if (ent.hasClass("Siege") && (!orderData || !orderData["target"] || !gameState.getEntityById(orderData["target"]) || !gameState.getEntityById(orderData["target"]).hasClass("ConquestCritical")) )
|
||||
needsUpdate = true;
|
||||
else if (!ent.hasClass("Siege") && orderData && orderData["target"] && gameState.getEntityById(orderData["target"]) && gameState.getEntityById(orderData["target"]).hasClass("Structure"))
|
||||
needsUpdate = true; // try to make it attack a unit instead
|
||||
|
||||
// don't update too soon.
|
||||
if (timeElapsed - ent.getMetadata(PlayerID, "lastAttackPlanUpdateTime") < 10000)
|
||||
needsUpdate = false;
|
||||
continue;
|
||||
|
||||
if (needsUpdate === true || arrivedthisTurn)
|
||||
{
|
||||
ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", timeElapsed);
|
||||
var mStruct = enemyStructures.filter(function (enemy) { //}){
|
||||
if (!enemy.position() || (enemy.hasClass("StoneWall") && ent.canAttackClass("StoneWall"))) {
|
||||
return false;
|
||||
}
|
||||
if (API3.SquareVectorDistance(enemy.position(),ent.position()) > 3000) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
if (needsUpdate === false && !arrivedthisTurn)
|
||||
continue;
|
||||
|
||||
ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", timeElapsed);
|
||||
|
||||
// let's filter targets further based on this unit.
|
||||
var mStruct = enemyStructures.filter(function (enemy) { //}){
|
||||
if (!enemy.position() || (enemy.hasClass("StoneWall") && ent.canAttackClass("StoneWall"))) {
|
||||
return false;
|
||||
}
|
||||
if (API3.SquareVectorDistance(enemy.position(),ent.position()) > 3000) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
var mUnit = enemyUnits.filter(function (enemy) {
|
||||
if (!enemy.position())
|
||||
return false;
|
||||
if (API3.SquareVectorDistance(enemy.position(),ent.position()) > 10000)
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
// Checking for gates if we're a siege unit.
|
||||
var isGate = false;
|
||||
mUnit = mUnit.toEntityArray();
|
||||
mStruct = mStruct.toEntityArray();
|
||||
if (ent.hasClass("Siege")) {
|
||||
mStruct.sort(function (structa,structb) {
|
||||
var vala = structa.costSum();
|
||||
if (structa.hasClass("Gates") && ent.canAttackClass("StoneWall")) { // we hate gates
|
||||
isGate = true;
|
||||
vala += 10000;
|
||||
} else if (structa.hasClass("ConquestCritical"))
|
||||
vala += 200;
|
||||
var valb = structb.costSum();
|
||||
if (structb.hasClass("Gates") && ent.canAttackClass("StoneWall")) { // we hate gates
|
||||
isGate = true;
|
||||
valb += 10000;
|
||||
} else if (structb.hasClass("ConquestCritical"))
|
||||
valb += 200;
|
||||
//warn ("Structure " +structa.genericName() + " is worth " +vala);
|
||||
//warn ("Structure " +structb.genericName() + " is worth " +valb);
|
||||
return (valb - vala);
|
||||
});
|
||||
var mUnit;
|
||||
if (ent.hasClass("Cavalry") && ent.countersClasses(["Support"])) {
|
||||
mUnit = enemyUnits.filter(function (enemy) { //}){
|
||||
if (!enemy.position()) {
|
||||
return false;
|
||||
}
|
||||
if (!enemy.hasClass("Support"))
|
||||
return false;
|
||||
if (API3.SquareVectorDistance(enemy.position(),ent.position()) > 10000) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
// TODO: handle ballistas here
|
||||
if (mStruct.length !== 0) {
|
||||
if (isGate)
|
||||
ent.attack(mStruct[0].id());
|
||||
else
|
||||
{
|
||||
var rand = Math.floor(Math.random() * mStruct.length*0.2);
|
||||
ent.attack(mStruct[+rand].id());
|
||||
//m.debug ("Siege units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName());
|
||||
}
|
||||
} else if (API3.SquareVectorDistance(self.targetPos, ent.position()) > 900 ) {
|
||||
//m.debug ("Siege units moving to " + uneval(self.targetPos));
|
||||
ent.move(self.targetPos[0],self.targetPos[1]);
|
||||
}
|
||||
if (!(ent.hasClass("Cavalry") && ent.countersClasses(["Support"])) || mUnit.length === 0) {
|
||||
mUnit = enemyUnits.filter(function (enemy) { //}){
|
||||
if (!enemy.position()) {
|
||||
return false;
|
||||
}
|
||||
if (API3.SquareVectorDistance(enemy.position(),ent.position()) > 10000) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (mUnit.length !== 0) {
|
||||
mUnit.sort(function (unitA,unitB) {
|
||||
var vala = unitA.hasClass("Support") ? 50 : 0;
|
||||
if (ent.countersClasses(unitA.classes()))
|
||||
vala += 100;
|
||||
var valb = unitB.hasClass("Support") ? 50 : 0;
|
||||
if (ent.countersClasses(unitB.classes()))
|
||||
valb += 100;
|
||||
return valb - vala;
|
||||
});
|
||||
}
|
||||
var isGate = false;
|
||||
mUnit = mUnit.toEntityArray();
|
||||
mStruct = mStruct.toEntityArray();
|
||||
if (ent.hasClass("Siege")) {
|
||||
var rand = Math.floor(Math.random() * mUnit.length*0.1);
|
||||
ent.attack(mUnit[(+rand)].id());
|
||||
//m.debug ("Units attacking a unit from " +mUnit[+rand].owner() + " , " +mUnit[+rand].templateName());
|
||||
} else if (API3.SquareVectorDistance(self.targetPos, ent.position()) > 900 ){
|
||||
//m.debug ("Units moving to " + uneval(self.targetPos));
|
||||
ent.move(self.targetPos[0],self.targetPos[1]);
|
||||
} else if (mStruct.length !== 0) {
|
||||
mStruct.sort(function (structa,structb) { //}){
|
||||
var vala = structa.costSum();
|
||||
if (structa.hasClass("Gates") && ent.canAttackClass("StoneWall")) // we hate gates
|
||||
|
|
@ -1095,66 +1112,23 @@ m.CityAttack.prototype.update = function(gameState, HQ, events){
|
|||
isGate = true;
|
||||
vala += 10000;
|
||||
} else if (structa.hasClass("ConquestCritical"))
|
||||
vala += 200;
|
||||
vala += 100;
|
||||
var valb = structb.costSum();
|
||||
if (structb.hasClass("Gates") && ent.canAttackClass("StoneWall")) // we hate gates
|
||||
{
|
||||
isGate = true;
|
||||
valb += 10000;
|
||||
} else if (structb.hasClass("ConquestCritical"))
|
||||
valb += 200;
|
||||
//warn ("Structure " +structa.genericName() + " is worth " +vala);
|
||||
//warn ("Structure " +structb.genericName() + " is worth " +valb);
|
||||
valb += 100;
|
||||
return (valb - vala);
|
||||
});
|
||||
// TODO: handle ballistas here
|
||||
if (mStruct.length !== 0) {
|
||||
if (isGate)
|
||||
ent.attack(mStruct[0].id());
|
||||
else
|
||||
{
|
||||
var rand = Math.floor(Math.random() * mStruct.length*0.1);
|
||||
ent.attack(mStruct[+rand].id());
|
||||
//m.debug ("Siege units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName());
|
||||
}
|
||||
} else if (API3.SquareVectorDistance(self.targetPos, ent.position()) > 900 ){
|
||||
//m.debug ("Siege units moving to " + uneval(self.targetPos));
|
||||
ent.move(self.targetPos[0],self.targetPos[1]);
|
||||
}
|
||||
} else {
|
||||
if (mUnit.length !== 0) {
|
||||
var rand = Math.floor(Math.random() * mUnit.length*0.99);
|
||||
ent.attack(mUnit[(+rand)].id());
|
||||
//m.debug ("Units attacking a unit from " +mUnit[+rand].owner() + " , " +mUnit[+rand].templateName());
|
||||
} else if (API3.SquareVectorDistance(self.targetPos, ent.position()) > 900 ){
|
||||
//m.debug ("Units moving to " + uneval(self.targetPos));
|
||||
ent.move(self.targetPos[0],self.targetPos[1]);
|
||||
} else if (mStruct.length !== 0) {
|
||||
mStruct.sort(function (structa,structb) { //}){
|
||||
var vala = structa.costSum();
|
||||
if (structa.hasClass("Gates") && ent.canAttackClass("StoneWall")) // we hate gates
|
||||
{
|
||||
isGate = true;
|
||||
vala += 10000;
|
||||
} else if (structa.hasClass("ConquestCritical"))
|
||||
vala += 100;
|
||||
var valb = structb.costSum();
|
||||
if (structb.hasClass("Gates") && ent.canAttackClass("StoneWall")) // we hate gates
|
||||
{
|
||||
isGate = true;
|
||||
valb += 10000;
|
||||
} else if (structb.hasClass("ConquestCritical"))
|
||||
valb += 100;
|
||||
return (valb - vala);
|
||||
});
|
||||
if (isGate)
|
||||
ent.attack(mStruct[0].id());
|
||||
else
|
||||
{
|
||||
var rand = Math.floor(Math.random() * mStruct.length*0.1);
|
||||
ent.attack(mStruct[+rand].id());
|
||||
//m.debug ("Units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName());
|
||||
}
|
||||
if (isGate)
|
||||
ent.attack(mStruct[0].id());
|
||||
else
|
||||
{
|
||||
var rand = Math.floor(Math.random() * mStruct.length*0.1);
|
||||
ent.attack(mStruct[+rand].id());
|
||||
//m.debug ("Units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,9 +58,9 @@ m.BaseManager.prototype.init = function(gameState, unconstructed){
|
|||
// TODO: difficulty levels for this?
|
||||
|
||||
// smallRadius is the distance necessary to mark a resource as linked to a dropsite.
|
||||
this.smallRadius = { 'food':40*40,'wood':45*45,'stone':40*40,'metal':40*40 };
|
||||
this.smallRadius = { 'food':40*40,'wood':50*50,'stone':40*40,'metal':40*40 };
|
||||
// medRadius is the maximal distance for a link, albeit one that would still make us want to build a new dropsite.
|
||||
this.medRadius = { 'food':70*70,'wood':70*70,'stone':80*80,'metal':80*80 };
|
||||
this.medRadius = { 'food':70*70,'wood':55*55,'stone':80*80,'metal':80*80 };
|
||||
// bigRadius is the distance for a weak link, mainly for optimizing search for resources when a DP is depleted.
|
||||
this.bigRadius = { 'food':70*70,'wood':200*200,'stone':200*200,'metal':200*200 };
|
||||
};
|
||||
|
|
@ -115,6 +115,9 @@ m.BaseManager.prototype.initTerritory = function(HQ, gameState) {
|
|||
var width = gameState.getMap().width;
|
||||
for (var xi = -radius; xi <= radius; ++xi)
|
||||
for (var yi = -radius; yi <= radius; ++yi)
|
||||
{
|
||||
if (x+xi >= width || y+yi >= width)
|
||||
continue;
|
||||
if (xi*xi+yi*yi < radius*radius && HQ.basesMap.map[(x+xi) + (y+yi)*width] === 0)
|
||||
{
|
||||
if (this.accessIndex == gameState.sharedScript.accessibility.landPassMap[x+xi + width*(y+yi)])
|
||||
|
|
@ -123,6 +126,7 @@ m.BaseManager.prototype.initTerritory = function(HQ, gameState) {
|
|||
HQ.basesMap.map[(x+xi) + (y+yi)*width] = this.ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.BaseManager.prototype.initGatheringFunctions = function(HQ, gameState, specTypes) {
|
||||
|
|
@ -189,10 +193,10 @@ m.BaseManager.prototype.checkEvents = function (gameState, events, queues) {
|
|||
if (ent.hasClass("CivCentre"))
|
||||
{
|
||||
// TODO: might want to tell the queue manager to pause other stuffs if we are the only base.
|
||||
queues.civilCentre.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_civil_centre", { "base" : this.ID, "baseAnchor" : true }, 0 , -1,ent.position()));
|
||||
queues.civilCentre.addItem(gameState, new m.ConstructionPlan(gameState, "structures/{civ}_civil_centre", { "base" : this.ID, "baseAnchor" : true }, ent.position()));
|
||||
} else {
|
||||
// TODO
|
||||
queues.civilCentre.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_civil_centre", { "base" : this.ID, "baseAnchor" : true },0,-1,ent.position()));
|
||||
queues.civilCentre.addItem(gameState, new m.ConstructionPlan(gameState, "structures/{civ}_civil_centre", { "base" : this.ID, "baseAnchor" : true }, ent.position()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -600,7 +604,7 @@ m.BaseManager.prototype.checkResourceLevels = function (gameState,queues) {
|
|||
plan.isGo = function() { return false; }; // don't start right away.
|
||||
queues.field.addItem(plan);
|
||||
}
|
||||
} else if (!this.isFarming && count < 650)
|
||||
} else if (!this.isFarming && count < 400)
|
||||
{
|
||||
for (var i in queues.field.queue)
|
||||
queues.field.queue[i].isGo = function() { return true; }; // start them
|
||||
|
|
@ -636,7 +640,7 @@ m.BaseManager.prototype.checkResourceLevels = function (gameState,queues) {
|
|||
// TODO: tell the HQ we'll be needing a new base for this resource, or tell it we've ran out of resource Z.
|
||||
} else {
|
||||
m.debug ("planning new dropsite for " + type);
|
||||
queues.dropsites.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_storehouse",{ "base" : this.ID }, 0, -1, pos));
|
||||
queues.dropsites.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_storehouse",{ "base" : this.ID }, pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -660,6 +664,15 @@ m.BaseManager.prototype.getGatherRates = function(gameState, currentRates) {
|
|||
if (gRate !== undefined)
|
||||
currentRates[i] += Math.log(1+gRate)/1.1;
|
||||
});
|
||||
if (i === "food")
|
||||
{
|
||||
units = this.workers.filter(API3.Filters.byMetadata(PlayerID, "subrole", "hunter"));
|
||||
units.forEach(function (ent) {
|
||||
var gRate = ent.currentGatherRate()
|
||||
if (gRate !== undefined)
|
||||
currentRates[i] += Math.log(1+gRate)/1.1;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -799,7 +812,12 @@ m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair) {
|
|||
|
||||
// TODO: this is not perfect performance-wise.
|
||||
var foundations = this.buildings.filter(API3.Filters.and(API3.Filters.isFoundation(),API3.Filters.not(API3.Filters.byClass("Field")))).toEntityArray();
|
||||
var damagedBuildings = this.buildings.filter(function (ent) { if (ent.needsRepair() && ent.getMetadata(PlayerID, "plan") == undefined) { return true; } return false; }).toEntityArray();
|
||||
|
||||
var damagedBuildings = this.buildings.filter(function (ent) {
|
||||
if (ent.foundationProgress() === undefined && ent.needsRepair())
|
||||
return true;
|
||||
return false;
|
||||
}).toEntityArray();
|
||||
|
||||
// Check if nothing to build
|
||||
if (!foundations.length && !damagedBuildings.length){
|
||||
|
|
@ -846,15 +864,18 @@ m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair) {
|
|||
|
||||
for (var i in foundations) {
|
||||
var target = foundations[i];
|
||||
// Removed: sometimes the AI would not notice it has empty unbuilt fields
|
||||
//if (target._template.BuildRestrictions.Category === "Field")
|
||||
// continue; // we do not build fields
|
||||
|
||||
if (target.hasClass("Field"))
|
||||
continue; // we do not build fields
|
||||
|
||||
var assigned = gameState.getOwnEntitiesByMetadata("target-foundation", target.id()).length;
|
||||
|
||||
var targetNB = this.Config.Economy.targetNumBuilders; // TODO: dynamic that.
|
||||
if (target.hasClass("CivCentre") || target.buildTime() > 150 || target.hasClass("House"))
|
||||
if (target.hasClass("House"))
|
||||
targetNB *= 2;
|
||||
else if (target.hasClass("Barracks"))
|
||||
targetNB = 4;
|
||||
else if (target.hasClass("Fortress"))
|
||||
targetNB = 7;
|
||||
if (target.getMetadata(PlayerID, "baseAnchor") == true)
|
||||
targetNB = 15;
|
||||
|
||||
|
|
@ -886,6 +907,11 @@ m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// auras/techs are buggy and the AI tries to repair healthy buildings.
|
||||
// TODO: reimplement once that's fixed.
|
||||
return;
|
||||
|
||||
// don't repair if we're still under attack, unless it's like a vital (civcentre or wall) building that's getting destroyed.
|
||||
for (var i in damagedBuildings) {
|
||||
var target = damagedBuildings[i];
|
||||
|
|
@ -935,9 +961,20 @@ m.BaseManager.prototype.update = function(gameState, queues, events) {
|
|||
{
|
||||
var terrMap = m.createTerritoryMap(gameState);
|
||||
if(terrMap.getOwner(this.anchor.position()) !== 0 && terrMap.getOwner(this.anchor.position()) !== PlayerID)
|
||||
this.anchor.destroy();
|
||||
{
|
||||
// we're in enemy territory. If we're too close from the enemy, destroy us.
|
||||
var eEnts = gameState.getEnemyStructures().filter(API3.Filters.byClass("CivCentre")).toEntityArray();
|
||||
for (var i in eEnts)
|
||||
{
|
||||
var entPos = eEnts[i].position();
|
||||
entPos = [entPos[0]/4.0,entPos[1]/4.0];
|
||||
if (API3.SquareVectorDistance(entPos, pos) < 500)
|
||||
this.anchor.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if (!this.constructing)
|
||||
// {
|
||||
if (gameState.ai.playedTurn % 2 === 0)
|
||||
|
|
|
|||
|
|
@ -1,33 +1,32 @@
|
|||
var AEGIS = function(m)
|
||||
{
|
||||
|
||||
// this defines the medium difficulty
|
||||
m.Config = function() {
|
||||
this.difficulty = 2; // 0 is sandbox, 1 is easy, 2 is medium, 3 is hard, 4 is very hard.
|
||||
|
||||
// overriden by the GUI, this defines the base difficulty.
|
||||
this.Military = {
|
||||
"fortressLapseTime" : 540, // Time to wait between building 2 fortresses
|
||||
"defenceBuildingTime" : 600, // Time to wait before building towers or fortresses
|
||||
"attackPlansStartTime" : 0, // time to wait before attacking. Start as soon as possible (first barracks)
|
||||
"attackPlansStartTime" : 0, // time to wait before attacking. Start as soon as possible.
|
||||
"techStartTime" : 120, // time to wait before teching. Will only start after town phase so it's irrelevant.
|
||||
"popForBarracks1" : 20,
|
||||
"popForBarracks1" : 25,
|
||||
"popForBarracks2" : 95,
|
||||
"timeForBlacksmith" : 900,
|
||||
};
|
||||
this.Economy = {
|
||||
"townPhase" : 180, // time to start trying to reach town phase (might be a while after. Still need the requirements + ress )
|
||||
"villagePopCap" : 40, // How many units we want before aging to town.
|
||||
"cityPhase" : 840, // time to start trying to reach city phase
|
||||
"popForMarket" : 80,
|
||||
"popForFarmstead" : 45,
|
||||
"popForMarket" : 50,
|
||||
"popForFarmstead" : 35,
|
||||
"dockStartTime" : 240, // Time to wait before building the dock
|
||||
"techStartTime" : 0, // time to wait before teching.
|
||||
"targetNumBuilders" : 1.5, // Base number of builders per foundation.
|
||||
"femaleRatio" : 0.4, // percent of females among the workforce.
|
||||
"femaleRatio" : 0.5, // percent of females among the workforce.
|
||||
"initialFields" : 2
|
||||
};
|
||||
|
||||
|
||||
// Note: attack settings are set directly in attack_plan.js
|
||||
|
||||
// defence
|
||||
this.Defence =
|
||||
{
|
||||
|
|
@ -72,7 +71,7 @@ m.Config = function() {
|
|||
"dropsites" : 120,
|
||||
"field" : 500,
|
||||
"economicBuilding" : 90,
|
||||
"militaryBuilding" : 140, // TODO: set to a lower value after the first barracks.
|
||||
"militaryBuilding" : 240, // set to something lower after the first barracks.
|
||||
"defenceBuilding" : 70,
|
||||
"civilCentre" : 400,
|
||||
"majorTech" : 700,
|
||||
|
|
|
|||
|
|
@ -139,6 +139,8 @@ m.Army.prototype.recalculatePosition = function(gameState, force)
|
|||
for (var i in this.entities)
|
||||
{
|
||||
var ent = gameState.getEntityById(this.entities[i]);
|
||||
if (!ent) // whaaat?
|
||||
continue;
|
||||
var epos = ent.position();
|
||||
if (epos == undefined)
|
||||
continue;
|
||||
|
|
@ -503,6 +505,8 @@ m.Army.prototype.update = function (gameState)
|
|||
{
|
||||
var id = this.entities[i];
|
||||
var ent = gameState.getEntityById(id);
|
||||
if (!ent.position) // shouldn't be able to happen but apparently does.
|
||||
continue;
|
||||
if (API3.SquareVectorDistance(ent.position(), this.position) > this.breakawaySize)
|
||||
{
|
||||
breakaways.push(id);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ m.Defence = function(Config)
|
|||
}
|
||||
|
||||
|
||||
m.Defence.prototype.init = function(gameState,events)
|
||||
m.Defence.prototype.init = function(gameState)
|
||||
{
|
||||
this.armyMergeSize = this.Config.Defence.armyMergeSize;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,13 +25,17 @@ m.HQ = function(Config) {
|
|||
this.dockFailed = false; // sanity check
|
||||
this.waterMap = false; // set by the aegis.js file.
|
||||
|
||||
this.econState = "growth"; // existing values: growth, townPhasing.
|
||||
|
||||
// tell if we can't gather from a resource type for sanity checks.
|
||||
this.outOf = { "food" : false, "wood" : false, "stone" : false, "metal" : false };
|
||||
|
||||
this.baseManagers = {};
|
||||
|
||||
// cache the rates currently want for resource gathering.
|
||||
this.wantedRates = {};
|
||||
// cache the rates.
|
||||
this.wantedRates = { "food": 0, "wood": 0, "stone":0, "metal": 0 };
|
||||
this.currentRates = { "food": 0, "wood": 0, "stone":0, "metal": 0 };
|
||||
this.currentRateLastUpdateTime = 0;
|
||||
|
||||
// this means we'll have about a big third of women, and thus we can maximize resource gathering rates.
|
||||
this.femaleRatio = this.Config.Economy.femaleRatio;
|
||||
|
|
@ -40,13 +44,13 @@ m.HQ = function(Config) {
|
|||
this.fortressLapseTime = this.Config.Military.fortressLapseTime * 1000;
|
||||
this.defenceBuildingTime = this.Config.Military.defenceBuildingTime * 1000;
|
||||
this.attackPlansStartTime = this.Config.Military.attackPlansStartTime * 1000;
|
||||
|
||||
this.defenceManager = new m.Defence(this.Config);
|
||||
|
||||
this.navalManager = new m.NavalManager();
|
||||
|
||||
this.TotalAttackNumber = 0;
|
||||
this.upcomingAttacks = { "CityAttack" : [] };
|
||||
this.startedAttacks = { "CityAttack" : [] };
|
||||
this.upcomingAttacks = { "CityAttack" : [], "Rush" : [] };
|
||||
this.startedAttacks = { "CityAttack" : [], "Rush" : [] };
|
||||
};
|
||||
|
||||
// More initialisation for stuff that needs the gameState
|
||||
|
|
@ -112,7 +116,7 @@ m.HQ.prototype.init = function(gameState, queues){
|
|||
var pos = this.baseManagers[1].findBestDropsiteLocation(gameState, "wood");
|
||||
if (pos)
|
||||
{
|
||||
queues.dropsites.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_storehouse",{ "base" : 1 }, 0, -1, pos));
|
||||
queues.dropsites.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_storehouse",{ "base" : 1 }, pos));
|
||||
queues.minorTech.addItem(new m.ResearchPlan(gameState, "gather_capacity_wheelbarrow"));
|
||||
}
|
||||
}
|
||||
|
|
@ -125,6 +129,7 @@ m.HQ.prototype.init = function(gameState, queues){
|
|||
//this.reassignIdleWorkers(gameState);
|
||||
|
||||
this.navalManager.init(gameState, queues);
|
||||
this.defenceManager.init(gameState);
|
||||
|
||||
// TODO: change that to something dynamic.
|
||||
var civ = gameState.playerData.civ;
|
||||
|
|
@ -184,11 +189,14 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues) {
|
|||
// Let's get a few units out there to build this.
|
||||
// TODO: select the best base, or use multiple bases.
|
||||
var builders = this.bulkPickWorkers(gameState, bID, 10);
|
||||
builders.forEach(function (worker) {
|
||||
worker.setMetadata(PlayerID, "base", bID);
|
||||
worker.setMetadata(PlayerID, "subrole", "builder");
|
||||
worker.setMetadata(PlayerID, "target-foundation", ent.id());
|
||||
});
|
||||
if (builders !== false)
|
||||
{
|
||||
builders.forEach(function (worker) {
|
||||
worker.setMetadata(PlayerID, "base", bID);
|
||||
worker.setMetadata(PlayerID, "subrole", "builder");
|
||||
worker.setMetadata(PlayerID, "target-foundation", ent.id());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -217,6 +225,17 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues) {
|
|||
}
|
||||
};
|
||||
|
||||
// Called by the "town phase" research plan once it's started
|
||||
m.HQ.prototype.OnTownPhase = function(gameState)
|
||||
{
|
||||
if (this.Config.difficulty >= 2 && this.femaleRatio !== 0.4)
|
||||
{
|
||||
this.femaleRatio = 0.4;
|
||||
gameState.ai.queues["villager"].empty();
|
||||
gameState.ai.queues["citizenSoldier"].empty();
|
||||
}
|
||||
}
|
||||
|
||||
// This code trains females and citizen workers, trying to keep close to a ratio of females/CS
|
||||
// TODO: this should choose a base depending on which base need workers
|
||||
// TODO: also there are several things that could be greatly improved here.
|
||||
|
|
@ -251,6 +270,9 @@ m.HQ.prototype.trainMoreWorkers = function(gameState, queues)
|
|||
if (numTotal > this.targetNumWorkers || numQueued > 50 || (numQueuedF > 20 && numQueuedS > 20) || numInTraining > 15)
|
||||
return;
|
||||
|
||||
if (numTotal >= this.Config.Economy.villagePopCap && gameState.currentPhase() === 1 && !gameState.isResearching(gameState.townPhase()))
|
||||
return;
|
||||
|
||||
// default template and size
|
||||
var template = gameState.applyCiv("units/{civ}_support_female_citizen");
|
||||
var size = Math.min(5, Math.ceil(numTotal / 10));
|
||||
|
|
@ -281,9 +303,9 @@ m.HQ.prototype.trainMoreWorkers = function(gameState, queues)
|
|||
|
||||
// base "0" means "auto"
|
||||
if (template === gameState.applyCiv("units/{civ}_support_female_citizen"))
|
||||
queues.villager.addItem(new m.TrainingPlan(gameState, template, { "role" : "worker", "base" : 0 }, size, 0, -1, size ));
|
||||
queues.villager.addItem(new m.TrainingPlan(gameState, template, { "role" : "worker", "base" : 0 }, size, size ));
|
||||
else
|
||||
queues.citizenSoldier.addItem(new m.TrainingPlan(gameState, template, { "role" : "worker", "base" : 0 }, size, 0, -1, size));
|
||||
queues.citizenSoldier.addItem(new m.TrainingPlan(gameState, template, { "role" : "worker", "base" : 0 }, size, size));
|
||||
};
|
||||
|
||||
// picks the best template based on parameters and classes
|
||||
|
|
@ -294,50 +316,6 @@ m.HQ.prototype.findBestTrainableUnit = function(gameState, classes, parameters)
|
|||
return undefined;
|
||||
|
||||
units.sort(function(a, b) {// }) {
|
||||
var aDivParam = 0, bDivParam = 0;
|
||||
var aTopParam = 0, bTopParam = 0;
|
||||
for (var i in parameters) {
|
||||
var param = parameters[i];
|
||||
|
||||
if (param[0] == "base") {
|
||||
aTopParam = param[1];
|
||||
bTopParam = param[1];
|
||||
}
|
||||
if (param[0] == "strength") {
|
||||
aTopParam += m.getMaxStrength(a[1]) * param[1];
|
||||
bTopParam += m.getMaxStrength(b[1]) * param[1];
|
||||
}
|
||||
if (param[0] == "speed") {
|
||||
aTopParam += a[1].walkSpeed() * param[1];
|
||||
bTopParam += b[1].walkSpeed() * param[1];
|
||||
}
|
||||
|
||||
if (param[0] == "cost") {
|
||||
aDivParam += a[1].costSum() * param[1];
|
||||
bDivParam += b[1].costSum() * param[1];
|
||||
}
|
||||
// requires a third parameter which is the resource
|
||||
if (param[0] == "costsResource") {
|
||||
if (a[1].cost()[param[2]])
|
||||
aTopParam *= param[1];
|
||||
if (b[1].cost()[param[2]])
|
||||
bTopParam *= param[1];
|
||||
}
|
||||
}
|
||||
return -(aTopParam/(aDivParam+1)) + (bTopParam/(bDivParam+1));
|
||||
});
|
||||
return units[0][0];
|
||||
};
|
||||
|
||||
// picks the best template based on parameters and classes
|
||||
m.HQ.prototype.findBestTrainableSoldier = function(gameState, classes, parameters) {
|
||||
var units = gameState.findTrainableUnits(classes);
|
||||
|
||||
if (units.length === 0)
|
||||
return undefined;
|
||||
|
||||
|
||||
units.sort(function(a, b) { //}) {
|
||||
var aDivParam = 0, bDivParam = 0;
|
||||
var aTopParam = 0, bTopParam = 0;
|
||||
for (var i in parameters) {
|
||||
|
|
@ -364,6 +342,13 @@ m.HQ.prototype.findBestTrainableSoldier = function(gameState, classes, parameter
|
|||
aDivParam += a[1].costSum() * param[1];
|
||||
bDivParam += b[1].costSum() * param[1];
|
||||
}
|
||||
// requires a third parameter which is the resource
|
||||
if (param[0] == "costsResource") {
|
||||
if (a[1].cost()[param[2]])
|
||||
aTopParam *= param[1];
|
||||
if (b[1].cost()[param[2]])
|
||||
bTopParam *= param[1];
|
||||
}
|
||||
if (param[0] == "canGather") {
|
||||
// checking against wood, could be anything else really.
|
||||
if (a[1].resourceGatherRates() && a[1].resourceGatherRates()["wood.tree"])
|
||||
|
|
@ -377,7 +362,6 @@ m.HQ.prototype.findBestTrainableSoldier = function(gameState, classes, parameter
|
|||
return units[0][0];
|
||||
};
|
||||
|
||||
|
||||
// Tries to research any available tech
|
||||
// Only one at once. Also does military tech (selection is completely random atm)
|
||||
// TODO: Lots, lots, lots here.
|
||||
|
|
@ -450,14 +434,18 @@ m.HQ.prototype.bulkPickWorkers = function(gameState, newBaseID, number) {
|
|||
m.HQ.prototype.GetCurrentGatherRates = function(gameState) {
|
||||
var self = this;
|
||||
|
||||
var currentRates = {};
|
||||
if (gameState.getTimeElapsed() - this.currentRateLastUpdateTime < 10000 && this.currentRateLastUpdateTime !== 0 && gameState.ai.playedTurn > 3)
|
||||
return this.currentRates;
|
||||
|
||||
this.currentRateLastUpdateTime = gameState.getTimeElapsed();
|
||||
|
||||
for (var type in this.wantedRates)
|
||||
currentRates[type] = 0;
|
||||
this.currentRates[type] = 0;
|
||||
|
||||
for (var i in this.baseManagers)
|
||||
this.baseManagers[i].getGatherRates(gameState, currentRates);
|
||||
this.baseManagers[i].getGatherRates(gameState, this.currentRates);
|
||||
|
||||
return currentRates;
|
||||
return this.currentRates;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -682,12 +670,15 @@ m.HQ.prototype.buildMarket = function(gameState, queues){
|
|||
|
||||
// Build a farmstead to go to town phase faster and prepare for research. Only really active on higher diff mode.
|
||||
m.HQ.prototype.buildFarmstead = function(gameState, queues){
|
||||
if (gameState.getPopulation() > this.Config.Economy.popForFarmstead) {
|
||||
if (gameState.getPopulation() > this.Config.Economy.popForFarmstead
|
||||
&& (gameState.currentPhase() > 1 || gameState.isResearching(gameState.townPhase()))) {
|
||||
// achtung: "DropsiteFood" does not refer to CCs.
|
||||
if (queues.economicBuilding.countQueuedUnitsWithClass("DropsiteFood") === 0 &&
|
||||
gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_farmstead"), true) === 0){
|
||||
//only ever build one storehouse/CC/market at a time
|
||||
queues.economicBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_farmstead", { "base" : 1 }));
|
||||
// add the farming plough to the research we want.
|
||||
queues.minorTech.addItem(new m.ResearchPlan(gameState, "gather_farming_plough"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -713,29 +704,83 @@ m.HQ.prototype.buildDock = function(gameState, queues){
|
|||
}
|
||||
};
|
||||
|
||||
// if Aegis has resources it doesn't need, it'll try to barter it for resources it needs
|
||||
// once per turn because the info doesn't update between a turn and I don't want to fix it.
|
||||
// Not sure how efficient it is but it seems to be sane, at least.
|
||||
m.HQ.prototype.tryBartering = function(gameState){
|
||||
var done = false;
|
||||
if (gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_market"), true) >= 1) {
|
||||
|
||||
var needs = gameState.ai.queueManager.futureNeeds(gameState);
|
||||
var ress = gameState.ai.queueManager.getAvailableResources(gameState);
|
||||
|
||||
for (var sell in needs) {
|
||||
for (var buy in needs) {
|
||||
if (!done && buy != sell && needs[sell] <= 0 && ress[sell] > 400) { // if we don't need it and have a buffer
|
||||
if ( (ress[buy] < 400) || needs[buy] > 0) { // if we need that other resource/ have too little of it
|
||||
var markets = gameState.getOwnEntitiesByType(gameState.applyCiv("structures/{civ}_market"), true).toEntityArray();
|
||||
markets[0].barter(buy,sell,100);
|
||||
//m.debug ("bartered " +sell +" for " + buy + ", value 100");
|
||||
done = true;
|
||||
}
|
||||
// Try to barter unneeded resources for needed resources.
|
||||
// once per turn because the info doesn't update between a turn and fixing isn't worth it.
|
||||
m.HQ.prototype.tryBartering = function(gameState) {
|
||||
var markets = gameState.getOwnEntitiesByType(gameState.applyCiv("structures/{civ}_market"), true).toEntityArray();
|
||||
|
||||
if (markets.length === 0)
|
||||
return false;
|
||||
|
||||
// Available resources after account substraction
|
||||
var available = gameState.ai.queueManager.getAvailableResources(gameState);
|
||||
|
||||
var rates = this.GetCurrentGatherRates(gameState)
|
||||
|
||||
var prices = gameState.getBarterPrices();
|
||||
// calculates conversion rates
|
||||
var getBarterRate = function (prices,buy,sell) { return Math.round(100 * prices["sell"][sell] / prices["buy"][buy]); };
|
||||
|
||||
// loop through each queues checking if we could barter and finish a queue quickly.
|
||||
for (var j in gameState.ai.queues)
|
||||
{
|
||||
var queue = gameState.ai.queues[j];
|
||||
if (queue.paused || queue.length() === 0)
|
||||
continue;
|
||||
|
||||
var account = gameState.ai.queueManager.accounts[j];
|
||||
var elem = queue.queue[0];
|
||||
var elemCost = elem.getCost();
|
||||
for each (var ress in elemCost.types)
|
||||
{
|
||||
if (available[ress] >= 0)
|
||||
continue; // don't care if we still have available resources or our rate is good enough
|
||||
var need = elemCost[ress] - account[ress];
|
||||
if (need <= 0 || rates[ress] >= need/50) // don't care if we don't need resources for our first item
|
||||
continue;
|
||||
|
||||
if (ress == "food" && need < 400)
|
||||
continue;
|
||||
|
||||
// pick the best resource to barter.
|
||||
var bestToBarter = "";
|
||||
var bestRate = 0;
|
||||
for each (var otherRess in elemCost.types)
|
||||
{
|
||||
if (ress === otherRess)
|
||||
continue;
|
||||
// I wanna keep some
|
||||
if (available[otherRess] < 130 + need)
|
||||
return false;
|
||||
var barterRate = getBarterRate(prices, ress, otherRess);
|
||||
if (barterRate > bestRate)
|
||||
{
|
||||
bestRate = barterRate;
|
||||
bestToBarter = otherRess;
|
||||
}
|
||||
}
|
||||
if (bestToBarter !== "")
|
||||
{
|
||||
markets[0].barter(buy,sell,100);
|
||||
m.debug ("Snipe bartered " + sell +" for " + buy + ", value 100");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// now barter for big needs.
|
||||
var needs = gameState.ai.queueManager.currentNeeds(gameState);
|
||||
for each (var sell in needs.types) {
|
||||
for each (var buy in needs.types) {
|
||||
if (buy != sell && needs[sell] <= 0 && available[sell] > 500) { // if we don't need it and have a buffer
|
||||
if (needs[buy] > rates[buy]*80) { // if we need that other resource terribly.
|
||||
markets[0].barter(buy,sell,100);
|
||||
m.debug ("Gross bartered " +sell +" for " + buy + ", value 100");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// build more houses if needed.
|
||||
|
|
@ -743,9 +788,7 @@ m.HQ.prototype.tryBartering = function(gameState){
|
|||
m.HQ.prototype.buildMoreHouses = function(gameState,queues) {
|
||||
|
||||
if (gameState.getPopulationLimit() < gameState.getPopulationMax()) {
|
||||
|
||||
var numPlanned = queues.house.length();
|
||||
|
||||
if (numPlanned < 3 || (numPlanned < 5 && gameState.getPopulation() > 80))
|
||||
{
|
||||
var plan = new m.ConstructionPlan(gameState, "structures/{civ}_house", { "base" : 1 });
|
||||
|
|
@ -762,13 +805,29 @@ m.HQ.prototype.buildMoreHouses = function(gameState,queues) {
|
|||
freeSlots = gameState.getPopulationLimit() + HouseNb*10 - gameState.getPopulation();
|
||||
if (gameState.getPopulation() > 55 && difficulty > 1)
|
||||
return (freeSlots <= 21);
|
||||
else if (gameState.getPopulation() >= 20 && difficulty !== 0)
|
||||
return (freeSlots <= 16);
|
||||
else if (gameState.getPopulation() >= 30 && difficulty !== 0)
|
||||
return (freeSlots <= 15);
|
||||
else
|
||||
return (freeSlots <= 10);
|
||||
}
|
||||
queues.house.addItem(plan);
|
||||
}
|
||||
if (numPlanned > 0 && this.econState == "townPhasing")
|
||||
{
|
||||
var houseQueue = queues.house.queue;
|
||||
var count = gameState.getOwnStructures().filter(API3.Filters.byClass("Village")).length;
|
||||
count += queues.militaryBuilding.length(); // barracks
|
||||
for (var i = 0; i < numPlanned; ++i)
|
||||
{
|
||||
if (houseQueue[i].isGo(gameState))
|
||||
++count;
|
||||
else if (count < 5)
|
||||
{
|
||||
houseQueue[i].isGo = function () { return true; }
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -809,7 +868,7 @@ m.HQ.prototype.checkBasesRessLevel = function(gameState,queues) {
|
|||
this.outOf[type] = true;
|
||||
} else {
|
||||
// base "-1" means new base.
|
||||
queues.civilCentre.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_civil_centre",{ "base" : -1 }, 0, -1, pos));
|
||||
queues.civilCentre.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_civil_centre",{ "base" : -1 }, pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -824,7 +883,7 @@ m.HQ.prototype.buildDefences = function(gameState, queues){
|
|||
var workersNumber = gameState.getOwnEntitiesByRole("worker", true).filter(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID,"plan"))).length;
|
||||
|
||||
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv('structures/{civ}_defense_tower'), true)
|
||||
+ queues.defenceBuilding.length() < gameState.getEntityLimits()["DefenseTower"] && queues.defenceBuilding.length() < 4 && gameState.currentPhase() > 1) {
|
||||
+ queues.defenceBuilding.length() < gameState.getEntityLimits()["DefenseTower"] && queues.defenceBuilding.length() === 0 && gameState.currentPhase() > 1) {
|
||||
for (var i in this.baseManagers)
|
||||
{
|
||||
for (var j in this.baseManagers[i].dropsites)
|
||||
|
|
@ -836,7 +895,7 @@ m.HQ.prototype.buildDefences = function(gameState, queues){
|
|||
{
|
||||
var position = dpEnt.position();
|
||||
if (position) {
|
||||
queues.defenceBuilding.addItem(new m.ConstructionPlan(gameState, 'structures/{civ}_defense_tower', { "base" : i }, 0 , -1, position));
|
||||
queues.defenceBuilding.addItem(new m.ConstructionPlan(gameState, 'structures/{civ}_defense_tower', { "base" : i }, position));
|
||||
}
|
||||
dpEnt.setMetadata(PlayerID, "defenseTower", true);
|
||||
}
|
||||
|
|
@ -896,19 +955,26 @@ m.HQ.prototype.constructTrainingBuildings = function(gameState, queues) {
|
|||
Engine.ProfileStart("Build buildings");
|
||||
var workersNumber = gameState.getOwnEntitiesByRole("worker", true).filter(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID, "plan"))).length;
|
||||
|
||||
if (workersNumber > this.Config.Military.popForBarracks1) {
|
||||
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0]), true) + queues.militaryBuilding.length() < 1) {
|
||||
var barrackNb = gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0]), true);
|
||||
|
||||
// first barracks.
|
||||
if (workersNumber > this.Config.Military.popForBarracks1 || (this.econState == "townPhasing" && gameState.getOwnStructures().filter(API3.Filters.byClass("Village")).length < 5)) {
|
||||
if (barrackNb + queues.militaryBuilding.length() < 1) {
|
||||
m.debug ("Trying to build barracks");
|
||||
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 }));
|
||||
var plan = new m.ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 });
|
||||
plan.onStart = function(gameState) { gameState.ai.queueManager.changePriority("militaryBuilding", 130); };
|
||||
queues.militaryBuilding.addItem(plan);
|
||||
}
|
||||
}
|
||||
|
||||
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0]), true) < 2 && workersNumber > this.Config.Military.popForBarracks2)
|
||||
|
||||
// second barracks.
|
||||
if (barrackNb < 2 && workersNumber > this.Config.Military.popForBarracks2)
|
||||
if (queues.militaryBuilding.length() < 1)
|
||||
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 }));
|
||||
|
||||
if (gameState.countEntitiesByType(gameState.applyCiv(this.bModerate[0]), true) === 2 && gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0]), true) < 3 && workersNumber > 125)
|
||||
if (queues.militaryBuilding.length() < 1)
|
||||
// third barracks (optional 4th/5th for some civs as they rely on barracks more.)
|
||||
if (barrackNb === 2 && barrackNb + queues.militaryBuilding.length() < 3 && workersNumber > 125)
|
||||
if (queues.militaryBuilding.length() === 0)
|
||||
{
|
||||
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 }));
|
||||
if (gameState.civ() == "gaul" || gameState.civ() == "brit" || gameState.civ() == "iber") {
|
||||
|
|
@ -916,6 +982,7 @@ m.HQ.prototype.constructTrainingBuildings = function(gameState, queues) {
|
|||
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 }));
|
||||
}
|
||||
}
|
||||
|
||||
//build advanced military buildings
|
||||
if (workersNumber >= this.Config.Military.popForBarracks2 - 15 && gameState.currentPhase() > 2){
|
||||
if (queues.militaryBuilding.length() === 0){
|
||||
|
|
@ -930,6 +997,7 @@ m.HQ.prototype.constructTrainingBuildings = function(gameState, queues) {
|
|||
}
|
||||
}
|
||||
}
|
||||
// build second advanced building except for some civs.
|
||||
if (gameState.civ() !== "gaul" && gameState.civ() !== "brit" && gameState.civ() !== "iber" &&
|
||||
workersNumber > 130 && gameState.currentPhase() > 2)
|
||||
{
|
||||
|
|
@ -1062,8 +1130,6 @@ m.HQ.prototype.update = function(gameState, queues, events) {
|
|||
|
||||
this.buildMoreHouses(gameState,queues);
|
||||
|
||||
this.GetCurrentGatherRates(gameState);
|
||||
|
||||
if (gameState.getTimeElapsed() > this.techStartTime && gameState.currentPhase() > 2 )
|
||||
this.tryResearchTechs(gameState,queues);
|
||||
|
||||
|
|
@ -1170,50 +1236,43 @@ m.HQ.prototype.update = function(gameState, queues, events) {
|
|||
}
|
||||
}
|
||||
|
||||
// creating plans after updating because an aborted plan might be reused in that case.
|
||||
|
||||
// TODO: remove the limitation to attacks when on water maps.
|
||||
|
||||
// Note: these indications of "rush" are currently unused.
|
||||
if (gameState.ai.strategy === "rush" && this.startedAttacks["CityAttack"].length !== 0) {
|
||||
// and then we revert.
|
||||
gameState.ai.strategy = "normal";
|
||||
this.Config.Economy.femaleRatio = 0.4;
|
||||
gameState.ai.modules.economy.targetNumWorkers = Math.max(Math.floor(gameState.getPopulationMax()*0.55), 1);
|
||||
} else if (gameState.ai.strategy === "rush" && this.upcomingAttacks["CityAttack"].length === 0)
|
||||
if (!this.waterMap && !this.attackPlansEncounteredWater)
|
||||
{
|
||||
Lalala = new m.CityAttack(gameState, this, this.Config, this.TotalAttackNumber, -1, "rush")
|
||||
this.TotalAttackNumber++;
|
||||
this.upcomingAttacks["CityAttack"].push(Lalala);
|
||||
m.debug ("Starting a little something");
|
||||
} else if (gameState.ai.strategy !== "rush" && !this.waterMap)
|
||||
{
|
||||
// creating plans after updating because an aborted plan might be reused in that case.
|
||||
if (gameState.countEntitiesByType(gameState.applyCiv(this.bModerate[0]), true) >= 1 && !this.attackPlansEncounteredWater
|
||||
&& gameState.getTimeElapsed() > this.attackPlansStartTime && gameState.currentPhase() > 1) {
|
||||
if (gameState.ai.aggressiveness > 0.75 && gameState.countEntitiesByType(gameState.applyCiv(this.bModerate[0]), true) >= 1
|
||||
&& gameState.getTimeElapsed() > this.attackPlansStartTime && gameState.getTimeElapsed() < 360000)
|
||||
{
|
||||
if (this.upcomingAttacks["Rush"].length === 0)
|
||||
{
|
||||
// we have a barracks and we want to rush, rush.
|
||||
var AttackPlan = new m.CityAttack(gameState, this, this.Config, this.TotalAttackNumber, -1, "Rush");
|
||||
m.debug ("Headquarters: Rushing plan " +this.TotalAttackNumber);
|
||||
this.TotalAttackNumber++;
|
||||
this.upcomingAttacks["Rush"].push(AttackPlan);
|
||||
}
|
||||
}
|
||||
// if we have a barracks, there's no water, we're at age >= 1 and we've decided to attack.
|
||||
else if (gameState.countEntitiesByType(gameState.applyCiv(this.bModerate[0]), true) >= 1 && !this.attackPlansEncounteredWater
|
||||
&& gameState.getTimeElapsed() > this.attackPlansStartTime && (gameState.currentPhase() > 1 || gameState.isResearching(gameState.townPhase()))) {
|
||||
if (gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_dock"), true) === 0 && this.waterMap)
|
||||
{
|
||||
// wait till we get a dock.
|
||||
} else {
|
||||
} else if (this.upcomingAttacks["CityAttack"].length === 0) {
|
||||
// basically only the first plan, really.
|
||||
if (this.upcomingAttacks["CityAttack"].length == 0 && gameState.getTimeElapsed() < 12*60000) {
|
||||
var Lalala = new m.CityAttack(gameState, this, this.Config, this.TotalAttackNumber, -1);
|
||||
if (Lalala.failed)
|
||||
{
|
||||
this.attackPlansEncounteredWater = true; // hack
|
||||
} else {
|
||||
m.debug ("Military Manager: Creating the plan " +this.TotalAttackNumber);
|
||||
this.TotalAttackNumber++;
|
||||
this.upcomingAttacks["CityAttack"].push(Lalala);
|
||||
}
|
||||
} else if (this.upcomingAttacks["CityAttack"].length == 0 && this.Config.difficulty !== 0) {
|
||||
var Lalala = new m.CityAttack(gameState, this, this.Config, this.TotalAttackNumber, -1, "superSized");
|
||||
if (Lalala.failed)
|
||||
{
|
||||
this.attackPlansEncounteredWater = true; // hack
|
||||
} else {
|
||||
m.debug ("Military Manager: Creating the super sized plan " +this.TotalAttackNumber);
|
||||
this.TotalAttackNumber++;
|
||||
this.upcomingAttacks["CityAttack"].push(Lalala);
|
||||
}
|
||||
var Lalala = undefined;
|
||||
if (gameState.getTimeElapsed() < 12*60000)
|
||||
Lalala = new m.CityAttack(gameState, this, this.Config, this.TotalAttackNumber, -1);
|
||||
else if (this.Config.difficulty !== 0)
|
||||
Lalala = new m.CityAttack(gameState, this, this.Config, this.TotalAttackNumber, -1, "superSized");
|
||||
|
||||
if (Lalala.failed)
|
||||
this.attackPlansEncounteredWater = true; // hack
|
||||
else {
|
||||
m.debug ("Military Manager: Creating the plan " +this.TotalAttackNumber);
|
||||
this.TotalAttackNumber++;
|
||||
this.upcomingAttacks["CityAttack"].push(Lalala);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ m.NavalManager.prototype.maintainFleet = function(gameState, queues, events) {
|
|||
&& tpNb + queues.ships.length() === 0 && gameState.getTemplate(gameState.applyCiv("units/{civ}_ship_bireme")).available(gameState))
|
||||
{
|
||||
// TODO: check our dock can build the wanted ship types, for Carthage.
|
||||
queues.ships.addItem(new m.TrainingPlan(gameState, "units/{civ}_ship_bireme", { "sea" : i }, 1, 0, -1, 1 ));
|
||||
queues.ships.addItem(new m.TrainingPlan(gameState, "units/{civ}_ship_bireme", { "sea" : i }, 1, 1 ));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,14 +24,12 @@ m.QueueManager = function(Config, queues, priorities) {
|
|||
this.Config = Config;
|
||||
this.queues = queues;
|
||||
this.priorities = priorities;
|
||||
this.account = {};
|
||||
this.accounts = {};
|
||||
|
||||
// the sorting would need to be updated on priority change but there is currently none.
|
||||
// the sorting is updated on priority change.
|
||||
var self = this;
|
||||
this.queueArrays = [];
|
||||
for (var p in this.queues) {
|
||||
this.account[p] = 0;
|
||||
this.accounts[p] = new API3.Resources();
|
||||
this.queueArrays.push([p,this.queues[p]]);
|
||||
}
|
||||
|
|
@ -59,31 +57,22 @@ m.QueueManager.prototype.getTotalAccountedResources = function(gameState) {
|
|||
};
|
||||
|
||||
m.QueueManager.prototype.currentNeeds = function(gameState) {
|
||||
var needs = new API3.Resources();
|
||||
// get out current resources, not removing accounts.
|
||||
var current = this.getAvailableResources(gameState, true);
|
||||
var needed = new API3.Resources();
|
||||
//queueArrays because it's faster.
|
||||
for (var i in this.queueArrays)
|
||||
{
|
||||
var name = this.queueArrays[i][0];
|
||||
var queue = this.queueArrays[i][1];
|
||||
if (queue.length() > 0 && queue.getNext().isGo(gameState))
|
||||
needs.add(queue.getNext().getCost());
|
||||
else if (queue.length() > 0 && !queue.getNext().isGo(gameState))
|
||||
{
|
||||
var cost = queue.getNext().getCost();
|
||||
cost.multiply(0.5);
|
||||
needs.add(cost);
|
||||
}
|
||||
if (queue.length() > 1 && queue.queue[1].isGo(gameState))
|
||||
needs.add(queue.queue[1].getCost());
|
||||
if (queue.length() == 0 || !queue.queue[0].isGo(gameState))
|
||||
continue;
|
||||
// we need resource if the account is smaller than the cost
|
||||
var costs = queue.queue[0].getCost();
|
||||
for each (var ress in costs.types)
|
||||
costs[ress] = Math.max(0, costs[ress] - this.accounts[name][ress]);
|
||||
|
||||
needed.add(costs);
|
||||
}
|
||||
return {
|
||||
"food" : Math.max(25 + needs.food - current.food, 0),
|
||||
"wood" : Math.max(needs.wood - current.wood, 0),
|
||||
"stone" : Math.max(needs.stone - current.stone, 0),
|
||||
"metal" : Math.max(needs.metal - current.metal, 0)
|
||||
};
|
||||
return needed;
|
||||
};
|
||||
|
||||
m.QueueManager.prototype.futureNeeds = function(gameState) {
|
||||
|
|
@ -137,10 +126,6 @@ m.QueueManager.prototype.wantedGatherRates = function(gameState) {
|
|||
var elem = queue.queue[j];
|
||||
var cost = elem.getCost();
|
||||
|
||||
if (qTime < elem.startTime)
|
||||
qTime = elem.startTime;
|
||||
// TODO: what is the else case here?
|
||||
|
||||
if (!elem.isGo(gameState))
|
||||
{
|
||||
// assume we'll be wanted in four minutes.
|
||||
|
|
@ -150,21 +135,12 @@ m.QueueManager.prototype.wantedGatherRates = function(gameState) {
|
|||
qTime += 240000;
|
||||
break; // disregard other stuffs.
|
||||
}
|
||||
if (!elem.endTime)
|
||||
{
|
||||
// Assume we want it in 30 seconds from current time.
|
||||
// Costs are made higher based on priority and lower based on current time.
|
||||
// TODO: work on this.
|
||||
for (var type in qCosts)
|
||||
qCosts[type] += (cost[type] + Math.min(cost[type],this.priorities[name])) / (qTime/time);
|
||||
qTime += 30000;
|
||||
} else {
|
||||
// TODO: work on this.
|
||||
for (var type in qCosts)
|
||||
qCosts[type] += (cost[type] + Math.min(cost[type],this.priorities[name])) / (qTime/time);
|
||||
// TODO: refine based on % completed.
|
||||
qTime += (elem.endTime-elem.startTime);
|
||||
}
|
||||
// Assume we want it in 30 seconds from current time.
|
||||
// Costs are made higher based on priority and lower based on current time.
|
||||
// TODO: work on this.
|
||||
for (var type in qCosts)
|
||||
qCosts[type] += (cost[type] + Math.min(cost[type],this.priorities[name])) / (qTime/time);
|
||||
qTime += 30000; // TODO: this needs a lot more work.
|
||||
}
|
||||
for (var j in qCosts)
|
||||
{
|
||||
|
|
@ -420,51 +396,45 @@ m.QueueManager.prototype.update = function(gameState) {
|
|||
var maxAdd = Math.floor(Math.min(maxNeed[j], toAdd));
|
||||
this.accounts[j][ress] += maxAdd;
|
||||
}
|
||||
}/* else if (ress != "population" && gameState.ai.playedTurn % 5 === 0) {
|
||||
// okay here we haev no resource available. We'll try to shift resources to complete plans if possible.
|
||||
// So basically if 2 queues have resources, and one is higher priority, and it needs resources
|
||||
// We'll shift from the lower priority to the higher if we can complete it.
|
||||
var queues = [];
|
||||
for (var j in this.queues) {
|
||||
if (this.queues[j].length() && this.queues[j].getNext().isGo(gameState) && this.accounts[j][ress] > 0)
|
||||
queues.push(j);
|
||||
}
|
||||
if (queues.length > 1)
|
||||
} else {
|
||||
// We have no available resources, see if we can't "compact" them in one queue.
|
||||
// compare queues 2 by 2, and if one with a higher priority could be completed by our amount, give it.
|
||||
// TODO: this isn't perfect compression.
|
||||
for (var j in this.queues)
|
||||
{
|
||||
// we'll work from the bottom to the top. ie lowest priority will try to give to highest priority.
|
||||
queues.sort(function (a,b) { return (self.priorities[a] < self.priorities[b]); });
|
||||
var under = 0, over = queues.length - 1;
|
||||
while (under !== over)
|
||||
var queue = this.queues[j];
|
||||
var queueCost = queue.maxAccountWanted(gameState);
|
||||
if (this.queues[j].length() === 0 || this.queues[j].paused)
|
||||
continue;
|
||||
|
||||
for (var i in this.queues)
|
||||
{
|
||||
var cost = this.queues[queues[over]].getNext().getCost()[ress];
|
||||
var totalCost = this.queues[queues[over]].maxAccountWanted(gameState)[ress];
|
||||
if (this.accounts[queues[over]] >= cost)
|
||||
{
|
||||
--over; // check the next one.
|
||||
if (i === j)
|
||||
continue;
|
||||
}
|
||||
// need some discrepancy in priorities
|
||||
if (this.priorities[queues[under]] < this.priorities[queues[over]] - 20)
|
||||
var otherQueue = this.queues[i];
|
||||
if (this.priorities[i] >= this.priorities[j] || otherQueue.switched !== 0)
|
||||
continue;
|
||||
|
||||
for (var ress in queueCost)
|
||||
{
|
||||
if (this.accounts[queues[under]] + this.accounts[queues[over]] >= cost)
|
||||
if (this.accounts[j][ress] >= queueCost[ress])
|
||||
continue;
|
||||
if (this.accounts[j][ress] + this.accounts[i][ress] >= queueCost[ress])
|
||||
{
|
||||
var amnt = cost - this.accounts[queues[over]];
|
||||
this.accounts[queues[under]] -= amnt;
|
||||
this.accounts[queues[over]] += amnt;
|
||||
--over;
|
||||
m.debug ("Shifting " + amnt + " from " + queues[under] + " to " +queues[over]);
|
||||
continue;
|
||||
} else {
|
||||
++under;
|
||||
continue;
|
||||
// we would be helped by it. Check if it's worth it.
|
||||
for (var otherRess in queueCost)
|
||||
if (otherRess !== ress && queueCost[otherRess] + 100 >= queueCost[ress])
|
||||
continue;
|
||||
var diff = Math.min(queueCost[ress] - this.accounts[j][ress],this.accounts[i][ress]);
|
||||
this.accounts[j][ress] += diff;
|
||||
this.accounts[i][ress] -= diff;
|
||||
++otherQueue.switched;
|
||||
//warn ("switching " + ress + " from " + i + " to " + j + " in amount " + diff);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// okaaaay.
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
Engine.ProfileStart("Pick items from queues");
|
||||
|
|
@ -487,10 +457,12 @@ m.QueueManager.prototype.update = function(gameState) {
|
|||
{
|
||||
this.accounts[name].subtract(item.getCost());
|
||||
queue.startNext(gameState);
|
||||
otherQueue.switched = 0;
|
||||
}
|
||||
}
|
||||
} else if (queue.length() === 0) {
|
||||
this.accounts[name].reset();
|
||||
otherQueue.switched = 0;
|
||||
}
|
||||
}
|
||||
//m.debug (uneval(this.accounts));
|
||||
|
|
@ -538,7 +510,6 @@ m.QueueManager.prototype.addQueue = function(queueName, priority) {
|
|||
if (this.queues[queueName] == undefined) {
|
||||
this.queues[queueName] = new m.Queue();
|
||||
this.priorities[queueName] = priority;
|
||||
this.account[queueName] = 0;
|
||||
this.accounts[queueName] = new API3.Resources();
|
||||
|
||||
var self = this;
|
||||
|
|
@ -555,7 +526,6 @@ m.QueueManager.prototype.removeQueue = function(queueName) {
|
|||
}
|
||||
delete this.queues[queueName];
|
||||
delete this.priorities[queueName];
|
||||
delete this.account[queueName];
|
||||
delete this.accounts[queueName];
|
||||
|
||||
var self = this;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ var AEGIS = function(m)
|
|||
m.Queue = function() {
|
||||
this.queue = [];
|
||||
this.paused = false;
|
||||
this.switched = 0;
|
||||
};
|
||||
|
||||
m.Queue.prototype.empty = function() {
|
||||
|
|
@ -52,7 +53,7 @@ m.Queue.prototype.maxAccountWanted = function(gameState) {
|
|||
if (this.queue.length > 1 && this.queue[1].isGo(gameState))
|
||||
{
|
||||
var costs = this.queue[1].getCost();
|
||||
costs.multiply(0.8);
|
||||
costs.multiply(0.4);
|
||||
cost.add(costs);
|
||||
}
|
||||
return cost;
|
||||
|
|
|
|||
|
|
@ -1,42 +1,25 @@
|
|||
var AEGIS = function(m)
|
||||
{
|
||||
|
||||
m.ConstructionPlan = function(gameState, type, metadata, startTime, expectedTime, position) {
|
||||
this.type = gameState.applyCiv(type);
|
||||
this.position = position;
|
||||
// Defines a construction plan, ie a building.
|
||||
// We'll try to fing a good position if non has been provided
|
||||
|
||||
this.metadata = metadata;
|
||||
|
||||
this.ID = m.playerGlobals[PlayerID].uniqueIDBOPlans++;
|
||||
|
||||
this.template = gameState.getTemplate(this.type);
|
||||
if (!this.template) {
|
||||
m.ConstructionPlan = function(gameState, type, metadata, position) {
|
||||
if (!m.QueuePlan.call(this, gameState, type, metadata))
|
||||
return false;
|
||||
}
|
||||
|
||||
this.category = "building";
|
||||
this.cost = new API3.Resources(this.template.cost());
|
||||
this.number = 1; // The number of buildings to build
|
||||
|
||||
if (!startTime)
|
||||
this.startTime = 0;
|
||||
else
|
||||
this.startTime = startTime;
|
||||
|
||||
if (!expectedTime)
|
||||
this.expectedTime = -1;
|
||||
else
|
||||
this.expectedTime = expectedTime;
|
||||
this.position = position ? position : 0;
|
||||
|
||||
this.category = "building";
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// return true if we willstart amassing resource for this plan
|
||||
m.ConstructionPlan.prototype.isGo = function(gameState) {
|
||||
return (gameState.getTimeElapsed() > this.startTime);
|
||||
};
|
||||
m.ConstructionPlan.prototype = Object.create(m.QueuePlan.prototype);
|
||||
|
||||
// checks other than resource ones.
|
||||
// TODO: change this.
|
||||
// TODO: if there are specific requirements here, maybe try to do them?
|
||||
m.ConstructionPlan.prototype.canStart = function(gameState) {
|
||||
if (gameState.buildingsBuilt > 0)
|
||||
return false;
|
||||
|
|
@ -81,12 +64,7 @@ m.ConstructionPlan.prototype.start = function(gameState) {
|
|||
}
|
||||
} else
|
||||
builders[0].construct(this.type, pos.x, pos.z, pos.angle, this.metadata);
|
||||
};
|
||||
|
||||
m.ConstructionPlan.prototype.getCost = function() {
|
||||
var costs = new API3.Resources();
|
||||
costs.add(this.cost);
|
||||
return costs;
|
||||
this.onStart(gameState);
|
||||
};
|
||||
|
||||
m.ConstructionPlan.prototype.findGoodPosition = function(gameState) {
|
||||
|
|
@ -119,54 +97,31 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState) {
|
|||
} else {
|
||||
// No position was specified so try and find a sensible place to build
|
||||
gameState.getOwnStructures().forEach(function(ent) {
|
||||
var infl = 32;
|
||||
if (ent.hasClass("CivCentre"))
|
||||
infl *= 4;
|
||||
|
||||
var pos = ent.position();
|
||||
var x = Math.round(pos[0] / cellSize);
|
||||
var z = Math.round(pos[1] / cellSize);
|
||||
|
||||
if (ent.buildCategory() == "Wall") { // no real blockers, but can't build where they are
|
||||
friendlyTiles.addInfluence(x, z, 2,-1000);
|
||||
return;
|
||||
}
|
||||
|
||||
if (template._template.BuildRestrictions.Category === "Field"){
|
||||
if (ent.resourceDropsiteTypes() && ent.resourceDropsiteTypes().indexOf("food") !== -1){
|
||||
if (ent.hasClass("CivCentre"))
|
||||
friendlyTiles.addInfluence(x, z, infl/4, infl);
|
||||
else
|
||||
friendlyTiles.addInfluence(x, z, infl, infl);
|
||||
|
||||
}
|
||||
}else{
|
||||
if (template.genericName() == "House" && ent.genericName() == "House") {
|
||||
friendlyTiles.addInfluence(x, z, 15.0,20,'linear'); // houses are close to other houses
|
||||
if (template.hasClass("Field")) {
|
||||
if (ent.resourceDropsiteTypes() && ent.resourceDropsiteTypes().indexOf("food") !== -1)
|
||||
friendlyTiles.addInfluence(x, z, 20, 50);
|
||||
} else if (template.hasClass("House")) {
|
||||
if (ent.hasClass("House"))
|
||||
{
|
||||
friendlyTiles.addInfluence(x, z, 15,40); // houses are close to other houses
|
||||
alreadyHasHouses = true;
|
||||
} else if (template.hasClass("GarrisonFortress") && ent.genericName() == "House")
|
||||
{
|
||||
} else {
|
||||
friendlyTiles.addInfluence(x, z, 15, -40); // and further away from other stuffs
|
||||
}
|
||||
} else {
|
||||
if (template.hasClass("GarrisonFortress") && ent.genericName() == "House")
|
||||
friendlyTiles.addInfluence(x, z, 30, -50);
|
||||
} else if (template.genericName() == "House") {
|
||||
friendlyTiles.addInfluence(x, z, Math.ceil(infl/4.0),-infl/2.0); // houses are farther away from other buildings but houses
|
||||
} else if (template.hasClass("GarrisonFortress"))
|
||||
{
|
||||
friendlyTiles.addInfluence(x, z, 20, 10);
|
||||
friendlyTiles.addInfluence(x, z, 10, -40, 'linear');
|
||||
} else if (ent.genericName() != "House") // houses have no influence on other buildings
|
||||
{
|
||||
friendlyTiles.addInfluence(x, z, infl);
|
||||
//avoid building too close to each other if possible.
|
||||
friendlyTiles.addInfluence(x, z, 5, -5, 'linear');
|
||||
}
|
||||
// If this is not a field add a negative influence near the CivCentre because we want to leave this
|
||||
// area for fields.
|
||||
if (ent.hasClass("CivCentre") && template.genericName() != "House"){
|
||||
friendlyTiles.addInfluence(x, z, Math.floor(infl/8), Math.floor(-infl/2));
|
||||
} else if (ent.hasClass("CivCentre")) {
|
||||
friendlyTiles.addInfluence(x, z, infl/3.0, infl + 1);
|
||||
friendlyTiles.addInfluence(x, z, Math.ceil(infl/5.0), -(infl/2.0), 'linear');
|
||||
}
|
||||
else if (template.hasClass("Military"))
|
||||
friendlyTiles.addInfluence(x, z, 10, -40);
|
||||
|
||||
// If this is not a field add a negative influence near the CivCentre because we want to leave this
|
||||
// area for fields.
|
||||
if (ent.hasClass("CivCentre"))
|
||||
friendlyTiles.addInfluence(x, z, 20, -20);
|
||||
}
|
||||
});
|
||||
if (this.metadata && this.metadata.base !== undefined)
|
||||
|
|
@ -231,6 +186,5 @@ m.ConstructionPlan.prototype.findGoodPosition = function(gameState) {
|
|||
};
|
||||
};
|
||||
|
||||
|
||||
return m;
|
||||
}(AEGIS);
|
||||
|
|
|
|||
|
|
@ -1,41 +1,21 @@
|
|||
var AEGIS = function(m)
|
||||
{
|
||||
|
||||
m.ResearchPlan = function(gameState, type, startTime, expectedTime, rush) {
|
||||
this.type = type;
|
||||
|
||||
this.ID = m.playerGlobals[PlayerID].uniqueIDBOPlans++;
|
||||
|
||||
this.template = gameState.getTemplate(this.type);
|
||||
if (!this.template || this.template.researchTime === undefined) {
|
||||
m.ResearchPlan = function(gameState, type, rush) {
|
||||
if (!m.QueuePlan.call(this, gameState, type, {}))
|
||||
return false;
|
||||
}
|
||||
this.category = "technology";
|
||||
this.cost = new API3.Resources(this.template.cost(),0);
|
||||
this.number = 1; // Obligatory for compatibility
|
||||
|
||||
if (!startTime)
|
||||
this.startTime = 0;
|
||||
else
|
||||
this.startTime = startTime;
|
||||
|
||||
if (!expectedTime)
|
||||
this.expectedTime = -1;
|
||||
else
|
||||
this.expectedTime = expectedTime;
|
||||
|
||||
if (rush)
|
||||
this.rush = true;
|
||||
else
|
||||
this.rush = false;
|
||||
if (this.template.researchTime === undefined)
|
||||
return false;
|
||||
|
||||
this.category = "technology";
|
||||
|
||||
this.rush = rush ? true : false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// return true if we willstart amassing resource for this plan
|
||||
m.ResearchPlan.prototype.isGo = function(gameState) {
|
||||
return (gameState.getTimeElapsed() > this.startTime);
|
||||
};
|
||||
m.ResearchPlan.prototype = Object.create(m.QueuePlan.prototype);
|
||||
|
||||
m.ResearchPlan.prototype.canStart = function(gameState) {
|
||||
// also checks canResearch
|
||||
|
|
@ -45,10 +25,6 @@ m.ResearchPlan.prototype.canStart = function(gameState) {
|
|||
m.ResearchPlan.prototype.start = function(gameState) {
|
||||
var self = this;
|
||||
|
||||
// TODO: this is special cased for "rush" technologies, ie the town phase
|
||||
// which currently is a 100% focus.
|
||||
gameState.ai.queueManager.unpauseAll();
|
||||
|
||||
//m.debug ("Starting the research plan for " + this.type);
|
||||
var trainers = gameState.findResearchers(this.type).toEntityArray();
|
||||
|
||||
|
|
@ -67,14 +43,8 @@ m.ResearchPlan.prototype.start = function(gameState) {
|
|||
trainers[0].stopAllProduction(0.45);
|
||||
trainers[0].research(this.type);
|
||||
}
|
||||
this.onStart(gameState);
|
||||
};
|
||||
|
||||
m.ResearchPlan.prototype.getCost = function(){
|
||||
var costs = new API3.Resources();
|
||||
costs.add(this.cost);
|
||||
return costs;
|
||||
};
|
||||
|
||||
|
||||
return m;
|
||||
}(AEGIS);
|
||||
|
|
|
|||
|
|
@ -1,44 +1,20 @@
|
|||
var AEGIS = function(m)
|
||||
{
|
||||
|
||||
m.TrainingPlan = function(gameState, type, metadata, number, startTime, expectedTime, maxMerge) {
|
||||
this.type = gameState.applyCiv(type);
|
||||
this.metadata = metadata;
|
||||
|
||||
this.ID = m.playerGlobals[PlayerID].uniqueIDBOPlans++;
|
||||
|
||||
this.template = gameState.getTemplate(this.type);
|
||||
if (!this.template)
|
||||
m.TrainingPlan = function(gameState, type, metadata, number, maxMerge) {
|
||||
if (!m.QueuePlan.call(this, gameState, type, metadata))
|
||||
return false;
|
||||
|
||||
this.category = "unit";
|
||||
this.cost = new API3.Resources(this.template.cost(), this.template._template.Cost.Population);
|
||||
if (!number)
|
||||
this.number = 1;
|
||||
else
|
||||
this.number = number;
|
||||
|
||||
if (!maxMerge)
|
||||
this.maxMerge = 5;
|
||||
else
|
||||
this.maxMerge = maxMerge;
|
||||
this.number = number !== undefined ? number : 1;
|
||||
this.maxMerge = maxMerge !== undefined ? maxMerge : 5;
|
||||
|
||||
if (!startTime)
|
||||
this.startTime = 0;
|
||||
else
|
||||
this.startTime = startTime;
|
||||
|
||||
if (!expectedTime)
|
||||
this.expectedTime = -1;
|
||||
else
|
||||
this.expectedTime = expectedTime;
|
||||
return true;
|
||||
};
|
||||
|
||||
// return true if we willstart amassing resource for this plan
|
||||
m.TrainingPlan.prototype.isGo = function(gameState) {
|
||||
return (gameState.getTimeElapsed() > this.startTime);
|
||||
};
|
||||
m.TrainingPlan.prototype = Object.create(m.QueuePlan.prototype);
|
||||
|
||||
m.TrainingPlan.prototype.canStart = function(gameState) {
|
||||
if (this.invalidTemplate)
|
||||
|
|
@ -73,21 +49,14 @@ m.TrainingPlan.prototype.start = function(gameState) {
|
|||
this.metadata.base = trainers[0].getMetadata(PlayerID,"base");
|
||||
trainers[0].train(this.type, this.number, this.metadata);
|
||||
}
|
||||
this.onStart(gameState);
|
||||
};
|
||||
|
||||
m.TrainingPlan.prototype.getCost = function(){
|
||||
var multCost = new API3.Resources();
|
||||
multCost.add(this.cost);
|
||||
multCost.multiply(this.number);
|
||||
return multCost;
|
||||
};
|
||||
|
||||
m.TrainingPlan.prototype.addItem = function(amount){
|
||||
m.TrainingPlan.prototype.addItem = function(amount) {
|
||||
if (amount === undefined)
|
||||
amount = 1;
|
||||
this.number += amount;
|
||||
};
|
||||
|
||||
|
||||
return m;
|
||||
}(AEGIS);
|
||||
|
|
|
|||
|
|
@ -143,9 +143,9 @@ m.Worker.prototype.update = function(baseManager, gameState) {
|
|||
} else if(subrole === "builder") {
|
||||
|
||||
// check for transport.
|
||||
if (gameState.ai.playedTurn % 5 === 0)
|
||||
/*if (gameState.ai.playedTurn % 5 === 0)
|
||||
{
|
||||
if (this.ent.unitAIOrderData().length && this.ent.unitAIState().split(".")[2] === "APPROACHING" && this.ent.unitAIOrderData()[0]["target"])
|
||||
if (this.ent.unitAIOrderData().length && this.ent.unitAIState().split(".")[2] && this.ent.unitAIState().split(".")[2] === "APPROACHING" && this.ent.unitAIOrderData()[0]["target"])
|
||||
{
|
||||
var ress = gameState.getEntityById(this.ent.unitAIOrderData()[0]["target"]);
|
||||
if (ress !== undefined)
|
||||
|
|
@ -154,12 +154,11 @@ m.Worker.prototype.update = function(baseManager, gameState) {
|
|||
var mIndex = gameState.ai.accessibility.getAccessValue(this.ent.position());
|
||||
if (index !== mIndex && index !== 1)
|
||||
{
|
||||
//gameState.ai.HQ.navalManager.askForTransport(this.ent.id(), this.ent.position(), ress.position());
|
||||
gameState.ai.HQ.navalManager.askForTransport(this.ent.id(), this.ent.position(), ress.position());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}*/
|
||||
|
||||
if (this.ent.unitAIState().split(".")[1] !== "REPAIR") {
|
||||
var target = gameState.getEntityById(this.ent.getMetadata(PlayerID, "target-foundation"));
|
||||
|
|
@ -169,8 +168,7 @@ m.Worker.prototype.update = function(baseManager, gameState) {
|
|||
{
|
||||
if (!this.ent.getMetadata(PlayerID, "keepSubrole"))
|
||||
this.ent.setMetadata(PlayerID, "subrole", "idle");
|
||||
}
|
||||
else
|
||||
} else
|
||||
this.ent.repair(target);
|
||||
}
|
||||
this.startApproachingResourceTime = gameState.getTimeElapsed();
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ m.BaseAI.prototype.Init = function(state, playerID, sharedAI)
|
|||
|
||||
this.timeElapsed = sharedAI.timeElapsed;
|
||||
|
||||
this.barterPrices = sharedAI.barterPrices;
|
||||
|
||||
this.CustomInit(this.gameState, this.sharedScript);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ m.EntityTemplate = m.Class({
|
|||
maxHitpoints: function()
|
||||
{
|
||||
if (this._template.Health !== undefined)
|
||||
return this._template.Health.Max;
|
||||
return GetTechModifiedProperty(this._techModifications, this._template, "Health/Max",+this._template.Health.Max);
|
||||
return 0;
|
||||
},
|
||||
isHealable: function()
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ m.GameState.prototype.init = function(SharedScript, state, player) {
|
|||
this.player = player;
|
||||
this.playerData = this.sharedScript.playersData[this.player];
|
||||
this.techModifications = SharedScript._techModifications[this.player];
|
||||
this.barterPrices = SharedScript.barterPrices;
|
||||
};
|
||||
|
||||
m.GameState.prototype.update = function(SharedScript, state) {
|
||||
|
|
@ -38,6 +39,7 @@ m.GameState.prototype.update = function(SharedScript, state) {
|
|||
this.entities = SharedScript.entities;
|
||||
this.playerData = SharedScript.playersData[this.player];
|
||||
this.techModifications = SharedScript._techModifications[this.player];
|
||||
this.barterPrices = SharedScript.barterPrices;
|
||||
|
||||
this.buildingsBuilt = 0;
|
||||
this.turnCache = {};
|
||||
|
|
@ -111,6 +113,11 @@ m.GameState.prototype.getTimeElapsed = function()
|
|||
return this.timeElapsed;
|
||||
};
|
||||
|
||||
m.GameState.prototype.getBarterPrices = function()
|
||||
{
|
||||
return this.barterPrices;
|
||||
};
|
||||
|
||||
m.GameState.prototype.getTemplate = function(type)
|
||||
{
|
||||
if (this.techTemplates[type] !== undefined)
|
||||
|
|
|
|||
|
|
@ -37,16 +37,18 @@ m.Map.prototype.gamePosToMapPos = function(p){
|
|||
|
||||
m.Map.prototype.point = function(p){
|
||||
var q = this.gamePosToMapPos(p);
|
||||
q[0] = q[0] >= this.width ? this.width : (q[0] < 0 ? 0 : q[0]);
|
||||
q[1] = q[1] >= this.width ? this.width : (q[1] < 0 ? 0 : q[1]);
|
||||
return this.map[q[0] + this.width * q[1]];
|
||||
};
|
||||
m.Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type) {
|
||||
strength = strength ? +strength : +maxDist;
|
||||
type = type ? type : 'linear';
|
||||
|
||||
var x0 = Math.max(0, cx - maxDist);
|
||||
var y0 = Math.max(0, cy - maxDist);
|
||||
var x1 = Math.min(this.width-1, cx + maxDist);
|
||||
var y1 = Math.min(this.height-1, cy + maxDist);
|
||||
var x0 = Math.floor(Math.max(0, cx - maxDist));
|
||||
var y0 = Math.floor(Math.max(0, cy - maxDist));
|
||||
var x1 = Math.floor(Math.min(this.width-1, cx + maxDist));
|
||||
var y1 = Math.floor(Math.min(this.height-1, cy + maxDist));
|
||||
var maxDist2 = maxDist * maxDist;
|
||||
|
||||
var str = 0.0;
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ m.SharedScript.prototype.init = function(state) {
|
|||
this.playersData = state.players;
|
||||
this.territoryMap = state.territoryMap;
|
||||
this.timeElapsed = state.timeElapsed;
|
||||
this.barterPrices = state.barterPrices;
|
||||
|
||||
for (var o in state.players)
|
||||
this._techModifications[o] = state.players[o].techModifications;
|
||||
|
|
@ -175,6 +176,7 @@ m.SharedScript.prototype.onUpdate = function(state)
|
|||
this.playersData = state.players;
|
||||
this.territoryMap = state.territoryMap;
|
||||
this.timeElapsed = state.timeElapsed;
|
||||
this.barterPrices = state.barterPrices;
|
||||
|
||||
for (var o in state.players)
|
||||
this._techModifications[o] = state.players[o].techModifications;
|
||||
|
|
|
|||
|
|
@ -135,6 +135,10 @@ GuiInterface.prototype.GetExtendedSimulationState = function(player)
|
|||
ret.players[i].statistics = cmpPlayerStatisticsTracker.GetStatistics();
|
||||
}
|
||||
|
||||
// Add bartering prices
|
||||
var cmpBarter = Engine.QueryInterface(SYSTEM_ENTITY, IID_Barter);
|
||||
ret.barterPrices = cmpBarter.GetPrices();
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue