fix(Core/Creature): Improve chase movement (#17557)

* Update TargetedMovementGenerator.cpp

* Adding Ovahlord as co-author

TIL: you can assign multiple authors to a commit 😛
Co-Authored-By: Ovahlord <18347559+ovahlord@users.noreply.github.com>

* Remove unused variable

* Undo some changes

---------

Co-authored-by: Ovahlord <18347559+ovahlord@users.noreply.github.com>
This commit is contained in:
AG
2023-11-26 17:09:24 +01:00
committed by GitHub
parent d01bf7685d
commit f7d7c4feb7

View File

@@ -32,14 +32,31 @@ static bool IsMutualChase(Unit* owner, Unit* target)
return target->GetVictim() == owner; return target->GetVictim() == owner;
} }
inline float GetChaseRange(Unit const* owner, Unit const* target)
{
float hitboxSum = owner->GetCombatReach() + target->GetCombatReach();
float hoverDelta = owner->GetHoverHeight() - target->GetHoverHeight();
if (hoverDelta != 0.0f)
return std::sqrt(std::max(hitboxSum * hitboxSum - hoverDelta * hoverDelta, 0.0f));
return hitboxSum;
}
template<class T> template<class T>
bool ChaseMovementGenerator<T>::PositionOkay(T* owner, Unit* target, Optional<float> maxDistance, Optional<ChaseAngle> angle) bool ChaseMovementGenerator<T>::PositionOkay(T* owner, Unit* target, Optional<float> maxDistance, Optional<ChaseAngle> angle)
{ {
float const distSq = owner->GetExactDistSq(target); float const distSq = owner->GetExactDistSq(target);
// Distance between owner(chaser) and target is greater than the allowed distance.
if (maxDistance && distSq > G3D::square(*maxDistance)) if (maxDistance && distSq > G3D::square(*maxDistance))
return false; return false;
// owner's relative angle to its target is not within boundaries
if (angle && !angle->IsAngleOkay(target->GetRelativeAngle(owner))) if (angle && !angle->IsAngleOkay(target->GetRelativeAngle(owner)))
return false; return false;
// owner cannot see its target
if (!owner->IsWithinLOSInMap(target)) if (!owner->IsWithinLOSInMap(target))
return false; return false;
return true; return true;
@@ -62,10 +79,7 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
owner->StopMoving(); owner->StopMoving();
_lastTargetPosition.reset(); _lastTargetPosition.reset();
if (Creature* cOwner2 = owner->ToCreature()) if (Creature* cOwner2 = owner->ToCreature())
{
cOwner2->SetCannotReachTarget(); cOwner2->SetCannotReachTarget();
}
return true; return true;
} }
@@ -78,85 +92,53 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
Unit* target = i_target.getTarget(); Unit* target = i_target.getTarget();
bool const mutualChase = IsMutualChase(owner, target); bool const mutualChase = IsMutualChase(owner, target);
float const hitboxSum = owner->GetCombatReach() + target->GetCombatReach(); float const chaseRange = GetChaseRange(owner, target);
float const minTarget = (_range ? _range->MinTolerance : 0.0f) + hitboxSum; float const minTarget = (_range ? _range->MinTolerance : 0.0f) + chaseRange;
float const maxRange = _range ? _range->MaxRange + hitboxSum : owner->GetMeleeRange(target); // melee range already includes hitboxes float const maxRange = _range ? _range->MaxRange + chaseRange : owner->GetMeleeRange(target); // melee range already includes hitboxes
float const maxTarget = _range ? _range->MaxTolerance + hitboxSum : CONTACT_DISTANCE + hitboxSum; float const maxTarget = _range ? _range->MaxTolerance + chaseRange : CONTACT_DISTANCE + chaseRange;
Optional<ChaseAngle> angle = mutualChase ? Optional<ChaseAngle>() : _angle; Optional<ChaseAngle> angle = mutualChase ? Optional<ChaseAngle>() : _angle;
// periodically check if we're already in the expected range...
i_recheckDistance.Update(time_diff); i_recheckDistance.Update(time_diff);
if (i_recheckDistance.Passed()) if (i_recheckDistance.Passed())
{ {
i_recheckDistance.Reset(100); i_recheckDistance.Reset(400); // Sniffed value
if (i_recalculateTravel && PositionOkay(owner, target, _movingTowards ? maxTarget : Optional<float>(), angle)) if (i_recalculateTravel && PositionOkay(owner, target, _movingTowards ? maxTarget : Optional<float>(), angle))
{ {
if (Creature* cOwner2 = owner->ToCreature()) if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && !target->isMoving() && !mutualChase)
{ {
cOwner2->SetCannotReachTarget();
}
i_recalculateTravel = false; i_recalculateTravel = false;
i_path = nullptr; i_path = nullptr;
if (Creature* cOwner2 = owner->ToCreature())
cOwner2->SetCannotReachTarget();
owner->StopMoving(); owner->StopMoving();
owner->SetInFront(target); owner->SetInFront(target);
MovementInform(owner); MovementInform(owner);
return true; return true;
} }
} }
}
// if we're done moving, we want to clean up
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && owner->movespline->Finalized()) if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && owner->movespline->Finalized())
{ {
i_recalculateTravel = false;
i_path = nullptr;
if (Creature* cOwner2 = owner->ToCreature())
cOwner2->SetCannotReachTarget();
owner->ClearUnitState(UNIT_STATE_CHASE_MOVE); owner->ClearUnitState(UNIT_STATE_CHASE_MOVE);
owner->SetInFront(target); owner->SetInFront(target);
MovementInform(owner); MovementInform(owner);
}
if (owner->IsWithinMeleeRange(this->i_target.getTarget())) // if the target moved, we have to consider whether to adjust
if (!_lastTargetPosition || target->GetPosition() != _lastTargetPosition.value() || mutualChase != _mutualChase)
{ {
owner->Attack(this->i_target.getTarget(), true); _lastTargetPosition = target->GetPosition();
if (Creature* cOwner2 = owner->ToCreature())
{
cOwner2->SetCannotReachTarget();
}
}
else if (i_path && i_path->GetPathType() & PATHFIND_INCOMPLETE)
{
if (Creature* cOwner2 = owner->ToCreature())
{
cOwner2->SetCannotReachTarget(this->i_target.getTarget()->GetGUID());
}
}
i_recalculateTravel = false;
i_path = nullptr;
}
if (_lastTargetPosition && i_target->GetPosition() == _lastTargetPosition.value() && mutualChase == _mutualChase)
return true;
_lastTargetPosition = i_target->GetPosition();
if (PositionOkay(owner, target, maxRange, angle) && !owner->HasUnitState(UNIT_STATE_CHASE_MOVE))
{
if (Creature* cOwner2 = owner->ToCreature())
{
cOwner2->SetCannotReachTarget();
}
return true;
}
float tarX, tarY, tarZ;
target->GetPosition(tarX, tarY, tarZ);
bool withinRange = owner->IsInDist(target, maxRange);
bool withinLOS = owner->IsWithinLOS(tarX, tarY, tarZ);
bool moveToward = !(withinRange && withinLOS);
_mutualChase = mutualChase; _mutualChase = mutualChase;
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) || !PositionOkay(owner, target, target->isMoving() ? maxTarget : maxRange, angle))
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE))
{ {
// can we get to the target? // can we get to the target?
if (cOwner && !target->isInAccessiblePlaceFor(cOwner)) if (cOwner && !target->isInAccessiblePlaceFor(cOwner))
@@ -166,15 +148,42 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
i_path = nullptr; i_path = nullptr;
return true; return true;
} }
}
// figure out which way we want to move
float tarX, tarY, tarZ;
target->GetPosition(tarX, tarY, tarZ);
bool withinRange = owner->IsInDist(target, maxRange);
bool withinLOS = owner->IsWithinLOS(tarX, tarY, tarZ);
bool moveToward = !(withinRange && withinLOS);
// make a new path if we have to...
if (!i_path || moveToward != _movingTowards) if (!i_path || moveToward != _movingTowards)
i_path = std::make_unique<PathGenerator>(owner); i_path = std::make_unique<PathGenerator>(owner);
else else
i_path->Clear(); i_path->Clear();
// Predict chase destination to keep up with chase target
float additionalRange = 0;
bool predictDestination = !mutualChase && target->isMoving();
if (predictDestination)
{
UnitMoveType moveType = MOVE_RUN;
if (target->CanFly())
moveType = target->HasUnitMovementFlag(MOVEMENTFLAG_BACKWARD) ? MOVE_FLIGHT_BACK : MOVE_FLIGHT;
else
{
if (target->IsWalking())
moveType = MOVE_WALK;
else
moveType = target->HasUnitMovementFlag(MOVEMENTFLAG_BACKWARD) ? MOVE_RUN_BACK : MOVE_RUN;
}
float speed = target->GetSpeed(moveType) * 0.5f;
additionalRange = owner->GetExactDistSq(target) < G3D::square(speed) ? 0 : speed;
}
float x, y, z; float x, y, z;
bool shortenPath; bool shortenPath;
// if we want to move toward the target and there's no fixed angle... // if we want to move toward the target and there's no fixed angle...
if (moveToward && !angle) if (moveToward && !angle)
{ {
@@ -185,15 +194,13 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
else else
{ {
// otherwise, we fall back to nearpoint finding // otherwise, we fall back to nearpoint finding
target->GetNearPoint(owner, x, y, z, (moveToward ? maxTarget : minTarget) - hitboxSum, 0, angle ? target->ToAbsoluteAngle(angle->RelativeAngle) : target->GetAngle(owner)); target->GetNearPoint(owner, x, y, z, (moveToward ? maxTarget : minTarget) - chaseRange - additionalRange, 0, angle ? target->ToAbsoluteAngle(angle->RelativeAngle) : target->GetAngle(owner));
shortenPath = false; shortenPath = false;
} }
if (owner->IsHovering()) if (owner->IsHovering())
owner->UpdateAllowedPositionZ(x, y, z); owner->UpdateAllowedPositionZ(x, y, z);
i_recalculateTravel = true;
bool success = i_path->CalculatePath(x, y, z, forceDest); bool success = i_path->CalculatePath(x, y, z, forceDest);
if (!success || i_path->GetPathType() & PATHFIND_NOPATH) if (!success || i_path->GetPathType() & PATHFIND_NOPATH)
{ {
@@ -202,6 +209,7 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
cOwner->SetCannotReachTarget(target->GetGUID()); cOwner->SetCannotReachTarget(target->GetGUID());
} }
owner->StopMoving();
return true; return true;
} }
@@ -219,8 +227,7 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
switch (cOwner->GetMovementTemplate().GetChase()) switch (cOwner->GetMovementTemplate().GetChase())
{ {
case CreatureChaseMovementType::CanWalk: case CreatureChaseMovementType::CanWalk:
if (owner->IsWalking()) walk = owner->IsWalking();
walk = true;
break; break;
case CreatureChaseMovementType::AlwaysWalk: case CreatureChaseMovementType::AlwaysWalk:
walk = true; walk = true;
@@ -231,12 +238,15 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
} }
owner->AddUnitState(UNIT_STATE_CHASE_MOVE); owner->AddUnitState(UNIT_STATE_CHASE_MOVE);
i_recalculateTravel = true;
Movement::MoveSplineInit init(owner); Movement::MoveSplineInit init(owner);
init.MovebyPath(i_path->GetPath()); init.MovebyPath(i_path->GetPath());
init.SetFacing(target); init.SetFacing(target);
init.SetWalk(walk); init.SetWalk(walk);
init.Launch(); init.Launch();
}
}
return true; return true;
} }
@@ -256,8 +266,8 @@ void ChaseMovementGenerator<Creature>::DoInitialize(Creature* owner)
{ {
i_path = nullptr; i_path = nullptr;
_lastTargetPosition.reset(); _lastTargetPosition.reset();
i_recheckDistance.Reset(0);
owner->SetWalk(false); owner->SetWalk(false);
owner->StopMoving();
owner->AddUnitState(UNIT_STATE_CHASE); owner->AddUnitState(UNIT_STATE_CHASE);
} }