1
0
forked from mirrors/0ad

Petra: revisit the attacks + several bugfixes + add some more debug printouts

This was SVN commit r15695.
This commit is contained in:
mimo
2014-08-30 20:06:48 +00:00
parent 611d20016a
commit 032d031729
15 changed files with 291 additions and 175 deletions
@@ -120,7 +120,7 @@ m.PetraBot.prototype.initPersonality = function()
this.Config.priorities.defenseBuilding = 60;
}
if (this.Config.debug == 0)
if (this.Config.debug < 2)
return;
API3.warn(" >>> Petra bot: personality = " + uneval(this.Config.personality));
};
@@ -50,7 +50,7 @@ m.AttackManager.prototype.init = function(gameState, queues, allowRush)
// Others once in a while
m.AttackManager.prototype.update = function(gameState, queues, events)
{
if (this.Config.debug == 2 && gameState.ai.elapsedTime > this.debugTime + 60)
if (this.Config.debug > 2 && gameState.ai.elapsedTime > this.debugTime + 60)
{
this.debugTime = gameState.ai.elapsedTime;
API3.warn(" upcoming attacks =================");
@@ -93,7 +93,7 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
}
else if (updateStep === 0 || updateStep === 3)
{
if (this.Config.debug)
if (this.Config.debug > 1)
API3.warn("Attack Manager: " + attack.getType() + " plan " + attack.getName() + " aborted.");
attack.Abort(gameState, this);
this.upcomingAttacks[attackType].splice(i--,1);
@@ -114,7 +114,7 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
var chatText = "I'm starting an attack against " + targetName + ".";
gameState.ai.chatTeam(chatText);
if (this.Config.debug)
if (this.Config.debug > 1)
API3.warn("Attack Manager: Starting " + attack.getType() + " plan " + attack.getName());
this.startedAttacks[attackType].push(attack);
}
@@ -137,7 +137,7 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
var chatText = "I'm starting an attack against " + targetName + ".";
gameState.ai.chatTeam(chatText);
if (this.Config.debug)
if (this.Config.debug > 1)
API3.warn("Attack Manager: Starting " + attack.getType() + " plan " + attack.getName());
this.startedAttacks[attackType].push(attack);
this.upcomingAttacks[attackType].splice(i--,1);
@@ -157,7 +157,7 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
var remaining = attack.update(gameState, events);
if (!remaining)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("Military Manager: " + attack.getType() + " plan " + attack.getName() + " is finished with remaining " + remaining);
attack.Abort(gameState);
this.startedAttacks[attackType].splice(i--,1);
@@ -176,7 +176,7 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
var attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, "Rush", data);
if (!attackPlan.failed)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("Headquarters: Rushing plan " + this.totalNumber + " with maxRushes " + this.maxRushes);
this.totalNumber++;
this.upcomingAttacks["Rush"].push(attackPlan);
@@ -201,7 +201,7 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
this.attackPlansEncounteredWater = true; // hack
else
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("Military Manager: Creating the plan " + type + " " + this.totalNumber);
this.totalNumber++;
this.upcomingAttacks[type].push(attackPlan);
@@ -226,7 +226,7 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
var attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, "Raid", data);
if (!attackPlan.failed)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("Headquarters: Raiding plan " + this.totalNumber);
this.totalNumber++;
this.upcomingAttacks["Raid"].push(attackPlan);
@@ -376,7 +376,7 @@ m.AttackPlan.prototype.updatePreparation = function(gameState, events)
return 2;
}
if (this.Config.debug > 2 && gameState.ai.playedTurn % 50 === 0)
if (this.Config.debug > 3 && gameState.ai.playedTurn % 50 === 0)
this.debugAttack();
// find our target
@@ -388,7 +388,7 @@ m.AttackPlan.prototype.updatePreparation = function(gameState, events)
var oldTargetPlayer = this.targetPlayer;
// may-be all our previous enemey targets have been destroyed ?
this.targetPlayer = this.getEnemyPlayer(gameState);
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn(" === no more target for enemy player " + oldTargetPlayer + " let us switch against player " + this.targetPlayer);
this.target = this.getNearestTarget(gameState, this.rallyPoint);
}
@@ -471,7 +471,7 @@ m.AttackPlan.prototype.updatePreparation = function(gameState, events)
}
else // Abort the plan so that its units will be reassigned to other plans.
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
{
var am = gameState.ai.HQ.attackManager;
API3.warn(" attacks upcoming: raid " + am.upcomingAttacks["Raid"].length
@@ -531,7 +531,7 @@ m.AttackPlan.prototype.updatePreparation = function(gameState, events)
}
var index = gameState.ai.accessibility.getAccessValue(entity.position());
if (index === rallyIndex)
entity.moveToRange(rallyPoint[0], rallyPoint[1], 0, 25, queued);
entity.moveToRange(rallyPoint[0], rallyPoint[1], 0, 15, queued);
else
gameState.ai.HQ.navalManager.requireTransport(gameState, entity, index, rallyIndex, rallyPoint);
});
@@ -569,7 +569,7 @@ m.AttackPlan.prototype.trainMoreUnits = function(gameState)
return va - vb;
});
if (this.Config.debug > 0 && gameState.ai.playedTurn%50 === 0)
if (this.Config.debug > 1 && gameState.ai.playedTurn%50 === 0)
{
API3.warn("====================================");
API3.warn("======== build order for plan " + this.name);
@@ -605,14 +605,14 @@ m.AttackPlan.prototype.trainMoreUnits = function(gameState)
// effectively removing the unit from the plan.
if (template === undefined)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("attack no template found " + this.buildOrder[0][1]);
delete this.unitStat[this.buildOrder[0][4]]; // deleting the associated unitstat.
this.buildOrder.splice(0,1);
}
else
{
if (this.Config.debug > 1)
if (this.Config.debug > 2)
API3.warn("attack template " + template + " added for plan " + this.name);
var max = this.buildOrder[0][3]["batchSize"];
var specialData = "Plan_" + this.name + "_" + this.buildOrder[0][4];
@@ -622,7 +622,7 @@ m.AttackPlan.prototype.trainMoreUnits = function(gameState)
var trainingPlan = new m.TrainingPlan(gameState, template, { "role": "attack", "plan": this.name, "special": specialData, "base": 0 }, max, max);
if (trainingPlan.template)
queue.addItem(trainingPlan);
else if (this.Config.debug > 0)
else if (this.Config.debug > 1)
API3.warn("training plan canceled because no template for " + template + " build1 " + uneval(this.buildOrder[0][1])
+ " build3 " + uneval(this.buildOrder[0][3]["interests"]));
}
@@ -930,7 +930,7 @@ m.AttackPlan.prototype.setRallyPoint = function(gameState)
// If we're here, it's because we have enough units.
m.AttackPlan.prototype.StartAttack = function(gameState)
{
if (this.Config.debug)
if (this.Config.debug > 1)
API3.warn("start attack " + this.name + " with type " + this.type);
if (!this.target || !gameState.getEntityById(this.target.id())) // our target was destroyed during our preparation
@@ -985,7 +985,7 @@ m.AttackPlan.prototype.StartAttack = function(gameState)
{
if (!this.path[0][0][0] || !this.path[0][0][1])
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("StartAttack: Problem with path " + uneval(this.path));
return false;
}
@@ -1034,9 +1034,9 @@ m.AttackPlan.prototype.update = function(gameState, events)
{
var done = true;
this.unitCollection.forEach(function (entity) {
if (self.Config.debug > 0 && entity.getMetadata(PlayerID, "transport") !== undefined)
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 > 0)
else if (self.Config.debug > 1)
Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [entity.id()], "rgb": [1,1,1]});
if (!done)
return;
@@ -1078,6 +1078,7 @@ m.AttackPlan.prototype.update = function(gameState, events)
// In case yes, we'll determine if we're simply off against an enemy army, a lone unit/building
// or if we reached the enemy base. Different plans may react differently.
var attackedNB = 0;
var attackedUnitNB = 0;
var attackedEvents = events["Attacked"];
for (var evt of attackedEvents)
{
@@ -1086,14 +1087,26 @@ m.AttackPlan.prototype.update = function(gameState, events)
var attacker = gameState.getEntityById(evt.attacker);
var ourUnit = gameState.getEntityById(evt.target);
if (attacker && attacker.position() && attacker.hasClass("Unit") && attacker.owner() != 0)
if (attacker && (attacker.owner() != 0 || this.targetPlayer === 0))
{
attackedNB++;
// if we're being attacked by a building, flee.
if (attacker && ourUnit && attacker.hasClass("Structure"))
ourUnit.flee(attacker);
if (attacker.hasClass("Unit"))
attackedUnitNB++;
}
}
// Are we arrived at destination ?
if ((gameState.ai.HQ.territoryMap.getOwner(this.position) === this.targetPlayer && attackedNB > 1) || attackedNB > 3)
var maybe = true;
if (attackedUnitNB == 0)
{
var siegeNB = 0;
this.unitCollection.forEach( function (ent) {
if (self.isSiegeUnit(gameState, ent))
siegeNB++;
});
if (siegeNB == 0)
maybe = false;
}
if (maybe && ((gameState.ai.HQ.territoryMap.getOwner(this.position) === this.targetPlayer && attackedNB > 1) || attackedNB > 3))
this.state = "arrived";
}
@@ -1131,13 +1144,13 @@ m.AttackPlan.prototype.update = function(gameState, events)
// there are walls but we can attack
if (nexttoWalls && this.unitCollection.filter(API3.Filters.byCanAttack("StoneWall")).length !== 0)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("Attack Plan " + this.type + " " + this.name + " has met walls and is not happy.");
this.state = "arrived";
}
else if (nexttoWalls) // abort plan
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("Attack Plan " + this.type + " " + this.name + " has met walls and gives up.");
Engine.ProfileStop();
return 0;
@@ -1153,7 +1166,7 @@ m.AttackPlan.prototype.update = function(gameState, events)
{
if (API3.SquareVectorDistance(this.position, this.targetPos) < 10000)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("Attack Plan " + this.type + " " + this.name + " has arrived to destination.");
this.state = "arrived";
}
@@ -1164,7 +1177,7 @@ m.AttackPlan.prototype.update = function(gameState, events)
this.unitCollection.move(this.path[0][0][0], this.path[0][0][1]);
else
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("Attack Plan " + this.type + " " + this.name + " has arrived to destination.");
this.state = "arrived";
}
@@ -1237,6 +1250,24 @@ m.AttackPlan.prototype.update = function(gameState, events)
var enemyUnits = gameState.getEnemyUnits(this.targetPlayer);
var enemyStructures = gameState.getEnemyStructures(this.targetPlayer);
var targetClassesUnit;
var targetClassesSiege;
if (this.type === "Rush")
targetClassesUnit = {"attack": ["Unit", "Structure"], "avoid": ["StoneWall", "Tower", "Fortress"]};
else
{
if (this.target.hasClass("Fortress"))
targetClassesUnit = {"attack": ["Unit", "Structure"], "avoid": ["StoneWall"]};
else if (this.target.hasClass("StoneWall"))
targetClassesUnit = {"attack": ["Unit", "Structure"], "avoid": ["Fortress"]};
else
targetClassesUnit = {"attack": ["Unit", "Structure"], "avoid": ["Fortress", "StoneWall"]};
}
if (this.target.hasClass("Structure"))
targetClassesSiege = {"attack": ["Structure"]};
else
targetClassesSiege = {"attack": ["Unit", "Structure"]};
if (this.unitCollUpdateArray === undefined || this.unitCollUpdateArray.length === 0)
this.unitCollUpdateArray = this.unitCollection.toIdArray();
@@ -1300,40 +1331,21 @@ m.AttackPlan.prototype.update = function(gameState, events)
else if (ent.hasClass("Cavalry"))
range += 30;
range = range * range;
// let's filter targets further based on this unit.
var entIndex = gameState.ai.accessibility.getAccessValue(ent.position());
var mStruct = enemyStructures.filter(function (enemy) {
if (!enemy.position() || (enemy.hasClass("StoneWall") && !ent.canAttackClass("StoneWall")))
return false;
if (API3.SquareVectorDistance(enemy.position(), ent.position()) > range)
return false;
if (siegeUnit && enemy.foundationProgress() === 0)
return false;
if (gameState.ai.accessibility.getAccessValue(enemy.position()) !== entIndex)
return false;
return true;
});
var nearby = (!ent.hasClass("Cavalry") && !ent.hasClass("Ranged"));
var mUnit = enemyUnits.filter(function (enemy) {
if (!enemy.position())
return false;
if (enemy.hasClass("Animal"))
return false;
if (nearby && enemy.hasClass("Female") && enemy.unitAIState().split(".")[1] == "FLEEING")
return false;
var dist = API3.SquareVectorDistance(enemy.position(), ent.position());
if (dist > range)
return false;
if (gameState.ai.accessibility.getAccessValue(enemy.position()) !== entIndex)
return false;
enemy.setMetadata(PlayerID, "distance", Math.sqrt(dist));
return true;
});
// Checking for gates if we're a siege unit.
mUnit = mUnit.toEntityArray();
mStruct = mStruct.toEntityArray();
if (siegeUnit)
{
var mStruct = enemyStructures.filter(function (enemy) {
if (!enemy.position() || (enemy.hasClass("StoneWall") && !ent.canAttackClass("StoneWall")))
return false;
if (API3.SquareVectorDistance(enemy.position(), ent.position()) > range)
return false;
if (enemy.foundationProgress() === 0)
return false;
if (gameState.ai.accessibility.getAccessValue(enemy.position()) !== entIndex)
return false;
return true;
}).toEntityArray();
if (mStruct.length !== 0)
{
mStruct.sort(function (structa,structb)
@@ -1363,10 +1375,26 @@ m.AttackPlan.prototype.update = function(gameState, events)
}
}
else
ent.attackMove(self.targetPos[0], self.targetPos[1], {"attack": ["Unit", "Structure"]});
ent.attackMove(self.targetPos[0], self.targetPos[1], targetClassesSiege);
}
else
{
var nearby = (!ent.hasClass("Cavalry") && !ent.hasClass("Ranged"));
var mUnit = enemyUnits.filter(function (enemy) {
if (!enemy.position())
return false;
if (enemy.hasClass("Animal"))
return false;
if (nearby && enemy.hasClass("Female") && enemy.unitAIState().split(".")[1] == "FLEEING")
return false;
var dist = API3.SquareVectorDistance(enemy.position(), ent.position());
if (dist > range)
return false;
if (gameState.ai.accessibility.getAccessValue(enemy.position()) !== entIndex)
return false;
enemy.setMetadata(PlayerID, "distance", Math.sqrt(dist));
return true;
}).toEntityArray();
if (mUnit.length !== 0)
{
mUnit.sort(function (unitA,unitB) {
@@ -1389,28 +1417,63 @@ m.AttackPlan.prototype.update = function(gameState, events)
ent.attack(mUnit[rand].id());
}
else if (API3.SquareVectorDistance(self.targetPos, ent.position()) > 2500 )
ent.attackMove(self.targetPos[0], self.targetPos[1], {"attack": ["Unit", "Structure"]});
else if (mStruct.length !== 0)
{
mStruct.sort(function (structa,structb) {
var vala = structa.costSum();
if (structa.hasClass("Gates") && ent.canAttackClass("StoneWall"))
vala += 10000;
else if (structa.hasClass("ConquestCritical"))
vala += 100;
var valb = structb.costSum();
if (structb.hasClass("Gates") && ent.canAttackClass("StoneWall"))
valb += 10000;
else if (structb.hasClass("ConquestCritical"))
valb += 100;
return (valb - vala);
});
if (mStruct[0].hasClass("Gates"))
ent.attack(mStruct[0].id());
else
ent.attackMove(self.targetPos[0], self.targetPos[1], targetClassesUnit);
ent.attackMove(self.targetPos[0], self.targetPos[1], {"attack": ["Unit", "Structure"]}, true); // in case we are blocked by walls
}
else
{
var mStruct = enemyStructures.filter(function (enemy) {
if (!enemy.position() || (enemy.hasClass("StoneWall") && !ent.canAttackClass("StoneWall")))
return false;
if (API3.SquareVectorDistance(enemy.position(), ent.position()) > range)
return false;
if (gameState.ai.accessibility.getAccessValue(enemy.position()) !== entIndex)
return false;
return true;
}).toEntityArray();
if (mStruct.length !== 0)
{
var rand = Math.floor(Math.random() * mStruct.length * 0.2);
ent.attack(mStruct[rand].id());
mStruct.sort(function (structa,structb) {
var vala = structa.costSum();
if (structa.hasClass("Gates") && ent.canAttackClass("StoneWall"))
vala += 10000;
else if (structa.hasClass("ConquestCritical"))
vala += 100;
var valb = structb.costSum();
if (structb.hasClass("Gates") && ent.canAttackClass("StoneWall"))
valb += 10000;
else if (structb.hasClass("ConquestCritical"))
valb += 100;
return (valb - vala);
});
if (mStruct[0].hasClass("Gates"))
ent.attack(mStruct[0].id());
else
{
var rand = Math.floor(Math.random() * mStruct.length * 0.2);
ent.attack(mStruct[rand].id());
}
}
else if (needsUpdate) // really nothing let's try to help our nearest unit
{
var distmin = Math.min();
var attackerId = undefined;
this.unitCollection.forEach( function (unit) {
if (!unit.position())
return;
if (unit.unitAIState().split(".")[1] !== "COMBAT" || unit.unitAIOrderData().length === 0
|| !unit.unitAIOrderData()[0]["target"])
return;
var dist = API3.SquareVectorDistance(unit.position(), ent.position());
if (dist > distmin)
return;
distmin = dist;
attackerId = unit.unitAIOrderData()[0]["target"];
});
if (attackerId)
ent.attack(attackerId);
}
}
}
@@ -1421,7 +1484,7 @@ m.AttackPlan.prototype.update = function(gameState, events)
// updating targets.
if (!this.target || !gameState.getEntityById(this.target.id()))
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("Seems like our target has been destroyed. Switching.");
this.target = this.getNearestTarget(gameState, this.position, true);
if (!this.target)
@@ -1447,12 +1510,12 @@ m.AttackPlan.prototype.update = function(gameState, events)
if (!this.target)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("No new target found. Remaining units " + this.unitCollection.length);
Engine.ProfileStop();
return false;
}
else if (this.Config.debug > 0)
else if (this.Config.debug > 1)
API3.warn("We will help one of our other attacks");
}
this.targetPos = this.target.position();
@@ -134,7 +134,7 @@ m.BaseManager.prototype.checkEvents = function (gameState, events, queues)
}
if (!basemin)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn(" base " + this.ID + " destroyed and no other bases found");
continue;
}
@@ -183,7 +183,7 @@ m.BaseManager.prototype.assignResourceToDropsite = function (gameState, dropsite
{
if (this.dropsites[dropsite.id()])
{
if (this.Config.debug)
if (this.Config.debug > 1)
warn("assignResourceToDropsite: dropsite already in the list. Should never happen");
return;
}
@@ -387,7 +387,7 @@ m.BaseManager.prototype.findBestDropsiteLocation = function(gameState, resource)
bestIdx = j;
}
if (this.Config.debug == 2)
if (this.Config.debug > 2)
warn(" for dropsite best is " + bestVal);
if (bestVal <= 0)
@@ -670,11 +670,13 @@ m.BaseManager.prototype.gatherersByType = function(gameState, type)
m.BaseManager.prototype.pickBuilders = function(gameState, workers, number)
{
var availableWorkers = this.workers.filter(function (ent) {
if (!ent.position())
return false;
if (ent.getMetadata(PlayerID, "plan") === -2 || ent.getMetadata(PlayerID, "plan") === -3)
return false;
if (ent.getMetadata(PlayerID, "transport") !== undefined)
if (ent.getMetadata(PlayerID, "transport"))
return false;
if (ent.hasClass("Cavalry") || ent.hasClass("Ship") || ent.position() === undefined)
if (ent.hasClass("Cavalry") || ent.hasClass("Ship"))
return false;
return true;
}).toEntityArray();
@@ -800,7 +802,17 @@ m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
});
if (assigned + addedToThis < targetNB)
{
var nonBuilderWorkers = workers.filter(function(ent) { return (ent.getMetadata(PlayerID, "subrole") !== "builder" && ent.position() !== undefined); }).toEntityArray();
var nonBuilderWorkers = workers.filter(function(ent) {
if (ent.getMetadata(PlayerID, "subrole") === "builder")
return false;
if (!ent.position())
return false;
if (ent.getMetadata(PlayerID, "plan") === -2 || ent.getMetadata(PlayerID, "plan") === -3)
return false;
if (ent.getMetadata(PlayerID, "transport"))
return false;
return true;
}).toEntityArray();
var time = target.buildTime();
nonBuilderWorkers.sort(function (workerA,workerB)
{
@@ -848,8 +860,18 @@ m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair)
if (assigned < targetNB/3)
{
if (builderWorkers.length + addedWorkers < targetNB*2)
{
var nonBuilderWorkers = workers.filter(function(ent) { return (ent.getMetadata(PlayerID, "subrole") !== "builder" && ent.position() !== undefined && ent.getMetadata(PlayerID, "transport") === undefined); });
{
var nonBuilderWorkers = workers.filter(function(ent) {
if (ent.getMetadata(PlayerID, "subrole") === "builder")
return false;
if (!ent.position())
return false;
if (ent.getMetadata(PlayerID, "plan") === -2 || ent.getMetadata(PlayerID, "plan") === -3)
return false;
if (ent.getMetadata(PlayerID, "transport"))
return false;
return true;
});
var nearestNonBuilders = nonBuilderWorkers.filterNearest(target.position(), targetNB/3 - assigned);
nearestNonBuilders.forEach(function(ent) {
@@ -4,6 +4,7 @@ var PETRA = function(m)
// this defines the medium difficulty
m.Config = function() {
this.difficulty = 2; // 0 is sandbox, 1 is easy, 2 is medium, 3 is hard, 4 is very hard.
// debug level: 0=none, 1=sanity checks, 2=debug; 3=detailed debug
this.debug = 0;
this.Military = {
@@ -96,15 +96,6 @@ m.DefenseManager.prototype.isDangerous = function(gameState, entity)
return true;
}
var myCCFoundations = gameState.getOwnFoundations().filter(API3.Filters.byClass("CivCentre"));
for (var i in myCCFoundations._entities)
{
if (!myCCFoundations._entities[i].getBuildersNb())
continue;
if (API3.SquareVectorDistance(myCCFoundations._entities[i].position(), entity.position()) < 6000)
return true;
}
if (this.Config.personality.cooperative > 0.3)
{
var allyCC = gameState.getExclusiveAllyEntities().filter(API3.Filters.byClass("CivCentre"));
@@ -119,8 +110,12 @@ m.DefenseManager.prototype.isDangerous = function(gameState, entity)
var myBuildings = gameState.getOwnStructures();
for (var i in myBuildings._entities)
{
if (myBuildings._entities[i].foundationProgress() === 0)
continue;
if (API3.SquareVectorDistance(myBuildings._entities[i].position(), entity.position()) < 6000)
return true;
}
return false;
};
@@ -134,11 +129,9 @@ m.DefenseManager.prototype.checkEnemyUnits = function(gameState)
return;
var self = this;
var filter = API3.Filters.and(API3.Filters.byClass("Unit"), API3.Filters.byOwner(i));
var enemyUnits = gameState.updatingGlobalCollection("player-" +i + "-units", filter);
// loop through enemy units
enemyUnits.forEach( function (ent) {
gameState.getEnemyUnits(i).forEach( function (ent) {
// first check: is this unit already part of an army.
if (ent.getMetadata(PlayerID, "PartOfArmy") !== undefined)
return;
@@ -225,7 +218,7 @@ m.DefenseManager.prototype.checkEnemyArmies = function(gameState, events)
{
if (API3.SquareVectorDistance(bases[i].position(), army.foePosition) < 40000)
{
if(this.Config.debug > 0)
if(this.Config.debug > 1)
API3.warn("army in neutral territory, but still near one of our CC");
stillDangerous = true;
break;
@@ -329,7 +322,7 @@ m.DefenseManager.prototype.assignDefenders = function(gameState)
};
// If our defense structures are attacked, garrison soldiers inside when possible
// and if a support unit is attacked and has less than 30% health, garrison it inside the nearest cc
// and if a support unit is attacked and has less than 45% health, garrison it inside the nearest cc
m.DefenseManager.prototype.checkEvents = function(gameState, events)
{
var self = this;
@@ -342,7 +335,8 @@ m.DefenseManager.prototype.checkEvents = function(gameState, events)
if (target.hasClass("Ship")) // TODO integrate ships later need to be sure it is accessible
continue;
if (target.hasClass("Support") && target.healthLevel() < 0.4 && !target.getMetadata(PlayerID, "transport"))
if (target.hasClass("Support") && target.healthLevel() < 0.45 && !target.getMetadata(PlayerID, "transport")
&& target.getMetadata(PlayerID, "plan") !== -2 && target.getMetadata(PlayerID, "plan") !== -3)
{
this.garrisonUnitForHealing(gameState, target);
continue;
@@ -65,5 +65,29 @@ m.getMaxStrength = function(ent, againstClass)
return strength * hp;
};
m.getHolder = function(ent, gameState)
{
var found = undefined;
gameState.getEntities().forEach(function (holder) {
if (found || !holder.isGarrisonHolder())
return;
if (holder._entity.garrisoned.indexOf(ent.id()) !== -1)
found = holder;
});
return found;
};
m.dumpEntity = function(ent)
{
if (!ent)
return;
API3.warn(" >>> pos " + ent.position() + " state " + ent.unitAIState());
API3.warn(" >>> role " + ent.getMetadata(PlayerID, "role") + " subrole " + ent.getMetadata(PlayerID, "subrole")
+ " garrisoning " + ent.getMetadata(PlayerID, "garrisoning") + " garrisonHolder " + ent.getMetadata(PlayerID, "garrisonHolder")
+ " plan " + ent.getMetadata(PlayerID, "plan") + " transport " + ent.getMetadata(PlayerID, "transport")
+ " gather-type " + ent.getMetadata(PlayerID, "gather-type") + " target-foundation " + ent.getMetadata(PlayerID, "target-foundation")
+ " PartOfArmy " + ent.getMetadata(PlayerID, "PartOfArmy"));
};
return m;
}(PETRA);
@@ -49,6 +49,38 @@ m.GarrisonManager.prototype.update = function(gameState, queues)
this.leaveGarrison(ent);
list.splice(j--, 1);
}
else
{
var ok = false;
var orders = ent.unitAIOrderData();
for (var order of orders)
{
if (!order.target || order.target != id)
continue;
ok = true;
break;
}
if (ok)
continue;
if (ent.getMetadata(PlayerID, "garrisonHolder") == +id)
{
// The garrison order must have failed
this.leaveGarrison(ent);
list.splice(j--, 1);
}
else
{
if (gameState.ai.HQ.Config.debug > 0)
{
API3.warn("Petra garrison error: unit " + ent.id() + " (" + ent.genericName()
+ ") is expected to garrison in " + id + " (" + holder.genericName()
+ "), but has no such garrison order " + uneval(orders));
m.dumpEntity(ent);
}
list.splice(j--, 1);
}
}
}
if (!holder.position()) // could happen with siege unit inside a ship
@@ -113,7 +145,7 @@ m.GarrisonManager.prototype.garrison = function(gameState, ent, holder, type)
this.registerHolder(gameState, holder);
this.holders[holder.id()].push(ent.id());
if (gameState.ai.HQ.Config.debug > 1)
if (gameState.ai.HQ.Config.debug > 2)
{
warn("garrison unit " + ent.genericName() + " in " + holder.genericName() + " with type " + type);
warn(" we try to garrison a unit with plan " + ent.getMetadata(PlayerID, "plan") + " and role " + ent.getMetadata(PlayerID, "role")
@@ -93,7 +93,7 @@ m.HQ.prototype.init = function(gameState, queues)
this.navalRegions.push(i);
}
}
if (this.Config.debug > 1)
if (this.Config.debug > 2)
{
for (var region in this.allowedRegions)
API3.warn(" >>> zone " + region + " taille " + gameState.ai.accessibility.regionSize[region]);
@@ -203,7 +203,7 @@ m.HQ.prototype.init = function(gameState, queues)
break;
}
}
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("starting size " + startingSize + "(cut at 1500 for fish pushing)");
if (startingSize < 1500)
{
@@ -227,7 +227,7 @@ m.HQ.prototype.init = function(gameState, queues)
}
}
}
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("startingWood: " + startingWood + "(cut at 8500 for no rush and 6000 for saveResources)");
if (startingWood < 6000)
{
@@ -242,7 +242,7 @@ m.HQ.prototype.init = function(gameState, queues)
var template = gameState.getTemplate(this.bBase[0]);
if (!template.available(gameState))
{
if (this.Config.debug > 0)
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();
@@ -250,7 +250,7 @@ m.HQ.prototype.init = function(gameState, queues)
{
// if we have some allies, keep a fraction of our units to defend them
// and devote the rest to atacks
if (this.Config.debug > 0)
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);
@@ -340,7 +340,7 @@ m.HQ.prototype.getSeaIndex = function (gameState, index1, index2)
return path[1];
else
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
{
API3.warn("bad path from " + index1 + " to " + index2 + " ??? " + uneval(path));
API3.warn(" regionLinks start " + uneval(gameState.ai.accessibility.regionLinks[index1]));
@@ -855,7 +855,7 @@ m.HQ.prototype.findEconomicCCLocation = function(gameState, template, resource,
var cut = 60;
if (fromStrategic) // be less restrictive
cut = 30;
if (this.Config.debug)
if (this.Config.debug > 1)
API3.warn("we have found a base for " + resource + " with best (cut=" + cut + ") = " + bestVal);
// not good enough.
if (bestVal < cut)
@@ -987,7 +987,7 @@ m.HQ.prototype.findStrategicCCLocation = function(gameState, template)
bestIdx = j;
}
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("We've found a strategic base with bestVal = " + bestVal);
Engine.ProfileStop();
@@ -1072,14 +1072,14 @@ m.HQ.prototype.findMarketLocation = function(gameState, template)
bestIdx = j;
}
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("We found a market position with bestVal = " + bestVal);
if (bestVal === undefined) // no constraints. For the time being, place it arbitrarily by the ConstructionPlan
return [-1, -1, -1];
var expectedGain = Math.round(bestVal / this.Config.distUnitGain);
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("this would give a trading gain of " + expectedGain);
// do not keep it if gain is too small, except if this is our first BarterMarket
if (expectedGain < 6 && (!template.hasClass("BarterMarket") || gameState.getOwnStructures().filter(API3.Filters.byClass("BarterMarket")).length > 0))
@@ -1287,7 +1287,7 @@ m.HQ.prototype.buildMoreHouses = function(gameState,queues)
var index = this.stopBuilding.indexOf(gameState.applyCiv("structures/{civ}_house"));
if (count < 5 && index !== -1)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("no room to place a house ... try to be less restrictive");
this.stopBuilding.splice(index, 1);
this.requireHouses = true;
@@ -1320,7 +1320,7 @@ m.HQ.prototype.buildMoreHouses = function(gameState,queues)
var index = this.stopBuilding.indexOf(gameState.applyCiv("structures/{civ}_house"));
if (index !== -1)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("no room to place a house ... try to improve with technology");
this.researchManager.researchPopulationBonus(gameState, queues);
}
@@ -1341,7 +1341,7 @@ m.HQ.prototype.checkBaseExpansion = function(gameState, queues)
// first expand if we have not enough room available for buildings
if (this.stopBuilding.length > 1)
{
if (this.Config.debug > 1)
if (this.Config.debug > 2)
API3.warn("try to build a new base because not enough room to build " + uneval(this.stopBuilding));
this.buildNewBase(gameState, queues);
return;
@@ -1353,7 +1353,7 @@ m.HQ.prototype.checkBaseExpansion = function(gameState, queues)
popForBase = this.Config.Economy.popForTown + 5;
if (Math.floor(numUnits/popForBase) >= gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")).length)
{
if (this.Config.debug > 1)
if (this.Config.debug > 2)
API3.warn("try to build a new base because of population " + numUnits + " for " + numCCs + " CCs");
this.buildNewBase(gameState, queues);
}
@@ -1369,7 +1369,7 @@ m.HQ.prototype.buildNewBase = function(gameState, queues, type)
return false;
// base "-1" means new base.
if (this.Config.debug > 0)
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 }));
return true;
@@ -1842,7 +1842,7 @@ m.HQ.prototype.update = function(gameState, queues, events)
this.territoryMap = m.createTerritoryMap(gameState);
if (this.Config.debug > 0)
if (this.Config.debug > 1)
{
gameState.getOwnUnits().forEach (function (ent) {
if (!ent.hasClass("CitizenSoldier") || ent.hasClass("Cavalry"))
@@ -1859,15 +1859,7 @@ m.HQ.prototype.update = function(gameState, queues, events)
if (gameState.ai.playedTurn - ent.getMetadata(PlayerID, "idleTim") < 50)
return;
API3.warn(" unit idle since " + (gameState.ai.playedTurn-ent.getMetadata(PlayerID, "idleTim")) + " turns");
API3.warn(" unitai state " + ent.unitAIState());
API3.warn(" >>> base " + ent.getMetadata(PlayerID, "base"));
API3.warn(" >>> role " + ent.getMetadata(PlayerID, "role"));
API3.warn(" >>> subrole " + ent.getMetadata(PlayerID, "subrole"));
API3.warn(" >>> gather-type " + ent.getMetadata(PlayerID, "gather-type"));
API3.warn(" >>> target-foundation " + ent.getMetadata(PlayerID, "target-foundation"));
API3.warn(" >>> PartOfArmy " + ent.getMetadata(PlayerID, "PartOfArmy"));
API3.warn(" >>> plan " + ent.getMetadata(PlayerID, "plan"));
API3.warn(" >>> transport " + ent.getMetadata(PlayerID, "transport"));
m.dumpEntity(ent);
ent.setMetadata(PlayerID, "idleTim", gameState.ai.playedTurn);
});
}
@@ -1944,18 +1936,6 @@ m.HQ.prototype.update = function(gameState, queues, events)
Engine.ProfileStop();
};
m.HQ.prototype.getHolder = function(gameState, ent)
{
var found = undefined;
gameState.getEntities().forEach(function (holder) {
if (found || !holder.isGarrisonHolder())
return;
if (holder._entity.garrisoned.indexOf(ent.id()) !== -1)
found = holder;
});
return found;
};
return m;
}(PETRA);
@@ -280,7 +280,7 @@ m.NavalManager.prototype.checkEvents = function(gameState, queues, events)
continue;
var shipId = evt.entityObj.id();
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("one ship " + shipId + " from plan " + plan.ID + " destroyed during " + plan.state);
if (plan.state === "boarding")
{
@@ -356,7 +356,7 @@ m.NavalManager.prototype.requireTransport = function(gameState, entity, startInd
var plan = new m.TransportPlan(gameState, [entity], startIndex, endIndex, endPos);
if (plan.failed)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn(">>>> transport plan aborted <<<<");
return false;
}
@@ -367,12 +367,12 @@ m.NavalManager.prototype.requireTransport = function(gameState, entity, startInd
// split a transport plan in two, moving all entities not yet affected to a ship in the new plan
m.NavalManager.prototype.splitTransport = function(gameState, plan)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn(">>>> split of transport plan started <<<<");
var newplan = new m.TransportPlan(gameState, [], plan.startIndex, plan.endIndex, plan.endPos);
if (newplan.failed)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn(">>>> split of transport plan aborted <<<<");
return false;
}
@@ -384,7 +384,7 @@ m.NavalManager.prototype.splitTransport = function(gameState, plan)
++nbUnits;
newplan.addUnit(ent, ent.getMetadata(PlayerID, "endPos"));
});
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn(">>>> previous plan left with units " + plan.units.length);
if (nbUnits)
this.transportPlans.push(newplan);
@@ -625,7 +625,7 @@ m.NavalManager.prototype.update = function(gameState, queues, events)
var remaining = this.transportPlans[i].update(gameState);
if (remaining === 0)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("no more units on transport plan " + this.transportPlans[i].ID);
this.transportPlans[i].releaseAll();
this.transportPlans.splice(i--, 1);
@@ -377,7 +377,7 @@ m.QueueManager.prototype.switchResource = function(gameState, res)
this.accounts[j][res] += diff;
this.accounts[i][res] -= diff;
++otherQueue.switched;
if (this.Config.debug > 1)
if (this.Config.debug > 2)
API3.warn ("switching queue " + res + " from " + i + " to " + j + " in amount " + diff);
break;
}
@@ -429,7 +429,7 @@ m.QueueManager.prototype.update = function(gameState)
// Start the next item in the queue if we can afford it.
this.startNextItems(gameState);
if (this.Config.debug > 0 && gameState.ai.playedTurn%50 === 0)
if (this.Config.debug > 1 && gameState.ai.playedTurn%50 === 0)
this.printQueues(gameState);
Engine.ProfileStop();
@@ -511,7 +511,7 @@ m.QueueManager.prototype.getPriority = function(queueName)
m.QueueManager.prototype.changePriority = function(queueName, newPriority)
{
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn(">>> Priority of queue " + queueName + " changed from " + this.priorities[queueName] + " to " + newPriority);
var self = this;
if (this.queues[queueName] !== undefined)
@@ -95,7 +95,7 @@ m.TrainingPlan.prototype.start = function(gameState)
this.metadata.base = trainers[0].getMetadata(PlayerID, "base");
trainers[0].train(this.type, this.number, this.metadata, this.promotedTypes(gameState));
}
else if (gameState.ai.Config.debug > 0)
else if (gameState.ai.Config.debug > 1)
warn(" no trainers for this queue " + this.type);
this.onStart(gameState);
};
@@ -109,7 +109,7 @@ m.TradeManager.prototype.buildTradeRoute = function(gameState, queues)
}
if (distmax > 0)
{
if (this.Config.debug > 1)
if (this.Config.debug > 2)
API3.warn(" a second market will be built in base " + base);
// TODO build also docks when better
queues.economicBuilding.addItem(new m.ConstructionPlan(gameState, "structures/{civ}_market", { "base": base }));
@@ -152,11 +152,11 @@ m.TradeManager.prototype.buildTradeRoute = function(gameState, queues)
}
if (distmax < 0)
{
if (this.Config.debug > 1)
if (this.Config.debug > 2)
API3.warn("no trade route possible");
return false;
}
if (this.Config.debug > 0)
if (this.Config.debug > 1)
API3.warn("one trade route set with gain " + Math.round(distmax / this.Config.distUnitGain));
return true;
};
@@ -201,7 +201,7 @@ m.TradeManager.prototype.setTradingGoods = function(gameState)
else
tradingGoods[mostNeeded[0].type] += nextNeed;
Engine.PostCommand(PlayerID, {"type": "set-trading-goods", "tradingGoods": tradingGoods});
if (this.Config.debug == 2)
if (this.Config.debug > 2)
API3.warn(" trading goods set to " + uneval(tradingGoods));
};
@@ -268,7 +268,7 @@ m.TradeManager.prototype.performBarter = function(gameState)
if (bestToSell !== undefined)
{
barterers[0].barter(buy, bestToSell, 100);
if (this.Config.debug > 1)
if (this.Config.debug > 2)
API3.warn("Necessity bartering: sold " + bestToSell +" for " + buy + " >> need sell " + needs[bestToSell]
+ " need buy " + needs[buy] + " rate buy " + rates[buy] + " available sell " + available[bestToSell]
+ " available buy " + available[buy] + " barterRate " + bestRate);
@@ -301,7 +301,7 @@ m.TradeManager.prototype.performBarter = function(gameState)
if (bestToBuy !== undefined)
{
barterers[0].barter(bestToBuy, "food", 100);
if (this.Config.debug > 1)
if (this.Config.debug > 2)
API3.warn("Contingency bartering: sold food for " + bestToBuy + " available sell " + available["food"]
+ " available buy " + available[bestToBuy] + " barterRate " + getBarterRate(prices, bestToBuy, "food"));
return true;
@@ -321,7 +321,7 @@ m.TradeManager.prototype.update = function(gameState, queues)
var target = this.tradeRoute.target;
if (!source || !target || !gameState.getEntityById(source.id()) || !gameState.getEntityById(target.id()))
{
if (this.Config.debug > 1)
if (this.Config.debug > 2)
API3.warn("We have lost our trade route");
this.tradeRoute = undefined;
return;
@@ -38,7 +38,7 @@ m.TransportPlan = function(gameState, units, startIndex, endIndex, endPos)
if (!this.sea)
{
this.failed = true;
if (this.debug > 0)
if (this.debug > 1)
API3.warn("transport plan with bad path: startIndex " + startIndex + " endIndex " + endIndex);
return false;
}
@@ -53,7 +53,7 @@ m.TransportPlan = function(gameState, units, startIndex, endIndex, endPos)
this.units.updateEnt(ent);
}
if (this.debug > 0)
if (this.debug > 1)
API3.warn("Starting a new transport plan with ID " + this.ID + " to index " + endIndex
+ " with units length " + units.length);
@@ -101,7 +101,7 @@ m.TransportPlan.prototype.assignUnitToShip = function(gameState, ent)
{
ent.setMetadata(PlayerID, "onBoard", ship.id());
done = true;
if (self.debug > 0)
if (self.debug > 1)
{
if (ent.getMetadata(PlayerID, "role") === "attack")
Engine.PostCommand(PlayerID,{"type": "set-shading-color", "entities": [ent.id()], "rgb": [2,0,0]});
@@ -272,7 +272,7 @@ m.TransportPlan.prototype.onBoarding = function(gameState)
if (self.nTry[shipId] > 1) // we must have been blocked by something ... try with another boarding point
{
self.nTry[shipId] = 0;
if (self.debug > 0)
if (self.debug > 1)
API3.warn("ship " + shipId + " new attempt for a landing point ");
self.boardingPos[shipId] = self.getBoardingPos(gameState, self.startIndex, self.sea, undefined, false);
}
@@ -293,7 +293,7 @@ m.TransportPlan.prototype.onBoarding = function(gameState)
++self.nTry[ent.id()];
if (self.nTry[ent.id()] > 5)
{
if (self.debug > 0)
if (self.debug > 1)
API3.warn("unit blocked, but no ways out of the trap ... destroy it");
self.resetUnit(gameState, ent);
ent.destroy();
@@ -399,13 +399,13 @@ m.TransportPlan.prototype.onSailing = function(gameState)
ship.moveApart(recov.entPos, 15);
continue;
}
if (gameState.ai.HQ.Config.debug > 0)
if (gameState.ai.HQ.Config.debug > 1)
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.Config.debug > 0)
if (gameState.ai.HQ.Config.debug > 1)
API3.warn(" recovered entity kept " + ent.id());
this.resetUnit(gameState, ent);
// TODO we should not destroy it, but now the unit could still be reloaded on the next turn
@@ -414,7 +414,7 @@ m.TransportPlan.prototype.onSailing = function(gameState)
}
else
{
if (gameState.ai.HQ.Config.debug > 0)
if (gameState.ai.HQ.Config.debug > 1)
API3.warn("recovered entity destroyed " + ent.id());
this.resetUnit(gameState, ent);
ent.destroy();
@@ -453,7 +453,7 @@ m.TransportPlan.prototype.onSailing = function(gameState)
else if (gameState.ai.accessibility.getAccessValue(ent.position()) !== this.endIndex)
{
// unit unloaded on a wrong region - try to regarrison it and move a bit the ship
if (gameState.ai.HQ.Config.debug > 0)
if (gameState.ai.HQ.Config.debug > 1)
API3.warn(">>> unit unloaded on a wrong region ! try to garrison it again <<<");
var ship = gameState.getEntityById(ent.getMetadata(PlayerID, "onBoard"));
if (ship && !this.canceled)
@@ -465,7 +465,7 @@ m.TransportPlan.prototype.onSailing = function(gameState)
}
else
{
if (gameState.ai.HQ.Config.debug > 0)
if (gameState.ai.HQ.Config.debug > 1)
API3.warn("no way ... we destroy it");
this.resetUnit(gameState, ent);
ent.destroy();
@@ -537,7 +537,7 @@ m.TransportPlan.prototype.onSailing = function(gameState)
if (self.nTry[shipId] > 2) // we must have been blocked by something ... try with another boarding point
{
self.nTry[shipId] = 0;
if (self.debug > 0)
if (self.debug > 1)
API3.warn(shipId + " new attempt for a landing point ");
self.boardingPos[shipId] = self.getBoardingPos(gameState, self.endIndex, self.sea, undefined, true);
}
@@ -13,7 +13,7 @@ m.Worker = function(ent)
m.Worker.prototype.update = function(baseManager, gameState)
{
if (!this.ent.position())
if (!this.ent.position() || this.ent.getMetadata(PlayerID, "plan") === -2 || this.ent.getMetadata(PlayerID, "plan") === -3)
return;
// If we are waiting for a transport or we are sailing, just wait
@@ -490,7 +490,7 @@ m.Worker.prototype.startGathering = function(gameState, baseManager)
// If we are here, we have nothing left to gather ... certainly no more resources of this type
gameState.ai.HQ.lastFailedGather[resource] = gameState.ai.elapsedTime;
if (gameState.ai.HQ.Config.debug > 1)
if (gameState.ai.HQ.Config.debug > 2)
warn(" >>>>> worker with gather-type " + resource + " with nothing to gather ");
this.ent.setMetadata(PlayerID, "subrole", "idle");
return false;