TrinityCore
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 "Transport.h"
28#include "WaypointManager.h"
29#include <sstream>
30
32 MovementWalkRunSpeedSelectionMode speedSelectionMode, Optional<std::pair<Milliseconds, Milliseconds>> waitTimeRangeAtPathEnd,
33 Optional<float> wanderDistanceAtPathEnds, Optional<bool> followPathBackwardsFromEndToStart, bool generatePath)
34 : _nextMoveTime(0), _pathId(pathId), _repeating(repeating), _loadedFromDB(true),
35 _speed(speed), _speedSelectionMode(speedSelectionMode), _waitTimeRangeAtPathEnd(std::move(waitTimeRangeAtPathEnd)),
36 _wanderDistanceAtPathEnds(wanderDistanceAtPathEnds), _followPathBackwardsFromEndToStart(followPathBackwardsFromEndToStart), _isReturningToStart(false),
37 _generatePath(generatePath)
38{
40 Priority = MOTION_PRIORITY_NORMAL;
42 BaseUnitState = UNIT_STATE_ROAMING;
43 if (duration)
44 _duration.emplace(*duration);
45}
46
48 MovementWalkRunSpeedSelectionMode speedSelectionMode, Optional<std::pair<Milliseconds, Milliseconds>> waitTimeRangeAtPathEnd,
49 Optional<float> wanderDistanceAtPathEnds, Optional<bool> followPathBackwardsFromEndToStart, bool generatePath)
50 : _nextMoveTime(0), _pathId(0), _repeating(repeating), _loadedFromDB(false),
51 _speed(speed), _speedSelectionMode(speedSelectionMode), _waitTimeRangeAtPathEnd(std::move(waitTimeRangeAtPathEnd)),
52 _wanderDistanceAtPathEnds(wanderDistanceAtPathEnds), _followPathBackwardsFromEndToStart(followPathBackwardsFromEndToStart), _isReturningToStart(false),
53 _generatePath(generatePath)
54{
55 _path = std::make_unique<WaypointPath>(path);
56
58 Priority = MOTION_PRIORITY_NORMAL;
60 BaseUnitState = UNIT_STATE_ROAMING;
61 if (duration)
62 _duration.emplace(*duration);
63}
64
66
68{
70}
71
73{
74 if (timer)
75 {
76 // Don't try to paused an already paused generator
78 return;
79
81 _nextMoveTime.Reset(timer);
83 }
84 else
85 {
87 _nextMoveTime.Reset(1); // Needed so that Update does not behave as if node was reached
89 }
90}
91
93{
94 if (overrideTimer)
95 _nextMoveTime.Reset(overrideTimer);
96
97 if (_nextMoveTime.Passed())
98 _nextMoveTime.Reset(1); // Needed so that Update does not behave as if node was reached
99
101}
102
103bool WaypointMovementGenerator<Creature>::GetResetPosition(Unit* /*owner*/, float& x, float& y, float& z)
104{
105 // prevent a crash at empty waypoint path.
106 if (!GetPath() || GetPath()->Nodes.empty())
107 return false;
108
109 ASSERT(_currentNode < GetPath()->Nodes.size(), "WaypointMovementGenerator::GetResetPosition: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, GetPath()->Id);
110 WaypointNode const& waypoint = GetPath()->Nodes[_currentNode];
111
112 x = waypoint.X;
113 y = waypoint.Y;
114 z = waypoint.Z;
115 return true;
116}
117
119{
121
122 if (_loadedFromDB)
123 {
124 if (!_pathId)
125 _pathId = owner->GetWaypointPathId();
126
127 _path = sWaypointMgr->GetPath(_pathId);
128 }
129
130 if (!GetPath())
131 {
132 TC_LOG_ERROR("sql.sql", "WaypointMovementGenerator::DoInitialize: couldn't load path for creature ({}) (_pathId: {})", owner->GetGUID().ToString(), _pathId);
133 return;
134 }
135
136 if (GetPath()->Nodes.size() == 1)
137 _repeating = false;
138
139 owner->StopMoving();
140
141 _nextMoveTime.Reset(1000);
142}
143
145{
147
148 owner->StopMoving();
149
150 if (!HasFlag(MOVEMENTGENERATOR_FLAG_FINALIZED) && _nextMoveTime.Passed())
151 _nextMoveTime.Reset(1); // Needed so that Update does not behave as if node was reached
152}
153
155{
156 if (!owner || !owner->IsAlive())
157 return true;
158
159 if (HasFlag(MOVEMENTGENERATOR_FLAG_FINALIZED | MOVEMENTGENERATOR_FLAG_PAUSED) || !GetPath() || GetPath()->Nodes.empty())
160 return true;
161
162 if (_duration)
163 {
164 _duration->Update(diff);
165 if (_duration->Passed())
166 {
169 return false;
170 }
171 }
172
174 {
176 owner->StopMoving();
177 return true;
178 }
179
181 {
182 /*
183 * relaunch only if
184 * - has a tiner? -> was it interrupted while not waiting aka moving? need to check both:
185 * -> has a timer - is it because its waiting to start next node?
186 * -> has a timer - is it because something set it while moving (like timed pause)?
187 *
188 * - doesnt have a timer? -> is movement valid?
189 *
190 * TODO: ((_nextMoveTime.Passed() && VALID_MOVEMENT) || (!_nextMoveTime.Passed() && !HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED)))
191 */
192 if (HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED) && (_nextMoveTime.Passed() || !HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED)))
193 {
194 StartMove(owner, true);
195 return true;
196 }
197
199 }
200
201 // if it's moving
202 if (!owner->movespline->Finalized())
203 {
204 // set home position at place (every MotionMaster::UpdateMotion)
205 if (owner->GetTransGUID().IsEmpty())
206 owner->SetHomePosition(owner->GetPosition());
207
208 // relaunch movement if its speed has changed
210 StartMove(owner, true);
211 }
212 else if (!_nextMoveTime.Passed()) // it's not moving, is there a timer?
213 {
214 if (UpdateTimer(diff))
215 {
216 if (!HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED)) // initial movement call
217 {
218 StartMove(owner);
219 return true;
220 }
221 else if (!HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED)) // timer set before node was reached, resume now
222 {
223 StartMove(owner, true);
224 return true;
225 }
226 }
227 else
228 return true; // keep waiting
229 }
230 else // not moving, no timer
231 {
233 {
234 OnArrived(owner); // hooks and wait timer reset (if necessary)
235 AddFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED); // signals to future StartMove that it reached a node
236 }
237
238 if (_nextMoveTime.Passed()) // OnArrived might have set a timer
239 StartMove(owner); // check path status, get next point and move if necessary & can
240 }
241
242 return true;
243}
244
246{
249}
250
251void WaypointMovementGenerator<Creature>::DoFinalize(Creature* owner, bool active, bool/* movementInform*/)
252{
254 if (active)
255 {
257
258 // TODO: Research if this modification is needed, which most likely isnt
259 owner->SetWalk(false);
260 }
261}
262
264{
265 WaypointNode const& waypoint = GetPath()->Nodes[_currentNode];
266 if (CreatureAI* AI = owner->AI())
267 {
268 AI->MovementInform(WAYPOINT_MOTION_TYPE, waypoint.Id);
269 AI->WaypointReached(waypoint.Id, GetPath()->Id);
270 }
271}
272
274{
275 if (!GetPath() || GetPath()->Nodes.empty())
276 return;
277
278 ASSERT(_currentNode < GetPath()->Nodes.size(), "WaypointMovementGenerator::OnArrived: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, GetPath()->Id);
279 WaypointNode const& waypoint = GetPath()->Nodes[_currentNode];
280 if (waypoint.Delay)
281 {
283 _nextMoveTime.Reset(waypoint.Delay);
284 }
285
286 if (_waitTimeRangeAtPathEnd && IsFollowingPathBackwardsFromEndToStart()
287 && ((_isReturningToStart && _currentNode == 0) || (!_isReturningToStart && _currentNode == GetPath()->Nodes.size() - 1)))
288 {
290 Milliseconds waitTime = randtime(_waitTimeRangeAtPathEnd->first, _waitTimeRangeAtPathEnd->second);
291 if (_duration)
292 _duration->Update(waitTime); // count the random movement time as part of waypoing movement action
293
294 if (_wanderDistanceAtPathEnds)
295 owner->GetMotionMaster()->MoveRandom(*_wanderDistanceAtPathEnds, waitTime, MOTION_SLOT_ACTIVE);
296 else
297 _nextMoveTime.Reset(waitTime);
298 }
299
300 MovementInform(owner);
301
302 owner->UpdateCurrentWaypointInfo(waypoint.Id, GetPath()->Id);
303}
304
305void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaunch/* = false*/)
306{
307 // sanity checks
308 if (!owner || !owner->IsAlive() || HasFlag(MOVEMENTGENERATOR_FLAG_FINALIZED) || !GetPath() || GetPath()->Nodes.empty() || (relaunch && (HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED) || !HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED))))
309 return;
310
311 if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting() || (owner->IsFormationLeader() && !owner->IsFormationLeaderMoveAllowed())) // if cannot move OR cannot move because of formation
312 {
313 _nextMoveTime.Reset(1000); // delay 1s
314 return;
315 }
316
317 bool const transportPath = !owner->GetTransGUID().IsEmpty();
318
320 {
321 if (ComputeNextNode())
322 {
323 ASSERT(_currentNode < GetPath()->Nodes.size(), "WaypointMovementGenerator::StartMove: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, GetPath()->Id);
324
325 // inform AI
326 if (CreatureAI* AI = owner->AI())
327 AI->WaypointStarted(GetPath()->Nodes[_currentNode].Id, GetPath()->Id);
328 }
329 else
330 {
331 WaypointNode const& waypoint = GetPath()->Nodes[_currentNode];
332 float x = waypoint.X;
333 float y = waypoint.Y;
334 float z = waypoint.Z;
335 float o = owner->GetOrientation();
336
337 if (!transportPath)
338 owner->SetHomePosition(x, y, z, o);
339 else
340 {
341 if (TransportBase* trans = owner->GetTransport())
342 {
343 o -= trans->GetTransportOrientation();
344 owner->SetTransportHomePosition(x, y, z, o);
345 trans->CalculatePassengerPosition(x, y, z, &o);
346 owner->SetHomePosition(x, y, z, o);
347 }
348 // else if (vehicle) - this should never happen, vehicle offsets are const
349 }
351 owner->UpdateCurrentWaypointInfo(0, 0);
352
353 // inform AI
354 if (CreatureAI* AI = owner->AI())
355 AI->WaypointPathEnded(waypoint.Id, GetPath()->Id);
356 return;
357 }
358 }
359 else if (!HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED))
360 {
362
363 // inform AI
364 if (CreatureAI* AI = owner->AI())
365 AI->WaypointStarted(GetPath()->Nodes[_currentNode].Id, GetPath()->Id);
366 }
367
368 ASSERT(_currentNode < GetPath()->Nodes.size(), "WaypointMovementGenerator::StartMove: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, GetPath()->Id);
369 WaypointNode const &waypoint = GetPath()->Nodes[_currentNode];
370
372
374
375 Movement::MoveSplineInit init(owner);
376
378 if (transportPath)
380
383 init.MoveTo(waypoint.X, waypoint.Y, waypoint.Z, _generatePath);
384
385 if (waypoint.Orientation.has_value() && (waypoint.Delay > 0 || _currentNode == GetPath()->Nodes.size() - 1))
386 init.SetFacing(*waypoint.Orientation);
387
388 switch (GetPath()->MoveType)
389 {
392 break;
395 break;
397 init.SetWalk(false);
398 break;
400 init.SetWalk(true);
401 break;
402 default:
403 break;
404 }
405
406 switch (_speedSelectionMode) // overrides move type from each waypoint if set
407 {
409 break;
411 init.SetWalk(false);
412 break;
414 init.SetWalk(true);
415 break;
416 default:
417 break;
418 }
419
420 if (_speed)
421 init.SetVelocity(*_speed);
422
423 init.Launch();
424
425 // inform formation
427}
428
430{
431 if ((_currentNode == GetPath()->Nodes.size() - 1) && !_repeating)
432 return false;
433
434 if (!IsFollowingPathBackwardsFromEndToStart() || GetPath()->Nodes.size() < WAYPOINT_PATH_FLAG_FOLLOW_PATH_BACKWARDS_MINIMUM_NODES)
435 _currentNode = (_currentNode + 1) % GetPath()->Nodes.size();
436 else
437 {
438 if (!_isReturningToStart)
439 {
440 if (++_currentNode >= GetPath()->Nodes.size())
441 {
443 _isReturningToStart = true;
444 }
445 }
446 else
447 {
448 if (_currentNode-- == 0)
449 {
450 _currentNode = 1;
451 _isReturningToStart = false;
452 }
453 }
454 }
455
456 return true;
457}
458
460{
461 if (_followPathBackwardsFromEndToStart)
462 return *_followPathBackwardsFromEndToStart;
463
464 return GetPath()->Flags.HasFlag(WaypointPathFlags::FollowPathBackwardsFromEndToStart);
465}
466
468{
469 std::stringstream sstr;
470 sstr << PathMovementBase::GetDebugInfo() << "\n"
472 return sstr.str();
473}
uint32_t uint32
Definition: Define.h:142
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition: Duration.h:29
std::string GetDebugInfo()
Definition: Errors.cpp:157
#define ASSERT
Definition: Errors.h:68
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
@ MOTION_MODE_DEFAULT
@ MOTION_PRIORITY_NORMAL
@ MOTION_SLOT_ACTIVE
MovementGeneratorType
@ WAYPOINT_MOTION_TYPE
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.
Definition: Optional.h:25
Milliseconds randtime(Milliseconds min, Milliseconds max)
Definition: Random.cpp:62
std::vector< Node const * > Nodes
Definition: TraitMgr.cpp:56
@ UNIT_STATE_NOT_MOVE
Definition: Unit.h:300
@ UNIT_STATE_ROAMING_MOVE
Definition: Unit.h:278
@ UNIT_STATE_LOST_CONTROL
Definition: Unit.h:296
@ UNIT_STATE_ROAMING
Definition: Unit.h:259
#define WAYPOINT_PATH_FLAG_FOLLOW_PATH_BACKWARDS_MINIMUM_NODES
@ FollowPathBackwardsFromEndToStart
#define sWaypointMgr
static float waypoint[6][3]
Definition: boss_alar.cpp:54
void SetHomePosition(float x, float y, float z, float o)
Definition: Creature.h:371
void SignalFormationMovement()
Definition: Creature.cpp:396
bool IsFormationLeaderMoveAllowed() const
Definition: Creature.cpp:407
void UpdateCurrentWaypointInfo(uint32 nodeId, uint32 pathId)
Definition: Creature.h:386
uint32 GetWaypointPathId() const
Definition: Creature.h:381
bool IsFormationLeader() const
Definition: Creature.cpp:388
void SetTransportHomePosition(float x, float y, float z, float o)
Definition: Creature.h:376
CreatureAI * AI() const
Definition: Creature.h:214
bool IsMovementPreventedByCasting() const override
Definition: Creature.cpp:3551
void MoveRandom(float wanderDistance=0.0f, Optional< Milliseconds > duration={}, MovementSlot slot=MOTION_SLOT_DEFAULT)
virtual std::string GetDebugInfo() const
void MoveTo(Vector3 const &destination, bool generatePath=true, bool forceDestination=false)
void DisableTransportPathTransformations()
void SetWalk(bool enable)
void SetVelocity(float velocity)
void SetFacing(float angle)
void SetAnimation(AnimTier anim, uint32 tierTransitionId=0, Milliseconds transitionStartTime=0ms)
bool IsEmpty() const
Definition: ObjectGuid.h:319
std::string ToString() const
Definition: ObjectGuid.cpp:554
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
virtual std::string GetDebugInfo() const
Definition: Unit.h:627
void ClearUnitState(uint32 f)
Definition: Unit.h:733
MotionMaster * GetMotionMaster()
Definition: Unit.h:1652
bool IsAlive() const
Definition: Unit.h:1164
void StopMoving()
Definition: Unit.cpp:10049
void AddUnitState(uint32 f)
Definition: Unit.h:731
bool SetWalk(bool enable)
Definition: Unit.cpp:12707
ObjectGuid GetTransGUID() const override
Definition: Unit.cpp:11510
bool HasUnitState(const uint32 f) const
Definition: Unit.h:732
std::unique_ptr< Movement::MoveSpline > movespline
Definition: Unit.h:1766
TransportBase * GetTransport() const
Definition: Object.h:750
STL namespace.
constexpr void GetPosition(float &x, float &y) const
Definition: Position.h:81
constexpr float GetOrientation() const
Definition: Position.h:79