mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-09-21 20:06:40 +00:00
Add GetEffectiveAttackRange wrapper
Wraps RangeManager.GetEffectiveParabolicRange for unified range resolution. Maintains symmetry with GetRange(), improves readability, and consistently translates NEVER_IN_RANGE. Simplifies callers in UnitAI and Attack.
This commit is contained in:
@@ -353,12 +353,14 @@ Attack.prototype.GetPreference = function(target)
|
|||||||
*/
|
*/
|
||||||
Attack.prototype.GetFullAttackRange = function()
|
Attack.prototype.GetFullAttackRange = function()
|
||||||
{
|
{
|
||||||
const ret = { "min": Infinity, "max": 0 };
|
const ret = { "min": Infinity, "max": 0, "parabolic": false };
|
||||||
for (const type of this.GetAttackTypes())
|
for (const type of this.GetAttackTypes())
|
||||||
{
|
{
|
||||||
const range = this.GetRange(type);
|
const range = this.GetRange(type);
|
||||||
ret.min = Math.min(ret.min, range.min);
|
ret.min = Math.min(ret.min, range.min);
|
||||||
ret.max = Math.max(ret.max, range.max);
|
ret.max = Math.max(ret.max, range.max);
|
||||||
|
if (range.parabolic)
|
||||||
|
ret.parabolic = true;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
@@ -475,7 +477,39 @@ Attack.prototype.GetRange = function(type)
|
|||||||
let min = +(this.template[type].MinRange || 0);
|
let min = +(this.template[type].MinRange || 0);
|
||||||
min = ApplyValueModificationsToEntity("Attack/" + type + "/MinRange", min, this.entity);
|
min = ApplyValueModificationsToEntity("Attack/" + type + "/MinRange", min, this.entity);
|
||||||
|
|
||||||
return { "max": max, "min": min };
|
return {
|
||||||
|
"max": max,
|
||||||
|
"min": min,
|
||||||
|
"parabolic": type === "Ranged"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the effective range for attacking a specific target, accounting
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
Attack.prototype.GetEffectiveAttackRange = function(target, type)
|
||||||
|
{
|
||||||
|
const range = this.GetRange(type);
|
||||||
|
|
||||||
|
// Only Parabolic attacks get parabolic elevation adjustment
|
||||||
|
if (!range.parabolic)
|
||||||
|
return range;
|
||||||
|
|
||||||
|
const cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||||
|
if (!cmpRangeManager)
|
||||||
|
return range;
|
||||||
|
|
||||||
|
const effectiveMax = cmpRangeManager.GetEffectiveParabolicRange(
|
||||||
|
this.entity, target, range.max, this.GetAttackYOrigin(type));
|
||||||
|
|
||||||
|
if (effectiveMax < 0)
|
||||||
|
return { "min": Infinity, "max": 0 }; // Out of range
|
||||||
|
|
||||||
|
return { "min": range.min, "max": effectiveMax };
|
||||||
};
|
};
|
||||||
|
|
||||||
Attack.prototype.GetAttackYOrigin = function(type)
|
Attack.prototype.GetAttackYOrigin = function(type)
|
||||||
@@ -813,14 +847,9 @@ Attack.prototype.PerformAttack = function(type, target)
|
|||||||
*/
|
*/
|
||||||
Attack.prototype.IsTargetInRange = function(target, type)
|
Attack.prototype.IsTargetInRange = function(target, type)
|
||||||
{
|
{
|
||||||
const range = this.GetRange(type);
|
const range = this.GetEffectiveAttackRange(target, type);
|
||||||
return Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager).IsInTargetParabolicRange(
|
return Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager).IsInTargetRange(
|
||||||
this.entity,
|
this.entity, target, range.min, range.max, false);
|
||||||
target,
|
|
||||||
range.min,
|
|
||||||
range.max,
|
|
||||||
this.GetAttackYOrigin(type),
|
|
||||||
false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Attack.prototype.OnValueModification = function(msg)
|
Attack.prototype.OnValueModification = function(msg)
|
||||||
|
|||||||
@@ -5167,24 +5167,24 @@ UnitAI.prototype.MoveToTargetAttackRange = function(target, type)
|
|||||||
if (cmpFormation)
|
if (cmpFormation)
|
||||||
target = cmpFormation.GetClosestMemberToEntity(this.entity);
|
target = cmpFormation.GetClosestMemberToEntity(this.entity);
|
||||||
|
|
||||||
if (type != "Ranged")
|
|
||||||
return this.MoveToTargetRange(target, IID_Attack, type);
|
|
||||||
|
|
||||||
if (!this.CheckTargetVisible(target))
|
if (!this.CheckTargetVisible(target))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
const cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
||||||
if (!cmpAttack)
|
if (!cmpAttack)
|
||||||
return false;
|
return false;
|
||||||
const range = cmpAttack.GetRange(type);
|
|
||||||
|
|
||||||
// In case the range returns negative, we are probably too high compared to the target. Hope we come close enough.
|
const flatRange = cmpAttack.GetRange(type);
|
||||||
const parabolicMaxRange = Math.max(0, Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).GetEffectiveParabolicRange(this.entity, target, range.max, cmpAttack.GetAttackYOrigin(type)));
|
const effectiveRange = cmpAttack.GetEffectiveAttackRange(target, type);
|
||||||
|
if (effectiveRange.max < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
// The parabole changes while walking so be cautious:
|
// The parabola changes while walking so be cautious:
|
||||||
const guessedMaxRange = parabolicMaxRange > range.max ? (range.max + parabolicMaxRange) / 2 : parabolicMaxRange;
|
const guessedMaxRange = effectiveRange.max > flatRange.max ?
|
||||||
|
(flatRange.max + effectiveRange.max) / 2 :
|
||||||
|
effectiveRange.max;
|
||||||
|
|
||||||
return cmpUnitMotion && cmpUnitMotion.MoveToTargetRange(target, range.min, guessedMaxRange);
|
return cmpUnitMotion && cmpUnitMotion.MoveToTargetRange(target, effectiveRange.min, guessedMaxRange);
|
||||||
};
|
};
|
||||||
|
|
||||||
UnitAI.prototype.MoveToTargetRangeExplicit = function(target, min, max)
|
UnitAI.prototype.MoveToTargetRangeExplicit = function(target, min, max)
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ attackComponentTest(undefined, true, (attacker, cmpAttack, defender) =>
|
|||||||
|
|
||||||
TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetPreferredClasses("Melee"), ["Civilian"]);
|
TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetPreferredClasses("Melee"), ["Civilian"]);
|
||||||
TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetRestrictedClasses("Melee"), ["Elephant", "Archer"]);
|
TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetRestrictedClasses("Melee"), ["Elephant", "Archer"]);
|
||||||
TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetFullAttackRange(), { "min": 0, "max": 80 });
|
TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetFullAttackRange(), { "min": 0, "max": 80, "parabolic": true });
|
||||||
TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackEffectsData("Capture"), { "Capture": 8 });
|
TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackEffectsData("Capture"), { "Capture": 8 });
|
||||||
|
|
||||||
TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackEffectsData("Ranged"), {
|
TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackEffectsData("Ranged"), {
|
||||||
|
|||||||
Reference in New Issue
Block a user