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)