Index: binaries/data/mods/public/simulation/ai/petra/attackManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/attackManager.js +++ binaries/data/mods/public/simulation/ai/petra/attackManager.js @@ -504,14 +504,14 @@ }; /** - * Response to the capture of one of our structure: - * transform all defense armies around into a new attack army and target this structure + * Response to the capture of one of our structures: + * transform all defense armies around into a new attack and target this structure */ -m.AttackManager.prototype.counterAttack = function(gameState, ent, range=150) +m.AttackManager.prototype.counterAttack = function(gameState, target, range=150) { - if (!ent || !ent.position()) + if (!target || !target.position()) return false; - let pos = ent.position(); + let pos = target.position(); let attackType = "Attack"; let attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, attackType); if (attackPlan.failed) @@ -546,13 +546,53 @@ 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; }; +/** + * Switch a defense army into an attack one against the given target + */ +m.AttackManager.prototype.switchDefenseToAttack = function(gameState, army, target) +{ + if (!army || !target || !target.position()) + return; + let pos = target.position(); + let attackType = "Attack"; + let attackPlan = new m.AttackPlan(gameState, this.Config, this.totalNumber, attackType, { "uniqueTargetId": target.id() }); + if (attackPlan.failed) + return; + this.totalNumber++; + attackPlan.init(gameState); + this.startedAttacks[attackType].push(attackPlan); + + while (army.foeEntities.length > 0) + army.removeFoe(gameState, army.foeEntities[0]); + while (army.ownEntities.length > 0) + { + let unitId = army.ownEntities[0]; + army.removeOwn(gameState, unitId); + let unit = gameState.getEntityById(unitId); + if (unit && attackPlan.isAvailableUnit(gameState, unit)) + { + unit.setMetadata(PlayerID, "plan", attackPlan.name); + attackPlan.unitCollection.updateEnt(unit); + } + } + if (!attackPlan.unitCollection.hasEntities()) + { + attackPlan.Abort(gameState); + return; + } + attackPlan.targetPlayer = target.owner(); + attackPlan.targetPos = pos; + attackPlan.target = target; + attackPlan.state = "arrived"; +}; + m.AttackManager.prototype.Serialize = function() { let properties = { Index: binaries/data/mods/public/simulation/ai/petra/attackPlan.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/attackPlan.js +++ 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: binaries/data/mods/public/simulation/ai/petra/defenseManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/defenseManager.js +++ 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); } @@ -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, army, target); + return; + } + } +}; + m.DefenseManager.prototype.Serialize = function() { let properties = {