[PetraAI] Do not try to attack entities which can't be attacked.
Avoid trying to attack unattackable entities based on restricted classes from Attack component. Differential Revision: https://code.wildfiregames.com/D2111 Patch by: Freagarach Reviewed by: Angen This was SVN commit r23759.
This commit is contained in:
parent
3336d6d543
commit
267ccd3127
@ -744,6 +744,34 @@ m.Entity = m.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Derived from Attack.js' similary named function.
|
||||
* @return {boolean} - Whether an entity can attack a given target.
|
||||
*/
|
||||
"canAttackTarget": function(target, allowCapture)
|
||||
{
|
||||
let attackTypes = this.get("Attack");
|
||||
if (!attackTypes)
|
||||
return false;
|
||||
|
||||
let canCapture = allowCapture && this.canCapture(target);
|
||||
let armourStrengths = target.get("Armour");
|
||||
if (!armourStrengths)
|
||||
return canCapture;
|
||||
|
||||
for (let type in attackTypes)
|
||||
{
|
||||
if (type == "Capture" ? !canCapture : target.isInvulnerable())
|
||||
continue;
|
||||
|
||||
let restrictedClasses = this.get("Attack/" + type + "/RestrictedClasses/_string");
|
||||
if (!restrictedClasses || !MatchesClassList(target.classes(), restrictedClasses))
|
||||
return true;
|
||||
};
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
"move": function(x, z, queued = false) {
|
||||
Engine.PostCommand(PlayerID, { "type": "walk", "entities": [this.id()], "x": x, "z": z, "queued": queued });
|
||||
return this;
|
||||
|
@ -82,6 +82,11 @@ m.Filters = {
|
||||
"dynamicProperties": []
|
||||
}),
|
||||
|
||||
"byCanAttackTarget": target => ({
|
||||
"func": ent => ent.canAttackTarget(target),
|
||||
"dynamicProperties": []
|
||||
}),
|
||||
|
||||
"isGarrisoned": () => ({
|
||||
"func": ent => ent.position() === undefined,
|
||||
"dynamicProperties": []
|
||||
|
@ -180,6 +180,9 @@ PETRA.AttackManager.prototype.assignBombers = function(gameState)
|
||||
let access = PETRA.getLandAccess(gameState, ent);
|
||||
for (let struct of gameState.getEnemyStructures().values())
|
||||
{
|
||||
if (!ent.canAttackTarget(struct, PETRA.allowCapture(gameState, ent, struct)))
|
||||
continue;
|
||||
|
||||
let structPos = struct.position();
|
||||
let x;
|
||||
let z;
|
||||
|
@ -1327,11 +1327,15 @@ PETRA.AttackPlan.prototype.update = function(gameState, events)
|
||||
{
|
||||
if (PETRA.isSiegeUnit(ent)) // needed as mauryan elephants are not filtered out
|
||||
continue;
|
||||
ent.attack(attacker.id(), PETRA.allowCapture(gameState, ent, attacker));
|
||||
|
||||
let allowCapture = PETRA.allowCapture(gameState, ent, attacker);
|
||||
if (!ent.canAttackTarget(attacker, allowCapture))
|
||||
continue;
|
||||
ent.attack(attacker.id(), allowCapture);
|
||||
ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time);
|
||||
}
|
||||
// And if this attacker is a non-ranged siege unit and our unit also, attack it
|
||||
if (PETRA.isSiegeUnit(attacker) && attacker.hasClass("Melee") && ourUnit.hasClass("Melee"))
|
||||
if (PETRA.isSiegeUnit(attacker) && attacker.hasClass("Melee") && ourUnit.hasClass("Melee") && ourUnit.canAttackTarget(attacker, PETRA.allowCapture(gameState, ourUnit, attacker)))
|
||||
{
|
||||
ourUnit.attack(attacker.id(), PETRA.allowCapture(gameState, ourUnit, attacker));
|
||||
ourUnit.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time);
|
||||
@ -1350,7 +1354,10 @@ PETRA.AttackPlan.prototype.update = function(gameState, events)
|
||||
let collec = this.unitCollection.filter(API3.Filters.byClass("Melee")).filterNearest(ourUnit.position(), 5);
|
||||
for (let ent of collec.values())
|
||||
{
|
||||
ent.attack(attacker.id(), PETRA.allowCapture(gameState, ent, attacker));
|
||||
let allowCapture = PETRA.allowCapture(gameState, ent, attacker);
|
||||
if (!ent.canAttackTarget(attacker, allowCapture))
|
||||
continue;
|
||||
ent.attack(attacker.id(), allowCapture);
|
||||
ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time);
|
||||
}
|
||||
}
|
||||
@ -1360,7 +1367,8 @@ PETRA.AttackPlan.prototype.update = function(gameState, events)
|
||||
let collec = this.unitCollection.filterNearest(ourUnit.position(), 2);
|
||||
for (let ent of collec.values())
|
||||
{
|
||||
if (PETRA.isSiegeUnit(ent))
|
||||
let allowCapture = PETRA.allowCapture(gameState, ent, attacker);
|
||||
if (PETRA.isSiegeUnit(ent) || !ent.canAttackTarget(attacker, allowCapture))
|
||||
continue;
|
||||
let orderData = ent.unitAIOrderData();
|
||||
if (orderData && orderData.length && orderData[0].target)
|
||||
@ -1371,7 +1379,7 @@ PETRA.AttackPlan.prototype.update = function(gameState, events)
|
||||
if (target && !target.hasClass("Structure") && !target.hasClass("Support"))
|
||||
continue;
|
||||
}
|
||||
ent.attack(attacker.id(), PETRA.allowCapture(gameState, ent, attacker));
|
||||
ent.attack(attacker.id(), allowCapture);
|
||||
ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time);
|
||||
}
|
||||
// Then the unit under attack: abandon its target (if it was a structure or a support) and retaliate
|
||||
@ -1388,8 +1396,12 @@ PETRA.AttackPlan.prototype.update = function(gameState, events)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ourUnit.attack(attacker.id(), PETRA.allowCapture(gameState, ourUnit, attacker));
|
||||
ourUnit.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time);
|
||||
let allowCapture = PETRA.allowCapture(gameState, ourUnit, attacker);
|
||||
if (ourUnit.canAttackTarget(attacker, allowCapture))
|
||||
{
|
||||
ourUnit.attack(attacker.id(), allowCapture);
|
||||
ourUnit.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1540,7 +1552,7 @@ PETRA.AttackPlan.prototype.update = function(gameState, events)
|
||||
if (siegeUnit)
|
||||
{
|
||||
let mStruct = enemyStructures.filter(enemy => {
|
||||
if (!enemy.position() || enemy.hasClass("StoneWall") && !ent.canAttackClass("StoneWall"))
|
||||
if (!enemy.position() || !ent.canAttackTarget(enemy, PETRA.allowCapture(gameState, ent, enemy)))
|
||||
return false;
|
||||
if (API3.SquareVectorDistance(enemy.position(), ent.position()) > range)
|
||||
return false;
|
||||
@ -1592,7 +1604,7 @@ PETRA.AttackPlan.prototype.update = function(gameState, events)
|
||||
{
|
||||
let nearby = !ent.hasClass("Cavalry") && !ent.hasClass("Ranged");
|
||||
let mUnit = enemyUnits.filter(enemy => {
|
||||
if (!enemy.position())
|
||||
if (!enemy.position() || !ent.canAttackTarget(enemy, PETRA.allowCapture(gameState, ent, enemy)))
|
||||
return false;
|
||||
if (enemy.hasClass("Animal"))
|
||||
return false;
|
||||
@ -1634,7 +1646,9 @@ PETRA.AttackPlan.prototype.update = function(gameState, events)
|
||||
let rand = randIntExclusive(0, mUnit.length * 0.1);
|
||||
ent.attack(mUnit[rand].id(), PETRA.allowCapture(gameState, ent, mUnit[rand]));
|
||||
}
|
||||
else if (this.isBlocked)
|
||||
// This may prove dangerous as we may be blocked by something we
|
||||
// cannot attack. See similar behaviour at #5741.
|
||||
else if (this.isBlocked && ent.canAttackTarget(this.target, false))
|
||||
ent.attack(this.target.id(), false);
|
||||
else if (API3.SquareVectorDistance(this.targetPos, ent.position()) > 2500)
|
||||
{
|
||||
@ -1655,7 +1669,7 @@ PETRA.AttackPlan.prototype.update = function(gameState, events)
|
||||
let mStruct = enemyStructures.filter(enemy => {
|
||||
if (this.isBlocked && enemy.id() != this.target.id())
|
||||
return false;
|
||||
if (!enemy.position() || enemy.hasClass("StoneWall") && !ent.canAttackClass("StoneWall"))
|
||||
if (!enemy.position() || !ent.canAttackTarget(enemy, PETRA.allowCapture(gameState, ent, enemy)))
|
||||
return false;
|
||||
if (API3.SquareVectorDistance(enemy.position(), ent.position()) > range)
|
||||
return false;
|
||||
@ -1696,13 +1710,16 @@ PETRA.AttackPlan.prototype.update = function(gameState, events)
|
||||
if (unit.unitAIState().split(".")[1] != "COMBAT" || !unit.unitAIOrderData().length ||
|
||||
!unit.unitAIOrderData()[0].target)
|
||||
return;
|
||||
if (!gameState.getEntityById(unit.unitAIOrderData()[0].target))
|
||||
let target = gameState.getEntityById(unit.unitAIOrderData()[0].target);
|
||||
if (!target)
|
||||
return;
|
||||
let dist = API3.SquareVectorDistance(unit.position(), ent.position());
|
||||
if (dist > distmin)
|
||||
return;
|
||||
distmin = dist;
|
||||
attacker = gameState.getEntityById(unit.unitAIOrderData()[0].target);
|
||||
if (!ent.canAttackTarget(target, PETRA.allowCapture(gameState, ent, target)))
|
||||
return;
|
||||
attacker = target;
|
||||
});
|
||||
if (attacker)
|
||||
ent.attack(attacker.id(), PETRA.allowCapture(gameState, ent, attacker));
|
||||
@ -1756,9 +1773,11 @@ PETRA.AttackPlan.prototype.UpdateTransporting = function(gameState, events)
|
||||
{
|
||||
if (ent.getMetadata(PlayerID, "transport") !== undefined)
|
||||
continue;
|
||||
if (!ent.isIdle())
|
||||
|
||||
let allowCapture = PETRA.allowCapture(gameState, ent, attacker);
|
||||
if (!ent.isIdle() || !ent.canAttackTarget(attacker, allowCapture))
|
||||
continue;
|
||||
ent.attack(attacker.id(), PETRA.allowCapture(gameState, ent, attacker));
|
||||
ent.attack(attacker.id(), allowCapture);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -312,6 +312,9 @@ PETRA.DefenseArmy.prototype.assignUnit = function(gameState, entID)
|
||||
if (!eEnt || !eEnt.position()) // probably can't happen.
|
||||
continue;
|
||||
|
||||
if (!ent.canAttackTarget(eEnt, PETRA.allowCapture(gameState, ent, eEnt)))
|
||||
continue;
|
||||
|
||||
if (eEnt.hasClass("Unit") && eEnt.unitAIOrderData() && eEnt.unitAIOrderData().length &&
|
||||
eEnt.unitAIOrderData()[0].target && eEnt.unitAIOrderData()[0].target == entID)
|
||||
{ // being attacked >>> target the unit
|
||||
|
@ -464,6 +464,14 @@ PETRA.DefenseManager.prototype.assignDefenders = function(gameState)
|
||||
{
|
||||
if (access && armiesNeeding[a].access != access)
|
||||
continue;
|
||||
|
||||
// Do not assign defender if it cannot attack at least part of the attacking army.
|
||||
if (!armiesNeeding[a].army.foeEntities.some(eEnt => {
|
||||
let eEntID = gameState.getEntityById(eEnt);
|
||||
return ent.canAttackTarget(eEntID, PETRA.allowCapture(gameState, ent, eEntID));
|
||||
}))
|
||||
continue;
|
||||
|
||||
let dist = API3.SquareVectorDistance(ent.position(), armiesNeeding[a].army.foePosition);
|
||||
if (aMin !== undefined && dist > distMin)
|
||||
continue;
|
||||
@ -705,7 +713,7 @@ PETRA.DefenseManager.prototype.checkEvents = function(gameState, events)
|
||||
if (allAttacked[entId])
|
||||
continue;
|
||||
let ent = gameState.getEntityById(entId);
|
||||
if (!ent || !ent.position())
|
||||
if (!ent || !ent.position() || !ent.canAttackTarget(attacker, PETRA.allowCapture(gameState, ent, attacker)))
|
||||
continue;
|
||||
// Check that the unit is still attacking the structure (since the last played turn).
|
||||
let state = ent.unitAIState();
|
||||
@ -728,7 +736,9 @@ PETRA.DefenseManager.prototype.checkEvents = function(gameState, events)
|
||||
}
|
||||
}
|
||||
}
|
||||
target.attack(attacker.id(), PETRA.allowCapture(gameState, target, attacker));
|
||||
let allowCapture = PETRA.allowCapture(gameState, target, attacker);
|
||||
if (target.canAttackTarget(attacker, allowCapture))
|
||||
target.attack(attacker.id(), allowCapture);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user