35constexpr Milliseconds SEND_NEXT_POINT_EARLY_DELTA = 1500ms;
45 _waitTimeRangeAtPathEnd(
std::move(waitTimeRangeAtPathEnd)), _wanderDistanceAtPathEnds(wanderDistanceAtPathEnds),
46 _followPathBackwardsFromEndToStart(followPathBackwardsFromEndToStart), _exactSplinePath(exactSplinePath), _repeating(repeating), _generatePath(generatePath),
47 _fadeObject(fadeObject), _moveTimer(0), _nextMoveTime(0), _waypointTransitionSplinePointsIndex(0), _isReturningToStart(false)
65 _waitTimeRangeAtPathEnd(
std::move(waitTimeRangeAtPathEnd)), _wanderDistanceAtPathEnds(wanderDistanceAtPathEnds),
66 _followPathBackwardsFromEndToStart(followPathBackwardsFromEndToStart), _exactSplinePath(exactSplinePath), _repeating(repeating), _generatePath(generatePath),
67 _fadeObject(fadeObject), _moveTimer(0), _nextMoveTime(0), _waypointTransitionSplinePointsIndex(0), _isReturningToStart(false)
77 std::get<std::unique_ptr<WaypointPath>>(this->
_path)->BuildSegments();
99 _nextMoveTime.Reset(timer);
105 _nextMoveTime.Reset(1);
114 _nextMoveTime.Reset(overrideTimer);
116 if (_nextMoveTime.Passed())
117 _nextMoveTime.Reset(1);
127 if (!path || path->
Nodes.empty())
130 ASSERT(_currentNode < path->
Nodes.size(),
"WaypointMovementGenerator::GetResetPosition: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, path->
Id);
147 TC_LOG_ERROR(
"sql.sql",
"WaypointMovementGenerator::DoInitialize: couldn't load path for {}", owner->GetGUID());
151 if (path->
Nodes.size() == 1)
156 _nextMoveTime.Reset(1000);
167 _nextMoveTime.Reset(1);
173 if (!owner->IsAlive())
180 if (!path || path->
Nodes.empty())
185 _duration->Update(diff);
186 if (_duration->Passed())
188 if constexpr (std::is_base_of_v<Creature, T>)
189 owner->UpdateCurrentWaypointInfo(0, 0);
220 StartMove(owner,
true);
228 if (!UpdateMoveTimer(diff) && !owner->movespline->Finalized())
231 if constexpr (std::is_base_of_v<Creature, T>)
232 if (owner->GetTransGUID().IsEmpty())
233 owner->SetHomePosition(owner->GetPosition());
236 if (IsExactSplinePath())
238 if (_waypointTransitionSplinePointsIndex < _waypointTransitionSplinePoints.size()
239 && owner->movespline->currentPathIdx() >= _waypointTransitionSplinePoints[_waypointTransitionSplinePointsIndex])
242 ++_waypointTransitionSplinePointsIndex;
243 if (ComputeNextNode())
245 if constexpr (std::is_base_of_v<Creature, T>)
247 ai->WaypointStarted(path->
Nodes[_currentNode].Id, path->
Id);
254 StartMove(owner,
true);
256 else if (!_nextMoveTime.Passed())
258 if (UpdateWaitTimer(diff))
267 StartMove(owner,
true);
282 if (_nextMoveTime.Passed())
305 if constexpr (std::is_base_of_v<Creature, T>)
306 owner->SetWalk(
false);
334 if (!path || path->
Nodes.empty())
337 ASSERT(_currentNode < path->
Nodes.size(),
"WaypointMovementGenerator::OnArrived: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, path->
Id);
343 _nextMoveTime.Reset(*
waypoint.Delay);
346 if (_waitTimeRangeAtPathEnd && IsFollowingPathBackwardsFromEndToStart()
347 && ((_isReturningToStart && _currentNode == 0) || (!_isReturningToStart && _currentNode == path->
Nodes.size() - 1)))
350 Milliseconds waitTime =
randtime(_waitTimeRangeAtPathEnd->first, _waitTimeRangeAtPathEnd->second);
352 _duration->Update(waitTime);
354 if (_wanderDistanceAtPathEnds)
355 owner->GetMotionMaster()->MoveRandom(*_wanderDistanceAtPathEnds, waitTime, _speed, _speedSelectionMode,
MOTION_SLOT_ACTIVE);
357 _nextMoveTime.Reset(waitTime);
360 MovementInform(owner);
362 if constexpr (std::is_base_of_v<Creature, T>)
363 owner->UpdateCurrentWaypointInfo(
waypoint.Id, path->
Id);
368void CreateSingularPointPath(
Unit const* owner,
WaypointPath const* path,
uint32 currentNode,
bool generatePath,
379 points->insert(points->end(), generator.GetPath().begin() + 1, generator.GetPath().end());
386 waypointTransitionSplinePoints->push_back(points->size() - 1);
390 bool isReturningToStart,
bool generatePath,
bool isCyclic,
393 std::span<WaypointNode const> segment = [&]
396 auto segmentItr = std::ranges::find_if(path->
ContinuousSegments, [&](std::pair<std::size_t, std::size_t>
const& segmentRange)
398 auto isInSegmentRange = [&](uint32 node) { return node >= segmentRange.first && node < segmentRange.first + segmentRange.second; };
399 return isInSegmentRange(currentNode) && isInSegmentRange(previousNode);
405 if (currentNode != 0 || previousNode != path->
Nodes.size() - 1)
406 return std::span(&path->
Nodes[currentNode], 1);
411 if (!isReturningToStart)
412 return std::span(&path->
Nodes[currentNode], segmentItr->second - (currentNode - segmentItr->first));
414 return std::span(&path->
Nodes[segmentItr->first], currentNode - segmentItr->first + 1);
417 *lastWaypointOnPath = !isReturningToStart ? &segment.back() : &segment.front();
419 waypointTransitionSplinePoints->clear();
420 auto fillPath = [&]<
typename iterator>(iterator itr, iterator end)
424 generator.emplace(owner);
435 points->insert(points->end(), generator->GetPath().begin() + 1, generator->GetPath().end());
441 points->emplace_back(itr->X, itr->Y, itr->Z);
443 waypointTransitionSplinePoints->push_back(points->size() - 1);
445 source.
Relocate(itr->X, itr->Y, itr->Z);
453 std::vector<WaypointNode> cyclicPath = path->
Nodes;
454 std::rotate(cyclicPath.begin(), cyclicPath.begin() + currentNode, cyclicPath.end());
455 fillPath(cyclicPath.begin(), cyclicPath.end());
459 if (!isReturningToStart)
460 fillPath(segment.begin(), segment.end());
462 fillPath(segment.rbegin(), segment.rend());
475 if (!path || path->
Nodes.empty())
480 _nextMoveTime.Reset(1000);
484 if constexpr (std::is_base_of_v<Creature, T>)
486 if (owner->IsFormationLeader() && !owner->IsFormationLeaderMoveAllowed())
488 _nextMoveTime.Reset(1000);
493 bool const transportPath = !owner->GetTransGUID().IsEmpty();
495 uint32 previousNode = _currentNode;
498 if (ComputeNextNode())
500 ASSERT(_currentNode < path->
Nodes.size(),
"WaypointMovementGenerator::StartMove: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, path->
Id);
503 if constexpr (std::is_base_of_v<Creature, T>)
505 AI->WaypointStarted(path->
Nodes[_currentNode].Id, path->
Id);
511 if constexpr (std::is_base_of_v<Creature, T>)
513 owner->UpdateCurrentWaypointInfo(0, 0);
519 float o = owner->GetOrientation();
522 owner->SetHomePosition(x, y, z, o);
527 o -= trans->GetTransportOrientation();
528 owner->SetTransportHomePosition(x, y, z, o);
529 owner->SetHomePosition(trans->GetPositionWithOffset(owner->GetTransportHomePosition()));
548 if constexpr (std::is_base_of_v<Creature, T>)
550 AI->WaypointStarted(path->
Nodes[_currentNode].Id, path->
Id);
553 ASSERT(_currentNode < path->
Nodes.size(),
"WaypointMovementGenerator::StartMove: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, path->
Id);
556 bool isCyclic = IsCyclic();
559 if (IsExactSplinePath())
560 CreateMergedPath(owner, path, previousNode, _currentNode, _isReturningToStart,
false, isCyclic,
561 &points, &_waypointTransitionSplinePoints, &lastWaypointForSegment);
563 CreateSingularPointPath(owner, path, _currentNode, _generatePath, &points, &_waypointTransitionSplinePoints);
565 _waypointTransitionSplinePointsIndex = 0;
573 bool isFirstCycle = relaunch || owner->movespline->Finalized() || !owner->movespline->isCyclic();
576 for (
int32& point : _waypointTransitionSplinePoints)
580 _moveTimer.Reset(
Milliseconds(owner->movespline->Duration()));
593 if (lastWaypointForSegment->
Orientation.has_value()
594 && (lastWaypointForSegment->
Delay || (_isReturningToStart ? _currentNode == 0 : _currentNode == path->
Nodes.size() - 1)))
597 if (_fadeObject && !_repeating)
599 std::size_t lastWaypointForPath = IsFollowingPathBackwardsFromEndToStart() ? 0 : path->
Nodes.size() - 1;
600 if (IsExactSplinePath())
603 if (lastWaypointForPath >= lastSegmentFirstNode && lastWaypointForPath < lastSegmentFirstNode + segmentLength)
608 if (lastWaypointForSegment->
Id == path->
Nodes[lastWaypointForPath].Id)
633 switch (_speedSelectionMode)
656 if (IsExactSplinePath() && (points.size() > 2 && owner->CanFly()))
661 if (!IsExactSplinePath()
662 && duration > 2 * SEND_NEXT_POINT_EARLY_DELTA
663 && !lastWaypointForSegment->
Delay
664 && path->
Nodes.size() > 2
666 && ((_currentNode != 0 && _currentNode != path->
Nodes.size() - 1) || (!IsFollowingPathBackwardsFromEndToStart() && _repeating)))
667 duration -= SEND_NEXT_POINT_EARLY_DELTA;
669 _moveTimer.Reset(duration);
672 if constexpr (std::is_base_of_v<Creature, T>)
673 owner->SignalFormationMovement();
680 if ((_currentNode == path->
Nodes.size() - 1) && !_repeating)
684 _currentNode = (_currentNode + 1) % path->
Nodes.size();
687 if (!_isReturningToStart)
689 if (++_currentNode >= path->
Nodes.size())
692 _isReturningToStart =
true;
697 if (_currentNode-- == 0)
700 _isReturningToStart =
false;
711 if (_followPathBackwardsFromEndToStart)
712 return *_followPathBackwardsFromEndToStart;
720 if (_exactSplinePath)
721 return *_exactSplinePath;
729 return !IsFollowingPathBackwardsFromEndToStart()
730 && IsExactSplinePath()
732 && GetPath()->ContinuousSegments.size() == 1;
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
#define TC_LOG_ERROR(filterType__, message__,...)
MovementWalkRunSpeedSelectionMode
@ MOVEMENTGENERATOR_FLAG_TIMED_PAUSED
@ MOVEMENTGENERATOR_FLAG_INITIALIZATION_PENDING
@ MOVEMENTGENERATOR_FLAG_PAUSED
@ MOVEMENTGENERATOR_FLAG_DEACTIVATED
@ MOVEMENTGENERATOR_FLAG_FINALIZED
@ MOVEMENTGENERATOR_FLAG_TRANSITORY
@ MOVEMENTGENERATOR_FLAG_INTERRUPTED
@ MOVEMENTGENERATOR_FLAG_INFORM_ENABLED
@ MOVEMENTGENERATOR_FLAG_INITIALIZED
@ MOVEMENTGENERATOR_FLAG_SPEED_UPDATE_PENDING
std::optional< T > Optional
Optional helper class to wrap optional values within.
Milliseconds randtime(Milliseconds min, Milliseconds max)
std::vector< Node const * > Nodes
@ UNIT_STATE_ROAMING_MOVE
@ UNIT_STATE_LOST_CONTROL
static constexpr std::size_t WAYPOINT_PATH_FLAG_FOLLOW_PATH_BACKWARDS_MINIMUM_NODES
@ FollowPathBackwardsFromEndToStart
static float waypoint[6][3]
uint32 GetWaypointPathId() const
Scripting::v2::ActionResultSetter< MovementStopReason > ScriptResult
void DisableTransportPathTransformations()
void SetWalk(bool enable)
void MovebyPath(std::span< Vector3 const > path, int32 pointId=0)
void SetVelocity(float velocity)
void SetFacing(float angle)
void SetFadeObject(Milliseconds fadeDuration=1s)
void SetAnimation(AnimTier anim, uint32 tierTransitionId=0, int32 transitionStartPoint=0)
virtual std::string GetDebugInfo() const
void DoDeactivate(T *owner)
bool IsExactSplinePath() const
void Resume(uint32 overrideTimer) override
MovementGeneratorType GetMovementGeneratorType() const override
void MovementInform(T const *owner) const
std::string GetDebugInfo() const override
void StartMove(T *owner, bool relaunch=false)
void DoFinalize(T *owner, bool active, bool movementInform)
~WaypointMovementGenerator()
bool DoUpdate(T *owner, uint32 diff)
bool IsFollowingPathBackwardsFromEndToStart() const
bool GetResetPosition(Unit *, float &x, float &y, float &z) override
Optional< TimeTracker > _duration
WaypointMovementGenerator(uint32 pathId, bool repeating, Optional< Milliseconds > duration={}, Optional< float > speed={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::Default, Optional< std::pair< Milliseconds, Milliseconds > > waitTimeRangeAtPathEnd={}, Optional< float > wanderDistanceAtPathEnds={}, Optional< bool > followPathBackwardsFromEndToStart={}, Optional< bool > exactSplinePath={}, bool generatePath=true, Optional< MovementFadeObject > fadeObject={}, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
void Pause(uint32 timer) override
void DoInitialize(T *owner)
std::vector< Vector3 > PointsArray
std::string StringFormat(FormatString< Args... > fmt, Args &&... args) noexcept
Default TC string format function.
constexpr float GetPositionX() const
constexpr float GetPositionY() const
constexpr void GetPosition(float &x, float &y) const
constexpr void Relocate(float x, float y)
constexpr float GetPositionZ() const
MovementGenerator * Create(Unit *object) const override
Optional< Milliseconds > Delay
Optional< float > Orientation
Optional< WaypointMoveType > MoveType
std::vector< std::pair< std::size_t, std::size_t > > ContinuousSegments
std::vector< WaypointNode > Nodes
Optional< float > Velocity
WaypointMoveType MoveType