1
0
forked from 0ad/0ad

Petra: bugfixes + improved attack/defense behaviour

This was SVN commit r15376.
This commit is contained in:
mimo 2014-06-16 22:03:30 +00:00
parent 013ab4bda7
commit d11e48cd85
5 changed files with 88 additions and 53 deletions

View File

@ -74,7 +74,7 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
// okay so we'll get the support plan
if (!attack.isStarted())
{
var updateStep = attack.updatePreparation(gameState, this,events);
var updateStep = attack.updatePreparation(gameState, events);
// now we're gonna check if the preparation time is over
if (updateStep === 1 || attack.isPaused() )
@ -146,7 +146,7 @@ m.AttackManager.prototype.update = function(gameState, queues, events)
// okay so then we'll update the attack.
if (attack.isPaused())
continue;
var remaining = attack.update(gameState,this, events);
var remaining = attack.update(gameState, events);
if (!remaining)
{
if (this.Config.debug > 0)

View File

@ -1130,27 +1130,31 @@ m.AttackPlan.prototype.update = function(gameState, events)
// basic state of attacking.
if (this.state === "")
{
var timeElapsed = gameState.getTimeElapsed();
var attackedEvents = events["Attacked"];
for (var key in attackedEvents)
for (var evt of attackedEvents)
{
var e = attackedEvents[key];
if (IDs.indexOf(e.target) === -1)
if (IDs.indexOf(evt.target) === -1)
continue;
var attacker = gameState.getEntityById(e.attacker);
var attacker = gameState.getEntityById(evt.attacker);
if (!attacker || !attacker.position() || !attacker.hasClass("Unit"))
continue;
var ourUnit = gameState.getEntityById(e.target);
var ourUnit = gameState.getEntityById(evt.target);
if (this.isSiegeUnit(gameState, ourUnit))
{
// if siege units are attacked, we'll send some units to deal with enemies.
var collec = this.unitCollection.filter(API3.Filters.not(API3.Filters.byClass("Siege"))).filterNearest(ourUnit.position(), 5).toEntityArray();
for (var ent of collec)
if (!this.isSiegeUnit(gameState, ent))
{
ent.attack(attacker.id());
ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", timeElapsed);
}
}
else
{
// if other units are attacked, abandon their target (if it was a structure or a support) and retaliate
// if units are attacked, abandon their target (if it was a structure or a support) and retaliate
var orderData = ourUnit.unitAIOrderData();
if (orderData.length !== 0 && orderData[0]["target"])
{
@ -1159,6 +1163,7 @@ m.AttackPlan.prototype.update = function(gameState, events)
continue;
}
ourUnit.attack(attacker.id());
ourUnit.setMetadata(PlayerID, "lastAttackPlanUpdateTime", timeElapsed);
}
}
@ -1168,8 +1173,6 @@ m.AttackPlan.prototype.update = function(gameState, events)
if (this.unitCollUpdateArray === undefined || this.unitCollUpdateArray.length === 0)
this.unitCollUpdateArray = this.unitCollection.toIdArray();
var timeElapsed = gameState.getTimeElapsed();
// Let's check a few units each time we update (currently 10) except when attack starts
if (this.unitCollUpdateArray.length < 15 || this.startingAttack)
var lgth = this.unitCollUpdateArray.length;
@ -1202,12 +1205,15 @@ m.AttackPlan.prototype.update = function(gameState, events)
else if(!target.hasClass("Structure"))
maybeUpdate = true;
}
else if (!ent.hasClass("Cavalry") && !ent.hasClass("Ranged") && orderData && orderData["target"])
else if(orderData && orderData["target"])
{
var target = gameState.getEntityById(orderData["target"]);
if (!target)
needsUpdate = true;
else if (target.hasClass("Female") && target.unitAIState().split(".")[1] == "FLEEING")
else if(target.hasClass("Structure"))
maybeUpdate = true;
else if (!ent.hasClass("Cavalry") && !ent.hasClass("Ranged")
&& target.hasClass("Female") && target.unitAIState().split(".")[1] == "FLEEING")
maybeUpdate = true;
}

View File

@ -331,17 +331,16 @@ m.DefenseManager.prototype.checkDefenseStructures = function(gameState, events)
{
var self = this;
var attackedEvents = events["Attacked"];
for (var key in attackedEvents)
for (var evt of attackedEvents)
{
var e = attackedEvents[key];
var target = gameState.getEntityById(e.target);
var target = gameState.getEntityById(evt.target);
if (!target || !gameState.isEntityOwn(target) || !target.getArrowMultiplier())
continue;
if (!target.isGarrisonHolder() || gameState.ai.HQ.garrisonManager.numberOfGarrisonedUnits(target) >= target.garrisonMax())
continue;
if (target.hasClass("Ship")) // TODO integrate ships later need to be sure it is accessible
continue;
var attacker = gameState.getEntityById(e.attacker);
var attacker = gameState.getEntityById(evt.attacker);
if (!attacker)
continue;
var attackTypes = target.attackTypes();
@ -359,11 +358,6 @@ m.DefenseManager.prototype.checkDefenseStructures = function(gameState, events)
if (!ent.position())
return;
var army = ent.getMetadata(PlayerID, "PartOfArmy");
if (army !== undefined)
army = self.getArmy(army);
if (army !== undefined)
army.removeOwn(gameState, ent.id(), ent);
if (ent.getMetadata(PlayerID, "transport") !== undefined)
return;
if (ent.getMetadata(PlayerID, "plan") === -2 || ent.getMetadata(PlayerID, "plan") === -3)
@ -376,6 +370,13 @@ m.DefenseManager.prototype.checkDefenseStructures = function(gameState, events)
}
if (gameState.ai.accessibility.getAccessValue(target.position()) !== index)
return;
var army = ent.getMetadata(PlayerID, "PartOfArmy");
if (army !== undefined)
{
army = self.getArmy(army);
if (army !== undefined)
army.removeOwn(gameState, ent.id(), ent);
}
garrisonManager.garrison(gameState, ent, target, "protection");
});
}

View File

@ -24,7 +24,7 @@ m.GarrisonManager.prototype.update = function(gameState, queues)
var holder = gameState.getEntityById(id);
if (!holder) // this holder was certainly destroyed. Let's remove it
{
for each (var entId in this.holders[id])
for (var entId of this.holders[id])
{
var ent = gameState.getEntityById(entId);
if (ent && ent.getMetadata(PlayerID, "garrisonHolder") === id)
@ -51,8 +51,7 @@ m.GarrisonManager.prototype.update = function(gameState, queues)
if (!holder.position()) // could happen with siege unit inside a ship
continue;
if (holder.getMetadata(PlayerID, "lastUpdate") === undefined
|| gameState.ai.playedTurn - holder.getMetadata(PlayerID, "lastUpdate") > 5)
if (gameState.ai.playedTurn - holder.getMetadata(PlayerID, "holderUpdate") > 5)
{
if (holder.attackRange("Ranged"))
var range = holder.attackRange("Ranged").max;
@ -87,7 +86,7 @@ m.GarrisonManager.prototype.update = function(gameState, queues)
if (this.numberOfGarrisonedUnits(holder) === 0)
this.holders[id] = undefined;
else
holder.setMetadata(PlayerID, "lastUpdate", gameState.ai.playedTurn);
holder.setMetadata(PlayerID, "holderUpdate", gameState.ai.playedTurn);
}
}
};
@ -107,13 +106,8 @@ m.GarrisonManager.prototype.garrison = function(gameState, ent, holder, type)
if (this.numberOfGarrisonedUnits(holder) >= holder.garrisonMax())
return;
if (!this.holders[holder.id()])
{
this.holders[holder.id()] = [ent.id()];
holder.setMetadata(PlayerID, "lastUpdate", gameState.ai.playedTurn);
}
else
this.holders[holder.id()].push(ent.id());
this.registerHolder(gameState, holder);
this.holders[holder.id()].push(ent.id());
if (gameState.ai.HQ.Config.debug > 1)
{
@ -167,5 +161,14 @@ m.GarrisonManager.prototype.keepGarrisoned = function(ent, holder, enemiesAround
}
};
// Add this holder in the list managed by the garrisonManager
m.GarrisonManager.prototype.registerHolder = function(gameState, holder)
{
if (this.holders[holder.id()]) // already registered
return;
this.holders[holder.id()] = [];
holder.setMetadata(PlayerID, "holderUpdate", gameState.ai.playedTurn);
};
return m;
}(PETRA);

View File

@ -332,11 +332,7 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues)
continue;
var metadata = ent._entity.trainingQueue[0].metadata;
if (metadata.garrisonType)
{
ent.setRallyPoint(ent, "garrison"); // trained units will autogarrison
if (!this.garrisonManager.holders[evt.entity])
this.garrisonManager.holders[evt.entity] = [];
}
else
ent.unsetRallyPoint();
}
@ -354,8 +350,9 @@ m.HQ.prototype.checkEvents = function (gameState, events, queues)
{
// we are autogarrisoned, check that the holder is registered in the garrisonManager
var holderId = ent.unitAIOrderData()[0]["target"];
if (!this.garrisonManager.holders[holderId])
this.garrisonManager.holders[holderId] = [];
var holder = gameState.getEntityById(holderId);
if (holder)
this.garrisonManager.registerHolder(gameState, holder);
}
else if (ent.getMetadata(PlayerID, "garrisonType"))
{
@ -406,6 +403,18 @@ m.HQ.prototype.trainMoreWorkers = function(gameState, queues)
numInTraining += item.count;
});
});
// Adapt the batch size of the first queued workers and females to the present population
// to ease a possible recovery if our population was drastically reduced by an attack
if (numWorkers < 12)
var size = 1;
else
var size = Math.min(5, Math.ceil(numWorkers / 10));
if (queues.villager.queue[0])
queues.villager.queue[0].number = Math.min(queues.villager.queue[0].number, size);
if (queues.citizenSoldier.queue[0])
queues.citizenSoldier.queue[0].number = Math.min(queues.citizenSoldier.queue[0].number, size);
var numQueuedF = queues.villager.countQueuedUnits();
var numQueuedS = queues.citizenSoldier.countQueuedUnits();
var numQueued = numQueuedS + numQueuedF;
@ -419,11 +428,13 @@ m.HQ.prototype.trainMoreWorkers = function(gameState, queues)
if (numQueued > 50 || (numQueuedF > 20 && numQueuedS > 20) || numInTraining > 15)
return;
// default template and size
// default template
var template = gameState.applyCiv("units/{civ}_support_female_citizen");
var size = Math.min(5, Math.ceil(numTotal / 10));
// anticipate the optimal batch size when this queue will start
if (numTotal < 12)
size = 1;
var size = 1;
else
var size = Math.min(5, Math.ceil(numTotal / 10));
// Choose whether we want soldiers instead.
if ((numFemales+numQueuedF) > 8 && (numFemales+numQueuedF)/numTotal > this.femaleRatio)
@ -436,8 +447,6 @@ m.HQ.prototype.trainMoreWorkers = function(gameState, queues)
template = gameState.applyCiv("units/{civ}_support_female_citizen");
}
// TODO: perhaps assign them a default resource and check the base according to that.
// base "0" means "auto"
if (template === gameState.applyCiv("units/{civ}_support_female_citizen"))
queues.villager.addItem(new m.TrainingPlan(gameState, template, { "role": "worker", "base": 0 }, size, size));
@ -1408,16 +1417,20 @@ m.HQ.prototype.trainEmergencyUnits = function(gameState, positions)
var nearestAnchor = undefined;
var templateAnchor = undefined;
var distmin = undefined;
var rangedUnit = false;
for (var pos of positions)
{
var access = gameState.ai.accessibility.getAccessValue(pos);
// check nearest base anchor
for each (var base in gameState.ai.HQ.baseManagers)
for each (var base in this.baseManagers)
{
if (!base.anchor || !base.anchor.position())
continue;
if (base.anchor.getMetadata(PlayerID, "access") !== access)
continue;
var trainables = base.anchor.trainableEntities();
if (!trainables) // base still in construction
continue;
var queue = base.anchor._entity.trainingQueue
if (queue)
{
@ -1428,18 +1441,30 @@ m.HQ.prototype.trainEmergencyUnits = function(gameState, positions)
if (time/1000 > 5)
continue;
}
var trainable = base.anchor.trainableEntities();
var templateFound = undefined;
for (var i in trainable)
for (var trainable of trainables)
{
var template = gameState.getTemplate(trainable[i]);
var template = gameState.getTemplate(trainable);
if (!template.hasClass("Infantry") || !template.hasClass("Ranged")
|| !template.hasClass("CitizenSoldier"))
continue;
if (!total.canAfford(new API3.Resources(template.cost())))
continue;
templateFound = [trainable[i], template];
break;
{
if (rangedUnit)
continue;
// if we still have no ranged units, but can afford a melee unit, let's try it
var template = gameState.getTemplate(trainable);
if (!template.hasClass("Infantry") || !template.hasClass("Melee")
|| !template.hasClass("CitizenSoldier"))
continue;
if (!total.canAfford(new API3.Resources(template.cost())))
continue;
}
else
rangedUnit = true;
templateFound = [trainable, template];
if (rangedUnit)
break;
}
if (!templateFound)
continue;
@ -1454,7 +1479,7 @@ m.HQ.prototype.trainEmergencyUnits = function(gameState, positions)
if (!nearestAnchor || distmin > distcut)
return;
var autogarrison = true;
var autogarrison = rangedUnit;
var numGarrisoned = this.garrisonManager.numberOfGarrisonedUnits(nearestAnchor);
if (nearestAnchor._entity.trainingQueue)
{
@ -1466,9 +1491,9 @@ m.HQ.prototype.trainEmergencyUnits = function(gameState, positions)
nearestAnchor.stopProduction(item.id);
}
}
if (numGarrisoned >= nearestAnchor.garrisonMax())
if (rangedUnit && numGarrisoned >= nearestAnchor.garrisonMax())
{
// No more room to garrison ... favor a melee unit
// No ranged more room to garrison ... favor a melee unit
autogarrison = false;
var trainables = nearestAnchor.trainableEntities();
for (var trainable of trainables)