fix(Core/Unit): improve leeway bonus range and speed calculations (#25958)

Co-authored-by: GuybrushGit <turinpt3@gmail.com>
Co-authored-by: lightshope <lh@lightshope.org>
Co-authored-by: sidsukana <pgsilent@gmail.com>
Co-authored-by: ratkosrb <ratkomladic2@abv.bg>
This commit is contained in:
sogladev
2026-06-30 18:29:28 +02:00
committed by GitHub
parent 2f13ace75f
commit 7a66aa94c1
5 changed files with 74 additions and 11 deletions

View File

@@ -1753,6 +1753,56 @@ float WorldObject::GetSightRange(WorldObject const* target) const
return 0.0f;
}
float WorldObject::GetLeewayBonusRangeForTargets(Player const* player, Unit const* target)
{
if (!player || !target)
return 0.0f;
constexpr uint32 leewayMoveFlags = MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT | MOVEMENTFLAG_FALLING;
if (player->HasUnitMovementFlag(leewayMoveFlags) && !player->IsWalking() && target->HasUnitMovementFlag(leewayMoveFlags) && !target->IsWalking())
return LEEWAY_BONUS_RANGE;
return 0.0f;
}
float WorldObject::GetLeewayBonusRange(Unit const* target) const
{
if (!target)
return 0.0f;
if (Player const* player = ToPlayer())
return GetLeewayBonusRangeForTargets(player, target);
if (Player const* playerTarget = target->ToPlayer())
return GetLeewayBonusRangeForTargets(playerTarget, ToUnit());
return 0.0f;
}
float WorldObject::GetLeewayBonusRadius() const
{
if (Player const* player = ToPlayer())
{
bool hasLeewayMovement = false;
if (player->HasUnitState(UNIT_STATE_JUMPING) || player->HasUnitMovementFlag(MOVEMENTFLAG_FALLING))
hasLeewayMovement = true;
else
{
float speedXY = (player->m_movementInfo.jump.xyspeed > 0.0f)
? player->m_movementInfo.jump.xyspeed
: player->GetSpeed(player->IsWalking() ? MOVE_WALK : MOVE_RUN);
hasLeewayMovement = speedXY > LEEWAY_MIN_MOVE_SPEED;
}
if (hasLeewayMovement)
return LEEWAY_BONUS_RANGE;
}
return 0.0f;
}
bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, bool distanceCheck, bool checkAlert) const
{
if (this == obj)

View File

@@ -605,6 +605,11 @@ public:
[[nodiscard]] float GetGridActivationRange() const;
[[nodiscard]] float GetVisibilityRange() const;
virtual float GetSightRange(WorldObject const* target = nullptr) const;
[[nodiscard]] static float GetLeewayBonusRangeForTargets(Player const* player, Unit const* target);
[[nodiscard]] float GetLeewayBonusRange(Unit const* target) const;
[[nodiscard]] float GetLeewayBonusRadius() const;
//bool CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth = false, bool distanceCheck = false) const;
bool CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth = false, bool distanceCheck = false, bool checkAlert = false) const;

View File

@@ -792,8 +792,7 @@ bool Unit::IsWithinMeleeRange(Unit const* obj, float dist) const
float maxdist = dist + GetMeleeRange(obj);
if ((IsPlayer() || obj->IsPlayer()) && HasLeewayMovement() && obj->HasLeewayMovement())
maxdist += LEEWAY_BONUS_RANGE;
maxdist += GetLeewayBonusRange(obj);
return distsq < maxdist * maxdist;
}

View File

@@ -1729,12 +1729,6 @@ public:
UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED) && !GetOwnerGUID();
}
[[nodiscard]] bool HasLeewayMovement() const
{
return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT | MOVEMENTFLAG_FALLING)
&& !IsWalking();
}
void KnockbackFrom(float x, float y, float speedXY, float speedZ);
void JumpTo(float speedXY, float speedZ, bool forward = true);
void JumpTo(WorldObject* obj, float speedZ);

View File

@@ -1248,6 +1248,9 @@ void Spell::SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTarge
}
float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod;
radius += m_caster->GetLeewayBonusRadius();
if (uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList))
{
Acore::WorldObjectSpellConeTargetCheck check(coneAngle, radius, m_caster, m_spellInfo, selectionType, condList);
@@ -1335,6 +1338,17 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge
// Xinef: the distance should be increased by caster size, it is neglected in latter calculations
std::list<WorldObject*> targets;
float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod;
switch (targetType.GetTarget())
{
case TARGET_UNIT_SRC_AREA_ENEMY:
case TARGET_UNIT_CASTER_AREA_PARTY:
case TARGET_UNIT_CASTER_AREA_RAID:
radius += m_caster->GetLeewayBonusRadius();
break;
default:
break;
}
SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), m_spellInfo->Effects[effIndex].ImplicitTargetConditions, Acore::WorldObjectSpellAreaTargetSearchReason::Area);
CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType);
@@ -7088,8 +7102,9 @@ SpellCastResult Spell::CheckRange(bool strict)
if (range_type == SPELL_RANGE_MELEE)
{
float real_max_range = max_range;
if (!m_caster->IsCreature() && m_caster->HasLeewayMovement() && target->HasLeewayMovement())
real_max_range -= MIN_MELEE_REACH; // Because of lag, we can not check too strictly here (is only used if both caster and target are moving)
if (m_caster->GetLeewayBonusRange(target) > 0.0f)
real_max_range -= MIN_MELEE_REACH; // less strict when leeway applies
else
real_max_range -= 2 * MIN_MELEE_REACH;
@@ -7124,7 +7139,7 @@ SpellCastResult Spell::CheckRange(bool strict)
if (m_targets.HasDst() && !m_targets.HasTraj())
{
if (!m_caster->IsWithinDist3d(m_targets.GetDstPos(), max_range))
if (!m_caster->IsWithinDist3d(m_targets.GetDstPos(), max_range + m_caster->GetLeewayBonusRadius()))
return SPELL_FAILED_OUT_OF_RANGE;
if (min_range && m_caster->IsWithinDist3d(m_targets.GetDstPos(), min_range))
return SPELL_FAILED_TOO_CLOSE;