Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/binaries/data/mods/public/simulation/ai/petra/attackPlan.js
Show First 20 Lines • Show All 1,321 Lines • ▼ Show 20 Lines | for (let evt of events.Attacked) | ||||
} | } | ||||
if (PETRA.isSiegeUnit(ourUnit)) | if (PETRA.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 (PETRA.isSiegeUnit(ent)) // needed as mauryan elephants are not filtered out | if (PETRA.isSiegeUnit(ent)) // needed as mauryan elephants are not filtered out | ||||
continue; | 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); | 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 (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.attack(attacker.id(), PETRA.allowCapture(gameState, ourUnit, attacker)); | ||||
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 (PETRA.isSiegeUnit(attacker)) | else if (PETRA.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()) | ||||
{ | { | ||||
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); | 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 (PETRA.isSiegeUnit(ent)) | let allowCapture = PETRA.allowCapture(gameState, ent, attacker); | ||||
if (PETRA.isSiegeUnit(ent) || !ent.canAttackTarget(attacker, allowCapture)) | |||||
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(), PETRA.allowCapture(gameState, ent, attacker)); | ent.attack(attacker.id(), allowCapture); | ||||
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; | ||||
} | } | ||||
} | } | ||||
ourUnit.attack(attacker.id(), PETRA.allowCapture(gameState, ourUnit, attacker)); | let allowCapture = PETRA.allowCapture(gameState, ourUnit, attacker); | ||||
if (ourUnit.canAttackTarget(attacker, allowCapture)) | |||||
{ | |||||
ourUnit.attack(attacker.id(), allowCapture); | |||||
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 131 Lines • ▼ Show 20 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; | ||||
let entAccess = PETRA.getLandAccess(gameState, ent); | let entAccess = PETRA.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() || !ent.canAttackTarget(enemy, PETRA.allowCapture(gameState, ent, enemy))) | ||||
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 (PETRA.getLandAccess(gameState, enemy) != entAccess) | if (PETRA.getLandAccess(gameState, enemy) != entAccess) | ||||
return false; | return false; | ||||
return true; | return true; | ||||
Show All 35 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, PETRA.allowCapture(gameState, ent, enemy))) | ||||
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; | ||||
Show All 25 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(), PETRA.allowCapture(gameState, ent, mUnit[rand])); | 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); | ent.attack(this.target.id(), false); | ||||
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() || !ent.canAttackTarget(enemy, PETRA.allowCapture(gameState, ent, enemy))) | ||||
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 (PETRA.getLandAccess(gameState, enemy) != entAccess) | if (PETRA.getLandAccess(gameState, enemy) != entAccess) | ||||
return false; | return false; | ||||
return true; | return true; | ||||
}, this).toEntityArray(); | }, this).toEntityArray(); | ||||
if (mStruct.length) | if (mStruct.length) | ||||
Show All 24 Lines | for (let check = 0; check < lgth; check++) | ||||
let distmin = Math.min(); | let distmin = Math.min(); | ||||
let attacker; | let attacker; | ||||
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)) | let target = gameState.getEntityById(unit.unitAIOrderData()[0].target); | ||||
if (!target) | |||||
return; | return; | ||||
let dist = API3.SquareVectorDistance(unit.position(), ent.position()); | let dist = API3.SquareVectorDistance(unit.position(), ent.position()); | ||||
if (dist > distmin) | if (dist > distmin) | ||||
return; | return; | ||||
distmin = dist; | distmin = dist; | ||||
attacker = gameState.getEntityById(unit.unitAIOrderData()[0].target); | if (!ent.canAttackTarget(target, PETRA.allowCapture(gameState, ent, target))) | ||||
return; | |||||
attacker = target; | |||||
}); | }); | ||||
if (attacker) | if (attacker) | ||||
ent.attack(attacker.id(), PETRA.allowCapture(gameState, ent, attacker)); | ent.attack(attacker.id(), PETRA.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()) | |||||
let allowCapture = PETRA.allowCapture(gameState, ent, attacker); | |||||
if (!ent.isIdle() || !ent.canAttackTarget(attacker, allowCapture)) | |||||
continue; | continue; | ||||
ent.attack(attacker.id(), PETRA.allowCapture(gameState, ent, attacker)); | ent.attack(attacker.id(), allowCapture); | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
}; | }; | ||||
PETRA.AttackPlan.prototype.UpdateWalking = function(gameState, events) | PETRA.AttackPlan.prototype.UpdateWalking = function(gameState, events) | ||||
{ | { | ||||
// we're marching towards the target | // we're marching towards the target | ||||
▲ Show 20 Lines • Show All 390 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator