From 2cc0cf457a766000e6372355e8c2b97e824bb962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lancelot=20de=20Ferri=C3=A8re?= Date: Tue, 18 Aug 2026 15:01:39 +0200 Subject: [PATCH] Fix the callers of GetEffectiveAttackRange's out-of-reach range Follows db23584fc37 - the caller of GetEffectiveAttackRange's didn't really use the NEVER_IN_RANGE result properly. The C++ engine does not handle Infinity (treated as 0). This also changes MoveToTargetAttackRange to explicitly try to get closer if the parabolic range is not in range, but it seems like a flat range could - on the assumption that once we get closer, the terrain height difference is lower. --- .../data/mods/public/simulation/components/Attack.js | 8 +++++++- .../data/mods/public/simulation/components/UnitAI.js | 10 +++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/binaries/data/mods/public/simulation/components/Attack.js b/binaries/data/mods/public/simulation/components/Attack.js index 1893123df6..2dd60f9e35 100644 --- a/binaries/data/mods/public/simulation/components/Attack.js +++ b/binaries/data/mods/public/simulation/components/Attack.js @@ -572,7 +572,9 @@ Attack.prototype.GetRange = function(type) * for elevation and projectile physics where applicable. * @param {number} target - The target entity ID. * @param {string} type - The attack type. - * @return {{ min: number, max: number }} - The min and max effective range. + * @return {{ min: number, max: number }} - The min and max effective range, + * or { Infinity, 0 } if the target cannot be reached on a parabolic + * trajectory given the current terrain height difference. */ Attack.prototype.GetEffectiveAttackRange = function(target, type) { @@ -931,6 +933,10 @@ Attack.prototype.PerformAttack = function(type, target) Attack.prototype.IsTargetInRange = function(target, type) { const range = this.GetEffectiveAttackRange(target, type); + // Out of parabolic reach: GetEffectiveAttackRange returns { Infinity, 0 } in that case. + if (range.min > range.max) + return false; + return Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager).IsInTargetRange( this.entity, target, range.min, range.max, false); }; diff --git a/binaries/data/mods/public/simulation/components/UnitAI.js b/binaries/data/mods/public/simulation/components/UnitAI.js index 01c5b0d1a4..086bb035f6 100644 --- a/binaries/data/mods/public/simulation/components/UnitAI.js +++ b/binaries/data/mods/public/simulation/components/UnitAI.js @@ -5187,15 +5187,19 @@ UnitAI.prototype.MoveToTargetAttackRange = function(target, type) const flatRange = cmpAttack.GetRange(type); const effectiveRange = cmpAttack.GetEffectiveAttackRange(target, type); - if (effectiveRange.max < 0) - return false; + + // Out of parabolic range: assume the terrain height difference is to blame, + // and close in to reassess from there. + if (effectiveRange.min > effectiveRange.max) + return cmpUnitMotion.MoveToTargetRange(target, flatRange.min, + Math.max(flatRange.min, flatRange.max / 2)); // The parabola changes while walking so be cautious: const guessedMaxRange = effectiveRange.max > flatRange.max ? (flatRange.max + effectiveRange.max) / 2 : effectiveRange.max; - return cmpUnitMotion && cmpUnitMotion.MoveToTargetRange(target, effectiveRange.min, guessedMaxRange); + return cmpUnitMotion.MoveToTargetRange(target, effectiveRange.min, guessedMaxRange); }; UnitAI.prototype.MoveToTargetRangeExplicit = function(target, min, max)