Changeset View
Standalone View
binaries/data/mods/public/simulation/ai/petra/attackPlan.js
Show First 20 Lines • Show All 1,314 Lines • ▼ Show 20 Lines | if (this.state == "") | ||||
let time = gameState.ai.elapsedTime; | let time = gameState.ai.elapsedTime; | ||||
let attackedByStructure = {}; | let attackedByStructure = {}; | ||||
for (let evt of events.Attacked) | for (let evt of events.Attacked) | ||||
{ | { | ||||
if (!this.unitCollection.hasEntId(evt.target)) | if (!this.unitCollection.hasEntId(evt.target)) | ||||
continue; | continue; | ||||
let attacker = gameState.getEntityById(evt.attacker); | let attacker = gameState.getEntityById(evt.attacker); | ||||
let ourUnit = gameState.getEntityById(evt.target); | let ourUnit = gameState.getEntityById(evt.target); | ||||
if (!ourUnit || !attacker || !attacker.position()) | if (!ourUnit || !attacker || !attacker.position()) | ||||
Silier: no here | |||||
continue; | continue; | ||||
if (!attacker.hasClass("Unit")) | if (!attacker.hasClass("Unit")) | ||||
{ | { | ||||
attackedByStructure[evt.target] = true; | attackedByStructure[evt.target] = true; | ||||
continue; | continue; | ||||
} | } | ||||
if (m.isSiegeUnit(ourUnit)) | if (m.isSiegeUnit(ourUnit)) | ||||
{ // if our siege units are attacked, we'll send some units to deal with enemies. | { // if our siege units are attacked, we'll send some units to deal with enemies. | ||||
let collec = this.unitCollection.filter(API3.Filters.not(API3.Filters.byClass("Siege"))).filterNearest(ourUnit.position(), 5); | let collec = this.unitCollection.filter(API3.Filters.not(API3.Filters.byClass("Siege"))).filterNearest(ourUnit.position(), 5); | ||||
for (let ent of collec.values()) | for (let ent of collec.values()) | ||||
{ | { | ||||
if (m.isSiegeUnit(ent)) // needed as mauryan elephants are not filtered out | if (m.isSiegeUnit(ent)) // needed as mauryan elephants are not filtered out | ||||
Done Inline Actionscheck here Silier: check here | |||||
continue; | continue; | ||||
if (!ent.canAttackTarget(attacker.id())) | |||||
continue; | |||||
ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); | ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); | ||||
ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); | ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); | ||||
} | } | ||||
// And if this attacker is a non-ranged siege unit and our unit also, attack it | // And if this attacker is a non-ranged siege unit and our unit also, attack it | ||||
if (m.isSiegeUnit(attacker) && attacker.hasClass("Melee") && ourUnit.hasClass("Melee")) | if (m.isSiegeUnit(attacker) && attacker.hasClass("Melee") && ourUnit.hasClass("Melee") && ourUnit.canAttackTarget(attacker.id())) | ||||
Done Inline Actionscheck here Silier: check here | |||||
{ | { | ||||
ourUnit.attack(attacker.id(), m.allowCapture(gameState, ourUnit, attacker)); | ourUnit.attack(attacker.id(), m.allowCapture(gameState, ourUnit, attacker)); | ||||
Not Done Inline Actionscache m.allowCapture before check at line 1341 and use that value Silier: cache m.allowCapture before check at line 1341 and use that value | |||||
Not Done Inline ActionsWhy would that be beneficient? As it is only called once here? Freagarach: Why would that be beneficient? As it is only called once here? | |||||
Not Done Inline Actionsbecause if you dont, you can end up returning capture as only option and not allowing it when calling attack Silier: because if you dont, you can end up returning capture as only option and not allowing it when… | |||||
Not Done Inline ActionsAh, good point. But won't that problem be also present when I cache the value a tad earlier? Freagarach: Ah, good point. But won't that problem be also present when I cache the value a tad earlier? | |||||
ourUnit.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); | ourUnit.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); | ||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
if (this.isBlocked && !ourUnit.hasClass("Ranged") && attacker.hasClass("Ranged")) | if (this.isBlocked && !ourUnit.hasClass("Ranged") && attacker.hasClass("Ranged")) | ||||
{ | { | ||||
// do not react if our melee units are attacked by ranged one and we are blocked by walls | // do not react if our melee units are attacked by ranged one and we are blocked by walls | ||||
// TODO check that the attacker is from behind the wall | // TODO check that the attacker is from behind the wall | ||||
continue; | continue; | ||||
} | } | ||||
else if (m.isSiegeUnit(attacker)) | else if (m.isSiegeUnit(attacker)) | ||||
{ // if our unit is attacked by a siege unit, we'll send some melee units to help it. | { // if our unit is attacked by a siege unit, we'll send some melee units to help it. | ||||
let collec = this.unitCollection.filter(API3.Filters.byClass("Melee")).filterNearest(ourUnit.position(), 5); | let collec = this.unitCollection.filter(API3.Filters.byClass("Melee")).filterNearest(ourUnit.position(), 5); | ||||
for (let ent of collec.values()) | for (let ent of collec.values()) | ||||
{ | { | ||||
if (!ent.canAttackTarget(attacker.id())) | |||||
continue; | |||||
ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); | ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); | ||||
Done Inline Actionscheck here before calling attack Silier: check here before calling attack | |||||
ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); | ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); | ||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
// Look first for nearby units to help us if possible | // Look first for nearby units to help us if possible | ||||
let collec = this.unitCollection.filterNearest(ourUnit.position(), 2); | let collec = this.unitCollection.filterNearest(ourUnit.position(), 2); | ||||
for (let ent of collec.values()) | for (let ent of collec.values()) | ||||
{ | { | ||||
if (m.isSiegeUnit(ent)) | if (m.isSiegeUnit(ent) || !ent.canAttackTarget(attacker.id())) | ||||
continue; | continue; | ||||
let orderData = ent.unitAIOrderData(); | let orderData = ent.unitAIOrderData(); | ||||
if (orderData && orderData.length && orderData[0].target) | if (orderData && orderData.length && orderData[0].target) | ||||
{ | { | ||||
if (orderData[0].target === attacker.id()) | if (orderData[0].target === attacker.id()) | ||||
continue; | continue; | ||||
let target = gameState.getEntityById(orderData[0].target); | let target = gameState.getEntityById(orderData[0].target); | ||||
if (target && !target.hasClass("Structure") && !target.hasClass("Support")) | if (target && !target.hasClass("Structure") && !target.hasClass("Support")) | ||||
continue; | continue; | ||||
} | } | ||||
ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); | ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); | ||||
Done Inline Actionscheck before calling attack here Silier: check before calling attack here | |||||
ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); | ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); | ||||
} | } | ||||
// Then the unit under attack: abandon its target (if it was a structure or a support) and retaliate | // Then the unit under attack: abandon its target (if it was a structure or a support) and retaliate | ||||
// also if our unit is attacking a range unit and the attacker is a melee unit, retaliate | // also if our unit is attacking a range unit and the attacker is a melee unit, retaliate | ||||
let orderData = ourUnit.unitAIOrderData(); | let orderData = ourUnit.unitAIOrderData(); | ||||
if (orderData && orderData.length && orderData[0].target) | if (orderData && orderData.length && orderData[0].target) | ||||
{ | { | ||||
if (orderData[0].target === attacker.id()) | if (orderData[0].target === attacker.id()) | ||||
continue; | continue; | ||||
let target = gameState.getEntityById(orderData[0].target); | let target = gameState.getEntityById(orderData[0].target); | ||||
if (target && !target.hasClass("Structure") && !target.hasClass("Support")) | if (target && !target.hasClass("Structure") && !target.hasClass("Support")) | ||||
{ | { | ||||
if (!target.hasClass("Ranged") || !attacker.hasClass("Melee")) | if (!target.hasClass("Ranged") || !attacker.hasClass("Melee")) | ||||
continue; | continue; | ||||
} | } | ||||
} | } | ||||
if (ourUnit.canAttackTarget(attacker.id())) | |||||
Done Inline Actionscheck here Silier: check here | |||||
{ | |||||
ourUnit.attack(attacker.id(), m.allowCapture(gameState, ourUnit, attacker)); | ourUnit.attack(attacker.id(), m.allowCapture(gameState, ourUnit, attacker)); | ||||
ourUnit.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); | ourUnit.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | |||||
let enemyUnits = gameState.getEnemyUnits(this.targetPlayer); | let enemyUnits = gameState.getEnemyUnits(this.targetPlayer); | ||||
let enemyStructures = gameState.getEnemyStructures(this.targetPlayer); | let enemyStructures = gameState.getEnemyStructures(this.targetPlayer); | ||||
// Count the number of times an enemy is targeted, to prevent all units to follow the same target | // Count the number of times an enemy is targeted, to prevent all units to follow the same target | ||||
let unitTargets = {}; | let unitTargets = {}; | ||||
for (let ent of this.unitCollection.values()) | for (let ent of this.unitCollection.values()) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 96 Lines • ▼ Show 20 Lines | for (let check = 0; check < lgth; check++) | ||||
--unitTargets[targetId]; | --unitTargets[targetId]; | ||||
} | } | ||||
else if (target.hasClass("Ship") && !ent.hasClass("Ship")) | else if (target.hasClass("Ship") && !ent.hasClass("Ship")) | ||||
maybeUpdate = true; | maybeUpdate = true; | ||||
else if (attackedByStructure[ent.id()] && target.hasClass("Field")) | else if (attackedByStructure[ent.id()] && target.hasClass("Field")) | ||||
maybeUpdate = true; | maybeUpdate = true; | ||||
else if (!ent.hasClass("Cavalry") && !ent.hasClass("Ranged") && | else if (!ent.hasClass("Cavalry") && !ent.hasClass("Ranged") && | ||||
target.hasClass("FemaleCitizen") && target.unitAIState().split(".")[1] == "FLEEING") | target.hasClass("FemaleCitizen") && target.unitAIState().split(".")[1] == "FLEEING") | ||||
maybeUpdate = true; | maybeUpdate = true; | ||||
Not Done Inline Actionspossibly if else and check, if cannot attack -> maybeUpdate = true ( question do we need it here ? - will UnitAI attack something cannot harm ?) Silier: possibly if else and check, if cannot attack -> maybeUpdate = true ( question do we need it… | |||||
Not Done Inline ActionsNo, UnitAI will not try to attack something it can't. So we do not need it here then? Freagarach: No, `UnitAI` will not try to attack something it can't. So we do not need it here then? | |||||
} | } | ||||
// don't update too soon if not necessary | // don't update too soon if not necessary | ||||
if (!needsUpdate) | if (!needsUpdate) | ||||
{ | { | ||||
if (!maybeUpdate) | if (!maybeUpdate) | ||||
continue; | continue; | ||||
let deltat = ent.unitAIState() === "INDIVIDUAL.COMBAT.APPROACHING" ? 10 : 5; | let deltat = ent.unitAIState() === "INDIVIDUAL.COMBAT.APPROACHING" ? 10 : 5; | ||||
Show All 18 Lines | for (let check = 0; check < lgth; check++) | ||||
else if (ent.hasClass("Cavalry")) | else if (ent.hasClass("Cavalry")) | ||||
range += 30; | range += 30; | ||||
range = range * range; | range = range * range; | ||||
let entAccess = m.getLandAccess(gameState, ent); | let entAccess = m.getLandAccess(gameState, ent); | ||||
// Checking for gates if we're a siege unit. | // Checking for gates if we're a siege unit. | ||||
if (siegeUnit) | if (siegeUnit) | ||||
{ | { | ||||
let mStruct = enemyStructures.filter(enemy => { | let mStruct = enemyStructures.filter(enemy => { | ||||
if (!enemy.position() || enemy.hasClass("StoneWall") && !ent.canAttackClass("StoneWall")) | if (!enemy.position() || enemy.hasClass("StoneWall") && !ent.canAttackClass("StoneWall") || !ent.canAttackTarget(enemy.id())) | ||||
return false; | return false; | ||||
if (API3.SquareVectorDistance(enemy.position(), ent.position()) > range) | if (API3.SquareVectorDistance(enemy.position(), ent.position()) > range) | ||||
return false; | return false; | ||||
if (enemy.foundationProgress() == 0) | if (enemy.foundationProgress() == 0) | ||||
return false; | return false; | ||||
if (m.getLandAccess(gameState, enemy) != entAccess) | if (m.getLandAccess(gameState, enemy) != entAccess) | ||||
return false; | return false; | ||||
return true; | return true; | ||||
Done Inline Actionsif canot attack -> return false Silier: if canot attack -> return false | |||||
}).toEntityArray(); | }).toEntityArray(); | ||||
if (mStruct.length) | if (mStruct.length) | ||||
{ | { | ||||
mStruct.sort((structa, structb) => { | mStruct.sort((structa, structb) => { | ||||
let vala = structa.costSum(); | let vala = structa.costSum(); | ||||
if (structa.hasClass("Gates") && ent.canAttackClass("StoneWall")) | if (structa.hasClass("Gates") && ent.canAttackClass("StoneWall")) | ||||
vala += 10000; | vala += 10000; | ||||
else if (structa.hasDefensiveFire()) | else if (structa.hasDefensiveFire()) | ||||
Show All 27 Lines | for (let check = 0; check < lgth; check++) | ||||
else | else | ||||
ent.attackMove(this.targetPos[0], this.targetPos[1], targetClassesSiege); | ent.attackMove(this.targetPos[0], this.targetPos[1], targetClassesSiege); | ||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
let nearby = !ent.hasClass("Cavalry") && !ent.hasClass("Ranged"); | let nearby = !ent.hasClass("Cavalry") && !ent.hasClass("Ranged"); | ||||
let mUnit = enemyUnits.filter(enemy => { | let mUnit = enemyUnits.filter(enemy => { | ||||
if (!enemy.position()) | if (!enemy.position() || !ent.canAttackTarget(enemy.id())) | ||||
return false; | return false; | ||||
if (enemy.hasClass("Animal")) | if (enemy.hasClass("Animal")) | ||||
return false; | return false; | ||||
if (nearby && enemy.hasClass("FemaleCitizen") && enemy.unitAIState().split(".")[1] == "FLEEING") | if (nearby && enemy.hasClass("FemaleCitizen") && enemy.unitAIState().split(".")[1] == "FLEEING") | ||||
return false; | return false; | ||||
let dist = API3.SquareVectorDistance(enemy.position(), ent.position()); | let dist = API3.SquareVectorDistance(enemy.position(), ent.position()); | ||||
if (dist > range) | if (dist > range) | ||||
return false; | return false; | ||||
if (m.getLandAccess(gameState, enemy) != entAccess) | if (m.getLandAccess(gameState, enemy) != entAccess) | ||||
return false; | return false; | ||||
// if already too much units targeting this enemy, let's continue towards our main target | // if already too much units targeting this enemy, let's continue towards our main target | ||||
if (veto[enemy.id()] && API3.SquareVectorDistance(this.targetPos, ent.position()) > 2500) | if (veto[enemy.id()] && API3.SquareVectorDistance(this.targetPos, ent.position()) > 2500) | ||||
return false; | return false; | ||||
enemy.setMetadata(PlayerID, "distance", Math.sqrt(dist)); | enemy.setMetadata(PlayerID, "distance", Math.sqrt(dist)); | ||||
return true; | return true; | ||||
Done Inline Actionsif cannot attack -> false Silier: if cannot attack -> false | |||||
}, this).toEntityArray(); | }, this).toEntityArray(); | ||||
if (mUnit.length) | if (mUnit.length) | ||||
{ | { | ||||
mUnit.sort((unitA, unitB) => { | mUnit.sort((unitA, unitB) => { | ||||
let vala = unitA.hasClass("Support") ? 50 : 0; | let vala = unitA.hasClass("Support") ? 50 : 0; | ||||
if (ent.countersClasses(unitA.classes())) | if (ent.countersClasses(unitA.classes())) | ||||
vala += 100; | vala += 100; | ||||
let valb = unitB.hasClass("Support") ? 50 : 0; | let valb = unitB.hasClass("Support") ? 50 : 0; | ||||
Show All 10 Lines | for (let check = 0; check < lgth; check++) | ||||
vala -= 20000; | vala -= 20000; | ||||
if (veto[unitB.id()]) | if (veto[unitB.id()]) | ||||
valb -= 20000; | valb -= 20000; | ||||
return valb - vala; | return valb - vala; | ||||
}); | }); | ||||
let rand = randIntExclusive(0, mUnit.length * 0.1); | let rand = randIntExclusive(0, mUnit.length * 0.1); | ||||
ent.attack(mUnit[rand].id(), m.allowCapture(gameState, ent, mUnit[rand])); | ent.attack(mUnit[rand].id(), m.allowCapture(gameState, ent, mUnit[rand])); | ||||
} | } | ||||
else if (this.isBlocked) | else if (this.isBlocked && ent.canAttackTarget(this.target.id())) | ||||
ent.attack(this.target.id(), false); | ent.attack(this.target.id(), false); | ||||
Done Inline Actionscheck here, need to think if should be inside if (this.isBlocked) or can be used && Silier: check here, need to think if should be inside if (this.isBlocked) or can be used && | |||||
else if (API3.SquareVectorDistance(this.targetPos, ent.position()) > 2500) | else if (API3.SquareVectorDistance(this.targetPos, ent.position()) > 2500) | ||||
{ | { | ||||
let targetClasses = targetClassesUnit; | let targetClasses = targetClassesUnit; | ||||
if (maybeUpdate && ent.unitAIState() === "INDIVIDUAL.COMBAT.APPROACHING") // we may be blocked by walls, attack everything | if (maybeUpdate && ent.unitAIState() === "INDIVIDUAL.COMBAT.APPROACHING") // we may be blocked by walls, attack everything | ||||
{ | { | ||||
if (!ent.hasClass("Ranged") && !ent.hasClass("Ship")) | if (!ent.hasClass("Ranged") && !ent.hasClass("Ship")) | ||||
targetClasses = { "attack": ["Unit", "Structure"], "avoid": ["Ship"], "vetoEntities": veto }; | targetClasses = { "attack": ["Unit", "Structure"], "avoid": ["Ship"], "vetoEntities": veto }; | ||||
else | else | ||||
targetClasses = { "attack": ["Unit", "Structure"], "vetoEntities": veto }; | targetClasses = { "attack": ["Unit", "Structure"], "vetoEntities": veto }; | ||||
} | } | ||||
else if (!ent.hasClass("Ranged") && !ent.hasClass("Ship")) | else if (!ent.hasClass("Ranged") && !ent.hasClass("Ship")) | ||||
targetClasses = { "attack": targetClassesUnit.attack, "avoid": targetClassesUnit.avoid.concat("Ship"), "vetoEntities": veto }; | targetClasses = { "attack": targetClassesUnit.attack, "avoid": targetClassesUnit.avoid.concat("Ship"), "vetoEntities": veto }; | ||||
ent.attackMove(this.targetPos[0], this.targetPos[1], targetClasses); | ent.attackMove(this.targetPos[0], this.targetPos[1], targetClasses); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
let mStruct = enemyStructures.filter(enemy => { | let mStruct = enemyStructures.filter(enemy => { | ||||
if (this.isBlocked && enemy.id() != this.target.id()) | if (this.isBlocked && enemy.id() != this.target.id()) | ||||
return false; | return false; | ||||
if (!enemy.position() || enemy.hasClass("StoneWall") && !ent.canAttackClass("StoneWall")) | if (!enemy.position() || enemy.hasClass("StoneWall") && !ent.canAttackClass("StoneWall") || !ent.canAttackTarget(enemy.id())) | ||||
return false; | return false; | ||||
if (API3.SquareVectorDistance(enemy.position(), ent.position()) > range) | if (API3.SquareVectorDistance(enemy.position(), ent.position()) > range) | ||||
return false; | return false; | ||||
if (m.getLandAccess(gameState, enemy) != entAccess) | if (m.getLandAccess(gameState, enemy) != entAccess) | ||||
return false; | return false; | ||||
return true; | return true; | ||||
Done Inline Actionsif cannot attack -> return false Silier: if cannot attack -> return false | |||||
}, this).toEntityArray(); | }, this).toEntityArray(); | ||||
if (mStruct.length) | if (mStruct.length) | ||||
{ | { | ||||
mStruct.sort((structa, structb) => { | mStruct.sort((structa, structb) => { | ||||
let vala = structa.costSum(); | let vala = structa.costSum(); | ||||
if (structa.hasClass("Gates") && ent.canAttackClass("StoneWall")) | if (structa.hasClass("Gates") && ent.canAttackClass("StoneWall")) | ||||
vala += 10000; | vala += 10000; | ||||
else if (structa.hasClass("ConquestCritical")) | else if (structa.hasClass("ConquestCritical")) | ||||
Show All 20 Lines | for (let check = 0; check < lgth; check++) | ||||
this.unitCollection.forEach(unit => { | this.unitCollection.forEach(unit => { | ||||
if (!unit.position()) | if (!unit.position()) | ||||
return; | return; | ||||
if (unit.unitAIState().split(".")[1] != "COMBAT" || !unit.unitAIOrderData().length || | if (unit.unitAIState().split(".")[1] != "COMBAT" || !unit.unitAIOrderData().length || | ||||
!unit.unitAIOrderData()[0].target) | !unit.unitAIOrderData()[0].target) | ||||
return; | return; | ||||
if (!gameState.getEntityById(unit.unitAIOrderData()[0].target)) | if (!gameState.getEntityById(unit.unitAIOrderData()[0].target)) | ||||
return; | return; | ||||
let dist = API3.SquareVectorDistance(unit.position(), ent.position()); | let dist = API3.SquareVectorDistance(unit.position(), ent.position()); | ||||
Done Inline Actionsif cannot attack -> return, dont know what is more expensive, distance or can attack Silier: if cannot attack -> return, dont know what is more expensive, distance or can attack | |||||
if (dist > distmin) | if (dist > distmin) | ||||
return; | return; | ||||
distmin = dist; | distmin = dist; | ||||
attacker = gameState.getEntityById(unit.unitAIOrderData()[0].target); | attacker = gameState.getEntityById(unit.unitAIOrderData()[0].target); | ||||
if (!ent.canAttackTarget(attacker.id())) | |||||
return; | |||||
}); | }); | ||||
if (attacker) | if (attacker) | ||||
ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); | ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
this.unitCollUpdateArray.splice(0, lgth); | this.unitCollUpdateArray.splice(0, lgth); | ||||
Show All 37 Lines | if (!this.unitCollection.hasEntId(evt.target)) | ||||
continue; | continue; | ||||
let attacker = gameState.getEntityById(evt.attacker); | let attacker = gameState.getEntityById(evt.attacker); | ||||
if (!attacker || !gameState.getEntityById(evt.target)) | if (!attacker || !gameState.getEntityById(evt.target)) | ||||
continue; | continue; | ||||
for (let ent of this.unitCollection.values()) | for (let ent of this.unitCollection.values()) | ||||
{ | { | ||||
if (ent.getMetadata(PlayerID, "transport") !== undefined) | if (ent.getMetadata(PlayerID, "transport") !== undefined) | ||||
continue; | continue; | ||||
if (!ent.isIdle()) | if (!ent.isIdle() || !ent.canAttackTarget(attacker.id())) | ||||
continue; | continue; | ||||
ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); | ent.attack(attacker.id(), m.allowCapture(gameState, ent, attacker)); | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
}; | }; | ||||
m.AttackPlan.prototype.UpdateWalking = function(gameState, events) | m.AttackPlan.prototype.UpdateWalking = function(gameState, events) | ||||
▲ Show 20 Lines • Show All 397 Lines • Show Last 20 Lines |
no here