mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-07-04 05:55:47 -07:00
Petra: continue rework of starting game to allow more game type
This was SVN commit r16135.
This commit is contained in:
parent
77dba7a8bb
commit
731e3dd13c
6 changed files with 475 additions and 403 deletions
|
|
@ -61,14 +61,15 @@ m.PetraBot.prototype.CustomInit = function(gameState, sharedScript)
|
|||
this.queues = this.queueManager.queues;
|
||||
|
||||
this.HQ = new m.HQ(this.Config);
|
||||
this.HQ.init(gameState, this.queues, true);
|
||||
this.HQ.init(gameState, this.queues);
|
||||
this.HQ.Deserialize(gameState, this.data.HQ);
|
||||
|
||||
this.uniqueIDs = this.data.uniqueIDs;
|
||||
this.isDeserialized = false;
|
||||
this.data = undefined;
|
||||
|
||||
this.HQ.start(gameState, true);
|
||||
// initialisation needed after the completion of the deserialization
|
||||
this.HQ.postinit(gameState);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -85,7 +86,8 @@ m.PetraBot.prototype.CustomInit = function(gameState, sharedScript)
|
|||
|
||||
this.HQ.init(gameState, this.queues);
|
||||
|
||||
this.HQ.start(gameState);
|
||||
// Analyze our starting position and set a strategy
|
||||
this.HQ.gameAnalysis(gameState);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -526,12 +526,13 @@ m.AttackPlan.prototype.updatePreparation = function(gameState, events)
|
|||
|
||||
var rallyPoint = this.rallyPoint;
|
||||
var rallyIndex = gameState.ai.accessibility.getAccessValue(rallyPoint);
|
||||
this.unitCollection.forEach(function (entity) {
|
||||
for (var entity of this.unitCollection.values())
|
||||
{
|
||||
// For the time being, if occupied in a transport, remove the unit from this plan TODO improve that
|
||||
if (entity.getMetadata(PlayerID, "transport") !== undefined || entity.getMetadata(PlayerID, "transporter") !== undefined)
|
||||
{
|
||||
entity.setMetadata(PlayerID, "plan", -1);
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
entity.setMetadata(PlayerID, "role", "attack");
|
||||
entity.setMetadata(PlayerID, "subrole", "completing");
|
||||
|
|
@ -543,7 +544,7 @@ m.AttackPlan.prototype.updatePreparation = function(gameState, events)
|
|||
entity.moveToRange(rallyPoint[0], rallyPoint[1], 0, 15, queued);
|
||||
else
|
||||
gameState.ai.HQ.navalManager.requireTransport(gameState, entity, index, rallyIndex, rallyPoint);
|
||||
});
|
||||
}
|
||||
|
||||
// reset all queued units
|
||||
var plan = this.name;
|
||||
|
|
@ -685,19 +686,20 @@ m.AttackPlan.prototype.assignUnits = function(gameState)
|
|||
}
|
||||
|
||||
var noRole = gameState.getOwnEntitiesByRole(undefined, false).filter(API3.Filters.byClass("Unit"));
|
||||
noRole.forEach(function(ent) {
|
||||
for (var ent of noRole.values())
|
||||
{
|
||||
if (!ent.position())
|
||||
return;
|
||||
continue;
|
||||
if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1)
|
||||
return;
|
||||
continue;
|
||||
if (ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined)
|
||||
return;
|
||||
continue;
|
||||
if (ent.hasClass("Ship") || ent.hasClass("Support") || ent.attackTypes() === undefined)
|
||||
return;
|
||||
continue;
|
||||
ent.setMetadata(PlayerID, "plan", plan);
|
||||
self.unitCollection.updateEnt(ent);
|
||||
added = true;
|
||||
});
|
||||
}
|
||||
// Add units previously in a plan, but which left it because needed for defense or attack finished
|
||||
gameState.ai.HQ.attackManager.outOfPlan.forEach(function(ent) {
|
||||
if (!ent.position())
|
||||
|
|
@ -715,21 +717,22 @@ m.AttackPlan.prototype.assignUnits = function(gameState)
|
|||
// For a rush, assign also workers (but keep a minimum number of defenders)
|
||||
var worker = gameState.getOwnEntitiesByRole("worker", true);
|
||||
var num = 0;
|
||||
worker.forEach(function(ent) {
|
||||
for (var ent of worker.values())
|
||||
{
|
||||
if (!ent.position())
|
||||
return;
|
||||
continue;
|
||||
if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1)
|
||||
return;
|
||||
continue;
|
||||
if (ent.getMetadata(PlayerID, "transport") !== undefined)
|
||||
return;
|
||||
continue;
|
||||
if (ent.hasClass("Ship") || ent.hasClass("Support") || ent.attackTypes() === undefined)
|
||||
return;
|
||||
continue;
|
||||
if (num++ < 9)
|
||||
return;
|
||||
continue;
|
||||
ent.setMetadata(PlayerID, "plan", plan);
|
||||
self.unitCollection.updateEnt(ent);
|
||||
added = true;
|
||||
});
|
||||
}
|
||||
return added;
|
||||
};
|
||||
|
||||
|
|
@ -737,15 +740,15 @@ m.AttackPlan.prototype.assignUnits = function(gameState)
|
|||
m.AttackPlan.prototype.reassignCavUnit = function(gameState)
|
||||
{
|
||||
var found = undefined;
|
||||
this.unitCollection.forEach(function(ent) {
|
||||
if (found)
|
||||
return;
|
||||
for (var ent of this.unitCollection.values())
|
||||
{
|
||||
if (!ent.position() || ent.getMetadata(PlayerID, "transport") !== undefined)
|
||||
return;
|
||||
continue;
|
||||
if (!ent.hasClass("Cavalry") || !ent.hasClass("CitizenSoldier"))
|
||||
return;
|
||||
continue;
|
||||
found = ent;
|
||||
});
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
return;
|
||||
let raid = gameState.ai.HQ.attackManager.getAttackInPreparation("Raid");
|
||||
|
|
@ -771,18 +774,19 @@ m.AttackPlan.prototype.getNearestTarget = function(gameState, position, sameLand
|
|||
// picking the nearest target
|
||||
var minDist = -1;
|
||||
var target = undefined;
|
||||
targets.forEach(function (ent) {
|
||||
for (var ent of targets.values())
|
||||
{
|
||||
if (!ent.position())
|
||||
return;
|
||||
continue;
|
||||
if (sameLand && gameState.ai.accessibility.getAccessValue(ent.position()) != land)
|
||||
return;
|
||||
continue;
|
||||
var dist = API3.SquareVectorDistance(ent.position(), position);
|
||||
if (dist < minDist || minDist == -1)
|
||||
{
|
||||
minDist = dist;
|
||||
target = ent;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!target)
|
||||
return undefined;
|
||||
// Rushes can change their enemy target if nothing found with the preferred enemy
|
||||
|
|
@ -1012,9 +1016,8 @@ m.AttackPlan.prototype.StartAttack = function(gameState)
|
|||
|
||||
var curPos = this.unitCollection.getCentrePosition();
|
||||
|
||||
this.unitCollection.forEach(function(ent) {
|
||||
for (var ent of this.unitCollection.values())
|
||||
ent.setMetadata(PlayerID, "subrole", "walking");
|
||||
});
|
||||
this.unitCollection.setStance("aggressive");
|
||||
|
||||
if (gameState.ai.accessibility.getAccessValue(this.targetPos) === gameState.ai.accessibility.getAccessValue(this.rallyPoint))
|
||||
|
|
@ -1036,9 +1039,8 @@ m.AttackPlan.prototype.StartAttack = function(gameState)
|
|||
var endPos = this.targetPos;
|
||||
// TODO require a global transport for the collection,
|
||||
// and put back its state to "walking" when the transport is finished
|
||||
this.unitCollection.forEach(function (entity) {
|
||||
gameState.ai.HQ.navalManager.requireTransport(gameState, entity, startIndex, endIndex, endPos);
|
||||
});
|
||||
for (var ent of this.unitCollection.values())
|
||||
gameState.ai.HQ.navalManager.requireTransport(gameState, ent, startIndex, endIndex, endPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -1069,16 +1071,17 @@ m.AttackPlan.prototype.update = function(gameState, events)
|
|||
if (this.state === "transporting")
|
||||
{
|
||||
var done = true;
|
||||
this.unitCollection.forEach(function (entity) {
|
||||
if (self.Config.debug > 1 && entity.getMetadata(PlayerID, "transport") !== undefined)
|
||||
Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [entity.id()], "rgb": [2,2,0]});
|
||||
else if (self.Config.debug > 1)
|
||||
Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [entity.id()], "rgb": [1,1,1]});
|
||||
for (var ent of this.unitCollection.values())
|
||||
{
|
||||
if (this.Config.debug > 1 && ent.getMetadata(PlayerID, "transport") !== undefined)
|
||||
Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [2,2,0]});
|
||||
else if (this.Config.debug > 1)
|
||||
Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [1,1,1]});
|
||||
if (!done)
|
||||
return;
|
||||
if (entity.getMetadata(PlayerID, "transport") !== undefined)
|
||||
continue;
|
||||
if (ent.getMetadata(PlayerID, "transport") !== undefined)
|
||||
done = false;
|
||||
});
|
||||
}
|
||||
|
||||
if (done)
|
||||
this.state = "arrived";
|
||||
|
|
@ -1094,13 +1097,14 @@ m.AttackPlan.prototype.update = function(gameState, events)
|
|||
var ourUnit = gameState.getEntityById(evt.target);
|
||||
if (!attacker || !ourUnit)
|
||||
continue;
|
||||
this.unitCollection.forEach(function (entity) {
|
||||
if (entity.getMetadata(PlayerID, "transport") !== undefined)
|
||||
return;
|
||||
if (!entity.isIdle())
|
||||
return;
|
||||
entity.attack(attacker.id());
|
||||
});
|
||||
for (var ent of this.unitCollection.values())
|
||||
{
|
||||
if (ent.getMetadata(PlayerID, "transport") !== undefined)
|
||||
continue;
|
||||
if (!ent.isIdle())
|
||||
continue;
|
||||
ent.attack(attacker.id());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1135,10 +1139,9 @@ m.AttackPlan.prototype.update = function(gameState, events)
|
|||
if (attackedUnitNB == 0)
|
||||
{
|
||||
var siegeNB = 0;
|
||||
this.unitCollection.forEach( function (ent) {
|
||||
if (self.isSiegeUnit(gameState, ent))
|
||||
for (var ent of this.unitCollection.values())
|
||||
if (this.isSiegeUnit(gameState, ent))
|
||||
siegeNB++;
|
||||
});
|
||||
if (siegeNB == 0)
|
||||
maybe = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
var PETRA = function(m)
|
||||
{
|
||||
|
||||
// Some functions that could be part of the gamestate but are Aegis specific.
|
||||
// Some functions that could be part of the gamestate.
|
||||
|
||||
// The next three are to register that we assigned a gatherer to a resource this turn.
|
||||
// expects an entity
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ m.HQ = function(Config)
|
|||
};
|
||||
|
||||
// More initialisation for stuff that needs the gameState
|
||||
m.HQ.prototype.init = function(gameState, queues, deserializing)
|
||||
m.HQ.prototype.init = function(gameState, queues)
|
||||
{
|
||||
this.territoryMap = m.createTerritoryMap(gameState);
|
||||
// initialize base map. Each pixel is a base ID, or 0 if not or not accessible
|
||||
|
|
@ -77,348 +77,26 @@ m.HQ.prototype.init = function(gameState, queues, deserializing)
|
|||
return false;
|
||||
});
|
||||
this.treasures.registerUpdates();
|
||||
|
||||
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
|
||||
if (civ in this.Config.buildings.base)
|
||||
this.bBase = this.Config.buildings.base[civ];
|
||||
else
|
||||
this.bBase = this.Config.buildings.base['default'];
|
||||
|
||||
if (civ in this.Config.buildings.advanced)
|
||||
this.bAdvanced = this.Config.buildings.advanced[civ];
|
||||
else
|
||||
this.bAdvanced = this.Config.buildings.advanced['default'];
|
||||
for (var i in this.bBase)
|
||||
this.bBase[i] = gameState.applyCiv(this.bBase[i]);
|
||||
for (var i in this.bAdvanced)
|
||||
this.bAdvanced[i] = gameState.applyCiv(this.bAdvanced[i]);
|
||||
|
||||
// Let's get our initial situation here.
|
||||
let nobase = new m.BaseManager(gameState, this.Config);
|
||||
nobase.init(gameState);
|
||||
nobase.accessIndex = 0;
|
||||
this.baseManagers.push(nobase); // baseManagers[0] will deal with unit/structure without base
|
||||
var ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre"));
|
||||
for (let cc of ccEnts.values())
|
||||
{
|
||||
let newbase = new m.BaseManager(gameState, this.Config);
|
||||
newbase.init(gameState);
|
||||
newbase.setAnchor(gameState, cc);
|
||||
this.baseManagers.push(newbase);
|
||||
}
|
||||
this.updateTerritories(gameState);
|
||||
|
||||
// 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())
|
||||
{
|
||||
// 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])
|
||||
this.landRegions[land] = true;
|
||||
let sea = gameState.ai.accessibility.navalPassMap[index];
|
||||
if (sea > 1 && !this.navalRegions[sea])
|
||||
this.navalRegions[sea] = true;
|
||||
}
|
||||
// if garrisoned units inside, ungarrison them except if a ship in which case we will make a transport
|
||||
if (ent.isGarrisonHolder() && ent.garrisoned().length && !ent.hasClass("Ship"))
|
||||
for (let id of ent.garrisoned())
|
||||
ent.unload(id);
|
||||
// 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"))
|
||||
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.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": 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.applyCiv("structures/{civ}_civil_centre");
|
||||
if (gameState.isDisabledTemplates(template) || !gameState.getTemplate(template).available(gameState))
|
||||
{
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn(" this AI is unable to produce any units");
|
||||
this.canBuildUnits = false;
|
||||
var allycc = gameState.getExclusiveAllyEntities().filter(API3.Filters.byClass("CivCentre")).toEntityArray();
|
||||
if (allycc.length)
|
||||
{
|
||||
// if we have some allies, keep a fraction of our units to defend them
|
||||
// and devote the rest to atacks
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn(" We have allied cc " + allycc.length + " and " + gameState.getOwnUnits().length + " units ");
|
||||
var units = gameState.getOwnUnits();
|
||||
var num = Math.max(Math.min(Math.round(0.08*(1+this.Config.personality.cooperative)*units.length), 20), 5);
|
||||
var num1 = Math.floor(num / 2);
|
||||
var num2 = num1;
|
||||
// first pass to affect ranged infantry
|
||||
units.filter(API3.Filters.byClassesAnd(["Infantry", "Ranged"])).forEach(function (ent) {
|
||||
if (!num || !num1)
|
||||
return;
|
||||
if (ent.getMetadata(PlayerID, "allied"))
|
||||
return;
|
||||
var access = gameState.ai.accessibility.getAccessValue(ent.position());
|
||||
for (var cc of allycc)
|
||||
{
|
||||
if (!cc.position())
|
||||
continue;
|
||||
if (gameState.ai.accessibility.getAccessValue(cc.position()) != access)
|
||||
continue;
|
||||
--num;
|
||||
--num1;
|
||||
ent.setMetadata(PlayerID, "allied", true);
|
||||
var range = 1.5 * cc.footprintRadius();
|
||||
ent.moveToRange(cc.position()[0], cc.position()[1], range, range);
|
||||
break;
|
||||
}
|
||||
});
|
||||
// second pass to affect melee infantry
|
||||
units.filter(API3.Filters.byClassesAnd(["Infantry", "Melee"])).forEach(function (ent) {
|
||||
if (!num || !num2)
|
||||
return;
|
||||
if (ent.getMetadata(PlayerID, "allied"))
|
||||
return;
|
||||
var access = gameState.ai.accessibility.getAccessValue(ent.position());
|
||||
for (var cc of allycc)
|
||||
{
|
||||
if (!cc.position())
|
||||
continue;
|
||||
if (gameState.ai.accessibility.getAccessValue(cc.position()) != access)
|
||||
continue;
|
||||
--num;
|
||||
--num2;
|
||||
ent.setMetadata(PlayerID, "allied", true);
|
||||
var range = 1.5 * cc.footprintRadius();
|
||||
ent.moveToRange(cc.position()[0], cc.position()[1], range, range);
|
||||
break;
|
||||
}
|
||||
});
|
||||
// and now complete the affectation, including all support units
|
||||
units.forEach(function (ent) {
|
||||
if (!num && !ent.hasClass("Support"))
|
||||
return;
|
||||
if (ent.getMetadata(PlayerID, "allied"))
|
||||
return;
|
||||
var access = gameState.ai.accessibility.getAccessValue(ent.position());
|
||||
for (var cc of allycc)
|
||||
{
|
||||
if (!cc.position())
|
||||
continue;
|
||||
if (gameState.ai.accessibility.getAccessValue(cc.position()) != access)
|
||||
continue;
|
||||
if (!ent.hasClass("Support"))
|
||||
--num;
|
||||
ent.setMetadata(PlayerID, "allied", true);
|
||||
var range = 1.5 * cc.footprintRadius();
|
||||
ent.moveToRange(cc.position()[0], cc.position()[1], range, range);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.attackManager.init(gameState);
|
||||
this.navalManager.init(gameState);
|
||||
this.tradeManager.init(gameState);
|
||||
};
|
||||
|
||||
m.HQ.prototype.start = function(gameState, deserializing)
|
||||
/**
|
||||
* initialization needed after deserialization (only called when deserialization)
|
||||
*/
|
||||
m.HQ.prototype.postinit = function(gameState)
|
||||
{
|
||||
if (deserializing)
|
||||
{
|
||||
// Rebuild the base maps from the territory indices of each base
|
||||
this.basesMap = new API3.Map(gameState.sharedScript, "territory");
|
||||
for (let base of this.baseManagers)
|
||||
for (let j of base.territoryIndices)
|
||||
this.basesMap.map[j] = base.ID;
|
||||
// Rebuild the base maps from the territory indices of each base
|
||||
this.basesMap = new API3.Map(gameState.sharedScript, "territory");
|
||||
for (let base of this.baseManagers)
|
||||
for (let j of base.territoryIndices)
|
||||
this.basesMap.map[j] = base.ID;
|
||||
|
||||
for (let ent of gameState.getOwnEntities().values())
|
||||
{
|
||||
if (!ent.resourceDropsiteTypes() || ent.hasClass("Elephant"))
|
||||
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 (let region in this.landRegions)
|
||||
for (let ent of gameState.getOwnEntities().values())
|
||||
{
|
||||
for (let base of this.baseManagers)
|
||||
{
|
||||
if (!base.anchor || base.accessIndex != +region)
|
||||
continue;
|
||||
startingSize += gameState.ai.accessibility.regionSize[region];
|
||||
break;
|
||||
}
|
||||
if (!ent.resourceDropsiteTypes() || ent.hasClass("Elephant"))
|
||||
continue;
|
||||
let base = this.getBaseByID(ent.getMetadata(PlayerID, "base"));
|
||||
base.assignResourceToDropsite(gameState, ent);
|
||||
}
|
||||
var cell = gameState.getMap().cellSize;
|
||||
startingSize = startingSize * cell * cell;
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("starting size " + startingSize + "(cut at 24000 for fish pushing)");
|
||||
if (startingSize < 24000)
|
||||
{
|
||||
this.saveSpace = true;
|
||||
this.Config.Economy.popForDock = Math.min(this.Config.Economy.popForDock, 16);
|
||||
this.Config.Economy.targetNumFishers = Math.max(this.Config.Economy.targetNumFishers, 2);
|
||||
}
|
||||
// - count the available wood resource, and allow rushes only if enough (we should otherwise favor expansion)
|
||||
var startingWood = gameState.getResources()["wood"];
|
||||
var check = {};
|
||||
for (var proxim of ["nearby", "medium", "faraway"])
|
||||
{
|
||||
for (let base of this.baseManagers)
|
||||
{
|
||||
for (var supply of base.dropsiteSupplies["wood"][proxim])
|
||||
{
|
||||
if (check[supply.id]) // avoid double counting as same resource can appear several time
|
||||
continue;
|
||||
check[supply.id] = true;
|
||||
startingWood += supply.ent.resourceSupplyAmount();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("startingWood: " + startingWood + "(cut at 8500 for no rush and 6000 for saveResources)");
|
||||
if (startingWood < 6000)
|
||||
this.saveResources = true;
|
||||
|
||||
if (startingWood > 8500 && this.canBuildUnits)
|
||||
this.attackManager.setRushes();
|
||||
};
|
||||
|
||||
// returns the sea index linking regions 1 and region 2 (supposed to be different land region)
|
||||
|
|
@ -492,6 +170,8 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues)
|
|||
// TODO: move to the base manager.
|
||||
if (evt.newentity)
|
||||
{
|
||||
if (evt.newentity === evt.entity) // repaired building
|
||||
continue;
|
||||
var ent = gameState.getEntityById(evt.newentity);
|
||||
if (!ent || !ent.isOwn(PlayerID))
|
||||
continue;
|
||||
|
|
@ -505,9 +185,17 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues)
|
|||
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;
|
||||
this.saveSpace = undefined;
|
||||
if (base.ID === this.baseManagers[1].ID)
|
||||
{
|
||||
// this is our first base, let us configure our starting resources
|
||||
this.configFirstBase(gameState);
|
||||
}
|
||||
else
|
||||
{
|
||||
// let us hope this new base will fix our possible resource shortage
|
||||
this.saveResources = undefined;
|
||||
this.saveSpace = undefined;
|
||||
}
|
||||
}
|
||||
else if (ent.hasTerritoryInfluence())
|
||||
this.updateTerritories(gameState);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,378 @@
|
|||
var PETRA = function(m)
|
||||
{
|
||||
/**
|
||||
* determines the strategy to adopt when starting a new game, depending on the initial conditions
|
||||
*/
|
||||
|
||||
m.HQ.prototype.gameAnalysis = function(gameState)
|
||||
{
|
||||
// Analysis of the terrain and the different access regions
|
||||
this.regionAnalysis(gameState);
|
||||
|
||||
// Make a list of buildable structures from the config file
|
||||
this.structureAnalysis(gameState);
|
||||
|
||||
// Let's get our initial situation here.
|
||||
let nobase = new m.BaseManager(gameState, this.Config);
|
||||
nobase.init(gameState);
|
||||
nobase.accessIndex = 0;
|
||||
this.baseManagers.push(nobase); // baseManagers[0] will deal with unit/structure without base
|
||||
var ccEnts = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre"));
|
||||
for (let cc of ccEnts.values())
|
||||
{
|
||||
let newbase = new m.BaseManager(gameState, this.Config);
|
||||
newbase.init(gameState);
|
||||
newbase.setAnchor(gameState, cc);
|
||||
this.baseManagers.push(newbase);
|
||||
}
|
||||
this.updateTerritories(gameState);
|
||||
|
||||
// Assign entities and resources in the different bases
|
||||
this.assignStartingEntities(gameState);
|
||||
|
||||
// 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.applyCiv("structures/{civ}_civil_centre");
|
||||
if (gameState.isDisabledTemplates(template) || !gameState.getTemplate(template).available(gameState))
|
||||
{
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn(" this AI is unable to produce any units");
|
||||
this.canBuildUnits = false;
|
||||
this.dispatchUnits(gameState);
|
||||
}
|
||||
}
|
||||
|
||||
this.attackManager.init(gameState);
|
||||
this.navalManager.init(gameState);
|
||||
this.tradeManager.init(gameState);
|
||||
|
||||
// configure our first base strategy
|
||||
if (this.baseManagers.length > 1)
|
||||
this.configFirstBase(gameState);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assign the starting entities to the different bases
|
||||
*/
|
||||
m.HQ.prototype.assignStartingEntities = function(gameState)
|
||||
{
|
||||
var defaultbase = (this.numActiveBase() > 0) ? 1 : 0;
|
||||
var width = gameState.getMap().width;
|
||||
for (var ent of gameState.getOwnEntities().values())
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
// TODO should support recursive garrisoning. Make a warning for now
|
||||
if (ent.isGarrisonHolder() && ent.garrisoned().length)
|
||||
API3.warn("Petra warning: support for garrisoned units inside garrisoned holders not yet implemented");
|
||||
continue;
|
||||
}
|
||||
|
||||
// make sure we have not rejected small regions with units (TODO should probably also check with other non-gaia units)
|
||||
let gamepos = gameState.ai.accessibility.gamePosToMapPos(pos);
|
||||
let index = gamepos[0] + gamepos[1]*gameState.ai.accessibility.width;
|
||||
let land = gameState.ai.accessibility.landPassMap[index];
|
||||
if (land > 1 && !this.landRegions[land])
|
||||
this.landRegions[land] = true;
|
||||
let sea = gameState.ai.accessibility.navalPassMap[index];
|
||||
if (sea > 1 && !this.navalRegions[sea])
|
||||
this.navalRegions[sea] = true;
|
||||
|
||||
// if garrisoned units inside, ungarrison them except if a ship in which case we will make a transport
|
||||
if (ent.isGarrisonHolder() && ent.garrisoned().length && !ent.hasClass("Ship"))
|
||||
for (let id of ent.garrisoned())
|
||||
ent.unload(id);
|
||||
|
||||
ent.setMetadata(PlayerID, "access", gameState.ai.accessibility.getAccessValue(pos));
|
||||
var bestbase = undefined;
|
||||
for (var i = 1; i < this.baseManagers.length; ++i)
|
||||
{
|
||||
var base = this.baseManagers[i];
|
||||
if (base.territoryIndices.indexOf(index) === -1)
|
||||
continue;
|
||||
base.assignEntity(ent);
|
||||
if (ent.resourceDropsiteTypes() && !ent.hasClass("Elephant"))
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* determine the main land Index (or water index if none)
|
||||
* as well as the list of allowed (land andf water) regions
|
||||
*/
|
||||
m.HQ.prototype.regionAnalysis = function(gameState)
|
||||
{
|
||||
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 < 3)
|
||||
return;
|
||||
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));
|
||||
};
|
||||
|
||||
/**
|
||||
* load units and buildings from the config files
|
||||
* TODO: change that to something dynamic
|
||||
*/
|
||||
m.HQ.prototype.structureAnalysis = function(gameState)
|
||||
{
|
||||
var civ = gameState.playerData.civ;
|
||||
if (civ in this.Config.buildings.base)
|
||||
this.bBase = this.Config.buildings.base[civ];
|
||||
else
|
||||
this.bBase = this.Config.buildings.base['default'];
|
||||
|
||||
if (civ in this.Config.buildings.advanced)
|
||||
this.bAdvanced = this.Config.buildings.advanced[civ];
|
||||
else
|
||||
this.bAdvanced = this.Config.buildings.advanced['default'];
|
||||
for (var i in this.bBase)
|
||||
this.bBase[i] = gameState.applyCiv(this.bBase[i]);
|
||||
for (var i in this.bAdvanced)
|
||||
this.bAdvanced[i] = gameState.applyCiv(this.bAdvanced[i]);
|
||||
};
|
||||
|
||||
/**
|
||||
* set strategy if game without construction:
|
||||
* - if one of our allies has a cc, affect a small fraction of our army for his defense, the rest will attack
|
||||
* - otherwise all units will attack
|
||||
*/
|
||||
m.HQ.prototype.dispatchUnits = function(gameState)
|
||||
{
|
||||
var allycc = gameState.getExclusiveAllyEntities().filter(API3.Filters.byClass("CivCentre")).toEntityArray();
|
||||
if (allycc.length)
|
||||
{
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn(" We have allied cc " + allycc.length + " and " + gameState.getOwnUnits().length + " units ");
|
||||
var units = gameState.getOwnUnits();
|
||||
var num = Math.max(Math.min(Math.round(0.08*(1+this.Config.personality.cooperative)*units.length), 20), 5);
|
||||
var num1 = Math.floor(num / 2);
|
||||
var num2 = num1;
|
||||
// first pass to affect ranged infantry
|
||||
units.filter(API3.Filters.byClassesAnd(["Infantry", "Ranged"])).forEach(function (ent) {
|
||||
if (!num || !num1)
|
||||
return;
|
||||
if (ent.getMetadata(PlayerID, "allied"))
|
||||
return;
|
||||
var access = gameState.ai.accessibility.getAccessValue(ent.position());
|
||||
for (var cc of allycc)
|
||||
{
|
||||
if (!cc.position())
|
||||
continue;
|
||||
if (gameState.ai.accessibility.getAccessValue(cc.position()) != access)
|
||||
continue;
|
||||
--num;
|
||||
--num1;
|
||||
ent.setMetadata(PlayerID, "allied", true);
|
||||
var range = 1.5 * cc.footprintRadius();
|
||||
ent.moveToRange(cc.position()[0], cc.position()[1], range, range);
|
||||
break;
|
||||
}
|
||||
});
|
||||
// second pass to affect melee infantry
|
||||
units.filter(API3.Filters.byClassesAnd(["Infantry", "Melee"])).forEach(function (ent) {
|
||||
if (!num || !num2)
|
||||
return;
|
||||
if (ent.getMetadata(PlayerID, "allied"))
|
||||
return;
|
||||
var access = gameState.ai.accessibility.getAccessValue(ent.position());
|
||||
for (var cc of allycc)
|
||||
{
|
||||
if (!cc.position())
|
||||
continue;
|
||||
if (gameState.ai.accessibility.getAccessValue(cc.position()) != access)
|
||||
continue;
|
||||
--num;
|
||||
--num2;
|
||||
ent.setMetadata(PlayerID, "allied", true);
|
||||
var range = 1.5 * cc.footprintRadius();
|
||||
ent.moveToRange(cc.position()[0], cc.position()[1], range, range);
|
||||
break;
|
||||
}
|
||||
});
|
||||
// and now complete the affectation, including all support units
|
||||
units.forEach(function (ent) {
|
||||
if (!num && !ent.hasClass("Support"))
|
||||
return;
|
||||
if (ent.getMetadata(PlayerID, "allied"))
|
||||
return;
|
||||
var access = gameState.ai.accessibility.getAccessValue(ent.position());
|
||||
for (var cc of allycc)
|
||||
{
|
||||
if (!cc.position())
|
||||
continue;
|
||||
if (gameState.ai.accessibility.getAccessValue(cc.position()) != access)
|
||||
continue;
|
||||
if (!ent.hasClass("Support"))
|
||||
--num;
|
||||
ent.setMetadata(PlayerID, "allied", true);
|
||||
var range = 1.5 * cc.footprintRadius();
|
||||
ent.moveToRange(cc.position()[0], cc.position()[1], range, range);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* configure our first base expansion
|
||||
* - if on a small island, favor fishing
|
||||
* - count the available wood resource, and allow rushes only if enough (we should otherwise favor expansion)
|
||||
*/
|
||||
m.HQ.prototype.configFirstBase = function(gameState)
|
||||
{
|
||||
if (this.baseManagers.length < 2)
|
||||
return;
|
||||
|
||||
var startingSize = 0;
|
||||
for (let region in this.landRegions)
|
||||
{
|
||||
for (let base of this.baseManagers)
|
||||
{
|
||||
if (!base.anchor || base.accessIndex != +region)
|
||||
continue;
|
||||
startingSize += gameState.ai.accessibility.regionSize[region];
|
||||
break;
|
||||
}
|
||||
}
|
||||
var cell = gameState.getMap().cellSize;
|
||||
startingSize = startingSize * cell * cell;
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("starting size " + startingSize + "(cut at 24000 for fish pushing)");
|
||||
if (startingSize < 24000)
|
||||
{
|
||||
this.saveSpace = true;
|
||||
this.Config.Economy.popForDock = Math.min(this.Config.Economy.popForDock, 16);
|
||||
this.Config.Economy.targetNumFishers = Math.max(this.Config.Economy.targetNumFishers, 2);
|
||||
}
|
||||
|
||||
// - count the available wood resource, and allow rushes only if enough (we should otherwise favor expansion)
|
||||
var startingWood = gameState.getResources()["wood"];
|
||||
var check = {};
|
||||
for (var proxim of ["nearby", "medium", "faraway"])
|
||||
{
|
||||
for (let base of this.baseManagers)
|
||||
{
|
||||
for (var supply of base.dropsiteSupplies["wood"][proxim])
|
||||
{
|
||||
if (check[supply.id]) // avoid double counting as same resource can appear several time
|
||||
continue;
|
||||
check[supply.id] = true;
|
||||
startingWood += supply.ent.resourceSupplyAmount();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("startingWood: " + startingWood + "(cut at 8500 for no rush and 6000 for saveResources)");
|
||||
if (startingWood < 6000)
|
||||
this.saveResources = true;
|
||||
if (startingWood > 8500 && this.canBuildUnits)
|
||||
this.attackManager.setRushes();
|
||||
|
||||
// immediatly build a wood dropsite if possible.
|
||||
var template = "structures/{civ}_storehouse";
|
||||
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(template), true) === 0 && this.canBuild(gameState, template))
|
||||
{
|
||||
var newDP = this.baseManagers[1].findBestDropsiteLocation(gameState, "wood");
|
||||
if (newDP.quality > 40)
|
||||
gameState.ai.queues.dropsites.addItem(new m.ConstructionPlan(gameState, template, { "base": this.baseManagers[1].ID }, newDP.pos));
|
||||
}
|
||||
};
|
||||
|
||||
return m;
|
||||
|
||||
}(PETRA);
|
||||
|
|
@ -678,21 +678,21 @@ m.Worker.prototype.startFishing = function(gameState)
|
|||
|
||||
m.Worker.prototype.gatherNearestField = function(gameState, baseID)
|
||||
{
|
||||
var self = this;
|
||||
var ownFields = gameState.getOwnEntitiesByType(gameState.applyCiv("structures/{civ}_field"), true).filter(API3.Filters.byMetadata(PlayerID, "base", baseID));
|
||||
var bestFarmEnt = false;
|
||||
var bestFarmDist = 10000000;
|
||||
|
||||
ownFields.forEach(function (field) {
|
||||
for (var field of ownFields.values())
|
||||
{
|
||||
if (m.IsSupplyFull(gameState, field) === true)
|
||||
return;
|
||||
var dist = API3.SquareVectorDistance(field.position(), self.ent.position());
|
||||
continue;
|
||||
var dist = API3.SquareVectorDistance(field.position(), this.ent.position());
|
||||
if (dist < bestFarmDist)
|
||||
{
|
||||
bestFarmEnt = field;
|
||||
bestFarmDist = dist;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (bestFarmEnt)
|
||||
{
|
||||
m.AddTCGatherer(gameState, bestFarmEnt.id());
|
||||
|
|
@ -712,18 +712,19 @@ m.Worker.prototype.buildAnyField = function(gameState, baseID)
|
|||
var bestFarmEnt = false;
|
||||
var bestFarmDist = 10000000;
|
||||
var pos = this.ent.position();
|
||||
baseFoundations.forEach(function (found) {
|
||||
for (var found of baseFoundations.values())
|
||||
{
|
||||
if (!found.hasClass("Field"))
|
||||
return;
|
||||
continue;
|
||||
var current = found.getBuildersNb();
|
||||
if (current === undefined || current >= maxGatherers)
|
||||
return;
|
||||
continue;
|
||||
var dist = API3.SquareVectorDistance(found.position(), pos);
|
||||
if (dist > bestFarmDist)
|
||||
return;
|
||||
continue;
|
||||
bestFarmEnt = found;
|
||||
bestFarmDist = dist;
|
||||
});
|
||||
}
|
||||
return bestFarmEnt;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue