0ad/binaries/data/mods/public/simulation/ai/aegis/headquarters.js

1322 lines
48 KiB
JavaScript
Raw Normal View History

var AEGIS = function(m)
{
/* Headquarters
* Deal with high level logic for the AI. Most of the interesting stuff gets done here.
* Some tasks:
-defining RESS needs
-BO decisions.
> training workers
> building stuff (though we'll send that to bases)
> researching
-picking strategy (specific manager?)
-diplomacy (specific manager?)
-planning attacks
-picking new CC locations.
*/
m.HQ = function(Config) {
this.Config = Config;
this.targetNumBuilders = this.Config.Economy.targetNumBuilders; // number of workers we want building stuff
this.dockStartTime = this.Config.Economy.dockStartTime * 1000;
this.techStartTime = this.Config.Economy.techStartTime * 1000;
this.dockFailed = false; // sanity check
this.waterMap = false; // set by the aegis.js file.
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.
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;
this.fortressStartTime = 0;
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" : [], "Rush" : [] };
this.startedAttacks = { "CityAttack" : [], "Rush" : [] };
};
// More initialisation for stuff that needs the gameState
m.HQ.prototype.init = function(gameState, queues){
// initialize base map. Each pixel is a base ID, or 0 if none
this.basesMap = new API3.Map(gameState.sharedScript, new Uint8Array(gameState.getMap().data.length));
this.basesMap.setMaxVal(255);
if (this.Config.Economy.targetNumWorkers)
this.targetNumWorkers = this.Config.Economy.targetNumWorkers;
else if (this.targetNumWorkers === undefined)
this.targetNumWorkers = Math.max(Math.floor(gameState.getPopulationMax()*(0.2 + Math.min(+(this.Config.difficulty)*0.125,0.3))), 1);
// Let's get our initial situation here.
// TODO: improve on this.
// TODO: aknowledge bases, assign workers already.
var ents = gameState.getEntities().filter(API3.Filters.byOwner(PlayerID));
var workersNB = 0;
var hasScout = false;
var treasureAmount = { 'food': 0, 'wood': 0, 'stone': 0, 'metal': 0 };
var hasCC = false;
if (ents.filter(API3.Filters.byClass("CivCentre")).length > 0)
hasCC = true;
workersNB = ents.filter(API3.Filters.byClass("Worker")).length;
if (ents.filter(API3.Filters.byClass("Cavalry")).length > 0)
hasScout = true;
// TODO: take multiple CCs into account.
if (hasCC)
{
var CC = ents.filter(API3.Filters.byClass("CivCentre")).toEntityArray()[0];
for (var i in treasureAmount)
gameState.getResourceSupplies(i).forEach( function (ent) {
if (ent.resourceSupplyType().generic === "treasure" && API3.SquareVectorDistance(ent.position(), CC.position()) < 5000)
treasureAmount[i] += ent.resourceSupplyMax();
});
this.baseManagers[1] = new m.BaseManager(this.Config);
this.baseManagers[1].init(gameState);
this.baseManagers[1].setAnchor(CC);
this.baseManagers[1].initTerritory(this, gameState);
this.baseManagers[1].initGatheringFunctions(this, gameState);
if (m.DebugEnabled())
this.basesMap.dumpIm("basesMap.png");
var self = this;
ents.forEach( function (ent) { //}){
self.baseManagers[1].assignEntity(ent);
});
}
// we now have enough data to decide on a few things.
// TODO: here would be where we pick our initial strategy.
// immediatly build a wood dropsite if possible.
if (this.baseManagers[1])
{
if (gameState.ai.queueManager.getAvailableResources(gameState)["wood"] >= 250)
{
var pos = this.baseManagers[1].findBestDropsiteLocation(gameState, "wood");
if (pos)
{
queues.dropsites.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_storehouse",{ "base" : 1 }, pos));
queues.minorTech.addItem(new m.ResearchPlan(gameState, "gather_capacity_wheelbarrow"));
}
}
}
var map = new API3.Map(gameState.sharedScript, gameState.sharedScript.CCResourceMaps["wood"].map);
if (m.DebugEnabled())
map.dumpIm("map_CC_Wood.png");
//this.reassignIdleWorkers(gameState);
this.navalManager.init(gameState, queues);
this.defenceManager.init(gameState);
// TODO: change that to something dynamic.
var civ = gameState.playerData.civ;
// load units and buildings from the config files
if (civ in this.Config.buildings.moderate){
this.bModerate = this.Config.buildings.moderate[civ];
}else{
this.bModerate = this.Config.buildings.moderate['default'];
}
if (civ in this.Config.buildings.advanced){
this.bAdvanced = this.Config.buildings.advanced[civ];
}else{
this.bAdvanced = this.Config.buildings.advanced['default'];
}
if (civ in this.Config.buildings.fort){
this.bFort = this.Config.buildings.fort[civ];
}else{
this.bFort = this.Config.buildings.fort['default'];
}
for (var i in this.bAdvanced){
this.bAdvanced[i] = gameState.applyCiv(this.bAdvanced[i]);
}
for (var i in this.bFort){
this.bFort[i] = gameState.applyCiv(this.bFort[i]);
}
};
m.HQ.prototype.checkEvents = function (gameState, events, queues) {
// TODO: probably check stuffs like a base destruction.
var CreateEvents = events["Create"];
var ConstructionEvents = events["ConstructionFinished"];
for (var i in CreateEvents)
{
var evt = CreateEvents[i];
// Let's check if we have a building set to create a new base.
if (evt && evt.entity)
{
var ent = gameState.getEntityById(evt.entity);
if (ent === undefined)
continue; // happens when this message is right before a "Destroy" one for the same entity.
if (ent.isOwn(PlayerID) && ent.getMetadata(PlayerID, "base") === -1)
{
// Okay so let's try to create a new base around this.
var bID = m.playerGlobals[PlayerID].uniqueIDBases;
this.baseManagers[bID] = new m.BaseManager(this.Config);
this.baseManagers[bID].init(gameState, events, true);
this.baseManagers[bID].setAnchor(ent);
this.baseManagers[bID].initTerritory(this, gameState);
// 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);
if (builders !== false)
{
builders.forEach(function (worker) {
worker.setMetadata(PlayerID, "base", bID);
worker.setMetadata(PlayerID, "subrole", "builder");
worker.setMetadata(PlayerID, "target-foundation", ent.id());
});
}
}
}
}
for (var i in ConstructionEvents)
{
var evt = ConstructionEvents[i];
// Let's check if we have a building set to create a new base.
// TODO: move to the base manager.
if (evt.newentity)
{
var ent = gameState.getEntityById(evt.newentity);
if (ent === undefined)
continue; // happens when this message is right before a "Destroy" one for the same entity.
if (ent.isOwn(PlayerID) && ent.getMetadata(PlayerID, "baseAnchor") == true)
{
var base = ent.getMetadata(PlayerID, "base");
if (this.baseManagers[base].constructing)
{
this.baseManagers[base].constructing = false;
this.baseManagers[base].initGatheringFunctions(this, gameState);
}
}
}
}
};
// 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.
m.HQ.prototype.trainMoreWorkers = function(gameState, queues)
{
// Get some data.
// Count the workers in the world and in progress
var numFemales = gameState.countEntitiesAndQueuedByType(gameState.applyCiv("units/{civ}_support_female_citizen"), true);
// counting the workers that aren't part of a plan
var numWorkers = 0;
gameState.getOwnUnits().forEach (function (ent) {
if (ent.getMetadata(PlayerID, "role") == "worker" && ent.getMetadata(PlayerID, "plan") == undefined)
numWorkers++;
});
var numInTraining = 0;
gameState.getOwnTrainingFacilities().forEach(function(ent) {
ent.trainingQueue().forEach(function(item) {
if (item.metadata && item.metadata.role && item.metadata.role == "worker" && item.metadata.plan == undefined)
numWorkers += item.count;
numInTraining += item.count;
});
});
var numQueuedF = queues.villager.countQueuedUnits();
var numQueuedS = queues.citizenSoldier.countQueuedUnits();
var numQueued = numQueuedS + numQueuedF;
var numTotal = numWorkers + numQueued;
// If we have too few, train more
// should plan enough to always have females…
// TODO: 15 here should be changed to something more sensible, such as nb of producing buildings.
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));
// Choose whether we want soldiers instead.
// TODO: we might want to adjust our female ratio.
if ((numFemales+numQueuedF)/numTotal > this.femaleRatio && numQueuedS < 20) {
if (numTotal < 35)
template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], [ ["cost",1], ["speed",0.5], ["costsResource", 0.5, "stone"], ["costsResource", 0.5, "metal"]]);
else
template = this.findBestTrainableUnit(gameState, ["CitizenSoldier", "Infantry"], [ ["strength",1] ]);
if (!template)
template = gameState.applyCiv("units/{civ}_support_female_citizen");
else
size = Math.min(5, Math.ceil(numTotal / 12));
}
// TODO: improve that logic.
/*
if (numFemales/numWorkers > this.femaleRatio && numQueuedS > 0 && numWorkers > 25)
queues.villager.paused = true;
else
queues.villager.paused = false;
*/
// TODO: perhaps assign them a default resource and check the base according to that.
// 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, size ));
else
queues.citizenSoldier.addItem(new m.TrainingPlan(gameState, template, { "role" : "worker", "base" : 0 }, size, size));
};
// picks the best template based on parameters and classes
m.HQ.prototype.findBestTrainableUnit = 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) {
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] == "siegeStrength") {
aTopParam += m.getMaxStrength(a[1], "Structure") * param[1];
bTopParam += m.getMaxStrength(b[1], "Structure") * param[1];
}
if (param[0] == "speed") {
aTopParam += a[1].walkSpeed() * param[1];
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];
}
if (param[0] == "canGather") {
// checking against wood, could be anything else really.
if (a[1].resourceGatherRates() && a[1].resourceGatherRates()["wood.tree"])
aTopParam *= param[1];
if (b[1].resourceGatherRates() && b[1].resourceGatherRates()["wood.tree"])
bTopParam *= param[1];
}
}
return -(aTopParam/(aDivParam+1)) + (bTopParam/(bDivParam+1));
});
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.
m.HQ.prototype.tryResearchTechs = function(gameState, queues) {
if (queues.minorTech.length() === 0)
{
var possibilities = gameState.findAvailableTech();
if (possibilities.length === 0)
return;
// randomly pick one. No worries about pairs in that case.
var p = Math.floor((Math.random()*possibilities.length));
queues.minorTech.addItem(new m.ResearchPlan(gameState, possibilities[p][0]));
}
}
// We're given a worker and a resource type
// We'll assign the worker for the best base for that resource type.
// TODO: improve choice alogrithm
m.HQ.prototype.switchWorkerBase = function(gameState, worker, type) {
var bestBase = 0;
for (var i in this.baseManagers)
{
if (this.baseManagers[i].willGather[type] >= 1)
{
if (this.baseManagers[i].accessIndex === this.baseManagers[worker.getMetadata(PlayerID,"base")].accessIndex
|| this.navalManager.canReach(gameState, this.baseManagers[i].accessIndex, this.baseManagers[worker.getMetadata(PlayerID,"base")].accessIndex))
{
bestBase = i;
break;
}
}
}
if (bestBase && bestBase !== worker.getMetadata(PlayerID,"base"))
{
worker.setMetadata(PlayerID,"base",bestBase);
return true;
} else {
return false;
}
};
// returns an entity collection of workers through BaseManager.pickBuilders
// TODO: better the choice algo.
// TODO: also can't get over multiple bases right now.
m.HQ.prototype.bulkPickWorkers = function(gameState, newBaseID, number) {
var accessIndex = this.baseManagers[newBaseID].accessIndex;
if (!accessIndex)
return false;
// sorting bases by whether they are on the same accessindex or not.
var baseBest = m.AssocArraytoArray(this.baseManagers).sort(function (a,b) {
if (a.accessIndex === accessIndex && b.accessIndex !== accessIndex)
return -1;
else if (b.accessIndex === accessIndex && a.accessIndex !== accessIndex)
return 1;
return 0;
});
for (var i in baseBest)
{
if (baseBest[i].workers.length > number)
{
return baseBest[i].pickBuilders(gameState,number);
}
}
return false;
}
// returns the current gather rate
// This is not per-se exact, it performs a few adjustments ad-hoc to account for travel distance, stuffs like that.
m.HQ.prototype.GetCurrentGatherRates = function(gameState) {
var self = this;
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)
this.currentRates[type] = 0;
for (var i in this.baseManagers)
this.baseManagers[i].getGatherRates(gameState, this.currentRates);
return this.currentRates;
};
/* Pick the resource which most needs another worker
* How this works:
* We get the rates we would want to have to be able to deal with our plans
* We get our current rates
* We compare; we pick the one where the discrepancy is highest.
* Need to balance long-term needs and possible short-term needs.
*/
m.HQ.prototype.pickMostNeededResources = function(gameState) {
var self = this;
this.wantedRates = gameState.ai.queueManager.wantedGatherRates(gameState);
var currentRates = {};
for (var type in this.wantedRates)
currentRates[type] = 0;
for (var i in this.baseManagers)
{
var base = this.baseManagers[i];
for (var type in this.wantedRates)
{
if (gameState.turnCache["gathererAssignementCache-" + type])
currentRates[type] += gameState.turnCache["gathererAssignementCache-" + type];
base.gatherersByType(gameState,type).forEach (function (ent) { //}){
var worker = ent.getMetadata(PlayerID, "worker-object");
if (worker)
currentRates[type] += worker.getGatherRate(gameState);
});
}
}
// let's get our ideal number.
var types = Object.keys(this.wantedRates);
types.sort(function(a, b) {
var va = (Math.max(0,self.wantedRates[a] - currentRates[a]))/ (currentRates[a]+1);
var vb = (Math.max(0,self.wantedRates[b] - currentRates[b]))/ (currentRates[b]+1);
// If they happen to be equal (generally this means "0" aka no need), make it fair.
if (va === vb)
return (self.wantedRates[b]/(currentRates[b]+1)) - (self.wantedRates[a]/(currentRates[a]+1));
return vb-va;
});
return types;
};
// If all the CC's are destroyed then build a new one
// TODO: rehabilitate.
m.HQ.prototype.buildNewCC= function(gameState, queues) {
var numCCs = gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_civil_centre"), true);
numCCs += queues.civilCentre.length();
// no use trying to lay foundations that will be destroyed
if (gameState.defcon() > 2)
for (var i = numCCs; i < 1; i++) {
gameState.ai.queueManager.clear();
this.baseNeed["food"] = 0;
this.baseNeed["wood"] = 50;
this.baseNeed["stone"] = 50;
this.baseNeed["metal"] = 50;
queues.civilCentre.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_civil_centre"));
}
return (gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_civil_centre"), true) == 0 && gameState.currentPhase() > 1);
};
// Returns the best position to build a new Civil Centre
// Whose primary function would be to reach new resources of type "resource".
m.HQ.prototype.findBestEcoCCLocation = function(gameState, resource){
var CCPlate = gameState.getTemplate("structures/{civ}_civil_centre");
// This builds a map. The procedure is fairly simple. It adds the resource maps
// (which are dynamically updated and are made so that they will facilitate DP placement)
// Then checks for a good spot in the territory. If none, and town/city phase, checks outside
// The AI will currently not build a CC if it wouldn't connect with an existing CC.
var territory = m.createTerritoryMap(gameState);
var obstructions = m.createObstructionMap(gameState, 0);
obstructions.expandInfluences();
// copy the resource map as initialization.
var friendlyTiles = new API3.Map(gameState.sharedScript, gameState.sharedScript.CCResourceMaps[resource].map, true);
friendlyTiles.setMaxVal(255);
var ents = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")).toEntityArray();
var eEnts = gameState.getEnemyStructures().filter(API3.Filters.byClass("CivCentre")).toEntityArray();
var dps = gameState.getOwnDropsites().toEntityArray();
for (var j = 0; j < friendlyTiles.length; ++j)
{
// We check for our other CCs: the distance must not be too big. Anything bigger will result in scrapping.
// This ensures territorial continuity.
// TODO: maybe whenever I get around to implement multi-base support (details below, requires being part of the team. If you're not, ask wraitii directly by PM).
// (see www.wildfiregames.com/forum/index.php?showtopic=16702&#entry255631 )
// TODO: figure out what I was trying to say above.
var canBuild = true;
var canBuild2 = false;
var pos = [j%friendlyTiles.width+0.5, Math.floor(j/friendlyTiles.width)+0.5];
for (var i in ents)
{
var entPos = ents[i].position();
entPos = [entPos[0]/4.0,entPos[1]/4.0];
var dist = API3.SquareVectorDistance(entPos, pos);
if (dist < 3500 || dist > 7900)
friendlyTiles.map[j] /= 2.0;
if (dist < 2120)
{
canBuild = false;
continue;
} else if (dist < 9200 || this.waterMap)
canBuild2 = true;
}
// checking for bases.
if (this.basesMap.map[j] !== 0)
canBuild = false;
if (!canBuild2)
canBuild = false;
if (canBuild)
{
// Checking for enemy CCs
for (var i in eEnts)
{
var entPos = eEnts[i].position();
entPos = [entPos[0]/4.0,entPos[1]/4.0];
// 7100 works well as a limit.
if (API3.SquareVectorDistance(entPos, pos) < 2500)
{
canBuild = false;
continue;
}
}
}
if (!canBuild)
{
friendlyTiles.map[j] = 0;
continue;
}
for (var i in dps)
{
var dpPos = dps[i].position();
if (dpPos === undefined)
{
// Probably a mauryan elephant, skip
continue;
}
dpPos = [dpPos[0]/4.0,dpPos[1]/4.0];
var dist = API3.SquareVectorDistance(dpPos, pos);
if (dist < 600)
{
friendlyTiles.map[j] = 0;
continue;
} else if (dist < 1500)
friendlyTiles.map[j] /= 2.0;
}
friendlyTiles.map[j] *= 1.5;
for (var i in gameState.sharedScript.CCResourceMaps)
if (friendlyTiles.map[j] !== 0 && i !== "food")
{
var val = friendlyTiles.map[j] + gameState.sharedScript.CCResourceMaps[i].map[j];
if (val < 255)
friendlyTiles.map[j] = val;
else
friendlyTiles.map[j] = 255;
}
}
var best = friendlyTiles.findBestTile(6, obstructions);
var bestIdx = best[0];
if (m.DebugEnabled())
{
friendlyTiles.map[bestIdx] = 270;
friendlyTiles.dumpIm("cc_placement_base_" + gameState.getTimeElapsed() + "_" + resource + "_" + best[1] + ".png",301);
//obstructions.dumpIm("cc_placement_base_" + gameState.getTimeElapsed() + "_" + resource + "_" + best[1] + "_obs.png", 20);
}
// not good enough.
if (best[1] < 60)
return false;
var x = ((bestIdx % friendlyTiles.width) + 0.5) * gameState.cellSize;
var z = (Math.floor(bestIdx / friendlyTiles.width) + 0.5) * gameState.cellSize;
m.debug ("Best for value " + best[1] + " at " + uneval([x,z]));
return [x,z];
};
m.HQ.prototype.buildTemple = function(gameState, queues){
if (gameState.currentPhase() >= 2 ) {
if (queues.economicBuilding.countQueuedUnits() === 0 &&
gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_temple"), true) === 0){
queues.economicBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_temple", { "base" : 1 }));
}
}
};
m.HQ.prototype.buildMarket = function(gameState, queues){
if (gameState.getPopulation() > this.Config.Economy.popForMarket && gameState.currentPhase() >= 2 ) {
if (queues.economicBuilding.countQueuedUnitsWithClass("BarterMarket") === 0 &&
gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_market"), true) === 0){
//only ever build one storehouse/CC/market at a time
queues.economicBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_market", { "base" : 1 }));
}
}
};
// Build a farmstead to go to town phase faster and prepare for research. Only really active on higher diff mode.
m.HQ.prototype.buildFarmstead = function(gameState, queues){
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"));
}
}
};
// TODO: generic this, probably per-base
m.HQ.prototype.buildDock = function(gameState, queues){
if (!this.waterMap || this.dockFailed)
return;
if (gameState.getTimeElapsed() > this.dockStartTime) {
if (queues.economicBuilding.countQueuedUnitsWithClass("NavalMarket") === 0 &&
gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_dock"), true) === 0) {
var tp = ""
if (gameState.civ() == "cart" && gameState.currentPhase() > 1)
tp = "structures/{civ}_super_dock";
else if (gameState.civ() !== "cart")
tp = "structures/{civ}_dock";
if (tp !== "")
{
var remaining = this.navalManager.getUnconnectedSeas(gameState, this.baseManagers[1].accessIndex);
queues.economicBuilding.addItem(new m.ConstructionPlan(gameState, tp, { "base" : 1, "sea" : remaining[0] }));
}
}
}
};
// 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.
// kinda ugly, lots of special cases to both build enough houses but not tooo many…
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 });
// make the difficulty available to the isGo function without having to pass it as argument
var difficulty = this.Config.difficulty;
// change the starting condition to "less than 15 slots left".
plan.isGo = function (gameState) {
var HouseNb = gameState.countEntitiesByType(gameState.applyCiv("foundation|structures/{civ}_house"), true);
var freeSlots = 0;
if (gameState.civ() == "gaul" || gameState.civ() == "brit" || gameState.civ() == "iber")
freeSlots = gameState.getPopulationLimit() + HouseNb*5 - gameState.getPopulation();
else
freeSlots = gameState.getPopulationLimit() + HouseNb*10 - gameState.getPopulation();
if (gameState.getPopulation() > 55 && difficulty > 1)
return (freeSlots <= 21);
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;
}
}
}
}
};
// checks if we have bases for all resource types (bar food for now) or if we need to expand.
m.HQ.prototype.checkBasesRessLevel = function(gameState,queues) {
if (gameState.currentPhase() === 1 && !gameState.isResearching(gameState.townPhase()))
return;
var count = { "wood" : 0, "stone" : 0, "metal" : 0 }
var capacity = { "wood" : 0, "stone" : 0, "metal" : 0 }
var need = { "wood" : true, "stone" : true, "metal" : true };
var posss = [];
for (var i in this.baseManagers)
{
var base = this.baseManagers[i];
for (var type in count)
{
if (base.getResourceLevel(gameState, type, "all") > 1500*Math.max(this.Config.difficulty,2))
count[type]++;
capacity[type] += base.getWorkerCapacity(gameState, type);
if (base.willGather[type] !== 2)
need[type] = false;
}
}
for (var type in count)
{
if (count[type] === 0 || need[type]
|| capacity[type] < gameState.getOwnUnits().filter(API3.Filters.and(API3.Filters.byMetadata(PlayerID, "subrole", "gatherer"), API3.Filters.byMetadata(PlayerID, "gather-type", type))).length * 1.05)
{
// plan a new base.
if (gameState.countFoundationsByType(gameState.applyCiv("structures/{civ}_civil_centre"), true) === 0 && queues.civilCentre.length() === 0) {
if (this.outOf[type] && gameState.ai.playedTurn % 10 !== 0)
continue;
var pos = this.findBestEcoCCLocation(gameState, type);
if (!pos)
{
// Okay so we'll set us as out of this.
this.outOf[type] = true;
} else {
// base "-1" means new base.
queues.civilCentre.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_civil_centre",{ "base" : -1 }, pos));
}
}
}
}
};
// Deals with building fortresses and towers.
// Currently build towers next to every useful dropsites.
// TODO: Fortresses are placed randomly atm.
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() === 0 && gameState.currentPhase() > 1) {
for (var i in this.baseManagers)
{
for (var j in this.baseManagers[i].dropsites)
{
var amnts = this.baseManagers[i].dropsites[j];
var dpEnt = gameState.getEntityById(j);
if (dpEnt !== undefined && dpEnt.getMetadata(PlayerID, "defenseTower") !== true)
if (amnts["wood"] || amnts["metal"] || amnts["stone"])
{
var position = dpEnt.position();
if (position) {
queues.defenceBuilding.addItem(new m.ConstructionPlan(gameState, 'structures/{civ}_defense_tower', { "base" : i }, position));
}
dpEnt.setMetadata(PlayerID, "defenseTower", true);
}
}
}
}
var numFortresses = 0;
for (var i in this.bFort){
numFortresses += gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bFort[i]), true);
}
if (queues.defenceBuilding.length() < 1 && (gameState.currentPhase() > 2 || gameState.isResearching("phase_city_generic")))
{
if (workersNumber >= 80 && gameState.getTimeElapsed() > numFortresses * this.fortressLapseTime + this.fortressStartTime)
{
if (!this.fortressStartTime)
this.fortressStartTime = gameState.getTimeElapsed();
queues.defenceBuilding.addItem(new m.ConstructionPlan(gameState, this.bFort[0], { "base" : 1 }));
m.debug ("Building a fortress");
}
}
if (gameState.countEntitiesByType(gameState.applyCiv(this.bFort[i]), true) >= 1) {
// let's add a siege building plan to the current attack plan if there is none currently.
if (this.upcomingAttacks["CityAttack"].length !== 0)
{
var attack = this.upcomingAttacks["CityAttack"][0];
if (!attack.unitStat["Siege"])
{
// no minsize as we don't want the plan to fail at the last minute though.
var stat = { "priority" : 1.1, "minSize" : 0, "targetSize" : 4, "batchSize" : 2, "classes" : ["Siege"],
"interests" : [ ["siegeStrength", 3], ["cost",1] ] ,"templates" : [] };
if (gameState.civ() == "cart" || gameState.civ() == "maur")
stat["classes"] = ["Elephant"];
attack.addBuildOrder(gameState, "Siege", stat, true);
}
}
}
};
m.HQ.prototype.buildBlacksmith = function(gameState, queues){
if (gameState.getTimeElapsed() > this.Config.Military.timeForBlacksmith*1000) {
if (queues.militaryBuilding.length() === 0 &&
gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_blacksmith"), true) === 0) {
var tp = gameState.getTemplate(gameState.applyCiv("structures/{civ}_blacksmith"));
if (tp.available(gameState))
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_blacksmith", { "base" : 1 }));
}
}
};
// Deals with constructing military buildings (barracks, stables…)
// They are mostly defined by Config.js. This is unreliable since changes could be done easily.
// TODO: We need to determine these dynamically. Also doesn't build fortresses since the above function does that.
// TODO: building placement is bad. Choice of buildings is also fairly dumb.
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;
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");
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);
}
}
// 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 }));
// 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") {
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 }));
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bModerate[0], { "base" : 1 }));
}
}
//build advanced military buildings
if (workersNumber >= this.Config.Military.popForBarracks2 - 15 && gameState.currentPhase() > 2){
if (queues.militaryBuilding.length() === 0){
var inConst = 0;
for (var i in this.bAdvanced)
inConst += gameState.countFoundationsByType(gameState.applyCiv(this.bAdvanced[i]));
if (inConst == 0 && this.bAdvanced && this.bAdvanced.length !== 0) {
var i = Math.floor(Math.random() * this.bAdvanced.length);
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bAdvanced[i]), true) < 1){
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bAdvanced[i], { "base" : 1 }));
}
}
}
}
// build second advanced building except for some civs.
if (gameState.civ() !== "gaul" && gameState.civ() !== "brit" && gameState.civ() !== "iber" &&
workersNumber > 130 && gameState.currentPhase() > 2)
{
var Const = 0;
for (var i in this.bAdvanced)
Const += gameState.countEntitiesByType(gameState.applyCiv(this.bAdvanced[i]), true);
if (inConst == 1) {
var i = Math.floor(Math.random() * this.bAdvanced.length);
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bAdvanced[i]), true) < 1){
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bAdvanced[i], { "base" : 1 }));
queues.militaryBuilding.addItem(new m.ConstructionPlan(gameState, this.bAdvanced[i], { "base" : 1 }));
}
}
}
Engine.ProfileStop();
};
// TODO: use pop(). Currently unused as this is too gameable.
m.HQ.prototype.garrisonAllFemales = function(gameState) {
var buildings = gameState.getOwnStructures().filter(API3.Filters.byCanGarrison()).toEntityArray();
var females = gameState.getOwnUnits().filter(API3.Filters.byClass("Support"));
var cache = {};
females.forEach( function (ent) {
for (var i in buildings)
{
if (ent.position())
{
var struct = buildings[i];
if (!cache[struct.id()])
cache[struct.id()] = 0;
if (struct.garrisoned() && struct.garrisonMax() - struct.garrisoned().length - cache[struct.id()] > 0)
{
ent.garrison(struct);
cache[struct.id()]++;
break;
}
}
}
});
this.hasGarrisonedFemales = true;
};
m.HQ.prototype.ungarrisonAll = function(gameState) {
this.hasGarrisonedFemales = false;
var buildings = gameState.getOwnStructures().filter(API3.Filters.and(API3.Filters.byClass("Structure"),API3.Filters.byCanGarrison())).toEntityArray();
buildings.forEach( function (struct) {
if (struct.garrisoned() && struct.garrisoned().length)
struct.unloadAll();
});
};
m.HQ.prototype.pausePlan = function(gameState, planName) {
for (var attackType in this.upcomingAttacks) {
for (var i in this.upcomingAttacks[attackType]) {
var attack = this.upcomingAttacks[attackType][i];
if (attack.getName() == planName)
attack.setPaused(gameState, true);
}
}
for (var attackType in this.startedAttacks) {
for (var i in this.startedAttacks[attackType]) {
var attack = this.startedAttacks[attackType][i];
if (attack.getName() == planName)
attack.setPaused(gameState, true);
}
}
}
m.HQ.prototype.unpausePlan = function(gameState, planName) {
for (var attackType in this.upcomingAttacks) {
for (var i in this.upcomingAttacks[attackType]) {
var attack = this.upcomingAttacks[attackType][i];
if (attack.getName() == planName)
attack.setPaused(gameState, false);
}
}
for (var attackType in this.startedAttacks) {
for (var i in this.startedAttacks[attackType]) {
var attack = this.startedAttacks[attackType][i];
if (attack.getName() == planName)
attack.setPaused(gameState, false);
}
}
}
m.HQ.prototype.pauseAllPlans = function(gameState) {
for (var attackType in this.upcomingAttacks) {
for (var i in this.upcomingAttacks[attackType]) {
var attack = this.upcomingAttacks[attackType][i];
attack.setPaused(gameState, true);
}
}
for (var attackType in this.startedAttacks) {
for (var i in this.startedAttacks[attackType]) {
var attack = this.startedAttacks[attackType][i];
attack.setPaused(gameState, true);
}
}
}
m.HQ.prototype.unpauseAllPlans = function(gameState) {
for (var attackType in this.upcomingAttacks) {
for (var i in this.upcomingAttacks[attackType]) {
var attack = this.upcomingAttacks[attackType][i];
attack.setPaused(gameState, false);
}
}
for (var attackType in this.startedAttacks) {
for (var i in this.startedAttacks[attackType]) {
var attack = this.startedAttacks[attackType][i];
attack.setPaused(gameState, false);
}
}
}
// Some functions are run every turn
// Others once in a while
m.HQ.prototype.update = function(gameState, queues, events) {
Engine.ProfileStart("Headquarters update");
this.checkEvents(gameState,events,queues);
//this.buildMoreHouses(gameState);
this.trainMoreWorkers(gameState, queues);
// sandbox doesn't expand.
if (this.Config.difficulty !== 0)
this.checkBasesRessLevel(gameState, queues);
this.buildMoreHouses(gameState,queues);
if (gameState.getTimeElapsed() > this.techStartTime && gameState.currentPhase() > 2 )
this.tryResearchTechs(gameState,queues);
if (this.Config.difficulty > 1)
this.tryBartering(gameState);
this.buildFarmstead(gameState, queues);
this.buildMarket(gameState, queues);
// Deactivated: the temple had no useful purpose for the AI now.
//if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_market"), true) === 1)
// this.buildTemple(gameState, queues);
this.buildDock(gameState, queues); // not if not a water map.
Engine.ProfileStart("Constructing military buildings and building defences");
this.constructTrainingBuildings(gameState, queues);
this.buildBlacksmith(gameState, queues);
if(gameState.getTimeElapsed() > this.defenceBuildingTime)
this.buildDefences(gameState, queues);
Engine.ProfileStop();
for (var i in this.baseManagers)
{
this.baseManagers[i].checkEvents(gameState, events, queues)
if ( ( (+i + gameState.ai.playedTurn) % (m.playerGlobals[PlayerID].uniqueIDBases - 1)) === 0)
this.baseManagers[i].update(gameState, queues, events);
}
this.navalManager.update(gameState, queues, events);
this.defenceManager.update(gameState, events, this);
Engine.ProfileStart("Looping through attack plans");
// TODO: bump this into a function.
// TODO: implement some form of check before starting a new attack plans. Sometimes it is not the priority.
if (1) {
for (var attackType in this.upcomingAttacks) {
for (var i = 0;i < this.upcomingAttacks[attackType].length; ++i) {
var attack = this.upcomingAttacks[attackType][i];
// okay so we'll get the support plan
if (!attack.isStarted()) {
var updateStep = attack.updatePreparation(gameState, this,events);
// now we're gonna check if the preparation time is over
if (updateStep === 1 || attack.isPaused() ) {
// just chillin'
} else if (updateStep === 0 || updateStep === 3) {
m.debug ("Military Manager: " +attack.getType() +" plan " +attack.getName() +" aborted.");
if (updateStep === 3) {
this.attackPlansEncounteredWater = true;
m.debug("No attack path found. Aborting.");
}
attack.Abort(gameState, this);
this.upcomingAttacks[attackType].splice(i--,1);
} else if (updateStep === 2) {
var chatText = "I am launching an attack against " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
if (Math.random() < 0.2)
chatText = "Attacking " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
else if (Math.random() < 0.3)
chatText = "I have sent an army against " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
else if (Math.random() < 0.3)
chatText = "I'm starting an attack against " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
gameState.ai.chatTeam(chatText);
m.debug ("Military Manager: Starting " +attack.getType() +" plan " +attack.getName());
attack.StartAttack(gameState,this);
this.startedAttacks[attackType].push(attack);
this.upcomingAttacks[attackType].splice(i--,1);
}
} else {
var chatText = "I am launching an attack against " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
if (Math.random() < 0.2)
chatText = "Attacking " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
else if (Math.random() < 0.3)
chatText = "I have sent an army against " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
else if (Math.random() < 0.3)
chatText = "I'm starting an attack against " + gameState.sharedScript.playersData[attack.targetPlayer].name + ".";
gameState.ai.chatTeam(chatText);
m.debug ("Military Manager: Starting " +attack.getType() +" plan " +attack.getName());
this.startedAttacks[attackType].push(attack);
this.upcomingAttacks[attackType].splice(i--,1);
}
}
}
}
for (var attackType in this.startedAttacks) {
for (var i = 0; i < this.startedAttacks[attackType].length; ++i) {
var attack = this.startedAttacks[attackType][i];
// okay so then we'll update the attack.
if (!attack.isPaused())
{
var remaining = attack.update(gameState,this,events);
if (remaining == 0 || remaining == undefined) {
m.debug ("Military Manager: " +attack.getType() +" plan " +attack.getName() +" is now finished.");
attack.Abort(gameState);
this.startedAttacks[attackType].splice(i--,1);
}
}
}
}
// creating plans after updating because an aborted plan might be reused in that case.
// TODO: remove the limitation to attacks when on water maps.
if (!this.waterMap && !this.attackPlansEncounteredWater)
{
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 if (this.upcomingAttacks["CityAttack"].length === 0) {
// basically only the first plan, really.
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);
}
}
}
}
/*
// very old relic. This should be reimplemented someday so the code stays here.
if (this.HarassRaiding && this.preparingRaidNumber + this.startedRaidNumber < 1 && gameState.getTimeElapsed() < 780000) {
var Lalala = new m.CityAttack(gameState, this,this.totalStartedAttackNumber, -1, "harass_raid");
if (!Lalala.createSupportPlans(gameState, this, )) {
m.debug ("Military Manager: harrassing plan not a valid option");
this.HarassRaiding = false;
} else {
m.debug ("Military Manager: Creating the harass raid plan " +this.totalStartedAttackNumber);
this.totalStartedAttackNumber++;
this.preparingRaidNumber++;
this.currentAttacks.push(Lalala);
}
}
*/
Engine.ProfileStop();
/*
Engine.ProfileStop();
Engine.ProfileStart("Build new Dropsites");
this.buildDropsites(gameState, queues);
Engine.ProfileStop();
if (this.Config.difficulty !== 0)
this.tryBartering(gameState);
this.buildFarmstead(gameState, queues);
this.buildMarket(gameState, queues);
// Deactivated: the temple had no useful purpose for the AI now.
//if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_market"), true === 1)
// this.buildTemple(gameState, queues);
this.buildDock(gameState, queues); // not if not a water map.
*/
Engine.ProfileStop(); // Heaquarters update
};
return m;
}(AEGIS);