Index: binaries/data/mods/public/simulation/ai/common-api/entity.js =================================================================== --- binaries/data/mods/public/simulation/ai/common-api/entity.js +++ binaries/data/mods/public/simulation/ai/common-api/entity.js @@ -233,7 +233,8 @@ return { "max": +this.get("Attack/" + type +"/MaxRange"), - "min": +(this.get("Attack/" + type +"/MinRange") || 0) + "min": +(this.get("Attack/" + type +"/MinRange") || 0), + "elevationBonus": +(this.get("Attack/" + type + "/ElevationBonus") || 0) }; }, Index: binaries/data/mods/public/simulation/ai/common-api/utils.js =================================================================== --- binaries/data/mods/public/simulation/ai/common-api/utils.js +++ binaries/data/mods/public/simulation/ai/common-api/utils.js @@ -27,6 +27,11 @@ return Math.euclidDistance2DSquared(a[0], a[1], b[0], b[1]); }; +m.SquaredParabolicRange = function(range, h) +{ + return range * range + 2 * range * h; +}; + /** Utility functions for conversions of maps of different sizes */ /** 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 @@ -175,7 +175,8 @@ if (alreadyBombing) break; - let range = ent.attackRange("Ranged").max; + let attackRange = ent.attackRange("Ranged"); + let range = API3.SquaredParabolicRange(attackRange.max, attackRange.elevationBonus); let entPos = ent.position(); let access = PETRA.getLandAccess(gameState, ent); for (let struct of gameState.getEnemyStructures().values()) 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 @@ -1526,22 +1526,31 @@ continue; } ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", time); - let range = 60; + let range = 3600; let attackTypes = ent.attackTypes(); if (this.isBlocked) { if (attackTypes && attackTypes.indexOf("Ranged") !== -1) - range = ent.attackRange("Ranged").max; + { + let attackRange = ent.attackRange("Ranged"); + range = API3.SquaredParabolicRange(attackRange.max, attackRange.elevationBonus); + } else if (attackTypes && attackTypes.indexOf("Melee") !== -1) - range = ent.attackRange("Melee").max; + { + let attackRange = ent.attackRange("Melee"); + range = attackRange.max * attackRange.max; + } else - range = 10; + range = 100; } else if (attackTypes && attackTypes.indexOf("Ranged") !== -1) - range = 30 + ent.attackRange("Ranged").max; + { + let attackRange = ent.attackRange("Ranged"); + range = 900 + API3.SquaredParabolicRange(attackRange.max, attackRange.elevationBonus); + } else if (ent.hasClass("FastMoving")) - range += 30; - range *= range; + range += 900; + let entAccess = PETRA.getLandAccess(gameState, ent); // Checking for gates if we're a siege unit. if (siegeUnit) 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 @@ -137,7 +137,10 @@ let dist2Min = 6000; // TODO the 30 is to take roughly into account the structure size in following checks. Can be improved. if (entity.attackTypes().indexOf("Ranged") != -1) - dist2Min = (entity.attackRange("Ranged").max + 30) * (entity.attackRange("Ranged").max + 30); + { + let attackRange = entity.attackRange("Ranged"); + dist2Min = 900 + API3.SquaredParabolicRange(attackRange.max, attackRange.elevationBonus); + } for (let targetId of this.targetList) { @@ -582,7 +585,8 @@ if (target.isIdle() || unitAIStateOrder == "GATHER") { let pos = attacker.position(); - let range = attacker.attackRange("Ranged") ? attacker.attackRange("Ranged").max + 15 : 25; + let attackRange = attacker.attackRange("Ranged"); + let range = attackRange ? attackRange.max + 15 : 25; if (range * range > API3.SquareVectorDistance(pos, target.position())) target.moveToRange(pos[0], pos[1], range, range); } @@ -756,8 +760,8 @@ if (!attackTypes || attackTypes.indexOf("Ranged") == -1) return false; let dist = API3.SquareVectorDistance(data.attacker.position(), target.position()); - let range = target.attackRange("Ranged").max; - if (dist >= range*range) + let attackRange = target.attackRange("Ranged"); + if (dist >= API3.SquaredParabolicRange(attackRange.max, attackRange.elevationBonus)) return false; } let access = PETRA.getLandAccess(gameState, target); Index: binaries/data/mods/public/simulation/ai/petra/garrisonManager.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/garrisonManager.js +++ binaries/data/mods/public/simulation/ai/petra/garrisonManager.js @@ -122,7 +122,8 @@ if (gameState.ai.elapsedTime - holder.getMetadata(PlayerID, "holderTimeUpdate") > 3) { - let range = holder.attackRange("Ranged") ? holder.attackRange("Ranged").max : 80; + let attackRange = holder.attackRange("Ranged"); + let range = attackRange ? API3.SquaredParabolicRange(attackRange.max, attackRange.elevationBonus) : 6400; let around = { "defenseStructure": false, "meleeSiege": false, "rangeSiege": false, "unit": false }; for (let ent of gameState.getEnemyEntities().values()) { @@ -141,7 +142,7 @@ if (!ent.position()) continue; let dist = API3.SquareVectorDistance(ent.position(), holder.position()); - if (dist > range*range) + if (dist > range) continue; if (ent.hasClass("Structure")) around.defenseStructure = true; Index: binaries/data/mods/public/simulation/ai/petra/headquarters.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/headquarters.js +++ binaries/data/mods/public/simulation/ai/petra/headquarters.js @@ -2473,10 +2473,12 @@ { if (!this.turnCache.firingStructures) this.turnCache.firingStructures = gameState.updatingCollection("diplo-FiringStructures", API3.Filters.hasDefensiveFire(), gameState.getEnemyStructures()); + radius *= radius; for (let ent of this.turnCache.firingStructures.values()) { - let range = radius + ent.attackRange("Ranged").max; - if (API3.SquareVectorDistance(ent.position(), pos) < range*range) + let attackRange = ent.attackRange("Ranged"); + let range = radius + API3.SquaredParabolicRange(attackRange.max, attackRange.elevationBonus); + if (API3.SquareVectorDistance(ent.position(), pos) < range) return true; } return false;