Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/attackManager.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/petra/attackManager.js +++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/attackManager.js @@ -504,28 +504,41 @@ }; /** - * Response to the capture of one of our structure: - * transform all defense armies around into a new attack army and target this structure + * Switch defense armies into an attack one against the given target + * data.range: transform all defense armies inside range of the target into a new attack + * data.armyID: transform only the defense army ID into a new attack + * data.uniqueTarget: the attack will stop when the target is destroyed or captured */ -m.AttackManager.prototype.counterAttack = function(gameState, ent, range=150) +m.AttackManager.prototype.switchDefenseToAttack = function(gameState, target, data) { - if (!ent || !ent.position()) + if (!target || !target.position()) return false; - let pos = ent.position(); + if (!data.range && !data.armyID) + { + API3.warn(" attackManager.switchToAttack inconsistent data " + uneval(data)); + return false; + } + attackData = data.uniqueTarget ? { "uniqueTargetId": target.id() } : undefined; + let pos = target.position(); let attackType = "Attack"; - let attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, attackType); + let attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, attackType, attackData); if (attackPlan.failed) return false; this.totalNumber++; attackPlan.init(gameState); this.startedAttacks[attackType].push(attackPlan); - for (let i = 0; i < gameState.ai.HQ.defenseManager.armies.length; ++i) + for (let army of gameState.ai.HQ.defenseManager.armies) { - let army = gameState.ai.HQ.defenseManager.armies[i]; - army.recalculatePosition(gameState); - if (API3.SquareVectorDistance(pos, army.foePosition) > range*range) + if (data.range) + { + army.recalculatePosition(gameState); + if (API3.SquareVectorDistance(pos, army.foePosition) > data.range * data.range) + continue; + } + else if (army.ID != +data.armyID) continue; + while (army.foeEntities.length > 0) army.removeFoe(gameState, army.foeEntities[0]); while (army.ownEntities.length > 0) @@ -539,16 +552,15 @@ attackPlan.unitCollection.updateEnt(unit); } } - gameState.ai.HQ.defenseManager.armies.splice(i--, 1); } if (!attackPlan.unitCollection.hasEntities()) { attackPlan.Abort(gameState); return false; } - attackPlan.targetPlayer = ent.owner(); + attackPlan.targetPlayer = target.owner(); attackPlan.targetPos = pos; - attackPlan.target = ent; + attackPlan.target = target; attackPlan.state = "arrived"; return true; }; Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/attackPlan.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/petra/attackPlan.js +++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/attackPlan.js @@ -28,6 +28,8 @@ this.targetPlayer = undefined; } + this.uniqueTargetId = data && data.uniqueTargetId || undefined; + // get a starting rallyPoint ... will be improved later let rallyPoint; let rallyAccess; @@ -712,6 +714,9 @@ this.target = this.getNearestTarget(gameState, this.rallyPoint); if (!this.target) { + if (this.uniqueTargetId) + return false; + // may-be all our previous enemey target (if not recomputed here) have been destroyed ? this.targetPlayer = gameState.ai.HQ.attackManager.getEnemyPlayer(gameState, this); if (this.targetPlayer !== undefined) @@ -782,12 +787,22 @@ this.isBlocked = false; let targets; - if (this.type === "Raid") - targets = this.raidTargetFinder(gameState); - else if (this.type === "Rush" || this.type === "Attack") - targets = this.rushTargetFinder(gameState, this.targetPlayer); + if (this.uniqueTargetId) + { + targets = new API3.EntityCollection(gameState.sharedScript); + let ent = gameState.getEntityById(this.uniqueTargetId); + if (ent) + targets.addEnt(ent); + } else - targets = this.defaultTargetFinder(gameState, this.targetPlayer); + { + if (this.type === "Raid") + targets = this.raidTargetFinder(gameState); + else if (this.type === "Rush" || this.type === "Attack") + targets = this.rushTargetFinder(gameState, this.targetPlayer); + else + targets = this.defaultTargetFinder(gameState, this.targetPlayer); + } if (!targets.hasEntities()) return undefined; @@ -1788,6 +1803,9 @@ this.target = this.getNearestTarget(gameState, this.position, true); if (!this.target) { + if (this.uniqueTargetId) + return false; + // Check if we could help any current attack let attackManager = gameState.ai.HQ.attackManager; let accessIndex = gameState.ai.accessibility.getAccessValue(this.position); @@ -1978,6 +1996,7 @@ "targetPlayer": this.targetPlayer, "target": this.target !== undefined ? this.target.id() : undefined, "targetPos": this.targetPos, + "uniqueTargetId": this.uniqueTargetId, "path": this.path }; Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/defenseManager.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/petra/defenseManager.js +++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/defenseManager.js @@ -266,6 +266,7 @@ if (army.getState() === 0) { + this.switchToAttack(gameState, army); army.clear(gameState); this.armies.splice(i--,1); continue; @@ -347,6 +348,7 @@ if (stillDangerous) continue; + this.switchToAttack(gameState, army); army.clear(gameState); this.armies.splice(i--,1); } @@ -471,7 +473,7 @@ { let ent = gameState.getEntityById(evt.entity); if (ent && ent.hasClass("CivCentre")) // one of our cc has been captured - gameState.ai.HQ.attackManager.counterAttack(gameState, ent); + gameState.ai.HQ.attackManager.switchDefenseToAttack(gameState, ent, { "range": 150 }); } } @@ -725,6 +727,36 @@ return cooperation; }; +/** + * Switch a defense army into an attack if needed + */ +m.DefenseManager.prototype.switchToAttack = function(gameState, army) +{ + if (!army) + return; + for (let targetId of this.targetList) + { + let target = gameState.getEntityById(targetId); + if (!target || !target.position() || !gameState.isPlayerEnemy(target.owner())) + continue; + let targetAccess = m.getLandAccess(gameState, target); + let targetPos = target.position(); + for (let entId of army.ownEntities) + { + let ent = gameState.getEntityById(entId); + if (!ent) + continue; + let pos = ent.position(); + if (!pos || gameState.ai.accessibility.getAccessValue(pos) !== targetAccess) + continue; + if (API3.SquareVectorDistance(targetPos, pos) > 14400) + continue; + gameState.ai.HQ.attackManager.switchDefenseToAttack(gameState, target, { "armyID": army.ID, "uniqueTarget": true }); + return; + } + } +}; + m.DefenseManager.prototype.Serialize = function() { let properties = {