Petra: first step of base management rework needed for nomad maps

This was SVN commit r16122.
This commit is contained in:
mimo 2015-01-07 21:40:10 +00:00
parent bb6003ea0c
commit 9b79b5b844
13 changed files with 478 additions and 307 deletions

View file

@ -578,7 +578,7 @@ m.Entity = m.Class({
return this._entity.idle;
},
unitAIState: function() { return this._entity.unitAIState; },
unitAIState: function() { if (this._entity.unitAIState) return this._entity.unitAIState; return undefined; },
unitAIOrderData: function() { return this._entity.unitAIOrderData; },
// TODO understand why we have sometimes rounding problems with maxHitpoints ? making wrongly isHurt=true

View file

@ -36,7 +36,6 @@ m.PetraBot.prototype.CustomInit = function(gameState, sharedScript)
this.turn = this.data.turn;
this.playedTurn = this.data.playedTurn;
this.elapsedTime = this.data.elapsedTime;
this.myIndex = this.data.myIndex;
this.savedEvents = this.data.savedEvents;
for (let key in this.savedEvents)
{
@ -84,11 +83,6 @@ m.PetraBot.prototype.CustomInit = function(gameState, sharedScript)
this.HQ = new m.HQ(this.Config);
var myKeyEntities = gameState.getOwnEntities().filter(API3.Filters.byClass("CivCentre"));
if (myKeyEntities.length == 0)
myKeyEntities = gameState.getOwnEntities();
this.myIndex = this.accessibility.getAccessValue(myKeyEntities.toEntityArray()[0].position());
this.HQ.init(gameState, this.queues);
this.HQ.start(gameState);
@ -161,7 +155,6 @@ m.PetraBot.prototype.Serialize = function()
"turn": this.turn,
"playedTurn": this.playedTurn,
"elapsedTime": this.elapsedTime,
"myIndex": this.myIndex,
"savedEvents": savedEvents,
"config": this.Config.Serialize(),
"queueManager": this.queueManager.Serialize(),

View file

@ -37,7 +37,7 @@ m.AttackPlan = function(gameState, Config, uniqueID, type, data)
// get a starting rallyPoint ... will be improved later
var rallyPoint = undefined;
for each (var base in gameState.ai.HQ.baseManagers)
for (var base of gameState.ai.HQ.baseManagers)
{
if (!base.anchor || !base.anchor.position())
continue;
@ -413,7 +413,7 @@ m.AttackPlan.prototype.updatePreparation = function(gameState, events)
var rallySame = undefined;
var distminDiff = Math.min();
var rallyDiff = undefined;
for each (var base in gameState.ai.HQ.baseManagers)
for (var base of gameState.ai.HQ.baseManagers)
{
var anchor = base.anchor;
if (!anchor || !anchor.position())

View file

@ -78,12 +78,12 @@ m.BaseManager.prototype.setAnchor = function(gameState, anchorEntity)
this.anchor.setMetadata(PlayerID, "baseAnchor", true);
this.buildings.updateEnt(this.anchor);
this.accessIndex = gameState.ai.accessibility.getAccessValue(this.anchor.position());
// in case all our other bases were destroyed, reaffect these destroyed bases to this base
for each (var base in gameState.ai.HQ.baseManagers)
// in case some of our other bases were destroyed, reaffect these destroyed bases to this base
for (var base of gameState.ai.HQ.baseManagers)
{
if (base.anchor || base.newbase)
if (base.anchor || base.newbaseID)
continue;
base.newbase = this.ID;
base.newbaseID = this.ID;
}
return true;
};
@ -108,31 +108,13 @@ m.BaseManager.prototype.checkEvents = function (gameState, events, queues)
// sounds like we lost our anchor. Let's reaffect our units and buildings
this.anchor = undefined;
this.anchorId = undefined;
var distmin = Math.min();
var basemin = undefined;
for each (var base in gameState.ai.HQ.baseManagers)
{
if (!base.anchor)
continue;
var dist = API3.SquareVectorDistance(base.anchor.position(), ent.position());
if (base.accessIndex != this.accessIndex)
dist += 100000000;
if (dist > distmin)
continue;
distmin = dist;
basemin = base;
}
if (!basemin)
{
if (this.Config.debug > 1)
API3.warn(" base " + this.ID + " destroyed and no other bases found");
continue;
}
this.newbase = basemin.ID;
this.units.forEach( function (ent) { ent.setMetadata(PlayerID, "base", basemin.ID); });
this.buildings.forEach( function (ent) { ent.setMetadata(PlayerID, "base", basemin.ID); });
}
let bestbase = m.getBestBase(ent, gameState);
this.newbaseID = bestbase.ID;
for (let entity of this.units.values())
bestbase.assignEntity(entity);
for (let entity of this.buildings.values())
bestbase.assignEntity(entity);
}
}
}
@ -538,14 +520,14 @@ m.BaseManager.prototype.getGatherRates = function(gameState, currentRates)
m.BaseManager.prototype.assignRolelessUnits = function(gameState)
{
// TODO: make this cleverer.
var roleless = this.units.filter(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID, "role")));
var self = this;
roleless.forEach(function(ent) {
var roleless = this.units.filter(API3.Filters.not(API3.Filters.byHasMetadata(PlayerID, "role"))).values();
for (var ent of roleless)
{
if (ent.hasClass("Worker") || ent.hasClass("CitizenSoldier") || ent.hasClass("FishingBoat"))
ent.setMetadata(PlayerID, "role", "worker");
else if (ent.hasClass("Support") && ent.hasClass("Elephant"))
ent.setMetadata(PlayerID, "role", "worker");
});
}
};
// If the numbers of workers on the resources is unbalanced then set some of workers to idle so
@ -754,7 +736,7 @@ m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
if (workers.length < 2)
{
var noobs = gameState.ai.HQ.bulkPickWorkers(gameState, this.ID, 2);
var noobs = gameState.ai.HQ.bulkPickWorkers(gameState, this, 2);
if(noobs)
{
noobs.forEach(function (worker) {
@ -919,19 +901,42 @@ m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
m.BaseManager.prototype.update = function(gameState, queues, events)
{
if (!this.anchor) // this base has been destroyed
if (this.ID === gameState.ai.HQ.baseManagers[0].ID) // base for unaffected units
{
// transfer possible remaining units (probably they were in training during previous transfers)
if (this.newbase)
// if some active base, reassigns the workers/buildings
// otherwise look for anything useful to do, i.e. treasures to gather
if (gameState.ai.HQ.numActiveBase() > 0)
{
var newbase = this.newbase;
this.units.forEach( function (ent) { ent.setMetadata(PlayerID, "base", newbase); });
this.buildings.forEach( function (ent) { ent.setMetadata(PlayerID, "base", newbase); });
for (var ent of this.units.values())
m.getBestBase(ent, gameState).assignEntity(ent, gameState);
for (var ent of this.buildings.values())
m.getBestBase(ent, gameState).assignEntity(ent, gameState);
}
else
{
this.assignRolelessUnits(gameState);
this.reassignIdleWorkers(gameState);
for (var ent of this.workers.values())
this.workerObject.update(ent, gameState);
}
return;
}
if (this.anchor && this.anchor.getMetadata(PlayerID, "access") != this.accessIndex)
if (!this.anchor) // this base has been destroyed
{
// transfer possible remaining units (probably they were in training during previous transfers)
if (this.newbaseID)
{
var newbaseID = this.newbaseID;
for (let ent of this.units.values())
ent.setMetadata(PlayerID, "base", newbaseID);
for (let ent of this.buildings.values())
ent.setMetadata(PlayerID, "base", newbaseID);
}
return;
}
if (this.anchor.getMetadata(PlayerID, "access") != this.accessIndex)
API3.warn("Petra baseManager " + this.ID + " problem with accessIndex " + this.accessIndex
+ " while metadata access is " + this.anchor.getMetadata(PlayerID, "access"));
@ -940,7 +945,7 @@ m.BaseManager.prototype.update = function(gameState, queues, events)
this.checkResourceLevels(gameState, queues);
this.assignToFoundations(gameState);
if (this.constructing && this.anchor)
if (this.constructing)
{
var owner = gameState.ai.HQ.territoryMap.getOwner(this.anchor.position());
if(owner !== 0 && !gameState.isPlayerAlly(owner))
@ -963,13 +968,10 @@ m.BaseManager.prototype.update = function(gameState, queues, events)
this.setWorkersIdleByPriority(gameState);
this.assignRolelessUnits(gameState);
// should probably be last to avoid reallocations of units that would have done stuffs otherwise.
this.reassignIdleWorkers(gameState);
// TODO: do this incrementally a la defense.js
var self = this;
this.workers.forEach(function(ent) { self.workerObject.update(ent, gameState); });
// check if workers can find something useful to do
for (var ent of this.workers.values())
this.workerObject.update(ent, gameState);
Engine.ProfileStop();
};

View file

@ -28,7 +28,7 @@ m.Config = function(difficulty)
"targetNumTraders" : 4, // Target number of traders
"targetNumFishers" : 1, // Target number of fishers per sea
"femaleRatio" : 0.5, // fraction of females among the workforce
"provisionFields" : 3
"provisionFields" : 2
};
this.distUnitGain = 110*110; // TODO take it directly from helpers/TraderGain.js
@ -136,7 +136,7 @@ m.Config.prototype.setConfig = function(gameState)
this.Economy.cityPhase = 1800;
this.Economy.popForMarket = 80;
this.Economy.femaleRatio = 0.6;
this.Economy.provisionFields = 2;
this.Economy.provisionFields = 1;
}
else
{

View file

@ -94,6 +94,43 @@ m.returnResources = function(ent, gameState)
return true;
};
/**
* get the best base (in terms of distance and accessIndex) for an entity
*/
m.getBestBase = function(ent, gameState)
{
var pos = ent.position();
if (!pos)
{
var holder = m.getHolder(ent, gameState);
if (!holder || !holder.position())
{
API3.warn("Petra error: entity without position, but not garrisoned");
m.dumpEntity(ent);
return gameState.ai.HQ.baseManagers[0];
}
pos = holder.position();
}
var distmin = Math.min();
var bestbase = undefined;
var accessIndex = gameState.ai.accessibility.getAccessValue(pos);
for (var base of gameState.ai.HQ.baseManagers)
{
if (!base.anchor)
continue;
var dist = API3.SquareVectorDistance(base.anchor.position(), pos);
if (base.accessIndex !== accessIndex)
dist += 100000000;
if (dist > distmin)
continue;
distmin = dist;
bestbase = base;
}
if (!bestbase)
bestbase = gameState.ai.HQ.baseManagers[0];
return bestbase;
};
m.getHolder = function(ent, gameState)
{
var found = undefined;

View file

@ -37,7 +37,7 @@ m.HQ = function(Config)
this.fortressStartTime = 0;
this.fortressLapseTime = this.Config.Military.fortressLapseTime;
this.baseManagers = {};
this.baseManagers = [];
this.attackManager = new m.AttackManager(this.Config);
this.defenseManager = new m.DefenseManager(this.Config);
this.tradeManager = new m.TradeManager(this.Config);
@ -58,46 +58,10 @@ m.HQ.prototype.init = function(gameState, queues, deserializing)
// initialize frontier map. Each cell is 2 if on the near frontier, 1 on the frontier and 0 otherwise
this.frontierMap = m.createFrontierMap(gameState);
// list of allowed regions
this.allowedRegions = {};
this.landRegions = {};
// try to determine if we have a water map
this.navalMap = false;
this.navalRegions = [];
var passabilityMap = gameState.getMap();
var totalSize = passabilityMap.width * passabilityMap.width;
var minLandSize = Math.floor(0.2*totalSize);
var minWaterSize = Math.floor(0.3*totalSize);
var cellArea = passabilityMap.cellSize * passabilityMap.cellSize;
for (var i = 0; i < gameState.ai.accessibility.regionSize.length; ++i)
{
if (i == gameState.ai.myIndex)
this.allowedRegions[i] = true;
else if (gameState.ai.accessibility.regionType[i] === "land" && cellArea*gameState.ai.accessibility.regionSize[i] > 320)
{
var seaIndex = this.getSeaIndex(gameState, gameState.ai.myIndex, i);
if (!seaIndex)
continue;
this.allowedRegions[i] = true;
if (gameState.ai.accessibility.regionSize[i] > minLandSize)
{
this.navalMap = true;
if (this.navalRegions.indexOf(seaIndex) == -1)
this.navalRegions.push(seaIndex);
}
}
else if (gameState.ai.accessibility.regionType[i] === "water" && gameState.ai.accessibility.regionSize[i] > minWaterSize)
{
this.navalMap = true;
if (this.navalRegions.indexOf(i) == -1)
this.navalRegions.push(i);
}
}
if (this.Config.debug > 2)
{
for (var region in this.allowedRegions)
API3.warn(" >>> zone " + region + " taille " + cellArea*gameState.ai.accessibility.regionSize[region]);
}
this.navalRegions = {};
if (this.Config.difficulty < 2)
this.targetNumWorkers = Math.max(1, Math.min(40, Math.floor(gameState.getPopulationMax())));
@ -117,6 +81,86 @@ m.HQ.prototype.init = function(gameState, queues, deserializing)
if (deserializing)
return;
// determine the main land Index (or water index if none)
let landIndex = undefined;
let seaIndex = undefined;
var ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre"));
for (let cc of ccEnts.values())
{
let land = gameState.ai.accessibility.getAccessValue(cc.position());
if (land > 1)
{
landIndex = land;
break;
}
}
if (!landIndex)
{
for (let ent of gameState.getOwnEntities().values())
{
if (!ent.position() || (!ent.hasClass("Unit") && !ent.trainableEntities()))
continue;
let land = gameState.ai.accessibility.getAccessValue(ent.position());
if (land > 1)
{
landIndex = land;
break;
}
let sea = gameState.ai.accessibility.getAccessValue(ent.position(), true);
if (!seaIndex && sea > 1)
seaIndex = sea;
}
}
if (!landIndex && !seaIndex)
API3.warn("Petra error: it does not know how to interpret this map");
var passabilityMap = gameState.getMap();
var totalSize = passabilityMap.width * passabilityMap.width;
var minLandSize = Math.floor(0.1*totalSize);
var minWaterSize = Math.floor(0.3*totalSize);
var cellArea = passabilityMap.cellSize * passabilityMap.cellSize;
for (var i = 0; i < gameState.ai.accessibility.regionSize.length; ++i)
{
if (landIndex && i == landIndex)
this.landRegions[i] = true;
else if (gameState.ai.accessibility.regionType[i] === "land" && cellArea*gameState.ai.accessibility.regionSize[i] > 320)
{
if (landIndex)
{
var sea = this.getSeaIndex(gameState, landIndex, i);
if (sea && (gameState.ai.accessibility.regionSize[i] > minLandSize || gameState.ai.accessibility.regionSize[sea] > minWaterSize))
{
this.navalMap = true;
this.landRegions[i] = true;
this.navalRegions[sea] = true;
}
}
else
{
var traject = gameState.ai.accessibility.getTrajectToIndex(seaIndex, i);
if (traject && traject.length === 2)
{
this.navalMap = true;
this.landRegions[i] = true;
this.navalRegions[seaIndex] = true;
}
}
}
else if (gameState.ai.accessibility.regionType[i] === "water" && gameState.ai.accessibility.regionSize[i] > minWaterSize)
{
this.navalMap = true;
this.navalRegions[i] = true;
}
}
if (this.Config.debug > 2)
{
for (var region in this.landRegions)
API3.warn(" >>> zone " + region + " taille " + cellArea*gameState.ai.accessibility.regionSize[region]);
API3.warn(" navalMap " + this.navalMap);
API3.warn(" landRegions " + uneval(this.landRegions));
API3.warn(" navalRegions " + uneval(this.navalRegions));
}
// TODO: change that to something dynamic.
var civ = gameState.playerData.civ;
// load units and buildings from the config files
@ -135,67 +179,104 @@ m.HQ.prototype.init = function(gameState, queues, deserializing)
this.bAdvanced[i] = gameState.applyCiv(this.bAdvanced[i]);
// Let's get our initial situation here.
var b0 = gameState.ai.uniqueIDs.bases;
var ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")).toEntityArray();
for (let i = 0; i < ccEnts.length; ++i)
let nobase = new m.BaseManager(gameState, this.Config);
nobase.init(gameState);
nobase.accessIndex = 0;
this.baseManagers.push(nobase); // baseManager[0] will deal with unit/structure witout bases
var ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre"));
for (let cc of ccEnts.values())
{
this.baseManagers[i+b0] = new m.BaseManager(gameState, this.Config);
this.baseManagers[i+b0].init(gameState);
this.baseManagers[i+b0].setAnchor(gameState, ccEnts[i]);
let newbase = new m.BaseManager(gameState, this.Config);
newbase.init(gameState);
newbase.setAnchor(gameState, cc);
this.baseManagers.push(newbase);
}
this.updateTerritories(gameState);
if (this.baseManagers[b0]) // Assign entities in the different bases
// Assign entities in the different bases
var defaultbase = (this.numActiveBase() > 0) ? 1 : 0;
var width = gameState.getMap().width;
for (var ent of gameState.getOwnEntities().values())
{
var self = this;
var width = gameState.getMap().width;
gameState.getOwnEntities().forEach( function (ent) {
// do not affect merchant ship immediately to trade as they may-be useful for transport
if (ent.hasClass("Trader") && !ent.hasClass("Ship"))
self.tradeManager.assignTrader(ent);
var pos = ent.position();
if (!pos)
{
// TODO temporarily assigned to base 1. Certainly a garrisoned unit,
// should assign it to the base of the garrison holder
self.baseManagers[b0].assignEntity(ent);
return;
}
ent.setMetadata(PlayerID, "access", gameState.ai.accessibility.getAccessValue(ent.position()));
var x = Math.round(pos[0] / gameState.cellSize);
var z = Math.round(pos[1] / gameState.cellSize);
var id = x + width*z;
for each (var base in self.baseManagers)
{
if (base.territoryIndices.indexOf(id) == -1)
continue;
base.assignEntity(ent);
if (ent.resourceDropsiteTypes() && !ent.hasClass("Elephant"))
base.assignResourceToDropsite(gameState, ent);
return;
}
// entity outside our territory, assign it to base b0
self.baseManagers[b0].assignEntity(ent);
// make sure we have not rejected small regions with units (TODO should probably also check with other non-gaia units)
if (ent.position())
{
let pos = gameState.ai.accessibility.gamePosToMapPos(ent.position());
let index = pos[0] + pos[1]*gameState.ai.accessibility.width;
let land = gameState.ai.accessibility.landPassMap[index];
if (land > 1 && !this.landRegions[land])
API3.warn("entity " + ent.genericName() + " in a not allowed land region " + land);
if (land > 1 && !this.landRegions[land])
this.landRegions[land] = true;
let sea = gameState.ai.accessibility.navalPassMap[index];
if (sea > 1 && !this.navalRegions[sea])
API3.warn("entity " + ent.genericName() + " in a not allowed sea region " + sea);
if (sea > 1 && !this.navalRegions[sea])
this.navalRegions[sea] = true;
}
// if garrisoned units inside, ungarrison them except if a ship in which case we make a transport
if (ent.isGarrisonHolder() && ent.garrisoned().length)
{
API3.warn(">>>>>>>>>>>>>>>> ent " + ent.id() + " name " + ent.genericName());
m.dumpEntity(ent);
API3.warn(" garrisoned " + uneval(ent.garrisoned()));
if (!ent.hasClass("Ship"))
for (let id of ent.garrisoned())
ent.unload(id);
else
warn(" ship garrisoned >>> create a transport");
}
// do not affect merchant ship immediately to trade as they may-be useful for transport
if (ent.hasClass("Trader") && !ent.hasClass("Ship"))
this.tradeManager.assignTrader(ent);
var pos = ent.position();
if (!pos)
continue;
ent.setMetadata(PlayerID, "access", gameState.ai.accessibility.getAccessValue(pos));
var x = Math.round(pos[0] / gameState.cellSize);
var z = Math.round(pos[1] / gameState.cellSize);
var id = x + width*z;
var bestbase = undefined;
for (var i = 1; i < this.baseManagers.length; ++i)
{
var base = this.baseManagers[i];
if (base.territoryIndices.indexOf(id) == -1)
continue;
base.assignEntity(ent);
if (ent.resourceDropsiteTypes() && !ent.hasClass("Elephant"))
self.baseManagers[b0].assignResourceToDropsite(gameState, ent);
});
base.assignResourceToDropsite(gameState, ent);
bestbase = base;
break;
}
if (!bestbase)
{
// entity outside our territory
var bestbase = m.getBestBase(ent, gameState);
bestbase.assignEntity(ent);
if (bestbase.ID !== this.baseManagers[0].ID && ent.resourceDropsiteTypes() && !ent.hasClass("Elephant"))
bestbase.assignResourceToDropsite(gameState, ent);
}
// now assign entities garrisoned inside this entity
if (ent.isGarrisonHolder() && ent.garrisoned().length)
for (let id of ent.garrisoned())
bestBase.assignEntity(gameState.getEntityByID(id));
}
// we now have enough data to decide on a few things.
// immediatly build a wood dropsite if possible.
if (this.baseManagers[1] && gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_storehouse"), true) == 0)
if (this.baseManagers.length > 1 && gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_storehouse"), true) == 0)
{
var newDP = this.baseManagers[1].findBestDropsiteLocation(gameState, "wood");
if (newDP.quality > 40 && this.canBuild(gameState, "structures/{civ}_storehouse"))
queues.dropsites.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_storehouse", { "base": 1 }, newDP.pos));
queues.dropsites.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_storehouse", { "base": this.baseManagers[1].ID }, newDP.pos));
}
// Check if we will ever be able to produce units
this.canBuildUnits = true;
if (!gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")).length)
{
var template = gameState.getTemplate(this.bBase[0]);
var template = gameState.getTemplate(gameState.applyCiv("structures/{civ}_civil_centre"));
if (!template.available(gameState))
{
if (this.Config.debug > 1)
@ -290,26 +371,26 @@ m.HQ.prototype.start = function(gameState, deserializing)
{
// Rebuild the base maps from the territory indices of each base
this.basesMap = new API3.Map(gameState.sharedScript, "territory");
for each (let base in this.baseManagers)
for (let base of this.baseManagers)
for (let j of base.territoryIndices)
this.basesMap.map[j] = base.ID;
var self = this;
gameState.getOwnEntities().forEach( function (ent) {
for (let ent of gameState.getOwnEntities().values())
{
if (!ent.resourceDropsiteTypes() || ent.hasClass("Elephant"))
return;
let base = self.baseManagers[ent.getMetadata(PlayerID, "base")];
continue;
let base = this.getBaseByID(ent.getMetadata(PlayerID, "base"));
base.assignResourceToDropsite(gameState, ent);
});
}
return;
}
// adapt our starting strategy to the available resources
// - if on a small island, favor fishing and require less fields to save room for buildings
var startingSize = 0;
for (var region in this.allowedRegions)
for (let region in this.landRegions)
{
for each (var base in this.baseManagers)
for (let base of this.baseManagers)
{
if (!base.anchor || base.accessIndex != +region)
continue;
@ -332,7 +413,7 @@ m.HQ.prototype.start = function(gameState, deserializing)
var check = {};
for (var proxim of ["nearby", "medium", "faraway"])
{
for each (var base in this.baseManagers)
for (let base of this.baseManagers)
{
for (var supply of base.dropsiteSupplies["wood"][proxim])
{
@ -385,17 +466,16 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues)
if (ent.getMetadata(PlayerID, "base") == -1)
{
// Okay so let's try to create a new base around this.
var bID = gameState.ai.uniqueIDs.bases;
this.baseManagers[bID] = new m.BaseManager(gameState, this.Config);
this.baseManagers[bID].init(gameState, true);
this.baseManagers[bID].setAnchor(gameState, ent);
// Let's get a few units out there to build this.
var builders = this.bulkPickWorkers(gameState, bID, 10);
let newbase = new m.BaseManager(gameState, this.Config);
newbase.init(gameState, true);
newbase.setAnchor(gameState, ent);
this.baseManagers.push(newbase);
// Let's get a few units from other bases there to build this.
var builders = this.bulkPickWorkers(gameState, newbase, 10);
if (builders !== false)
{
builders.forEach(function (worker) {
worker.setMetadata(PlayerID, "base", bID);
worker.setMetadata(PlayerID, "base", newbase.ID);
worker.setMetadata(PlayerID, "subrole", "builder");
worker.setMetadata(PlayerID, "target-foundation", ent.id());
});
@ -403,15 +483,13 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues)
}
else if (ent.hasClass("Wonder") && gameState.getGameType() === "wonder")
{
var base = ent.getMetadata(PlayerID, "base");
if (!base)
API3.warn("Petra: wonder foundation without base ???");
// Let's get a few units out there to build this.
// Let's get a few units from other bases there to build this.
var base = this.getBaseByID(ent.getMetadata(PlayerID, "base"));
var builders = this.bulkPickWorkers(gameState, base, 10);
if (builders !== false)
{
builders.forEach(function (worker) {
worker.setMetadata(PlayerID, "base", base);
worker.setMetadata(PlayerID, "base", baseID);
worker.setMetadata(PlayerID, "subrole", "builder");
worker.setMetadata(PlayerID, "target-foundation", ent.id());
});
@ -432,12 +510,12 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues)
if (ent.getMetadata(PlayerID, "baseAnchor") == true)
{
var base = ent.getMetadata(PlayerID, "base");
if (this.baseManagers[base].constructing)
this.baseManagers[base].constructing = false;
this.baseManagers[base].anchor = ent;
this.baseManagers[base].anchorId = evt.newentity;
this.baseManagers[base].buildings.updateEnt(ent);
let base = this.getBaseByID(ent.getMetadata(PlayerID, "base"));
if (base.constructing)
base.constructing = false;
base.anchor = ent;
base.anchorId = evt.newentity;
base.buildings.updateEnt(ent);
this.updateTerritories(gameState);
// let us hope this new base will fix our resource shortage
this.saveResources = undefined;
@ -691,13 +769,13 @@ m.HQ.prototype.findBestTrainableUnit = function(gameState, classes, requirements
// returns an entity collection of workers through BaseManager.pickBuilders
// TODO: when same accessIndex, sort by distance
m.HQ.prototype.bulkPickWorkers = function(gameState, newBaseID, number)
m.HQ.prototype.bulkPickWorkers = function(gameState, baseRef, number)
{
var accessIndex = this.baseManagers[newBaseID].accessIndex;
var accessIndex = baseRef.accessIndex;
if (!accessIndex)
return false;
// sorting bases by whether they are on the same accessindex or not.
var baseBest = API3.AssocArraytoArray(this.baseManagers).sort(function (a,b) {
var baseBest = this.baseManagers.slice().sort(function (a,b) {
if (a.accessIndex == accessIndex && b.accessIndex != accessIndex)
return -1;
else if (b.accessIndex == accessIndex && a.accessIndex != accessIndex)
@ -709,7 +787,7 @@ m.HQ.prototype.bulkPickWorkers = function(gameState, newBaseID, number)
var workers = new API3.EntityCollection(gameState.sharedScript);
for (let base of baseBest)
{
if (base.ID === newBaseID)
if (base.ID === baseRef.ID)
continue;
base.pickBuilders(gameState, workers, needed);
if (workers.length < number)
@ -725,7 +803,7 @@ m.HQ.prototype.bulkPickWorkers = function(gameState, newBaseID, number)
m.HQ.prototype.getTotalResourceLevel = function(gameState)
{
var total = { "food": 0, "wood": 0, "stone": 0, "metal": 0 };
for each (var base in this.baseManagers)
for (var base of this.baseManagers)
for (var type in total)
total[type] += base.getResourceLevel(gameState, type);
@ -739,7 +817,7 @@ m.HQ.prototype.GetCurrentGatherRates = function(gameState)
for (var type in this.wantedRates)
this.currentRates[type] = 0;
for each (var base in this.baseManagers)
for (var base of this.baseManagers)
base.getGatherRates(gameState, this.currentRates);
return this.currentRates;
@ -755,8 +833,6 @@ m.HQ.prototype.GetCurrentGatherRates = function(gameState)
*/
m.HQ.prototype.pickMostNeededResources = function(gameState)
{
var self = this;
this.wantedRates = gameState.ai.queueManager.wantedGatherRates(gameState);
var currentRates = this.GetCurrentGatherRates(gameState);
@ -813,7 +889,7 @@ m.HQ.prototype.findEconomicCCLocation = function(gameState, template, resource,
continue;
// We require that it is accessible
var index = gameState.ai.accessibility.landPassMap[j];
if (!this.allowedRegions[index])
if (!this.landRegions[index])
continue;
// and with enough room around to build the cc
var i = API3.getMaxMapIndex(j, this.territoryMap, obstructions);
@ -847,7 +923,7 @@ m.HQ.prototype.findEconomicCCLocation = function(gameState, template, resource,
}
if (norm == 0)
continue;
if (minDist > 170000 && !this.navalMap) // Reject if too far from any allied cc (-> not connected)
/* rototo if (minDist > 170000 && !this.navalMap) // Reject if too far from any allied cc (-> not connected)
{
norm = 0;
continue;
@ -863,7 +939,7 @@ m.HQ.prototype.findEconomicCCLocation = function(gameState, template, resource,
}
else
norm *= 0.5;
}
} */
for (var dp of dpList)
{
@ -911,7 +987,7 @@ m.HQ.prototype.findEconomicCCLocation = function(gameState, template, resource,
// Define a minimal number of wanted ships in the seas reaching this new base
var index = gameState.ai.accessibility.landPassMap[i];
for each (var base in this.baseManagers)
for (var base of this.baseManagers)
{
if (!base.anchor || base.accessIndex === index)
continue;
@ -966,7 +1042,7 @@ m.HQ.prototype.findStrategicCCLocation = function(gameState, template)
continue;
// We require that it is accessible
var index = gameState.ai.accessibility.landPassMap[j];
if (!this.allowedRegions[index])
if (!this.landRegions[index])
continue;
// and with enough room around to build the cc
var i = API3.getMaxMapIndex(j, this.territoryMap, obstructions);
@ -1046,7 +1122,7 @@ m.HQ.prototype.findStrategicCCLocation = function(gameState, template)
// Define a minimal number of wanted ships in the seas reaching this new base
var index = gameState.ai.accessibility.landPassMap[i];
for each (var base in this.baseManagers)
for (var base of this.baseManagers)
{
if (!base.anchor || base.accessIndex === index)
continue;
@ -1095,7 +1171,7 @@ m.HQ.prototype.findMarketLocation = function(gameState, template)
if (obstructions.map[i] <= radius)
continue;
var index = gameState.ai.accessibility.landPassMap[i];
if (!this.allowedRegions[index])
if (!this.landRegions[index])
continue;
var pos = [cellSize * (j%width+0.5), cellSize * (Math.floor(j/width)+0.5)];
@ -1397,7 +1473,13 @@ m.HQ.prototype.checkBaseExpansion = function(gameState, queues)
{
if (queues.civilCentre.length() > 0)
return;
// first expand if we have not enough room available for buildings
// first build one cc if none already available
if (this.numActiveBase() < 1)
{
this.buildNewBase(gameState, queues);
return;
}
// then expand if we have not enough room available for buildings
if (this.stopBuilding.length > 1)
{
if (this.Config.debug > 2)
@ -1413,24 +1495,25 @@ m.HQ.prototype.checkBaseExpansion = function(gameState, queues)
if (Math.floor(numUnits/popForBase) >= gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")).length)
{
if (this.Config.debug > 2)
API3.warn("try to build a new base because of population " + numUnits + " for " + numCCs + " CCs");
API3.warn("try to build a new base because of population " + numUnits + " for " + this.numActiveBase() + " CCs");
this.buildNewBase(gameState, queues);
}
};
m.HQ.prototype.buildNewBase = function(gameState, queues, type)
{
if (gameState.currentPhase() == 1 && !gameState.isResearching(gameState.townPhase()))
if (this.numActiveBase() > 0 && gameState.currentPhase() == 1 && !gameState.isResearching(gameState.townPhase()))
return false;
if (gameState.countFoundationsByType(this.bBase[0], true) > 0 || queues.civilCentre.length() > 0)
var template = (this.numActiveBase() > 0) ? this.bBase[0] : gameState.applyCiv("structures/{civ}_civil_centre");
if (gameState.countFoundationsByType(template, true) > 0 || queues.civilCentre.length() > 0)
return false;
if (!this.canBuild(gameState, this.bBase[0]))
if (!this.canBuild(gameState, template))
return false;
// base "-1" means new base.
if (this.Config.debug > 1)
API3.warn("new base planned with type " + type);
queues.civilCentre.addItem(new m.ConstructionPlan(gameState, this.bBase[0], { "base": -1, "type": type }));
queues.civilCentre.addItem(new m.ConstructionPlan(gameState, template, { "base": -1, "type": type }));
return true;
};
@ -1634,7 +1717,7 @@ m.HQ.prototype.trainEmergencyUnits = function(gameState, positions)
{
var access = gameState.ai.accessibility.getAccessValue(pos);
// check nearest base anchor
for each (var base in this.baseManagers)
for (var base of this.baseManagers)
{
if (!base.anchor || !base.anchor.position())
continue;
@ -1801,14 +1884,14 @@ m.HQ.prototype.updateTerritories = function(gameState)
{
if (this.basesMap.map[j] == 0)
continue;
var baseID = this.basesMap.map[j];
var index = this.baseManagers[baseID].territoryIndices.indexOf(j);
var base = this.getBaseByID(this.basesMap.map[j]);
var index = base.territoryIndices.indexOf(j);
if (index == -1)
{
API3.warn(" problem in headquarters::updateTerritories for base " + baseID);
API3.warn(" problem in headquarters::updateTerritories for base " + this.basesMap.map[j]);
continue;
}
this.baseManagers[baseID].territoryIndices.splice(index, 1);
base.territoryIndices.splice(index, 1);
this.basesMap.map[j] = 0;
}
else if (this.basesMap.map[j] == 0)
@ -1818,7 +1901,7 @@ m.HQ.prototype.updateTerritories = function(gameState)
var access;
for (var k of ind)
{
if (!this.allowedRegions[gameState.ai.accessibility.landPassMap[k]])
if (!this.landRegions[gameState.ai.accessibility.landPassMap[k]])
continue;
landPassable = true;
access = gameState.ai.accessibility.landPassMap[k];
@ -1829,7 +1912,7 @@ m.HQ.prototype.updateTerritories = function(gameState)
var distmin = Math.min();
var baseID = undefined;
var pos = [cellSize * (j%width+0.5), cellSize * (Math.floor(j/width)+0.5)];
for each (var base in this.baseManagers)
for (var base of this.baseManagers)
{
if (!base.anchor || !base.anchor.position())
continue;
@ -1843,7 +1926,7 @@ m.HQ.prototype.updateTerritories = function(gameState)
}
if (!baseID)
continue;
this.baseManagers[baseID].territoryIndices.push(j);
this.getBaseByID(baseID).territoryIndices.push(j);
this.basesMap.map[j] = baseID;
expansion++;
}
@ -1861,11 +1944,37 @@ m.HQ.prototype.updateTerritories = function(gameState)
this.tradeManager.routeProspection = true;
};
/**
* returns the base corresponding to baseID
*/
m.HQ.prototype.getBaseByID = function(baseID)
{
for (let base of this.baseManagers)
if (base.ID === baseID)
return base;
API3.warn("Petra error: no base found with ID " + baseID);
return undefined;
};
/**
* returns the number of active (i.e. with one cc) bases
* TODO should be cached
*/
m.HQ.prototype.numActiveBase = function()
{
let num = 0;
for (let base of this.baseManagers)
if (base.anchor)
++num;
return num;
};
// Count gatherers returning resources in the number of gatherers of resourceSupplies
// to prevent the AI always reaffecting idle workers to these resourceSupplies (specially in naval maps).
m.HQ.prototype.assignGatherers = function(gameState)
{
for each (var base in this.baseManagers)
for (var base of this.baseManagers)
{
base.workers.forEach( function (worker) {
if (worker.unitAIState().split(".")[1] !== "RETURNRESOURCE")
@ -1933,7 +2042,7 @@ m.HQ.prototype.update = function(gameState, queues, events)
if (gameState.getGameType() === "wonder")
this.buildWonder(gameState, queues);
if (this.baseManagers[1])
if (this.numActiveBase() > 0)
{
this.trainMoreWorkers(gameState, queues);
@ -1947,12 +2056,12 @@ m.HQ.prototype.update = function(gameState, queues, events)
this.researchManager.update(gameState, queues);
}
if (this.numActiveBase() < 1 ||
(this.Config.difficulty > 0 && gameState.ai.playedTurn % 10 == 7 && gameState.currentPhase() > 1))
this.checkBaseExpansion(gameState, queues);
if (gameState.currentPhase() > 1)
{
// sandbox doesn't expand
if (this.Config.difficulty > 0 && gameState.ai.playedTurn % 10 == 7)
this.checkBaseExpansion(gameState, queues);
if (!this.saveResources)
{
this.buildMarket(gameState, queues);
@ -1974,15 +2083,17 @@ m.HQ.prototype.update = function(gameState, queues, events)
this.buildDefenses(gameState, queues);
this.assignGatherers(gameState);
for (var i in this.baseManagers)
for (let i = 0; i < this.baseManagers.length; ++i)
{
this.baseManagers[i].checkEvents(gameState, events, queues);
if (((+i + gameState.ai.playedTurn)%(gameState.ai.uniqueIDs.bases - 1)) == 0)
if (((i + gameState.ai.playedTurn)%this.baseManagers.length) === 0)
this.baseManagers[i].update(gameState, queues, events);
}
this.navalManager.update(gameState, queues, events);
var rototo = false;
if (rototo)
if (this.Config.difficulty > 0)
this.attackManager.update(gameState, queues, events);
@ -2011,12 +2122,15 @@ m.HQ.prototype.Serialize = function()
"bAdvanced": this.bAdvanced,
"saveResources": this.saveResources,
"saveSpace": this.saveSpace,
"canBuildUnits": this.canBuildUnits
"canBuildUnits": this.canBuildUnits,
"navalMap": this.navalMap,
"landRegions": this.landRegions,
"navalRegions": this.navalRegions
};
let baseManagers = {};
for (let base in this.baseManagers)
baseManagers[base] = this.baseManagers[base].Serialize();
let baseManagers = [];
for (let base of this.baseManagers)
baseManagers.push(base.Serialize());
if (this.Config.debug == -100)
{
@ -2051,14 +2165,15 @@ m.HQ.prototype.Deserialize = function(gameState, data)
for (let key in data.properties)
this[key] = data.properties[key];
this.baseManagers = {};
for (let base in data.baseManagers)
this.baseManagers = [];
for (let base of data.baseManagers)
{
// the first call to deserialize set the ID base needed by entitycollections
this.baseManagers[base] = new m.BaseManager(gameState, this.Config);
this.baseManagers[base].Deserialize(gameState, data.baseManagers[base]);
this.baseManagers[base].init(gameState);
this.baseManagers[base].Deserialize(gameState, data.baseManagers[base]);
var newbase = new m.BaseManager(gameState, this.Config);
newbase.Deserialize(gameState, base);
newbase.init(gameState);
newbase.Deserialize(gameState, base);
this.baseManagers.push(newbase);
}
this.attackManager = new m.AttackManager(this.Config);

View file

@ -43,11 +43,6 @@ m.createObstructionMap = function(gameState, accessIndex, template)
var iter = xter + yter*territoryMap.width;
var tilePlayer = (territoryMap.data[iter] & m.TERRITORY_PLAYER_MASK);
//if (gameState.ai.myIndex !== gameState.ai.accessibility.landPassMap[i])
//{
// obstructionTiles[i] = 0;
// continue;
//}
if (gameState.isPlayerEnemy(tilePlayer) && tilePlayer !== 0)
{
obstructionTiles[i] = 0;

View file

@ -57,7 +57,7 @@ m.NavalManager.prototype.init = function(gameState, deserializing)
for (var i = 0; i < gameState.ai.accessibility.regionSize.length; ++i)
{
if (gameState.ai.HQ.navalRegions.indexOf(i) === -1)
if (!gameState.ai.HQ.navalRegions[i])
{
// push dummies
this.seaShips.push(undefined);
@ -162,9 +162,11 @@ m.NavalManager.prototype.init = function(gameState, deserializing)
if (deserializing)
return;
// Assign our docks
var self = this;
this.docks.forEach(function(dock) { self.assignDock(gameState, dock); });
// Assign our initial docks and ships
for (let ship of this.ships.values())
this.setShipIndex(gameState, ship);
for (let dock of this.docks.values())
this.setDockIndex(gameState, dock);
};
m.NavalManager.prototype.resetFishingBoats = function(gameState)
@ -173,7 +175,13 @@ m.NavalManager.prototype.resetFishingBoats = function(gameState)
this.wantedFishShips[i] = 0;
};
m.NavalManager.prototype.assignDock = function(gameState, dock)
m.NavalManager.prototype.setShipIndex = function(gameState, ship)
{
let sea = gameState.ai.accessibility.getAccessValue(ship.position(), true);
ship.setMetadata(PlayerID, "sea", sea);
};
m.NavalManager.prototype.setDockIndex = function(gameState, dock)
{
var land = dock.getMetadata(PlayerID, "access");
if (land === undefined)
@ -238,7 +246,7 @@ m.NavalManager.prototype.checkEvents = function(gameState, queues, events)
continue;
var entity = gameState.getEntityById(evt.newentity);
if (entity && entity.hasClass("Dock") && entity.isOwn(PlayerID))
this.assignDock(gameState, entity);
this.setDockIndex(gameState, entity);
}
var evts = events["TrainingFinished"];
@ -251,10 +259,7 @@ m.NavalManager.prototype.checkEvents = function(gameState, queues, events)
var entity = gameState.getEntityById(entId);
if (!entity || !entity.hasClass("Ship") || !entity.isOwn(PlayerID))
continue;
var pos = gameState.ai.accessibility.gamePosToMapPos(entity.position());
var index = pos[0] + pos[1]*gameState.ai.accessibility.width;
var sea = gameState.ai.accessibility.navalPassMap[index];
entity.setMetadata(PlayerID, "sea", sea);
this.setShipIndex(gameState, entity);
}
}
@ -533,7 +538,7 @@ m.NavalManager.prototype.buildNavalStructures = function(gameState, queues)
&& gameState.ai.HQ.canBuild(gameState, "structures/{civ}_dock"))
{
var dockStarted = false;
for each (var base in gameState.ai.HQ.baseManagers)
for (var base of gameState.ai.HQ.baseManagers)
{
if (dockStarted)
break;
@ -542,7 +547,7 @@ m.NavalManager.prototype.buildNavalStructures = function(gameState, queues)
var remaining = this.getUnconnectedSeas(gameState, base.accessIndex);
for (var sea of remaining)
{
if (gameState.ai.HQ.navalRegions.indexOf(sea) === -1)
if (!gameState.ai.HQ.navalRegions[sea])
continue;
queues.economicBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_dock", { "land": base.accessIndex, "sea": sea }));
dockStarted = true;

View file

@ -374,14 +374,13 @@ m.ConstructionPlan.prototype.findDockPosition = function(gameState)
var baseIndex = gameState.ai.HQ.basesMap.map[bestIdx];
if (!baseIndex)
{
for (let i in gameState.ai.HQ.baseManagers)
for (let base of gameState.ai.HQ.baseManagers)
{
let base = gameState.ai.HQ.baseManagers[i];
if (!base.anchor || !base.anchor.position())
continue;
if (base.accessIndex !== access)
continue;
baseIndex = i;
baseIndex = base.ID;
break;
}
if (!baseIndex)

View file

@ -85,16 +85,16 @@ m.TrainingPlan.prototype.start = function(gameState)
let bBase = b.getMetadata(PlayerID, "base");
if (wantedIndex)
{
if (!aBase || gameState.ai.HQ.baseManagers[aBase].accessIndex != wantedIndex)
if (!aBase || gameState.ai.HQ.getBaseByID(aBase).accessIndex != wantedIndex)
aa += 30;
if (!bBase || gameState.ai.HQ.baseManagers[bBase].accessIndex != wantedIndex)
if (!bBase || gameState.ai.HQ.getBaseByID(bBase).accessIndex != wantedIndex)
bb += 30;
}
// then, if worker, small preference for bases with less workers
if (workerUnit && aBase && bBase && aBase != bBase)
{
let apop = gameState.ai.HQ.baseManagers[aBase].workers.length;
let bpop = gameState.ai.HQ.baseManagers[bBase].workers.length;
let apop = gameState.ai.HQ.getBaseByID(aBase).workers.length;
let bpop = gameState.ai.HQ.getBaseByID(bBase).workers.length;
if (apop > bpop)
aa++;
else if (bpop > apop)

View file

@ -184,22 +184,21 @@ m.TransportPlan.prototype.releaseAll = function()
m.TransportPlan.prototype.cancelTransport = function(gameState)
{
var ent = this.units.toEntityArray()[0];
var base = gameState.ai.HQ.baseManagers[ent.getMetadata(PlayerID, "base")];
var base = gameState.ai.HQ.getBaseByID(ent.getMetadata(PlayerID, "base"));
if (!base.anchor || !base.anchor.position())
{
for (var i in gameState.ai.HQ.baseManagers)
for (let newbase of gameState.ai.HQ.baseManagers)
{
base = gameState.ai.HQ.baseManagers[i];
if (base.anchor && base.anchor.position())
{
ent.setMetadata(PlayerID, "base", +i);
break;
}
if (!newbase.anchor || !newbase.anchor.position())
continue;
ent.setMetadata(PlayerID, "base", newbase.ID);
base = newbase;
break;
}
if (!base.anchor || !base.anchor.position())
return false;
this.units.forEach(function (ent) {
ent.setMetadata(PlayerID, "base", base);
ent.setMetadata(PlayerID, "base", base.ID);
});
}
this.endIndex = this.startIndex;
@ -401,7 +400,7 @@ m.TransportPlan.prototype.onSailing = function(gameState)
API3.warn(">>> transport " + this.ID + " reloading failed ... <<<");
// destroy the unit if inaccessible otherwise leave it there
var index = gameState.ai.accessibility.getAccessValue(ent.position());
if (gameState.ai.HQ.allowedRegions[index])
if (gameState.ai.HQ.landRegions[index])
{
if (this.debug > 1)
API3.warn(" recovered entity kept " + ent.id());

View file

@ -5,11 +5,11 @@ var PETRA = function(m)
* This class makes a worker do as instructed by the economy manager
*/
m.Worker = function(baseManager)
m.Worker = function(base)
{
this.ent = undefined;
this.baseManager = baseManager
this.baseID = baseManager.ID;
this.base = base
this.baseID = base.ID;
};
m.Worker.prototype.update = function(ent, gameState)
@ -22,6 +22,12 @@ m.Worker.prototype.update = function(ent, gameState)
return;
this.ent = ent;
// base 0 for unassigned entities has no accessIndex, so take the one from the entity
if (this.baseID === gameState.ai.HQ.baseManagers[0].ID)
this.accessIndex = gameState.ai.accessibility.getAccessValue(ent.position());
else
this.accessIndex = this.base.accessIndex;
var subrole = this.ent.getMetadata(PlayerID, "subrole");
// Check for inaccessible targets (in RMS maps, we quite often have chicken or bushes inside obstruction of other entities).
@ -77,7 +83,7 @@ m.Worker.prototype.update = function(ent, gameState)
else
{
var gatherType = this.ent.getMetadata(PlayerID, "gather-type");
var nearby = this.baseManager.dropsiteSupplies[gatherType]["nearby"];
var nearby = this.base.dropsiteSupplies[gatherType]["nearby"];
var isNearby = nearby.some(function(sup) {
if (sup.id === supplyId)
return true;
@ -152,9 +158,8 @@ m.Worker.prototype.update = function(ent, gameState)
{
// nothing to hunt around. Try another region if any
var nowhereToHunt = true;
for (var i in gameState.ai.HQ.baseManagers)
for (var base of gameState.ai.HQ.baseManagers)
{
var base = gameState.ai.HQ.baseManagers[i];
if (!base.anchor || !base.anchor.position())
continue;
var basePos = base.anchor.position();
@ -222,40 +227,8 @@ m.Worker.prototype.startGathering = function(gameState)
var access = gameState.ai.accessibility.getAccessValue(this.ent.position());
// First look for possible treasure if any
var treasureFound = undefined;
var distmin = Math.min();
gameState.ai.HQ.treasures.forEach(function (treasure) {
var treasureAccess = treasure.getMetadata(PlayerID, "access");
if (!treasureAccess)
{
treasureAccess = gameState.ai.accessibility.getAccessValue(treasure.position());
treasure.setMetadata(PlayerID, "access", treasureAccess);
}
if (treasureAccess !== access)
return;
var territoryOwner = gameState.ai.HQ.territoryMap.getOwner(treasure.position());
if (territoryOwner !== 0 && !gameState.isPlayerAlly(territoryOwner))
return;
var lastGathered = treasure.getMetadata(PlayerID, "lastGathered");
// let some time for the previous gatherer to reach the treasure
if (lastGathered && gameState.ai.elapsedTime - lastGathered < 20)
return;
var dist = API3.SquareVectorDistance(self.ent.position(), treasure.position());
if (territoryOwner !== PlayerID && dist > 14000) // AI has no LOS, so restrict it a bit
return;
if (dist > distmin)
return;
distmin = dist;
treasureFound = treasure;
});
if (treasureFound)
{
treasureFound.setMetadata(PlayerID, "lastGathered", gameState.ai.elapsedTime);
this.ent.gather(treasureFound);
m.AddTCGatherer(gameState, treasureFound.id());
this.ent.setMetadata(PlayerID, "supply", treasureFound.id());
if (this.gatherTreasure(gameState))
return true;
}
var resource = this.ent.getMetadata(PlayerID, "gather-type");
@ -300,9 +273,9 @@ m.Worker.prototype.startGathering = function(gameState)
var supply;
// first look in our own base if accessible from our present position
if (this.baseManager.accessIndex === access)
if (this.base.accessIndex === access)
{
if ((supply = findSupply(this.ent, this.baseManager.dropsiteSupplies[resource]["nearby"])))
if ((supply = findSupply(this.ent, this.base.dropsiteSupplies[resource]["nearby"])))
{
this.ent.gather(supply);
return true;
@ -321,7 +294,7 @@ m.Worker.prototype.startGathering = function(gameState)
return true;
}
}
if ((supply = findSupply(this.ent, this.baseManager.dropsiteSupplies[resource]["medium"])))
if ((supply = findSupply(this.ent, this.base.dropsiteSupplies[resource]["medium"])))
{
this.ent.gather(supply);
return true;
@ -329,7 +302,7 @@ m.Worker.prototype.startGathering = function(gameState)
}
// So if we're here we have checked our whole base for a proper resource (or it was not accessible)
// --> check other bases directly accessible
for each (var base in gameState.ai.HQ.baseManagers)
for (var base of gameState.ai.HQ.baseManagers)
{
if (base.ID === this.baseID)
continue;
@ -344,7 +317,7 @@ m.Worker.prototype.startGathering = function(gameState)
}
if (resource === "food") // --> for food, try to gather from fields if any, otherwise build one if any
{
for each (var base in gameState.ai.HQ.baseManagers)
for (var base of gameState.ai.HQ.baseManagers)
{
if (base.ID === this.baseID)
continue;
@ -364,7 +337,7 @@ m.Worker.prototype.startGathering = function(gameState)
}
}
}
for each (var base in gameState.ai.HQ.baseManagers)
for (var base of gameState.ai.HQ.baseManagers)
{
if (base.ID === this.baseID)
continue;
@ -398,7 +371,7 @@ m.Worker.prototype.startGathering = function(gameState)
return true;
// Still nothing ... try bases which need a transport
for each (var base in gameState.ai.HQ.baseManagers)
for (var base of gameState.ai.HQ.baseManagers)
{
if (base.accessIndex === access)
continue;
@ -412,7 +385,7 @@ m.Worker.prototype.startGathering = function(gameState)
}
if (resource === "food") // --> for food, try to gather from fields if any, otherwise build one if any
{
for each (var base in gameState.ai.HQ.baseManagers)
for (var base of gameState.ai.HQ.baseManagers)
{
if (base.accessIndex === access)
continue;
@ -432,7 +405,7 @@ m.Worker.prototype.startGathering = function(gameState)
}
}
}
for each (var base in gameState.ai.HQ.baseManagers)
for (var base of gameState.ai.HQ.baseManagers)
{
if (base.accessIndex === access)
continue;
@ -463,15 +436,15 @@ m.Worker.prototype.startGathering = function(gameState)
return true;
// Still nothing, we look now for faraway resources, first in the accessible ones, then in the others
if (this.baseManager.accessIndex === access)
if (this.accessIndex === access)
{
if ((supply = findSupply(this.ent, this.baseManager.dropsiteSupplies[resource]["faraway"])))
if ((supply = findSupply(this.ent, this.base.dropsiteSupplies[resource]["faraway"])))
{
this.ent.gather(supply);
return true;
}
}
for each (var base in gameState.ai.HQ.baseManagers)
for (var base of gameState.ai.HQ.baseManagers)
{
if (base.ID === this.baseID)
continue;
@ -484,7 +457,7 @@ m.Worker.prototype.startGathering = function(gameState)
return true;
}
}
for each (var base in gameState.ai.HQ.baseManagers)
for (var base of gameState.ai.HQ.baseManagers)
{
if (base.accessIndex === access)
continue;
@ -505,10 +478,16 @@ m.Worker.prototype.startGathering = function(gameState)
return false;
};
// if position is given, we only check if we could hunt from this position but do nothing
// otherwise the position of the entity is taken, and if something is found, we directly start the hunt
/**
* if position is given, we only check if we could hunt from this position but do nothing
* otherwise the position of the entity is taken, and if something is found, we directly start the hunt
*/
m.Worker.prototype.startHunting = function(gameState, position)
{
// First look for possible treasure if any
if (!position && this.gatherTreasure(gameState))
return true;
var resources = gameState.getHuntableSupplies();
if (resources.length === 0)
return false;
@ -748,11 +727,58 @@ m.Worker.prototype.buildAnyField = function(gameState, baseID)
return bestFarmEnt;
};
// Workers elephant should move away from the buildings they've built to avoid being trapped in between constructions
// For the time being, we move towards the nearest gatherer (providing him a dropsite)
/**
* Look for some treasure to gather
*/
m.Worker.prototype.gatherTreasure = function(gameState)
{
var rates = this.ent.resourceGatherRates();
if (!rates || !rates["treasure"] || rates["treasure"] <= 0)
return false;
var treasureFound = undefined;
var distmin = Math.min();
var access = gameState.ai.accessibility.getAccessValue(this.ent.position());
for (var treasure of gameState.ai.HQ.treasures.values())
{
// let some time for the previous gatherer to reach the treasure befor trying again
var lastGathered = treasure.getMetadata(PlayerID, "lastGathered");
if (lastGathered && gameState.ai.elapsedTime - lastGathered < 20)
continue;
var treasureAccess = treasure.getMetadata(PlayerID, "access");
if (!treasureAccess)
{
treasureAccess = gameState.ai.accessibility.getAccessValue(treasure.position());
treasure.setMetadata(PlayerID, "access", treasureAccess);
}
if (treasureAccess !== access)
continue;
var territoryOwner = gameState.ai.HQ.territoryMap.getOwner(treasure.position());
if (territoryOwner !== 0 && !gameState.isPlayerAlly(territoryOwner))
continue;
var dist = API3.SquareVectorDistance(this.ent.position(), treasure.position());
if (territoryOwner !== PlayerID && dist > 14000) // AI has no LOS, so restrict it a bit
continue;
if (dist > distmin)
continue;
distmin = dist;
treasureFound = treasure;
}
if (!treasureFound)
return false;
treasureFound.setMetadata(PlayerID, "lastGathered", gameState.ai.elapsedTime);
this.ent.gather(treasureFound);
m.AddTCGatherer(gameState, treasureFound.id());
this.ent.setMetadata(PlayerID, "supply", treasureFound.id());
return true;
};
/**
* Workers elephant should move away from the buildings they've built to avoid being trapped in between constructions
* For the time being, we move towards the nearest gatherer (providing him a dropsite)
*/
m.Worker.prototype.moveAway = function(gameState)
{
var gatherers = this.baseManager.workersBySubrole(gameState, "gatherer");
var gatherers = this.base.workersBySubrole(gameState, "gatherer");
var pos = this.ent.position();
var dist = Math.min();
var destination = pos;