TrinityCore
Loading...
Searching...
No Matches
WaypointMovementGenerator.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
19#include "Creature.h"
20#include "CreatureAI.h"
21#include "Errors.h"
22#include "Log.h"
23#include "MotionMaster.h"
24#include "MoveSpline.h"
25#include "MoveSplineInit.h"
26#include "MovementDefines.h"
27#include "PathGenerator.h"
28#include "Player.h"
29#include "Transport.h"
30#include "WaypointManager.h"
31#include <span>
32
33namespace
34{
35constexpr Milliseconds SEND_NEXT_POINT_EARLY_DELTA = 1500ms;
36}
37
38template <typename T>
40 MovementWalkRunSpeedSelectionMode speedSelectionMode, Optional<std::pair<Milliseconds, Milliseconds>> waitTimeRangeAtPathEnd,
41 Optional<float> wanderDistanceAtPathEnds, Optional<bool> followPathBackwardsFromEndToStart, Optional<bool> exactSplinePath, bool generatePath,
42 Optional<MovementFadeObject> fadeObject /*= {}*/,
44 : PathMovementBase(sWaypointMgr->GetPath(pathId)), _speed(speed), _speedSelectionMode(speedSelectionMode),
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)
48{
53 this->ScriptResult = std::move(scriptResult);
54 if (duration)
55 _duration.emplace(*duration);
56}
57
58template <typename T>
60 MovementWalkRunSpeedSelectionMode speedSelectionMode, Optional<std::pair<Milliseconds, Milliseconds>> waitTimeRangeAtPathEnd,
61 Optional<float> wanderDistanceAtPathEnds, Optional<bool> followPathBackwardsFromEndToStart, Optional<bool> exactSplinePath, bool generatePath,
64 : PathMovementBase(std::make_unique<WaypointPath>(path)), _speed(speed), _speedSelectionMode(speedSelectionMode),
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)
68{
73 this->ScriptResult = std::move(scriptResult);
74 if (duration)
75 _duration.emplace(*duration);
76
77 std::get<std::unique_ptr<WaypointPath>>(this->_path)->BuildSegments();
78}
79
80template <typename T>
82
83template <typename T>
88
89template <typename T>
91{
92 if (timer)
93 {
94 // Don't try to paused an already paused generator
95 if (this->HasFlag(MOVEMENTGENERATOR_FLAG_PAUSED))
96 return;
97
99 _nextMoveTime.Reset(timer);
100 this->RemoveFlag(MOVEMENTGENERATOR_FLAG_PAUSED);
101 }
102 else
103 {
104 this->AddFlag(MOVEMENTGENERATOR_FLAG_PAUSED);
105 _nextMoveTime.Reset(1); // Needed so that Update does not behave as if node was reached
106 this->RemoveFlag(MOVEMENTGENERATOR_FLAG_TIMED_PAUSED);
107 }
108}
109
110template <typename T>
112{
113 if (overrideTimer)
114 _nextMoveTime.Reset(overrideTimer);
115
116 if (_nextMoveTime.Passed())
117 _nextMoveTime.Reset(1); // Needed so that Update does not behave as if node was reached
118
119 this->RemoveFlag(MOVEMENTGENERATOR_FLAG_PAUSED);
120}
121
122template <typename T>
123bool WaypointMovementGenerator<T>::GetResetPosition(Unit* /*owner*/, float& x, float& y, float& z)
124{
125 // prevent a crash at empty waypoint path.
126 WaypointPath const* path = GetPath();
127 if (!path || path->Nodes.empty())
128 return false;
129
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);
131 WaypointNode const& waypoint = path->Nodes[_currentNode];
132
133 x = waypoint.X;
134 y = waypoint.Y;
135 z = waypoint.Z;
136 return true;
137}
138
139template <typename T>
141{
143
144 WaypointPath const* path = GetPath();
145 if (!path)
146 {
147 TC_LOG_ERROR("sql.sql", "WaypointMovementGenerator::DoInitialize: couldn't load path for {}", owner->GetGUID());
148 return;
149 }
150
151 if (path->Nodes.size() == 1)
152 _repeating = false;
153
154 owner->StopMoving();
155
156 _nextMoveTime.Reset(1000);
157}
158
159template <typename T>
161{
163
164 owner->StopMoving();
165
166 if (!this->HasFlag(MOVEMENTGENERATOR_FLAG_FINALIZED) && _nextMoveTime.Passed())
167 _nextMoveTime.Reset(1); // Needed so that Update does not behave as if node was reached
168}
169
170template <typename T>
172{
173 if (!owner->IsAlive())
174 return true;
175
177 return true;
178
179 WaypointPath const* path = GetPath();
180 if (!path || path->Nodes.empty())
181 return true;
182
183 if (_duration)
184 {
185 _duration->Update(diff);
186 if (_duration->Passed())
187 {
188 if constexpr (std::is_base_of_v<Creature, T>)
189 owner->UpdateCurrentWaypointInfo(0, 0);
190
191 this->RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY);
194 this->SetScriptResult(MovementStopReason::Finished);
195 return false;
196 }
197 }
198
199 if (owner->HasUnitState(UNIT_STATE_NOT_MOVE | UNIT_STATE_LOST_CONTROL) || owner->IsMovementPreventedByCasting())
200 {
202 owner->StopMoving();
203 return true;
204 }
205
206 if (this->HasFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED))
207 {
208 /*
209 * relaunch only if
210 * - has a tiner? -> was it interrupted while not waiting aka moving? need to check both:
211 * -> has a timer - is it because its waiting to start next node?
212 * -> has a timer - is it because something set it while moving (like timed pause)?
213 *
214 * - doesnt have a timer? -> is movement valid?
215 *
216 * TODO: ((_nextMoveTime.Passed() && VALID_MOVEMENT) || (!_nextMoveTime.Passed() && !HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED)))
217 */
218 if (this->HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED) && (_nextMoveTime.Passed() || !this->HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED)))
219 {
220 StartMove(owner, true);
221 return true;
222 }
223
224 this->RemoveFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED);
225 }
226
227 // if it's moving
228 if (!UpdateMoveTimer(diff) && !owner->movespline->Finalized())
229 {
230 // set home position at place (every MotionMaster::UpdateMotion)
231 if constexpr (std::is_base_of_v<Creature, T>)
232 if (owner->GetTransGUID().IsEmpty())
233 owner->SetHomePosition(owner->GetPosition());
234
235 // handle switching points in continuous segments
236 if (IsExactSplinePath())
237 {
238 if (_waypointTransitionSplinePointsIndex < _waypointTransitionSplinePoints.size()
239 && owner->movespline->currentPathIdx() >= _waypointTransitionSplinePoints[_waypointTransitionSplinePointsIndex])
240 {
241 OnArrived(owner);
242 ++_waypointTransitionSplinePointsIndex;
243 if (ComputeNextNode())
244 {
245 if constexpr (std::is_base_of_v<Creature, T>)
246 if (CreatureAI* ai = owner->AI())
247 ai->WaypointStarted(path->Nodes[_currentNode].Id, path->Id);
248 }
249 }
250 }
251
252 // relaunch movement if its speed has changed
254 StartMove(owner, true);
255 }
256 else if (!_nextMoveTime.Passed()) // it's not moving, is there a timer?
257 {
258 if (UpdateWaitTimer(diff))
259 {
260 if (!this->HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED)) // initial movement call
261 {
262 StartMove(owner);
263 return true;
264 }
265 else if (!this->HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED)) // timer set before node was reached, resume now
266 {
267 StartMove(owner, true);
268 return true;
269 }
270 }
271 else
272 return true; // keep waiting
273 }
274 else // not moving, no timer
275 {
277 {
278 OnArrived(owner); // hooks and wait timer reset (if necessary)
279 this->AddFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED); // signals to future StartMove that it reached a node
280 }
281
282 if (_nextMoveTime.Passed()) // OnArrived might have set a timer
283 StartMove(owner); // check path status, get next point and move if necessary & can
284 }
285
286 return true;
287}
288
289template <typename T>
291{
293 owner->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
294}
295
296template <typename T>
297void WaypointMovementGenerator<T>::DoFinalize(T* owner, bool active, bool movementInform)
298{
300 if (active)
301 {
302 owner->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
303
304 // TODO: Research if this modification is needed, which most likely isnt
305 if constexpr (std::is_base_of_v<Creature, T>)
306 owner->SetWalk(false);
307 }
308
309 if (movementInform)
310 this->SetScriptResult(MovementStopReason::Finished);
311}
312
313template <typename T>
315{
316}
317
318template <>
320{
321 WaypointPath const* path = GetPath();
322 WaypointNode const& waypoint = path->Nodes[_currentNode];
323 if (CreatureAI* AI = owner->AI())
324 {
325 AI->MovementInform(WAYPOINT_MOTION_TYPE, waypoint.Id);
326 AI->WaypointReached(waypoint.Id, path->Id);
327 }
328}
329
330template <typename T>
332{
333 WaypointPath const* path = GetPath();
334 if (!path || path->Nodes.empty())
335 return;
336
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);
338 WaypointNode const& waypoint = path->Nodes[_currentNode];
339
340 if (waypoint.Delay)
341 {
342 owner->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
343 _nextMoveTime.Reset(*waypoint.Delay);
344 }
345
346 if (_waitTimeRangeAtPathEnd && IsFollowingPathBackwardsFromEndToStart()
347 && ((_isReturningToStart && _currentNode == 0) || (!_isReturningToStart && _currentNode == path->Nodes.size() - 1)))
348 {
349 owner->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
350 Milliseconds waitTime = randtime(_waitTimeRangeAtPathEnd->first, _waitTimeRangeAtPathEnd->second);
351 if (_duration)
352 _duration->Update(waitTime); // count the random movement time as part of waypoing movement action
353
354 if (_wanderDistanceAtPathEnds)
355 owner->GetMotionMaster()->MoveRandom(*_wanderDistanceAtPathEnds, waitTime, _speed, _speedSelectionMode, MOTION_SLOT_ACTIVE);
356 else
357 _nextMoveTime.Reset(waitTime);
358 }
359
360 MovementInform(owner);
361
362 if constexpr (std::is_base_of_v<Creature, T>)
363 owner->UpdateCurrentWaypointInfo(waypoint.Id, path->Id);
364}
365
366namespace
367{
368void CreateSingularPointPath(Unit const* owner, WaypointPath const* path, uint32 currentNode, bool generatePath,
369 Movement::PointsArray* points, std::vector<int32>* waypointTransitionSplinePoints)
370{
371 WaypointNode const& waypoint = path->Nodes[currentNode];
372 points->emplace_back(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ());
373
374 if (generatePath)
375 {
376 PathGenerator generator(owner);
377 bool result = generator.CalculatePath(waypoint.X, waypoint.Y, waypoint.Z);
378 if (result && !(generator.GetPathType() & PATHFIND_NOPATH))
379 points->insert(points->end(), generator.GetPath().begin() + 1, generator.GetPath().end());
380 else
381 points->emplace_back(waypoint.X, waypoint.Y, waypoint.Z);
382 }
383 else
384 points->emplace_back(waypoint.X, waypoint.Y, waypoint.Z);
385
386 waypointTransitionSplinePoints->push_back(points->size() - 1);
387}
388
389void CreateMergedPath(Unit const* owner, WaypointPath const* path, uint32 previousNode, uint32 currentNode,
390 bool isReturningToStart, bool generatePath, bool isCyclic,
391 Movement::PointsArray* points, std::vector<int32>* waypointTransitionSplinePoints, WaypointNode const** lastWaypointOnPath)
392{
393 std::span<WaypointNode const> segment = [&]
394 {
395 // find the continuous segment that our destination waypoint is on
396 auto segmentItr = std::ranges::find_if(path->ContinuousSegments, [&](std::pair<std::size_t, std::size_t> const& segmentRange)
397 {
398 auto isInSegmentRange = [&](uint32 node) { return node >= segmentRange.first && node < segmentRange.first + segmentRange.second; };
399 return isInSegmentRange(currentNode) && isInSegmentRange(previousNode);
400 });
401
402 // handle path returning directly from last point to first
403 if (segmentItr == path->ContinuousSegments.end())
404 {
405 if (currentNode != 0 || previousNode != path->Nodes.size() - 1)
406 return std::span(&path->Nodes[currentNode], 1);
407
408 segmentItr = path->ContinuousSegments.begin();
409 }
410
411 if (!isReturningToStart)
412 return std::span(&path->Nodes[currentNode], segmentItr->second - (currentNode - segmentItr->first));
413
414 return std::span(&path->Nodes[segmentItr->first], currentNode - segmentItr->first + 1);
415 }();
416
417 *lastWaypointOnPath = !isReturningToStart ? &segment.back() : &segment.front();
418
419 waypointTransitionSplinePoints->clear();
420 auto fillPath = [&]<typename iterator>(iterator itr, iterator end)
421 {
422 Optional<PathGenerator> generator;
423 if (generatePath)
424 generator.emplace(owner);
425
426 Position source = owner->GetPosition();
427 points->emplace_back(source.GetPositionX(), source.GetPositionY(), source.GetPositionZ());
428
429 while (itr != end)
430 {
431 if (generator)
432 {
433 bool result = generator->CalculatePath(source.GetPositionX(), source.GetPositionY(), source.GetPositionZ(), itr->X, itr->Y, itr->Z);
434 if (result && !(generator->GetPathType() & PATHFIND_NOPATH))
435 points->insert(points->end(), generator->GetPath().begin() + 1, generator->GetPath().end());
436 else
437 generator.reset(); // when path generation to a waypoint fails, add all remaining points without pathfinding (preserve legacy behavior of MoveSplineInit::MoveTo)
438 }
439
440 if (!generator)
441 points->emplace_back(itr->X, itr->Y, itr->Z);
442
443 waypointTransitionSplinePoints->push_back(points->size() - 1);
444
445 source.Relocate(itr->X, itr->Y, itr->Z);
446 ++itr;
447 }
448 };
449
450 if (isCyclic)
451 {
452 // create new cyclic path starting at current node
453 std::vector<WaypointNode> cyclicPath = path->Nodes;
454 std::rotate(cyclicPath.begin(), cyclicPath.begin() + currentNode, cyclicPath.end());
455 fillPath(cyclicPath.begin(), cyclicPath.end());
456 return;
457 }
458
459 if (!isReturningToStart)
460 fillPath(segment.begin(), segment.end());
461 else
462 fillPath(segment.rbegin(), segment.rend());
463}
464}
465
466template <typename T>
467void WaypointMovementGenerator<T>::StartMove(T* owner, bool relaunch/* = false*/)
468{
469 // sanity checks
470 if (!owner->IsAlive() || this->HasFlag(MOVEMENTGENERATOR_FLAG_FINALIZED)
471 || (relaunch && (this->HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED) || !this->HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED))))
472 return;
473
474 WaypointPath const* path = GetPath();
475 if (!path || path->Nodes.empty())
476 return;
477
478 if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) // if cannot move
479 {
480 _nextMoveTime.Reset(1000); // delay 1s
481 return;
482 }
483
484 if constexpr (std::is_base_of_v<Creature, T>)
485 {
486 if (owner->IsFormationLeader() && !owner->IsFormationLeaderMoveAllowed()) // if cannot move because of formation
487 {
488 _nextMoveTime.Reset(1000); // delay 1s
489 return;
490 }
491 }
492
493 bool const transportPath = !owner->GetTransGUID().IsEmpty();
494
495 uint32 previousNode = _currentNode;
497 {
498 if (ComputeNextNode())
499 {
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);
501
502 // inform AI
503 if constexpr (std::is_base_of_v<Creature, T>)
504 if (CreatureAI* AI = owner->AI())
505 AI->WaypointStarted(path->Nodes[_currentNode].Id, path->Id);
506 }
507 else
508 {
510
511 if constexpr (std::is_base_of_v<Creature, T>)
512 {
513 owner->UpdateCurrentWaypointInfo(0, 0);
514
515 WaypointNode const& waypoint = path->Nodes[_currentNode];
516 float x = waypoint.X;
517 float y = waypoint.Y;
518 float z = waypoint.Z;
519 float o = owner->GetOrientation();
520
521 if (!transportPath)
522 owner->SetHomePosition(x, y, z, o);
523 else
524 {
525 if (TransportBase* trans = owner->GetTransport())
526 {
527 o -= trans->GetTransportOrientation();
528 owner->SetTransportHomePosition(x, y, z, o);
529 owner->SetHomePosition(trans->GetPositionWithOffset(owner->GetTransportHomePosition()));
530 }
531 // else if (vehicle) - this should never happen, vehicle offsets are const
532 }
533
534 // inform AI
535 if (CreatureAI* AI = owner->AI())
536 AI->WaypointPathEnded(waypoint.Id, path->Id);
537 }
538
539 this->SetScriptResult(MovementStopReason::Finished);
540 return;
541 }
542 }
543 else if (!this->HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED))
544 {
546
547 // inform AI
548 if constexpr (std::is_base_of_v<Creature, T>)
549 if (CreatureAI* AI = owner->AI())
550 AI->WaypointStarted(path->Nodes[_currentNode].Id, path->Id);
551 }
552
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);
554 WaypointNode const* lastWaypointForSegment = &path->Nodes[_currentNode];
555
556 bool isCyclic = IsCyclic();
558
559 if (IsExactSplinePath())
560 CreateMergedPath(owner, path, previousNode, _currentNode, _isReturningToStart, false, isCyclic,
561 &points, &_waypointTransitionSplinePoints, &lastWaypointForSegment);
562 else
563 CreateSingularPointPath(owner, path, _currentNode, _generatePath, &points, &_waypointTransitionSplinePoints);
564
565 _waypointTransitionSplinePointsIndex = 0;
566
568
569 owner->AddUnitState(UNIT_STATE_ROAMING_MOVE);
570
571 if (isCyclic)
572 {
573 bool isFirstCycle = relaunch || owner->movespline->Finalized() || !owner->movespline->isCyclic();
574 if (!isFirstCycle)
575 {
576 for (int32& point : _waypointTransitionSplinePoints)
577 --point;
578
579 // cyclic paths are using identical duration to first cycle with EnterCycle
580 _moveTimer.Reset(Milliseconds(owner->movespline->Duration()));
581 return;
582 }
583 }
584
585 Movement::MoveSplineInit init(owner);
586
588 if (transportPath)
590
591 init.MovebyPath(points);
592
593 if (lastWaypointForSegment->Orientation.has_value()
594 && (lastWaypointForSegment->Delay || (_isReturningToStart ? _currentNode == 0 : _currentNode == path->Nodes.size() - 1)))
595 init.SetFacing(*lastWaypointForSegment->Orientation);
596
597 if (_fadeObject && !_repeating)
598 {
599 std::size_t lastWaypointForPath = IsFollowingPathBackwardsFromEndToStart() ? 0 : path->Nodes.size() - 1;
600 if (IsExactSplinePath())
601 {
602 auto [lastSegmentFirstNode, segmentLength] = path->ContinuousSegments[IsFollowingPathBackwardsFromEndToStart() ? 0 : path->ContinuousSegments.size() - 1];
603 if (lastWaypointForPath >= lastSegmentFirstNode && lastWaypointForPath < lastSegmentFirstNode + segmentLength)
604 init.SetFadeObject(_fadeObject->Duration.value_or(1s));
605 }
606 else
607 {
608 if (lastWaypointForSegment->Id == path->Nodes[lastWaypointForPath].Id)
609 init.SetFadeObject(_fadeObject->Duration.value_or(1s));
610 }
611 }
612
613 switch (lastWaypointForSegment->MoveType.value_or(path->MoveType))
614 {
617 init.SetFly();
618 break;
621 init.SetFly();
622 break;
624 init.SetWalk(false);
625 break;
627 init.SetWalk(true);
628 break;
629 default:
630 break;
631 }
632
633 switch (_speedSelectionMode) // overrides move type from each waypoint if set
634 {
636 break;
638 init.SetWalk(false);
639 break;
641 init.SetWalk(true);
642 break;
643 default:
644 break;
645 }
646
647 if (path->Velocity && !_speed)
648 _speed = path->Velocity;
649
650 if (_speed)
651 init.SetVelocity(*_speed);
652
653 if (isCyclic)
654 init.SetCyclic();
655
656 if (IsExactSplinePath() && (points.size() > 2 && owner->CanFly()))
657 init.SetSmooth();
658
659 Milliseconds duration(init.Launch());
660
661 if (!IsExactSplinePath()
662 && duration > 2 * SEND_NEXT_POINT_EARLY_DELTA
663 && !lastWaypointForSegment->Delay
664 && path->Nodes.size() > 2
665 // don't cut movement short at ends of path if its not a looping path or if it can be traversed backwards
666 && ((_currentNode != 0 && _currentNode != path->Nodes.size() - 1) || (!IsFollowingPathBackwardsFromEndToStart() && _repeating)))
667 duration -= SEND_NEXT_POINT_EARLY_DELTA;
668
669 _moveTimer.Reset(duration);
670
671 // inform formation
672 if constexpr (std::is_base_of_v<Creature, T>)
673 owner->SignalFormationMovement();
674}
675
676template <typename T>
678{
679 WaypointPath const* path = GetPath();
680 if ((_currentNode == path->Nodes.size() - 1) && !_repeating)
681 return false;
682
683 if (!IsFollowingPathBackwardsFromEndToStart() || path->Nodes.size() < WAYPOINT_PATH_FLAG_FOLLOW_PATH_BACKWARDS_MINIMUM_NODES)
684 _currentNode = (_currentNode + 1) % path->Nodes.size();
685 else
686 {
687 if (!_isReturningToStart)
688 {
689 if (++_currentNode >= path->Nodes.size())
690 {
692 _isReturningToStart = true;
693 }
694 }
695 else
696 {
697 if (_currentNode-- == 0)
698 {
699 _currentNode = 1;
700 _isReturningToStart = false;
701 }
702 }
703 }
704
705 return true;
706}
707
708template <typename T>
710{
711 if (_followPathBackwardsFromEndToStart)
712 return *_followPathBackwardsFromEndToStart;
713
714 return GetPath()->Flags.HasFlag(WaypointPathFlags::FollowPathBackwardsFromEndToStart);
715}
716
717template <typename T>
719{
720 if (_exactSplinePath)
721 return *_exactSplinePath;
722
723 return GetPath()->Flags.HasFlag(WaypointPathFlags::ExactSplinePath);
724}
725
726template <typename T>
728{
729 return !IsFollowingPathBackwardsFromEndToStart()
730 && IsExactSplinePath()
731 && _repeating
732 && GetPath()->ContinuousSegments.size() == 1;
733}
734
735template <typename T>
742
747
int32_t int32
Definition Define.h:150
uint32_t uint32
Definition Define.h:154
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition Duration.h:24
#define ASSERT
Definition Errors.h:80
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
@ MOTION_MODE_DEFAULT
@ MOTION_PRIORITY_NORMAL
@ MOTION_SLOT_ACTIVE
MovementWalkRunSpeedSelectionMode
MovementGeneratorType
@ WAYPOINT_MOTION_TYPE
@ 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.
Definition Optional.h:25
@ PATHFIND_NOPATH
Milliseconds randtime(Milliseconds min, Milliseconds max)
Definition Random.cpp:62
std::vector< Node const * > Nodes
Definition TraitMgr.cpp:58
@ UNIT_STATE_NOT_MOVE
Definition Unit.h:306
@ UNIT_STATE_ROAMING_MOVE
Definition Unit.h:284
@ UNIT_STATE_LOST_CONTROL
Definition Unit.h:302
@ UNIT_STATE_ROAMING
Definition Unit.h:265
static constexpr std::size_t WAYPOINT_PATH_FLAG_FOLLOW_PATH_BACKWARDS_MINIMUM_NODES
@ FollowPathBackwardsFromEndToStart
#define sWaypointMgr
static float waypoint[6][3]
Definition boss_alar.cpp:54
uint32 GetWaypointPathId() const
Definition Creature.h:396
CreatureAI * AI() const
Definition Creature.h:228
Scripting::v2::ActionResultSetter< MovementStopReason > ScriptResult
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)
Creature * ToCreature()
Definition Object.h:121
virtual std::string GetDebugInfo() const
Definition Unit.h:635
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)
bool DoUpdate(T *owner, uint32 diff)
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
std::vector< Vector3 > PointsArray
std::string StringFormat(FormatString< Args... > fmt, Args &&... args) noexcept
Default TC string format function.
STL namespace.
constexpr float GetPositionX() const
Definition Position.h:87
constexpr float GetPositionY() const
Definition Position.h:88
constexpr void GetPosition(float &x, float &y) const
Definition Position.h:92
constexpr void Relocate(float x, float y)
Definition Position.h:74
constexpr float GetPositionZ() const
Definition Position.h:89
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