Core/Movement: reworked chase movement
* the destination prediction is now much more reliable * the movement generator will now more frequently check for valid positions to match retail behavior * the catch-up mechanic will now adapt its speed to the target's one when being close enough to hit it * temporarily disabled backwards movement when being too close to a target to avoid some edge cases that need additional research * fixed the pathfinding reaching behavior so that they no longer follow you to unreachable obstacles and return home when they can't reach you anymore closes #345 closes #260
This commit is contained in:
1 parent
63be5a888c
commit
0840143137
9 files changed
+204
-120
No files matched your search
@@ -485,9 +485,10 @@ void PetAI::DoAttack(Unit* target, bool chase)
|
||||
|
||||
float chaseDistance = me->GetPetChaseDistance();
|
||||
|
||||
// Pets with ranged attacks should not care about the chase angle at all.
|
||||
ChaseAngle angle = ChaseAngle(chaseDistance == 0.f ? float(M_PI) : 0.f, chaseDistance == 0.f ? float(M_PI_4) : float(M_PI * 2));
|
||||
me->GetMotionMaster()->MoveChase(target, chaseDistance, angle);
|
||||
if (chaseDistance == 0.f) // @todo: the chase angle of pets is relative to the angle between target and owner so it always faces away from the owner
|
||||
me->GetMotionMaster()->MoveChase(target/*, std::nullopt, ChaseAngle(float(M_PI), float(M_PI_4)) */);
|
||||
else
|
||||
me->GetMotionMaster()->MoveChase(target, chaseDistance);
|
||||
}
|
||||
else // (Stay && ((Aggressive || Defensive) && In Melee Range)))
|
||||
{
|
||||
|
||||
@@ -156,7 +156,7 @@ void ScriptedAI::UpdateAI(uint32 /*diff*/)
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
|
||||
void ScriptedAI::DoStartMovement(Unit* victim, float distance, float angle)
|
||||
void ScriptedAI::DoStartMovement(Unit* victim, Optional<float> distance /*= std::nullopt*/, Optional<float> angle /*= std::nullopt*/)
|
||||
{
|
||||
if (victim)
|
||||
me->GetMotionMaster()->MoveChase(victim, distance, angle);
|
||||
|
||||
@@ -162,7 +162,7 @@ struct TC_GAME_API ScriptedAI : public CreatureAI
|
||||
// *************
|
||||
|
||||
//Start movement toward victim
|
||||
void DoStartMovement(Unit* target, float distance = 0.0f, float angle = 0.0f);
|
||||
void DoStartMovement(Unit* target, Optional<float> distance = std::nullopt, Optional<float> angle = std::nullopt);
|
||||
|
||||
//Start no movement on victim
|
||||
void DoStartNoMovement(Unit* target);
|
||||
|
||||
@@ -1669,12 +1669,15 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
|
||||
float attackDistance = float(e.action.setRangedMovement.distance);
|
||||
float attackAngle = float(e.action.setRangedMovement.angle) / 180.0f * float(M_PI);
|
||||
Optional<ChaseAngle> angle;
|
||||
if (attackAngle != 0.f)
|
||||
angle = ChaseAngle(attackAngle);
|
||||
|
||||
for (WorldObject* target : targets)
|
||||
if (Creature* creature = target->ToCreature())
|
||||
if (IsSmart(creature) && creature->GetVictim())
|
||||
if (ENSURE_AI(SmartAI, creature->AI())->CanCombatMove())
|
||||
creature->GetMotionMaster()->MoveChase(creature->GetVictim(), attackDistance, attackAngle);
|
||||
creature->GetMotionMaster()->MoveChase(creature->GetVictim(), attackDistance, angle);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, bool joinFo
|
||||
Mutate(new FollowMovementGenerator(target, dist, angle, joinFormation, catchUpToTarget, faceTarget), slot);
|
||||
}
|
||||
|
||||
void MotionMaster::MoveChase(Unit* target, float dist, Optional<ChaseAngle> angle)
|
||||
void MotionMaster::MoveChase(Unit* target, Optional<float> dist /*= std::nullopt*/, Optional<ChaseAngle> angle /*= std::nullopt*/)
|
||||
{
|
||||
// ignore movement request if target not exist
|
||||
if (!target || target == _owner)
|
||||
|
||||
@@ -150,8 +150,7 @@ class TC_GAME_API MotionMaster
|
||||
void MoveRandom(float wanderDistance = 0.0f);
|
||||
void MoveFollow(Unit* target, float dist, float angle, bool joinFormation = false, bool catchUpToTarget = false, bool faceTarget = false, MovementSlot slot = MOTION_SLOT_IDLE);
|
||||
|
||||
void MoveChase(Unit* target, float dist = 0.f, Optional<ChaseAngle> angle = {});
|
||||
void MoveChase(Unit* target, float dist, float angle) { MoveChase(target, dist, ChaseAngle(angle)); }
|
||||
void MoveChase(Unit* target, Optional<float> dist = std::nullopt, Optional<ChaseAngle> angle = std::nullopt);
|
||||
|
||||
void MoveConfused();
|
||||
void MoveFleeing(Unit* enemy, uint32 time = 0);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "Util.h"
|
||||
#include "Vehicle.h"
|
||||
|
||||
/*
|
||||
static bool IsMutualChase(Unit const* owner, Unit const* target)
|
||||
{
|
||||
MovementGenerator* gen = target->GetMotionMaster()->topOrNull();
|
||||
@@ -35,39 +36,38 @@ static bool IsMutualChase(Unit const* owner, Unit const* target)
|
||||
|
||||
return (static_cast<ChaseMovementGenerator*>(gen)->GetTarget() == owner);
|
||||
}
|
||||
*/
|
||||
|
||||
static void DoMovementInform(Unit* owner, Unit const* target)
|
||||
enum ChaseMovementPositionCheckResult : uint8
|
||||
{
|
||||
if (owner->GetTypeId() != TYPEID_UNIT)
|
||||
CHASE_POSITION_CHECK_RESULT_VALID = 0x0,
|
||||
CHASE_POSITION_CHECK_RESULT_TOO_FAR = 0x1,
|
||||
CHASE_POSITION_CHECK_RESULT_TOO_CLOSE = 0x2,
|
||||
CHASE_POSITION_CHECK_RESULT_ANGLE_INVALID = 0x4,
|
||||
CHASE_POSITION_CHECK_RESULT_NOT_IN_LOS = 0x8,
|
||||
};
|
||||
|
||||
enum ChasePositionCheckOptions : uint8
|
||||
{
|
||||
CHASE_POSITION_CHECK_NONE = 0x0, // everything's fine
|
||||
CHASE_POSITION_CHECK_DISTANCE = 0x1, // the target is too far away
|
||||
CHASE_POSITION_CHECK_BOUNDARY = 0x2, // the target is too close
|
||||
CHASE_POSITION_CHECK_ANGLE = 0x4, // our angle towards the target is out of bounds
|
||||
CHASE_POSITION_CHECK_LOS = 0x8 // our target is not in line of sight
|
||||
};
|
||||
|
||||
inline void DoMovementInform(Unit* owner, Unit const* target)
|
||||
{
|
||||
if (owner->IsCreature())
|
||||
return;
|
||||
|
||||
if (CreatureAI* AI = owner->ToCreature()->AI())
|
||||
AI->MovementInform(CHASE_MOTION_TYPE, target->GetGUID().GetCounter());
|
||||
}
|
||||
|
||||
static bool PositionOkay(Unit const* owner, Unit const* target, float distance, Optional<ChaseAngle> angle)
|
||||
{
|
||||
float const dist = owner->GetExactDist2d(target);
|
||||
|
||||
// owner's distance to its chase target is outside of its range
|
||||
if (dist > distance)
|
||||
return false;
|
||||
|
||||
// owner's relative angle to its target is not within boundaries
|
||||
if (angle && !angle->IsAngleOkay(target->GetRelativeAngle(owner)))
|
||||
return false;
|
||||
|
||||
// owner cannot see its target
|
||||
if (!owner->IsWithinLOSInMap(target))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline float GetChaseRange(Unit const* owner, Unit const* target)
|
||||
inline float GetHitboxSum(Unit const* owner, Unit const* target)
|
||||
{
|
||||
float hitboxSum = owner->GetCombatReach() + target->GetCombatReach();
|
||||
|
||||
float hoverDelta = owner->GetHoverOffset() - target->GetHoverOffset();
|
||||
if (hoverDelta != 0.0f)
|
||||
return std::sqrt(std::max(hitboxSum * hitboxSum - hoverDelta * hoverDelta, 0.0f));
|
||||
@@ -75,16 +75,25 @@ inline float GetChaseRange(Unit const* owner, Unit const* target)
|
||||
return hitboxSum;
|
||||
}
|
||||
|
||||
ChaseMovementGenerator::ChaseMovementGenerator(Unit* target, float range, Optional<ChaseAngle> angle) : AbstractPursuer(PursuingType::Chase, ASSERT_NOTNULL(target)), _range(range), _angle(angle) { }
|
||||
ChaseMovementGenerator::~ChaseMovementGenerator() = default;
|
||||
inline float SelectMovementSpeed(Unit const* target)
|
||||
{
|
||||
UnitMoveType moveType = MOVE_RUN;
|
||||
if (target->IsFlying())
|
||||
moveType = MOVE_FLIGHT;
|
||||
|
||||
return target->GetSpeed(moveType);
|
||||
}
|
||||
|
||||
ChaseMovementGenerator::ChaseMovementGenerator(Unit* target, Optional<float> range /*= {}*/, Optional<ChaseAngle> angle /*= {}*/) :
|
||||
AbstractPursuer(PursuingType::Chase, ASSERT_NOTNULL(target)),
|
||||
_range(range), _angle(angle), _previousChaseSplineId(0), _moveTimer(0), _positionCheckTimer(0) { }
|
||||
|
||||
void ChaseMovementGenerator::Initialize(Unit* owner)
|
||||
{
|
||||
owner->AddUnitState(UNIT_STATE_CHASE);
|
||||
owner->SetWalk(false);
|
||||
_lastTargetPosition.reset();
|
||||
_nextMovementTimer.Reset(0);
|
||||
_nextRepositioningTimer.Reset(0);
|
||||
_pathGenerator = std::make_unique<PathGenerator>(owner);
|
||||
_moveTimer.Reset(0);
|
||||
}
|
||||
|
||||
bool ChaseMovementGenerator::Update(Unit* owner, uint32 diff)
|
||||
@@ -93,40 +102,40 @@ bool ChaseMovementGenerator::Update(Unit* owner, uint32 diff)
|
||||
if (!owner || !owner->IsAlive())
|
||||
return false;
|
||||
|
||||
// validate our target
|
||||
Unit* target = GetTarget();
|
||||
|
||||
// our target might have gone away
|
||||
if (!target || !target->IsInWorld())
|
||||
if (!target || !target->IsInWorld() || owner->GetVictim() != target)
|
||||
return false;
|
||||
|
||||
// the owner might've selected a different target (feels like we shouldn't check this here...)
|
||||
if (owner->GetVictim() != target)
|
||||
return false;
|
||||
_moveTimer.Update(diff);
|
||||
_positionCheckTimer.Update(diff);
|
||||
|
||||
// ------------------------ This segment will handle the clearing of the chase state and the movement inform of the previous updtae cycle
|
||||
|
||||
// Make sure that the spline that we are moving along right now is our current chase spline
|
||||
if (owner->movespline->Finalized() || owner->movespline->GetId() != _previousChaseSplineId)
|
||||
{
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE))
|
||||
{
|
||||
owner->ClearUnitState(UNIT_STATE_CHASE_MOVE);
|
||||
// trigger movement inform if we have just reached our target
|
||||
if (owner->movespline->GetId() == _previousChaseSplineId)
|
||||
DoMovementInform(owner, target);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------ Next up we will perform somce basic checks that prevent the chasing unit to move
|
||||
|
||||
// the owner might be unable to move (rooted or casting), pause movement
|
||||
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting())
|
||||
{
|
||||
owner->StopMoving();
|
||||
_lastTargetPosition.reset();
|
||||
if (Creature* cOwner = owner->ToCreature())
|
||||
cOwner->SetCannotReachTarget(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// We are done moving. Trigger movement inform hook and clear chase move state
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && owner->movespline->Finalized())
|
||||
{
|
||||
if (Creature* cOwner = owner->ToCreature())
|
||||
cOwner->SetCannotReachTarget(false);
|
||||
|
||||
owner->ClearUnitState(UNIT_STATE_CHASE_MOVE);
|
||||
DoMovementInform(owner, target);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Owner cannot reach target (example: target is in water and owner cannot swim)
|
||||
Creature* creature = owner->ToCreature();
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && creature)
|
||||
// check if the chase target is in an accessible place
|
||||
if (creature)
|
||||
{
|
||||
if (!target->isInAccessiblePlaceFor(creature))
|
||||
{
|
||||
@@ -135,58 +144,50 @@ bool ChaseMovementGenerator::Update(Unit* owner, uint32 diff)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (creature && !target->isInAccessiblePlaceFor(creature))
|
||||
|
||||
// ------------------------ Now we can finally perform the movement actions
|
||||
|
||||
if (_positionCheckTimer.Passed())
|
||||
{
|
||||
creature->SetCannotReachTarget(true);
|
||||
return true;
|
||||
}
|
||||
_positionCheckTimer.Reset(POSITION_CHECK_INTERVAL);
|
||||
|
||||
bool const mutualChase = IsMutualChase(owner, target);
|
||||
float const chaseRange = GetChaseRange(owner, target);
|
||||
float const rangeTolerance = _range > 0.f ? _range : chaseRange;
|
||||
Optional<ChaseAngle> chaseAngle = mutualChase ? Optional<ChaseAngle>() : _angle;
|
||||
// Check the target angle and update the destination
|
||||
Position destination = target->GetVehicle() ? target->GetVehicle()->GetBase()->GetPosition() : target->GetPosition();
|
||||
uint8 result = checkPosition(CHASE_POSITION_CHECK_ANGLE, owner, target);
|
||||
|
||||
// Update Movement
|
||||
_nextMovementTimer.Update(diff);
|
||||
_nextRepositioningTimer.Update(diff);
|
||||
|
||||
// Handle repositioning and scattering arround target
|
||||
if (_nextRepositioningTimer.Passed())
|
||||
{
|
||||
_nextRepositioningTimer.Reset(REPOSITION_MOVEMENT_INTERVAL);
|
||||
|
||||
if (!owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && owner->IsCreature() && !target->isMoving())
|
||||
// The target is still moving so we move our chase destination ahead of the target to keep up
|
||||
if (target->isMoving())
|
||||
{
|
||||
// Owner is too close to its target. Step back.
|
||||
if (owner->GetBoundaryRadius() <= 5.f && owner->GetExactDist2d(target) < owner->GetBoundaryRadius())
|
||||
{
|
||||
LaunchMovement(owner, chaseRange, true, mutualChase);
|
||||
return true;
|
||||
}
|
||||
float angle = target->IsPlayer() ? target->m_movementInfo.GetMovementDirection() : 0.f;
|
||||
if (result & CHASE_POSITION_CHECK_RESULT_ANGLE_INVALID)
|
||||
angle = Position::NormalizeOrientation(_angle->RelativeAngle + float(M_PI));
|
||||
|
||||
target->MovePositionToFirstCollision(destination, SelectMovementSpeed(owner) * 0.5f, angle);
|
||||
}
|
||||
}
|
||||
|
||||
if (_nextMovementTimer.Passed())
|
||||
{
|
||||
_nextMovementTimer.Reset(CHASE_MOVEMENT_INTERVAL);
|
||||
|
||||
// Target has moved since we last checked its position. Handle new cases
|
||||
if (!_lastTargetPosition || target->GetPosition() != _lastTargetPosition.value())
|
||||
else
|
||||
{
|
||||
// Create new snapshot of our target's position
|
||||
_lastTargetPosition = target->GetPosition();
|
||||
float angle = target->GetRelativeAngle(owner);
|
||||
if (result & CHASE_POSITION_CHECK_RESULT_ANGLE_INVALID)
|
||||
angle = _angle->RelativeAngle;
|
||||
|
||||
if (PositionOkay(owner, target, rangeTolerance, chaseAngle))
|
||||
{
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && !target->isMoving() && !mutualChase)
|
||||
{
|
||||
// Our current position is fine. Stop movement.
|
||||
owner->StopMoving();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (owner->GetExactDist2d(target) > rangeTolerance + 0.1f) // 0.1f here to avoid edge cases when the owner has stepped back before
|
||||
LaunchMovement(owner, chaseRange, false, mutualChase);
|
||||
target->MovePositionToFirstCollision(destination, GetHitboxSum(owner, target), angle);
|
||||
}
|
||||
|
||||
owner->UpdateAllowedPositionZ(destination.GetPositionX(), destination.GetPositionY(), destination.m_positionZ);
|
||||
|
||||
// Destination set and ready. Now the usual checks
|
||||
result |= checkPosition(ChasePositionCheckOptions(CHASE_POSITION_CHECK_DISTANCE | CHASE_POSITION_CHECK_LOS), owner, target, &destination);
|
||||
|
||||
if (result == CHASE_POSITION_CHECK_RESULT_VALID)
|
||||
{
|
||||
owner->StopMoving();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_moveTimer.Passed())
|
||||
{
|
||||
_moveTimer.Reset(CHASE_MOVEMENT_INTERVAL);
|
||||
launchSpline(owner, target, destination);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +201,82 @@ void ChaseMovementGenerator::Finalize(Unit* owner)
|
||||
cOwner->SetCannotReachTarget(false);
|
||||
}
|
||||
|
||||
void ChaseMovementGenerator::LaunchMovement(Unit* owner, float chaseRange, bool backward /*= false*/, bool mutualChase /*= false*/)
|
||||
ChaseMovementPositionCheckResult ChaseMovementGenerator::checkPosition(ChasePositionCheckOptions checkOptions, Unit* owner, Unit* target, Position const* destination /*nullptr*/) const
|
||||
{
|
||||
uint8 result = CHASE_POSITION_CHECK_RESULT_VALID;
|
||||
if (checkOptions & CHASE_POSITION_CHECK_DISTANCE)
|
||||
{
|
||||
float range = _range.has_value() ? *_range : (GetHitboxSum(owner, target) + float(CONTACT_DISTANCE));
|
||||
|
||||
// We have yet to reach the destination of the target so keep moving
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && target->isMoving() && destination)
|
||||
{
|
||||
if (owner->GetExactDist(destination) >= range)
|
||||
result |= CHASE_POSITION_CHECK_RESULT_TOO_FAR;
|
||||
}
|
||||
else if (owner->GetExactDist(target) >= range)
|
||||
result |= CHASE_POSITION_CHECK_RESULT_TOO_FAR;
|
||||
}
|
||||
|
||||
//if (checkOptions & CHASE_POSITION_CHECK_BOUNDARY)
|
||||
{
|
||||
}
|
||||
|
||||
if (checkOptions & CHASE_POSITION_CHECK_ANGLE)
|
||||
{
|
||||
// owner's relative angle to its target is not within boundaries
|
||||
if (_angle.has_value() && !_angle->IsAngleOkay(target->GetRelativeAngle(owner)))
|
||||
result |= CHASE_POSITION_CHECK_RESULT_ANGLE_INVALID;
|
||||
}
|
||||
|
||||
if (checkOptions & CHASE_POSITION_CHECK_LOS)
|
||||
{
|
||||
// check if the target is in line of sight
|
||||
if (!owner->IsWithinLOSInMap(target))
|
||||
result |= CHASE_POSITION_CHECK_RESULT_NOT_IN_LOS;
|
||||
}
|
||||
|
||||
return static_cast<ChaseMovementPositionCheckResult>(result);
|
||||
}
|
||||
|
||||
void ChaseMovementGenerator::launchSpline(Unit* owner, Unit* target, Position& destination)
|
||||
{
|
||||
bool success = _pathGenerator->CalculatePath(destination.GetPositionX(), destination.GetPositionY(), destination.GetPositionZ(), owner->IsFlying());
|
||||
uint32 deniedPathResultTypes = PATHFIND_NOPATH | PATHFIND_INCOMPLETE;
|
||||
if (!owner->IsFlying() || (target->IsInWater() && !owner->CanEnterWater())) // only flying and swimming units may use shortcuts
|
||||
deniedPathResultTypes |= PATHFIND_SHORTCUT;
|
||||
|
||||
bool cantReachTarget = !success || (_pathGenerator->GetPathType() & deniedPathResultTypes);
|
||||
if (cantReachTarget)
|
||||
{
|
||||
owner->ToCreature()->SetCannotReachTarget(true);
|
||||
owner->StopMoving();
|
||||
return;
|
||||
}
|
||||
else
|
||||
owner->ToCreature()->SetCannotReachTarget(false);
|
||||
|
||||
Movement::MoveSplineInit init(owner);
|
||||
init.MovebyPath(_pathGenerator->GetPath());
|
||||
init.SetWalk(false);
|
||||
init.SetFacing(target);
|
||||
|
||||
if (target->isMoving())
|
||||
{
|
||||
// When the target is still moving but the chaser is already within the combat reach of the target, it adapts its speed to the target's one
|
||||
float targetSpeed = SelectMovementSpeed(target);
|
||||
if (owner->GetExactDist(target) <= (GetHitboxSum(owner, target) + CONTACT_DISTANCE) && SelectMovementSpeed(owner) > targetSpeed)
|
||||
init.SetVelocity(targetSpeed);
|
||||
}
|
||||
|
||||
if (init.Launch())
|
||||
{
|
||||
_previousChaseSplineId = owner->movespline->GetId();
|
||||
owner->AddUnitState(UNIT_STATE_CHASE_MOVE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
Unit* target = GetTarget();
|
||||
|
||||
@@ -241,7 +317,7 @@ void ChaseMovementGenerator::LaunchMovement(Unit* owner, float chaseRange, bool
|
||||
|
||||
PathGenerator path(owner);
|
||||
bool success = path.CalculatePath(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), owner->CanFly());
|
||||
if (!success || (path.GetPathType() & (PATHFIND_NOPATH /*| PATHFIND_INCOMPLETE*/)))
|
||||
if (!success || (path.GetPathType() & (PATHFIND_NOPATH /*| PATHFIND_INCOMPLETE)))
|
||||
{
|
||||
if (creature)
|
||||
creature->SetCannotReachTarget(true);
|
||||
@@ -265,3 +341,4 @@ void ChaseMovementGenerator::LaunchMovement(Unit* owner, float chaseRange, bool
|
||||
if (creature)
|
||||
creature->SetCannotReachTarget(false);
|
||||
}
|
||||
*/
|
||||
@@ -24,34 +24,38 @@
|
||||
#include "Timer.h"
|
||||
|
||||
class Unit;
|
||||
enum ChaseMovementPositionCheckResult : uint8;
|
||||
enum ChasePositionCheckOptions : uint8;
|
||||
|
||||
static constexpr uint32 CHASE_MOVEMENT_INTERVAL = 400; // sniffed value (1 batch update cyclice)
|
||||
static constexpr uint32 POSITION_CHECK_INTERVAL = 100; // the interval at which we check if we already have reached the target
|
||||
static constexpr uint32 REPOSITION_MOVEMENT_INTERVAL = 1200; // (3 batch update cycles) TODO: verify
|
||||
|
||||
class ChaseMovementGenerator : public MovementGenerator, public AbstractPursuer
|
||||
{
|
||||
public:
|
||||
MovementGeneratorType GetMovementGeneratorType() const override { return CHASE_MOTION_TYPE; }
|
||||
|
||||
ChaseMovementGenerator(Unit* target, float range, Optional<ChaseAngle> angle = {});
|
||||
~ChaseMovementGenerator();
|
||||
explicit ChaseMovementGenerator(Unit* target, Optional<float> range = {}, Optional<ChaseAngle> angle = {});
|
||||
~ChaseMovementGenerator() = default;
|
||||
|
||||
void Initialize(Unit* owner) override;
|
||||
void Reset(Unit* owner) override { Initialize(owner); }
|
||||
bool Update(Unit* owner, uint32 diff) override;
|
||||
void Finalize(Unit* owner) override;
|
||||
|
||||
void UnitSpeedChanged() override { _lastTargetPosition.reset(); }
|
||||
void UnitSpeedChanged() override { }
|
||||
|
||||
private:
|
||||
void LaunchMovement(Unit* owner, float chaseRange, bool backward = false, bool mutualChase = false);
|
||||
Optional<float> _range;
|
||||
Optional<ChaseAngle> _angle;
|
||||
uint32 _previousChaseSplineId;
|
||||
TimeTrackerSmall _moveTimer;
|
||||
TimeTracker _positionCheckTimer;
|
||||
std::unique_ptr<PathGenerator> _pathGenerator;
|
||||
|
||||
static constexpr uint32 CHASE_MOVEMENT_INTERVAL = 400; // sniffed value (1 batch update cyclice)
|
||||
static constexpr uint32 REPOSITION_MOVEMENT_INTERVAL = 1200; // (3 batch update cycles) TODO: verify
|
||||
|
||||
TimeTrackerSmall _nextMovementTimer;
|
||||
TimeTrackerSmall _nextRepositioningTimer;
|
||||
|
||||
Optional<Position> _lastTargetPosition;
|
||||
float const _range;
|
||||
Optional<ChaseAngle> const _angle;
|
||||
ChaseMovementPositionCheckResult checkPosition(ChasePositionCheckOptions checkOptions, Unit* owner, Unit* target, Position const* destination = nullptr) const;
|
||||
void launchSpline(Unit* owner, Unit* target, Position& destination);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -490,7 +490,7 @@ struct npc_drahga_invoked_flaming_spirit : public ScriptedAI
|
||||
AddThreat(target, 1500000.0f); // Todo: this should be handled by serverside spell 95207
|
||||
me->GetThreatManager().FixateTarget(target);
|
||||
me->ClearUnitState(UNIT_STATE_CASTING);
|
||||
me->GetMotionMaster()->MoveChase(target, 0.f, 0.f);
|
||||
me->GetMotionMaster()->MoveChase(target);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
Reference in new issue
Block a user