fix(Core/Movement): Rewritten follow movement generator for pets (#7324)
- Closes #7296
This commit is contained in:
@@ -328,11 +328,6 @@ void PetAI::UpdateAI(uint32 diff)
|
|||||||
for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr)
|
for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr)
|
||||||
delete itr->second;
|
delete itr->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update speed as needed to prevent dropping too far behind and despawning
|
|
||||||
me->UpdateSpeed(MOVE_RUN, true);
|
|
||||||
me->UpdateSpeed(MOVE_WALK, true);
|
|
||||||
me->UpdateSpeed(MOVE_FLIGHT, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PetAI::UpdateAllies()
|
void PetAI::UpdateAllies()
|
||||||
|
|||||||
@@ -2539,7 +2539,7 @@ namespace Acore
|
|||||||
|
|
||||||
//===================================================================================================
|
//===================================================================================================
|
||||||
|
|
||||||
void WorldObject::GetNearPoint2D(WorldObject const* searcher, float& x, float& y, float distance2d, float absAngle) const
|
void WorldObject::GetNearPoint2D(WorldObject const* searcher, float& x, float& y, float distance2d, float absAngle, Position const* startPos) const
|
||||||
{
|
{
|
||||||
float effectiveReach = GetCombatReach();
|
float effectiveReach = GetCombatReach();
|
||||||
|
|
||||||
@@ -2561,24 +2561,28 @@ void WorldObject::GetNearPoint2D(WorldObject const* searcher, float& x, float& y
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x = GetPositionX() + (effectiveReach + distance2d) * std::cos(absAngle);
|
float positionX = startPos ? startPos->GetPositionX() : GetPositionX();
|
||||||
y = GetPositionY() + (effectiveReach + distance2d) * std::sin(absAngle);
|
float positionY = startPos ? startPos->GetPositionY() : GetPositionY();
|
||||||
|
|
||||||
|
x = positionX + (effectiveReach + distance2d) * std::cos(absAngle);
|
||||||
|
y = positionY + (effectiveReach + distance2d) * std::sin(absAngle);
|
||||||
|
|
||||||
Acore::NormalizeMapCoord(x);
|
Acore::NormalizeMapCoord(x);
|
||||||
Acore::NormalizeMapCoord(y);
|
Acore::NormalizeMapCoord(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldObject::GetNearPoint2D(float& x, float& y, float distance2d, float absAngle) const
|
void WorldObject::GetNearPoint2D(float& x, float& y, float distance2d, float absAngle, Position const* startPos) const
|
||||||
{
|
{
|
||||||
GetNearPoint2D(nullptr, x, y, distance2d, absAngle);
|
GetNearPoint2D(nullptr, x, y, distance2d, absAngle, startPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y, float& z, float searcher_size, float distance2d, float absAngle, float controlZ) const
|
void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y, float& z, float searcher_size, float distance2d, float absAngle, float controlZ, Position const* startPos) const
|
||||||
{
|
{
|
||||||
GetNearPoint2D(x, y, distance2d + searcher_size, absAngle);
|
GetNearPoint2D(x, y, distance2d + searcher_size, absAngle, startPos);
|
||||||
z = GetPositionZ();
|
z = GetPositionZ();
|
||||||
|
|
||||||
if (searcher) {
|
if (searcher)
|
||||||
|
{
|
||||||
if (Unit const* unit = searcher->ToUnit(); Unit const* target = ToUnit())
|
if (Unit const* unit = searcher->ToUnit(); Unit const* target = ToUnit())
|
||||||
{
|
{
|
||||||
if (unit && target && unit->IsInWater() && target->IsInWater())
|
if (unit && target && unit->IsInWater() && target->IsInWater())
|
||||||
@@ -2614,7 +2618,7 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y,
|
|||||||
// loop in a circle to look for a point in LoS using small steps
|
// loop in a circle to look for a point in LoS using small steps
|
||||||
for (float angle = float(M_PI) / 8; angle < float(M_PI) * 2; angle += float(M_PI) / 8)
|
for (float angle = float(M_PI) / 8; angle < float(M_PI) * 2; angle += float(M_PI) / 8)
|
||||||
{
|
{
|
||||||
GetNearPoint2D(x, y, distance2d + searcher_size, absAngle + angle);
|
GetNearPoint2D(x, y, distance2d + searcher_size, absAngle + angle, startPos);
|
||||||
z = GetPositionZ();
|
z = GetPositionZ();
|
||||||
UpdateAllowedPositionZ(x, y, z);
|
UpdateAllowedPositionZ(x, y, z);
|
||||||
if (controlZ && fabsf(GetPositionZ() - z) > controlZ)
|
if (controlZ && fabsf(GetPositionZ() - z) > controlZ)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include "G3D/Vector3.h"
|
||||||
|
|
||||||
#ifdef ELUNA
|
#ifdef ELUNA
|
||||||
class ElunaEventProcessor;
|
class ElunaEventProcessor;
|
||||||
@@ -376,6 +377,11 @@ struct Position
|
|||||||
return !(operator==(a));
|
return !(operator==(a));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator G3D::Vector3() const
|
||||||
|
{
|
||||||
|
return { m_positionX, m_positionY, m_positionZ };
|
||||||
|
}
|
||||||
|
|
||||||
void Relocate(float x, float y)
|
void Relocate(float x, float y)
|
||||||
{
|
{
|
||||||
m_positionX = x;
|
m_positionX = x;
|
||||||
@@ -758,9 +764,9 @@ public:
|
|||||||
ElunaEventProcessor* elunaEvents;
|
ElunaEventProcessor* elunaEvents;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void GetNearPoint2D(WorldObject const* searcher, float& x, float& y, float distance, float absAngle) const;
|
void GetNearPoint2D(WorldObject const* searcher, float& x, float& y, float distance, float absAngle, Position const* startPos = nullptr) const;
|
||||||
void GetNearPoint2D(float& x, float& y, float distance, float absAngle) const;
|
void GetNearPoint2D(float& x, float& y, float distance, float absAngle, Position const* startPos = nullptr) const;
|
||||||
void GetNearPoint(WorldObject const* searcher, float& x, float& y, float& z, float searcher_size, float distance2d, float absAngle, float controlZ = 0) const;
|
void GetNearPoint(WorldObject const* searcher, float& x, float& y, float& z, float searcher_size, float distance2d, float absAngle, float controlZ = 0, Position const* startPos = nullptr) const;
|
||||||
void GetVoidClosePoint(float& x, float& y, float& z, float size, float distance2d = 0, float relAngle = 0, float controlZ = 0) const;
|
void GetVoidClosePoint(float& x, float& y, float& z, float size, float distance2d = 0, float relAngle = 0, float controlZ = 0) const;
|
||||||
bool GetClosePoint(float& x, float& y, float& z, float size, float distance2d = 0, float angle = 0, const WorldObject* forWho = nullptr, bool force = false) const;
|
bool GetClosePoint(float& x, float& y, float& z, float size, float distance2d = 0, float angle = 0, const WorldObject* forWho = nullptr, bool force = false) const;
|
||||||
void MovePosition(Position& pos, float dist, float angle);
|
void MovePosition(Position& pos, float dist, float angle);
|
||||||
|
|||||||
@@ -13579,29 +13579,7 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
|
|||||||
{
|
{
|
||||||
if (GetTypeId() == TYPEID_UNIT)
|
if (GetTypeId() == TYPEID_UNIT)
|
||||||
{
|
{
|
||||||
Unit* followed = nullptr;
|
speed *= ToCreature()->GetCreatureTemplate()->speed_run; // at this point, MOVE_WALK is never reached
|
||||||
if (GetMotionMaster()->GetCurrentMovementGeneratorType() == FOLLOW_MOTION_TYPE)
|
|
||||||
{
|
|
||||||
followed = static_cast<FollowMovementGenerator<Creature>const*>(GetMotionMaster()->top())->GetTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (followed && !IsInCombat() && !IsVehicle() && !HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED) && (IsPet() || IsGuardian() || GetGUID() == followed->GetCritterGUID() || GetCharmerOrOwnerGUID() == followed->GetGUID()))
|
|
||||||
{
|
|
||||||
if (followed->GetTypeId() != TYPEID_PLAYER)
|
|
||||||
{
|
|
||||||
if (speed < followed->GetSpeedRate(mtype) + 0.1f)
|
|
||||||
speed = followed->GetSpeedRate(mtype) + 0.1f; // pets derive speed from owner when not in combat
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
float ownerSpeed = followed->GetSpeedRate(mtype);
|
|
||||||
if (speed < ownerSpeed || IsWithinDist3d(followed, 10.0f))
|
|
||||||
speed = ownerSpeed;
|
|
||||||
speed *= std::min(std::max(1.0f, 0.75f + (GetDistance(followed) - PET_FOLLOW_DIST) * 0.05f), 1.3f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
speed *= ToCreature()->GetCreatureTemplate()->speed_run; // at this point, MOVE_WALK is never reached
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize speed by 191 aura SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED if need
|
// Normalize speed by 191 aura SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED if need
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ static bool IsMutualChase(Unit* owner, Unit* target)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
bool ChaseMovementGenerator<T>::PositionOkay(T* owner, Unit* target, std::optional<float> maxDistance, std::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);
|
||||||
if (maxDistance && distSq > G3D::square(*maxDistance))
|
if (maxDistance && distSq > G3D::square(*maxDistance))
|
||||||
@@ -71,14 +71,14 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
|||||||
float const minTarget = (_range ? _range->MinTolerance : 0.0f) + hitboxSum;
|
float const minTarget = (_range ? _range->MinTolerance : 0.0f) + hitboxSum;
|
||||||
float const maxRange = _range ? _range->MaxRange + hitboxSum : owner->GetMeleeRange(target); // melee range already includes hitboxes
|
float const maxRange = _range ? _range->MaxRange + hitboxSum : owner->GetMeleeRange(target); // melee range already includes hitboxes
|
||||||
float const maxTarget = _range ? _range->MaxTolerance + hitboxSum : CONTACT_DISTANCE + hitboxSum;
|
float const maxTarget = _range ? _range->MaxTolerance + hitboxSum : CONTACT_DISTANCE + hitboxSum;
|
||||||
std::optional<ChaseAngle> angle = mutualChase ? std::optional<ChaseAngle>() : _angle;
|
Optional<ChaseAngle> angle = mutualChase ? Optional<ChaseAngle>() : _angle;
|
||||||
|
|
||||||
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(100);
|
||||||
|
|
||||||
if (i_recalculateTravel && PositionOkay(owner, target, _movingTowards ? maxTarget : std::optional<float>(), angle))
|
if (i_recalculateTravel && PositionOkay(owner, target, _movingTowards ? maxTarget : Optional<float>(), angle))
|
||||||
{
|
{
|
||||||
i_recalculateTravel = false;
|
i_recalculateTravel = false;
|
||||||
i_path = nullptr;
|
i_path = nullptr;
|
||||||
@@ -231,13 +231,118 @@ void ChaseMovementGenerator<T>::MovementInform(T* owner)
|
|||||||
|
|
||||||
//-----------------------------------------------//
|
//-----------------------------------------------//
|
||||||
|
|
||||||
template<class T>
|
static Optional<float> GetVelocity(Unit* owner, Unit* target, G3D::Vector3 const& dest, bool playerPet)
|
||||||
bool FollowMovementGenerator<T>::PositionOkay(T* owner, Unit* target, float range, std::optional<ChaseAngle> angle)
|
|
||||||
{
|
{
|
||||||
if (owner->GetExactDistSq(target) > G3D::square(owner->GetCombatReach() + target->GetCombatReach() + range))
|
Optional<float> speed = {};
|
||||||
|
if (!owner->IsInCombat() && !owner->IsVehicle() && !owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED) &&
|
||||||
|
(owner->IsPet() || owner->IsGuardian() || owner->GetGUID() == target->GetCritterGUID() || owner->GetCharmerOrOwnerGUID() == target->GetGUID()))
|
||||||
|
{
|
||||||
|
UnitMoveType moveType = Movement::SelectSpeedType(target->GetUnitMovementFlags());
|
||||||
|
speed = std::max(target->GetSpeed(moveType), owner->GetSpeed(moveType));
|
||||||
|
|
||||||
|
if (playerPet)
|
||||||
|
{
|
||||||
|
float distance = owner->GetDistance2d(dest.x, dest.y) - (*speed / 2.f);
|
||||||
|
if (distance > 0.f)
|
||||||
|
{
|
||||||
|
float multiplier = 1.f + (distance / 10.f);
|
||||||
|
*speed *= multiplier;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (moveType)
|
||||||
|
{
|
||||||
|
case MOVE_RUN_BACK:
|
||||||
|
case MOVE_SWIM_BACK:
|
||||||
|
case MOVE_FLIGHT_BACK:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*speed *= 0.9f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Position const PredictPosition(Unit* target)
|
||||||
|
{
|
||||||
|
Position pos = target->GetPosition();
|
||||||
|
|
||||||
|
// 0.5 - it's time (0.5 sec) between starting movement opcode (e.g. MSG_MOVE_START_FORWARD) and MSG_MOVE_HEARTBEAT sent by client
|
||||||
|
float speed = target->GetSpeed(Movement::SelectSpeedType(target->GetUnitMovementFlags())) * 0.5f;
|
||||||
|
float orientation = target->GetOrientation();
|
||||||
|
|
||||||
|
if (target->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FORWARD))
|
||||||
|
{
|
||||||
|
pos.m_positionX += cos(orientation) * speed;
|
||||||
|
pos.m_positionY += sin(orientation) * speed;
|
||||||
|
}
|
||||||
|
else if (target->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_BACKWARD))
|
||||||
|
{
|
||||||
|
pos.m_positionX -= cos(orientation) * speed;
|
||||||
|
pos.m_positionY -= sin(orientation) * speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_STRAFE_LEFT))
|
||||||
|
{
|
||||||
|
pos.m_positionX += cos(orientation + M_PI / 2.f) * speed;
|
||||||
|
pos.m_positionY += sin(orientation + M_PI / 2.f) * speed;
|
||||||
|
}
|
||||||
|
else if (target->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_STRAFE_RIGHT))
|
||||||
|
{
|
||||||
|
pos.m_positionX += cos(orientation - M_PI / 2.f) * speed;
|
||||||
|
pos.m_positionY += sin(orientation - M_PI / 2.f) * speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
bool FollowMovementGenerator<T>::PositionOkay(Unit* target, bool isPlayerPet, bool& targetIsMoving, uint32 diff)
|
||||||
|
{
|
||||||
|
if (!_lastTargetPosition)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return !angle || angle->IsAngleOkay(target->GetRelativeAngle(owner));
|
float exactDistSq = target->GetExactDistSq(_lastTargetPosition->GetPositionX(), _lastTargetPosition->GetPositionY(), _lastTargetPosition->GetPositionZ());
|
||||||
|
float distanceTolerance = 0.25f;
|
||||||
|
// For creatures, increase tolerance
|
||||||
|
if (target->GetTypeId() == TYPEID_UNIT)
|
||||||
|
{
|
||||||
|
distanceTolerance += _range + _range;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPlayerPet)
|
||||||
|
{
|
||||||
|
targetIsMoving = target->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_BACKWARD | MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exactDistSq > distanceTolerance)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (isPlayerPet)
|
||||||
|
{
|
||||||
|
if (!targetIsMoving)
|
||||||
|
{
|
||||||
|
if (i_recheckPredictedDistanceTimer.GetExpiry())
|
||||||
|
{
|
||||||
|
i_recheckPredictedDistanceTimer.Update(diff);
|
||||||
|
if (i_recheckPredictedDistanceTimer.Passed())
|
||||||
|
{
|
||||||
|
i_recheckPredictedDistanceTimer = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
@@ -274,123 +379,93 @@ bool FollowMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
|||||||
(i_target->GetTypeId() == TYPEID_PLAYER && i_target->ToPlayer()->IsGameMaster()) // for .npc follow
|
(i_target->GetTypeId() == TYPEID_PLAYER && i_target->ToPlayer()->IsGameMaster()) // for .npc follow
|
||||||
; // closes "bool forceDest", that way it is more appropriate, so we can comment out crap whenever we need to
|
; // closes "bool forceDest", that way it is more appropriate, so we can comment out crap whenever we need to
|
||||||
|
|
||||||
i_recheckDistance.Update(time_diff);
|
bool targetIsMoving = false;
|
||||||
if (i_recheckDistance.Passed())
|
if (PositionOkay(target, owner->IsGuardian() && target->GetTypeId() == TYPEID_PLAYER, targetIsMoving, time_diff))
|
||||||
{
|
{
|
||||||
i_recheckDistance.Reset(100);
|
if (owner->HasUnitState(UNIT_STATE_FOLLOW_MOVE) && owner->movespline->Finalized())
|
||||||
|
|
||||||
if (i_recalculateTravel && PositionOkay(owner, target, _range, _angle))
|
|
||||||
{
|
{
|
||||||
i_recalculateTravel = false;
|
owner->ClearUnitState(UNIT_STATE_FOLLOW_MOVE);
|
||||||
i_path = nullptr;
|
i_path = nullptr;
|
||||||
owner->StopMoving();
|
|
||||||
_lastTargetPosition.reset();
|
|
||||||
MovementInform(owner);
|
MovementInform(owner);
|
||||||
return true;
|
|
||||||
|
if (i_recheckPredictedDistance)
|
||||||
|
{
|
||||||
|
i_recheckPredictedDistanceTimer.Reset(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
owner->SetFacingTo(target->GetOrientation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (owner->HasUnitState(UNIT_STATE_FOLLOW_MOVE) && owner->movespline->Finalized())
|
|
||||||
{
|
|
||||||
i_recalculateTravel = false;
|
|
||||||
i_path = nullptr;
|
|
||||||
owner->ClearUnitState(UNIT_STATE_FOLLOW_MOVE);
|
|
||||||
MovementInform(owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
Position targetPosition = i_target->GetPosition();
|
|
||||||
|
|
||||||
if (_lastTargetPosition && _lastTargetPosition->GetExactDistSq(&targetPosition) == 0.0f)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
_lastTargetPosition = targetPosition;
|
|
||||||
|
|
||||||
if (PositionOkay(owner, target, _range + PET_FOLLOW_DIST) && !owner->HasUnitState(UNIT_STATE_FOLLOW_MOVE))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!i_path)
|
|
||||||
i_path = std::make_unique<PathGenerator>(owner);
|
|
||||||
|
|
||||||
float x, y, z;
|
|
||||||
// select angle
|
|
||||||
float tAngle;
|
|
||||||
float const curAngle = target->GetRelativeAngle(owner);
|
|
||||||
if (!oPet)
|
|
||||||
{
|
|
||||||
// for non pets, keep the relative angle
|
|
||||||
// decided during the summon
|
|
||||||
tAngle = _angle.RelativeAngle;
|
|
||||||
}
|
|
||||||
else if (_angle.IsAngleOkay(curAngle))
|
|
||||||
{
|
|
||||||
tAngle = curAngle;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
float const diffUpper = Position::NormalizeOrientation(curAngle - _angle.UpperBound());
|
Position targetPosition = target->GetPosition();
|
||||||
float const diffLower = Position::NormalizeOrientation(_angle.LowerBound() - curAngle);
|
_lastTargetPosition = targetPosition;
|
||||||
if (diffUpper < diffLower)
|
|
||||||
tAngle = _angle.UpperBound();
|
// If player is moving and their position is not updated, we need to predict position
|
||||||
|
if (targetIsMoving)
|
||||||
|
{
|
||||||
|
Position predictedPosition = PredictPosition(target);
|
||||||
|
if (_lastPredictedPosition && _lastPredictedPosition->GetExactDistSq(&predictedPosition) < 0.25f)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
_lastPredictedPosition = predictedPosition;
|
||||||
|
targetPosition = predictedPosition;
|
||||||
|
i_recheckPredictedDistance = true;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
tAngle = _angle.LowerBound();
|
{
|
||||||
|
i_recheckPredictedDistance = false;
|
||||||
|
i_recheckPredictedDistanceTimer.Reset(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!i_path)
|
||||||
|
i_path = std::make_unique<PathGenerator>(owner);
|
||||||
|
else
|
||||||
|
i_path->Clear();
|
||||||
|
|
||||||
|
float distance = _range - target->GetCombatReach();
|
||||||
|
|
||||||
|
float relAngle = _angle.RelativeAngle;
|
||||||
|
float x, y, z;
|
||||||
|
target->GetNearPoint(owner, x, y, z, owner->GetCombatReach(), distance, target->ToAbsoluteAngle(relAngle), 0.f, &targetPosition);
|
||||||
|
|
||||||
|
if (owner->IsHovering())
|
||||||
|
owner->UpdateAllowedPositionZ(x, y, z);
|
||||||
|
|
||||||
|
bool success = i_path->CalculatePath(x, y, z, forceDest);
|
||||||
|
if (!success || i_path->GetPathType() & PATHFIND_NOPATH)
|
||||||
|
{
|
||||||
|
if (!owner->IsStopped())
|
||||||
|
owner->StopMoving();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
owner->AddUnitState(UNIT_STATE_FOLLOW_MOVE);
|
||||||
|
|
||||||
|
Movement::MoveSplineInit init(owner);
|
||||||
|
init.MovebyPath(i_path->GetPath());
|
||||||
|
init.SetWalk(target->IsWalking());
|
||||||
|
if (Optional<float> velocity = GetVelocity(owner, target, i_path->GetActualEndPosition(), owner->IsGuardian() && target->GetTypeId() == TYPEID_PLAYER))
|
||||||
|
init.SetVelocity(*velocity);
|
||||||
|
init.Launch();
|
||||||
}
|
}
|
||||||
|
|
||||||
target->GetNearPoint(owner, x, y, z, _range, 0.f, target->ToAbsoluteAngle(tAngle));
|
|
||||||
|
|
||||||
i_recalculateTravel = true;
|
|
||||||
|
|
||||||
bool success = i_path->CalculatePath(x, y, z, forceDest);
|
|
||||||
if (!success || i_path->GetPathType() & PATHFIND_NOPATH)
|
|
||||||
{
|
|
||||||
if (cOwner)
|
|
||||||
cOwner->SetCannotReachTarget(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
owner->AddUnitState(UNIT_STATE_FOLLOW_MOVE);
|
|
||||||
|
|
||||||
Movement::MoveSplineInit init(owner);
|
|
||||||
init.MovebyPath(i_path->GetPath());
|
|
||||||
init.SetFacing(target->GetOrientation());
|
|
||||||
init.SetWalk(target->IsWalking());
|
|
||||||
init.Launch();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
|
||||||
void FollowMovementGenerator<Player>::_updateSpeed(Player* /*owner*/)
|
|
||||||
{
|
|
||||||
// nothing to do for Player
|
|
||||||
}
|
|
||||||
|
|
||||||
template<>
|
|
||||||
void FollowMovementGenerator<Creature>::_updateSpeed(Creature* owner)
|
|
||||||
{
|
|
||||||
// pet only sync speed with owner
|
|
||||||
/// Make sure we are not in the process of a map change (IsInWorld)
|
|
||||||
if (!owner->GetOwnerGUID().IsPlayer() || !owner->IsInWorld() || !i_target.isValid() || i_target->GetGUID() != owner->GetOwnerGUID())
|
|
||||||
return;
|
|
||||||
|
|
||||||
owner->UpdateSpeed(MOVE_RUN, true);
|
|
||||||
owner->UpdateSpeed(MOVE_WALK, true);
|
|
||||||
owner->UpdateSpeed(MOVE_SWIM, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
void FollowMovementGenerator<T>::DoInitialize(T* owner)
|
void FollowMovementGenerator<T>::DoInitialize(T* owner)
|
||||||
{
|
{
|
||||||
i_path = nullptr;
|
i_path = nullptr;
|
||||||
_lastTargetPosition.reset();
|
_lastTargetPosition.reset();
|
||||||
owner->AddUnitState(UNIT_STATE_FOLLOW);
|
owner->AddUnitState(UNIT_STATE_FOLLOW);
|
||||||
_updateSpeed(owner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
void FollowMovementGenerator<T>::DoFinalize(T* owner)
|
void FollowMovementGenerator<T>::DoFinalize(T* owner)
|
||||||
{
|
{
|
||||||
owner->ClearUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
|
owner->ClearUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
|
||||||
_updateSpeed(owner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
|
|
||||||
#include "FollowerReference.h"
|
#include "FollowerReference.h"
|
||||||
#include "MovementGenerator.h"
|
#include "MovementGenerator.h"
|
||||||
|
#include "Optional.h"
|
||||||
#include "PathGenerator.h"
|
#include "PathGenerator.h"
|
||||||
#include "Timer.h"
|
#include "Timer.h"
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
class TargetedMovementGeneratorBase
|
class TargetedMovementGeneratorBase
|
||||||
{
|
{
|
||||||
@@ -27,7 +27,7 @@ template<class T>
|
|||||||
class ChaseMovementGenerator : public MovementGeneratorMedium<T, ChaseMovementGenerator<T>>, public TargetedMovementGeneratorBase
|
class ChaseMovementGenerator : public MovementGeneratorMedium<T, ChaseMovementGenerator<T>>, public TargetedMovementGeneratorBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ChaseMovementGenerator(Unit* target, std::optional<ChaseRange> range = {}, std::optional<ChaseAngle> angle = {})
|
ChaseMovementGenerator(Unit* target, Optional<ChaseRange> range = {}, Optional<ChaseAngle> angle = {})
|
||||||
: TargetedMovementGeneratorBase(target), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle) {}
|
: TargetedMovementGeneratorBase(target), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle) {}
|
||||||
~ChaseMovementGenerator() { }
|
~ChaseMovementGenerator() { }
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ public:
|
|||||||
void DoReset(T*);
|
void DoReset(T*);
|
||||||
void MovementInform(T*);
|
void MovementInform(T*);
|
||||||
|
|
||||||
bool PositionOkay(T* owner, Unit* target, std::optional<float> maxDistance, std::optional<ChaseAngle> angle);
|
bool PositionOkay(T* owner, Unit* target, Optional<float> maxDistance, Optional<ChaseAngle> angle);
|
||||||
|
|
||||||
void unitSpeedChanged() { _lastTargetPosition.reset(); }
|
void unitSpeedChanged() { _lastTargetPosition.reset(); }
|
||||||
Unit* GetTarget() const { return i_target.getTarget(); }
|
Unit* GetTarget() const { return i_target.getTarget(); }
|
||||||
@@ -52,9 +52,9 @@ private:
|
|||||||
TimeTrackerSmall i_recheckDistance;
|
TimeTrackerSmall i_recheckDistance;
|
||||||
bool i_recalculateTravel;
|
bool i_recalculateTravel;
|
||||||
|
|
||||||
std::optional<Position> _lastTargetPosition;
|
Optional<Position> _lastTargetPosition;
|
||||||
std::optional<ChaseRange> const _range;
|
Optional<ChaseRange> const _range;
|
||||||
std::optional<ChaseAngle> const _angle;
|
Optional<ChaseAngle> const _angle;
|
||||||
bool _movingTowards = true;
|
bool _movingTowards = true;
|
||||||
bool _mutualChase = true;
|
bool _mutualChase = true;
|
||||||
};
|
};
|
||||||
@@ -64,7 +64,7 @@ class FollowMovementGenerator : public MovementGeneratorMedium<T, FollowMovement
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FollowMovementGenerator(Unit* target, float range, ChaseAngle angle)
|
FollowMovementGenerator(Unit* target, float range, ChaseAngle angle)
|
||||||
: TargetedMovementGeneratorBase(target), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle) {}
|
: TargetedMovementGeneratorBase(target), i_path(nullptr), i_recheckPredictedDistanceTimer(0), i_recheckPredictedDistance(false), _range(range), _angle(angle) {}
|
||||||
~FollowMovementGenerator() { }
|
~FollowMovementGenerator() { }
|
||||||
|
|
||||||
MovementGeneratorType GetMovementGeneratorType() { return FOLLOW_MOTION_TYPE; }
|
MovementGeneratorType GetMovementGeneratorType() { return FOLLOW_MOTION_TYPE; }
|
||||||
@@ -79,19 +79,20 @@ public:
|
|||||||
|
|
||||||
void unitSpeedChanged() { _lastTargetPosition.reset(); }
|
void unitSpeedChanged() { _lastTargetPosition.reset(); }
|
||||||
|
|
||||||
bool PositionOkay(T* owner, Unit* target, float range, std::optional<ChaseAngle> angle = {});
|
bool PositionOkay(Unit* target, bool isPlayerPet, bool& targetIsMoving, uint32 diff);
|
||||||
|
|
||||||
static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_FOLLOW_MOVE); }
|
static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_FOLLOW_MOVE); }
|
||||||
static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_FOLLOW_MOVE); }
|
static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_FOLLOW_MOVE); }
|
||||||
|
|
||||||
void _updateSpeed(T* owner);
|
float GetFollowRange() const { return _range; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<PathGenerator> i_path;
|
std::unique_ptr<PathGenerator> i_path;
|
||||||
TimeTrackerSmall i_recheckDistance;
|
TimeTrackerSmall i_recheckPredictedDistanceTimer;
|
||||||
bool i_recalculateTravel;
|
bool i_recheckPredictedDistance;
|
||||||
|
|
||||||
std::optional<Position> _lastTargetPosition;
|
Optional<Position> _lastTargetPosition;
|
||||||
|
Optional<Position> _lastPredictedPosition;
|
||||||
float _range;
|
float _range;
|
||||||
ChaseAngle _angle;
|
ChaseAngle _angle;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user