TrinityCore
Loading...
Searching...
No Matches
MotionMaster.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
18#include "MotionMaster.h"
19#include "Creature.h"
20#include "CreatureAISelector.h"
21#include "DB2Stores.h"
22#include "Errors.h"
23#include "G3DPosition.hpp"
24#include "Log.h"
25#include "MapUtils.h"
26#include "Memory.h"
27#include "MoveSpline.h"
28#include "ObjectAccessor.h"
29#include "PathGenerator.h"
30#include "Player.h"
31#include "ScriptSystem.h"
32#include "Types.h"
33#include <boost/container/static_vector.hpp>
34#include <algorithm>
35#include <iterator>
36
50
52{
53 return sMovementGeneratorRegistry->GetRegistryItem(IDLE_MOTION_TYPE)->Create();
54}
55
56inline bool IsStatic(MovementGenerator* movement)
57{
58 return (movement == GetIdleMovementGenerator());
59}
60
62{
63 if (a != nullptr && !IsStatic(a))
64 delete a;
65}
66
71
73{
74 return a->Priority > b->Priority;
75}
76
77MovementGeneratorInformation::MovementGeneratorInformation(MovementGeneratorType type, ObjectGuid targetGUID, std::string const& targetName) : Type(type), TargetGUID(targetGUID), TargetName(targetName) { }
78
79MotionMaster::MotionMaster(Unit* unit) : _owner(unit), _defaultGenerator(nullptr), _flags(MOTIONMASTER_FLAG_INITIALIZATION_PENDING) { }
80
88
90{
92 return;
93
95 {
96 DelayedActionDefine action = [this]()
97 {
98 Initialize();
99 };
100 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_INITIALIZE);
101 return;
102 }
103
105}
106
111
125
127{
128 return !_defaultGenerator && _generators.empty();
129}
130
132{
133 return (_defaultGenerator ? 1 : 0) + uint32(_generators.size());
134}
135
136std::vector<MovementGeneratorInformation> MotionMaster::GetMovementGeneratorsInformation() const
137{
138 std::vector<MovementGeneratorInformation> list;
139
141 list.emplace_back(_defaultGenerator->GetMovementGeneratorType(), ObjectGuid::Empty, std::string());
142
143 for (auto itr = _generators.begin(); itr != _generators.end(); ++itr)
144 {
145 MovementGenerator* movement = *itr;
146 MovementGeneratorType const type = movement->GetMovementGeneratorType();
147 switch (type)
148 {
151 if (AbstractFollower* followInformation = dynamic_cast<AbstractFollower*>(movement))
152 {
153 if (Unit* target = followInformation->GetTarget())
154 list.emplace_back(type, target->GetGUID(), target->GetName());
155 else
156 list.emplace_back(type, ObjectGuid::Empty, std::string());
157 }
158 else
159 list.emplace_back(type, ObjectGuid::Empty, std::string());
160 break;
161 default:
162 list.emplace_back(type, ObjectGuid::Empty, std::string());
163 break;
164 }
165 }
166
167 return list;
168}
169
171{
172 if (!_generators.empty())
173 return MOTION_SLOT_ACTIVE;
174
176 return MOTION_SLOT_DEFAULT;
177
178 return MAX_MOTION_SLOT;
179}
180
182{
183 if (!_generators.empty())
184 return *_generators.begin();
185
187 return _defaultGenerator.get();
188
189 return nullptr;
190}
191
193{
194 if (Empty())
195 return MAX_MOTION_TYPE;
196
198 if (!movement)
199 return MAX_MOTION_TYPE;
200
201 return movement->GetMovementGeneratorType();
202}
203
205{
206 if (Empty() || IsInvalidMovementSlot(slot))
207 return MAX_MOTION_TYPE;
208
209 if (slot == MOTION_SLOT_ACTIVE && !_generators.empty())
210 return (*_generators.begin())->GetMovementGeneratorType();
211
213 return _defaultGenerator->GetMovementGeneratorType();
214
215 return MAX_MOTION_TYPE;
216}
217
219{
220 if (Empty() || IsInvalidMovementSlot(slot))
221 return nullptr;
222
223 if (slot == MOTION_SLOT_ACTIVE && !_generators.empty())
224 return *_generators.begin();
225
227 return _defaultGenerator.get();
228
229 return nullptr;
230}
231
232MovementGenerator* MotionMaster::GetMovementGenerator(std::function<bool(MovementGenerator const*)> const& filter, MovementSlot slot) const
233{
234 if (Empty() || IsInvalidMovementSlot(slot))
235 return nullptr;
236
237 MovementGenerator* movement = nullptr;
238 switch (slot)
239 {
241 if (_defaultGenerator && filter(_defaultGenerator.get()))
242 movement = _defaultGenerator.get();
243 break;
245 {
246 auto itr = std::ranges::find_if(_generators, std::ref(filter));
247 if (itr != _generators.end())
248 movement = *itr;
249 break;
250 }
251 default:
252 break;
253 }
254
255 return movement;
256}
257
258bool MotionMaster::HasMovementGenerator(std::function<bool(MovementGenerator const*)> const& filter, MovementSlot slot) const
259{
260 if (Empty() || IsInvalidMovementSlot(slot))
261 return false;
262
263 bool value = false;
264 switch (slot)
265 {
267 value = _defaultGenerator && filter(_defaultGenerator.get());
268 break;
270 value = std::ranges::any_of(_generators, std::ref(filter));
271 break;
272 default:
273 break;
274 }
275
276 return value;
277}
278
280{
281 if (!_owner)
282 return;
283
285 return;
286
287 ASSERT(!Empty(), "MotionMaster:Update: update called without Initializing! (%s)", _owner->GetGUID().ToString().c_str());
288
290
291 enum class InitState : uint8
292 {
293 Failed,
294 Success,
295 AlreadyInitialized
296 } initializationState = InitState::AlreadyInitialized;
297
300 {
302 initializationState = top->Initialize(_owner) ? InitState::Success : InitState::Failed;
303 }
305 initializationState = top->Initialize(_owner) ? InitState::Success : InitState::Failed;
307 initializationState = top->Reset(_owner) ? InitState::Success : InitState::Failed;
308
309 ASSERT(!top->HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZATION_PENDING | MOVEMENTGENERATOR_FLAG_DEACTIVATED), "MotionMaster:Update: update called on an uninitialized top! (%s) (type: %u, flags: %u)", _owner->GetGUID().ToString().c_str(), top->GetMovementGeneratorType(), top->Flags);
310
311 if (initializationState == InitState::Failed || !top->Update(_owner, initializationState == InitState::AlreadyInitialized ? diff : 0))
312 {
313 ASSERT(top == GetCurrentMovementGenerator(), "MotionMaster::Update: top was modified while updating! (%s)", _owner->GetGUID().ToString().c_str());
314
315 // Since all the actions that modify any slot are delayed, this movement is guaranteed to be top
316 Pop(true, true); // Natural, and only, call to MovementInform
317 }
318
320
322}
323
324void MotionMaster::Add(MovementGenerator* movement, MovementSlot slot/* = MOTION_SLOT_ACTIVE*/)
325{
326 if (!movement)
327 return;
328
329 if (IsInvalidMovementSlot(slot))
330 {
331 delete movement;
332 return;
333 }
334
336 {
337 bool wouldBecomeTop = _generators.lower_bound(movement) == _generators.begin();
338 if (!wouldBecomeTop || !movement->Initialize(_owner))
339 {
340 delete movement;
341 return;
342 }
343 }
344
346 {
347 DelayedActionDefine action = [this, movement, slot]()
348 {
349 Add(movement, slot);
350 };
351 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_ADD);
352 }
353 else
354 DirectAdd(movement, slot);
355}
356
357void MotionMaster::Remove(MovementGenerator* movement, MovementSlot slot/* = MOTION_SLOT_ACTIVE*/)
358{
359 if (!movement || IsInvalidMovementSlot(slot))
360 return;
361
363 {
364 DelayedActionDefine action = [this, movement, slot]()
365 {
366 Remove(movement, slot);
367 };
368 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_REMOVE);
369 return;
370 }
371
372 if (Empty())
373 return;
374
375 switch (slot)
376 {
378 if (_defaultGenerator && _defaultGenerator.get() == movement)
380 break;
382 {
383 auto itr = _generators.find(movement);
384 if (itr != _generators.end())
385 Remove(itr, GetCurrentMovementGenerator() == *itr, false);
386 break;
387 }
388 default:
389 break;
390 }
391}
392
393void MotionMaster::Remove(MovementGeneratorType type, MovementSlot slot/* = MOTION_SLOT_ACTIVE*/)
394{
396 return;
397
399 {
400 DelayedActionDefine action = [this, type, slot]()
401 {
402 Remove(type, slot);
403 };
404 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_REMOVE_TYPE);
405 return;
406 }
407
408 if (Empty())
409 return;
410
411 switch (slot)
412 {
414 if (_defaultGenerator && _defaultGenerator->GetMovementGeneratorType() == type)
416 break;
418 DirectClear([type](MovementGenerator const* a) { return a->GetMovementGeneratorType() == type; });
419 break;
420 default:
421 break;
422 }
423}
424
426{
428 {
429 DelayedActionDefine action = [this]()
430 {
431 Clear();
432 };
433 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_CLEAR);
434 return;
435 }
436
437 if (!Empty())
438 DirectClear();
439}
440
442{
443 if (IsInvalidMovementSlot(slot))
444 return;
445
447 {
448 DelayedActionDefine action = [this, slot]()
449 {
450 Clear(slot);
451 };
452 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_CLEAR_SLOT);
453 return;
454 }
455
456 if (Empty())
457 return;
458
459 switch (slot)
460 {
463 break;
465 DirectClear();
466 break;
467 default:
468 break;
469 }
470}
471
473{
475 {
476 DelayedActionDefine action = [this, priority]()
477 {
478 Clear(priority);
479 };
480 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_CLEAR_PRIORITY);
481 return;
482 }
483
484 if (Empty())
485 return;
486
487 DirectClear([priority](MovementGenerator const* a) { return a->Priority == priority; });
488}
489
491{
492 if (Empty())
493 return;
494
496 if (!movement)
497 return;
498
499 movement->UnitSpeedChanged();
500}
501
502bool MotionMaster::GetDestination(float &x, float &y, float &z)
503{
504 if (_owner->movespline->Finalized())
505 return false;
506
507 G3D::Vector3 const& dest = _owner->movespline->FinalDestination();
508 x = dest.x;
509 y = dest.y;
510 z = dest.z;
511 return true;
512}
513
515{
516 if (MovementGenerator* movementGenerator = GetCurrentMovementGenerator())
517 if (movementGenerator->HasFlag(MOVEMENTGENERATOR_FLAG_PERSIST_ON_DEATH))
518 return false;
519
520 if (_owner->IsInWorld())
521 {
522 // Only clear MotionMaster for entities that exists in world
523 // Avoids crashes in the following conditions :
524 // * Using 'call pet' on dead pets
525 // * Using 'call stabled pet'
526 // * Logging in with dead pets
527 Clear();
528 MoveIdle();
529 }
530
532
533 return true;
534}
535
537{
540 top->Deactivate(_owner); // only deactivate top, don't remove it. non-resumable generators will clean up themselves on next update
541}
542
547
549{
550 Creature* owner = _owner->ToCreature();
551 if (!owner)
552 {
553 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::MoveTargetedHome: '{}', attempted to move towards target home.", _owner->GetGUID());
554 return;
555 }
556
557 Clear();
558
559 Unit* target = owner->GetCharmerOrOwner();
560 if (!target)
561 {
562 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveTargetedHome: '{}', targeted home.", _owner->GetGUID());
564 }
565 else
566 {
567 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveTargetedHome: '{}', starts following '{}'", _owner->GetGUID(), target->GetGUID());
569 }
570}
571
572void MotionMaster::MoveRandom(float wanderDistance /*= 0.0f*/, Optional<Milliseconds> duration /*= {}*/, Optional<float> speed /*= {}*/,
573 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::ForceWalk*/, MovementSlot slot /*= MOTION_SLOT_DEFAULT*/,
575{
576 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveRandom: '{}', started random movement (spawnDist: {})", _owner->GetGUID(), wanderDistance);
577 if (_owner->GetTypeId() == TYPEID_UNIT)
578 Add(new RandomMovementGenerator<Creature>(wanderDistance, duration, speed, speedSelectionMode, std::move(scriptResult)), slot);
579 else
580 Add(new RandomMovementGenerator<Player>(wanderDistance, duration, speed, speedSelectionMode, std::move(scriptResult)));
581}
582
583void MotionMaster::MoveFollow(Unit* target, float dist, Optional<ChaseAngle> angle /*= {}*/, Optional<Milliseconds> duration /*= {}*/, bool ignoreTargetWalk /*= false*/, MovementSlot slot/* = MOTION_SLOT_ACTIVE*/,
585{
586 // Ignore movement request if target not exist
587 if (!target || target == _owner)
588 {
589 if (scriptResult)
590 scriptResult.SetResult(MovementStopReason::Interrupted);
591 return;
592 }
593
594 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveFollow: '{}', starts following '{}'", _owner->GetGUID(), target->GetGUID());
595 Add(new FollowMovementGenerator(target, dist, angle, duration, ignoreTargetWalk, std::move(scriptResult)), slot);
596}
597
599{
600 // Ignore movement request if target not exist
601 if (!target || target == _owner)
602 return;
603
604 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveChase: '{}', starts chasing '{}'", _owner->GetGUID(), target->GetGUID());
605 Add(new ChaseMovementGenerator(target, dist, angle));
606}
607
609{
611 {
612 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveConfused: '{}', started confused movement.", _owner->GetGUID());
614 }
615 else
616 {
617 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveConfused: '{}', started confused movement.", _owner->GetGUID());
619 }
620}
621
622void MotionMaster::MoveFleeing(Unit* enemy, Milliseconds time /*= 0ms*/,
624{
625 if (!enemy)
626 {
627 if (scriptResult)
628 scriptResult.SetResult(MovementStopReason::Interrupted);
629 return;
630 }
631
632 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveFleeing: '{}', flees from '{}' (time: {}ms)", _owner->GetGUID(), enemy->GetGUID(), time.count());
633 if (_owner->GetTypeId() == TYPEID_UNIT && time > 0ms)
634 Add(new TimedFleeingMovementGenerator(enemy->GetGUID(), time, std::move(scriptResult)));
635 else
636 Add(new FleeingMovementGenerator(enemy->GetGUID(), std::move(scriptResult)));
637}
638
639void MotionMaster::MovePoint(uint32 id, Position const& pos, bool generatePath/* = true*/, Optional<float> finalOrient/* = {}*/, Optional<float> speed /*= {}*/,
640 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/, Optional<float> closeEnoughDistance /*= {}*/,
641 Optional<MovementFadeObject> fadeObject /*= {}*/,
643{
644 MovePoint(id, pos.m_positionX, pos.m_positionY, pos.m_positionZ, generatePath, finalOrient, speed, speedSelectionMode, closeEnoughDistance,
645 fadeObject, std::move(scriptResult));
646}
647
648void MotionMaster::MovePoint(uint32 id, float x, float y, float z, bool generatePath /*= true*/, Optional<float> finalOrient /*= {}*/, Optional<float> speed /*= {}*/,
649 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/, Optional<float> closeEnoughDistance /*= {}*/,
650 Optional<MovementFadeObject> fadeObject /*= {}*/,
652{
653 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MovePoint: '{}', targeted point Id: {} (X: {}, Y: {}, Z: {})", _owner->GetGUID(), id, x, y, z);
654 Add(new PointMovementGenerator(id, x, y, z, generatePath, speed, finalOrient, nullptr, nullptr, speedSelectionMode, closeEnoughDistance,
655 fadeObject, std::move(scriptResult)));
656}
657
658void MotionMaster::MoveCloserAndStop(uint32 id, Unit* target, float distance)
659{
660 float distanceToTravel = _owner->GetExactDist2d(target) - distance;
661 if (distanceToTravel > 0.0f)
662 {
663 float angle = _owner->GetAbsoluteAngle(target);
664 float destx = _owner->GetPositionX() + distanceToTravel * std::cos(angle);
665 float desty = _owner->GetPositionY() + distanceToTravel * std::sin(angle);
666 MovePoint(id, destx, desty, target->GetPositionZ());
667 }
668 else
669 {
670 // We are already close enough. We just need to turn toward the target without changing position.
671 std::function<void(Movement::MoveSplineInit&)> initializer = [=, this, target = target->GetGUID()](Movement::MoveSplineInit& init)
672 {
674 if (Unit const* refreshedTarget = ObjectAccessor::GetUnit(*_owner, target))
675 init.SetFacing(refreshedTarget);
676 };
677 Add(new GenericMovementGenerator(std::move(initializer), EFFECT_MOTION_TYPE, id));
678 }
679}
680
681void MotionMaster::MoveLand(uint32 id, Position const& pos, Optional<int32> tierTransitionId /*= {}*/, Optional<float> velocity /*= {}*/,
682 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/,
684{
685 MoveTierTransition(id, pos, AnimTier::Ground, tierTransitionId, velocity, speedSelectionMode, std::move(scriptResult));
686}
687
688void MotionMaster::MoveTakeoff(uint32 id, Position const& pos, Optional<int32> tierTransitionId /*= {}*/, Optional<float> velocity /*= {}*/,
689 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/,
691{
692 MoveTierTransition(id, pos, AnimTier::Fly, tierTransitionId, velocity, speedSelectionMode, std::move(scriptResult));
693}
694
695void MotionMaster::MoveTierTransition(uint32 id, Position const& pos, AnimTier newAnimTier, Optional<int32> tierTransitionId /*= {}*/, Optional<float> velocity /*= {}*/,
696 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/,
698{
699 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveTierTransition: '{}', anim tier transition to {} Id: {} (X: {}, Y: {}, Z: {})",
700 _owner->GetGUID(), newAnimTier, id, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
701
702 bool flyingTransition = newAnimTier == AnimTier::Fly || _owner->GetAnimTier() == AnimTier::Fly;
703 if (!tierTransitionId)
704 {
705 switch (newAnimTier)
706 {
707 case AnimTier::Ground:
708 tierTransitionId = 208;
709 break;
710 case AnimTier::Swim:
711 tierTransitionId = 149;
712 break;
713 case AnimTier::Hover:
714 tierTransitionId = 25;
715 break;
716 case AnimTier::Fly:
717 tierTransitionId = 17;
718 break;
719 default:
720 break;
721 }
722 }
723
724 std::function<void(Movement::MoveSplineInit&)> initializer = [=, tierTransitionId = tierTransitionId.value_or(0)](Movement::MoveSplineInit& init)
725 {
726 init.MoveTo(PositionToVector3(pos), false);
727 init.SetAnimation(newAnimTier, tierTransitionId);
728 if (flyingTransition)
729 init.SetFly(); // ensure smooth animation even if gravity is disabled after/enabled before calling this function
730 init.SetSmooth();
731 switch (speedSelectionMode)
732 {
734 init.SetWalk(false);
735 break;
737 init.SetWalk(true);
738 break;
740 default:
741 break;
742 }
743 if (velocity)
744 init.SetVelocity(*velocity);
745 };
746
747 Add(new GenericMovementGenerator(std::move(initializer), EFFECT_MOTION_TYPE, id, { .ScriptResult = std::move(scriptResult) }));
748}
749
750void MotionMaster::MoveCharge(float x, float y, float z, float speed /*= SPEED_CHARGE*/, uint32 id /*= EVENT_CHARGE*/, bool generatePath /*= false*/,
751 Unit const* target /*= nullptr*/, Movement::SpellEffectExtraData const* spellEffectExtraData /*= nullptr*/)
752{
753/*
754 if (_slot[MOTION_SLOT_CONTROLLED] && _slot[MOTION_SLOT_CONTROLLED]->GetMovementGeneratorType() != DISTRACT_MOTION_TYPE)
755 return;
756*/
757 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveCharge: '{}', charging point Id: {} (X: {}, Y: {}, Z: {})", _owner->GetGUID(), id, x, y, z);
758 PointMovementGenerator* movement = new PointMovementGenerator(id, x, y, z, generatePath, speed, {}, target, spellEffectExtraData);
761 Add(movement);
762}
763
764void MotionMaster::MoveCharge(PathGenerator const& path, float speed /*= SPEED_CHARGE*/, Unit const* target /*= nullptr*/,
765 Movement::SpellEffectExtraData const* spellEffectExtraData /*= nullptr*/)
766{
767 G3D::Vector3 dest = path.GetActualEndPosition();
768
769 MoveCharge(dest.x, dest.y, dest.z, speed, EVENT_CHARGE_PREPATH);
770
771 // If this is ever changed to not happen immediately then all spell effect handlers that use this must be updated
772
773 // Charge movement is not started when using EVENT_CHARGE_PREPATH
775 init.MovebyPath(path.GetPath());
776 init.SetVelocity(speed);
777 if (target)
778 init.SetFacing(target);
779 if (spellEffectExtraData)
780 init.SetSpellEffectExtraData(*spellEffectExtraData);
781 init.Launch();
782}
783
784void MotionMaster::MoveKnockbackFrom(Position const& origin, float speedXY, float speedZ, float angle /*= M_PI*/, Movement::SpellEffectExtraData const* spellEffectExtraData /*= nullptr*/)
785{
786 if (std::abs(speedXY) < 0.01f && std::abs(speedZ) < 0.01f)
787 return;
788
789 Position dest = _owner->GetPosition();
790 float o = (dest == origin ? 0.0f : _owner->GetRelativeAngle(origin)) + angle;
791 if (speedXY < 0)
792 {
793 speedXY = -speedXY;
794 o = o - float(M_PI);
795 }
796
797 if (speedZ < 0)
798 speedZ = -speedZ; // doesn't seem to be supported on official servers - packet sent for knockback with positive and negative speed has the same flags and JumpGravity
799
800 float moveTimeHalf = speedZ / Movement::gravity;
801 float dist = 2 * moveTimeHalf * speedXY;
802 float max_height = -Movement::computeFallElevation(moveTimeHalf, false, -speedZ);
803
804 boost::container::static_vector<G3D::Vector3, 3> path;
805 path.push_back(PositionToVector3(dest));
806
807 if (dist > 0.01f)
808 {
809 // Use a mmap raycast to get a valid destination.
810 _owner->MovePositionToFirstCollision(dest, dist, o);
811 path.push_back(PositionToVector3(dest));
812 }
813 else
814 {
815 // vertical knockbacks get a fake 0.5 higher additional point to avoid clientside spline length checks
816 // sniffs confirmed that it is always 0.5, no matter what the max height is
817 path.push_back(PositionToVector3(dest.GetPositionWithOffset({ 0.0f, 0.0f, 0.5f })));
818 path.push_back(PositionToVector3(dest));
819 }
820
821 std::function<void(Movement::MoveSplineInit&)> initializer = [=, effect = (spellEffectExtraData ? Optional<Movement::SpellEffectExtraData>(*spellEffectExtraData) : Optional<Movement::SpellEffectExtraData>())](Movement::MoveSplineInit& init)
822 {
823 init.MovebyPath({ path.data(), path.size() });
824 init.SetParabolic(max_height, 0);
825 init.SetOrientationFixed(true);
826 init.SetVelocity(speedXY);
827 if (effect)
828 init.SetSpellEffectExtraData(*effect);
829 };
830
831 GenericMovementGenerator* movement = new GenericMovementGenerator(std::move(initializer), EFFECT_MOTION_TYPE, 0);
834 Add(movement);
835}
836
837void MotionMaster::MoveJump(uint32 id, Position const& pos, std::variant<std::monostate, float, Milliseconds> speedOrTime /*= {}*/,
838 Optional<float> minHeight /*= {}*/, Optional<float> maxHeight /*= {}*/,
839 MovementFacingTarget const& facing /*= {}*/, bool orientationFixed, bool unlimitedSpeed /*= false*/, Optional<float> speedMultiplier /*= {}*/,
840 JumpArrivalCastArgs const* arrivalCast /*= nullptr*/, Movement::SpellEffectExtraData const* spellEffectExtraData /*= nullptr*/,
842{
843 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveJump: '{}', jumps to point Id: {} ({})", _owner->GetGUID(), id, pos);
844
845 float dist = _owner->GetExactDist(pos);
846
847 float speedXY = std::visit([owner = _owner, speedMultiplier = speedMultiplier.value_or(1.0f), dist]<typename T>(T speedOrTime) noexcept -> float
848 {
849 if constexpr (std::is_same_v<T, std::monostate>)
850 {
851 float baseSpeed = owner->IsControlledByPlayer() ? playerBaseMoveSpeed[MOVE_RUN] : baseMoveSpeed[MOVE_RUN];
852 if (Creature* creature = owner->ToCreature())
853 baseSpeed *= creature->GetCreatureTemplate()->speed_run;
854
855 return baseSpeed * 3.0f * speedMultiplier;
856 }
857 else if constexpr (std::is_same_v<T, float>)
858 {
859 return speedOrTime;
860 }
861 else if constexpr (std::is_same_v<T, Milliseconds>)
862 {
863 return dist / duration_cast<FloatSeconds>(speedOrTime).count();
864 }
865 else
866 {
867 static_assert(Trinity::dependant_false_v<T>, "Unhandled type");
868 }
869 }, speedOrTime);
870
871 if (!unlimitedSpeed)
872 speedXY = std::min(speedXY, 50.0f);
873
874 if (speedXY < 0.01f)
875 {
876 if (scriptResult)
877 scriptResult.SetResult(MovementStopReason::Interrupted);
878 return;
879 }
880
881 float duration = dist / speedXY;
882 float durationSqr = duration * duration;
883 float height = std::clamp(Movement::gravity * durationSqr / 8, minHeight.value_or(0.5f), maxHeight.value_or(1000.0f));
884
885 std::function<void(Movement::MoveSplineInit&)> initializer = [=, effect = (spellEffectExtraData ? Optional<Movement::SpellEffectExtraData>(*spellEffectExtraData) : Optional<Movement::SpellEffectExtraData>())](Movement::MoveSplineInit& init)
886 {
887 init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), false);
888 init.SetParabolic(height, 0);
889 init.SetVelocity(speedXY);
890 std::visit(Movement::MoveSplineInitFacingVisitor(init), facing);
891 init.SetJumpOrientationFixed(orientationFixed);
892 if (unlimitedSpeed)
893 init.SetUnlimitedSpeed();
894 if (effect)
895 init.SetSpellEffectExtraData(*effect);
896 };
897
898 uint32 arrivalSpellId = 0;
899 ObjectGuid arrivalSpellTargetGuid;
900 if (arrivalCast)
901 {
902 arrivalSpellId = arrivalCast->SpellId;
903 arrivalSpellTargetGuid = arrivalCast->Target;
904 }
905
906 GenericMovementGenerator* movement = new GenericMovementGenerator(std::move(initializer), EFFECT_MOTION_TYPE, id,
907 { .ArrivalSpellId = arrivalSpellId, .ArrivalSpellTarget = arrivalSpellTargetGuid, .ScriptResult = std::move(scriptResult) });
910 Add(movement);
911}
912
913void MotionMaster::MoveCirclePath(float x, float y, float z, float radius, bool clockwise, uint8 stepCount,
914 Optional<Milliseconds> duration /*= {}*/, Optional<float> speed /*= {}*/,
915 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/,
917{
918 std::function<void(Movement::MoveSplineInit&)> initializer = [=, this](Movement::MoveSplineInit& init)
919 {
920 float step = 2 * float(M_PI) / stepCount * (clockwise ? -1.0f : 1.0f);
921 Position const& pos = { x, y, z, 0.0f };
922 float angle = pos.GetAbsoluteAngle(_owner->GetPositionX(), _owner->GetPositionY());
923
924 init.Path().reserve(stepCount + 1);
925
926 // add the owner's current position as starting point as it gets removed after entering the cycle
927 init.Path().emplace_back(_owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ());
928
929 for (uint8 i = 0; i < stepCount; angle += step, ++i)
930 {
931 G3D::Vector3& point = init.Path().emplace_back();
932 point.x = x + radius * cosf(angle);
933 point.y = y + radius * sinf(angle);
934
935 if (_owner->IsFlying())
936 point.z = z;
937 else
938 point.z = _owner->GetMapHeight(point.x, point.y, z) + _owner->GetHoverOffset();
939 }
940
941 init.SetCyclic();
942 if (_owner->IsFlying())
943 {
944 init.SetFly();
945 init.SetAnimation(AnimTier::Hover);
946 }
947 else
948 init.SetWalk(true);
949
950 switch (speedSelectionMode)
951 {
953 init.SetWalk(false);
954 break;
956 init.SetWalk(true);
957 break;
959 default:
960 break;
961 }
962 if (speed)
963 init.SetVelocity(*speed);
964 };
965
966 Add(new GenericMovementGenerator(std::move(initializer), EFFECT_MOTION_TYPE, 0, { .Duration = duration, .ScriptResult = std::move(scriptResult) }));
967}
968
969void MotionMaster::MoveAlongSplineChain(uint32 pointId, uint16 dbChainId, bool walk)
970{
971 Creature* owner = _owner->ToCreature();
972 if (!owner)
973 {
974 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::MoveAlongSplineChain: '{}', tried to walk along DB spline chain. Ignoring.", _owner->GetGUID());
975 return;
976 }
977 std::vector<SplineChainLink> const* chain = sScriptSystemMgr->GetSplineChain(owner, dbChainId);
978 if (!chain)
979 {
980 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::MoveAlongSplineChain: '{}', tried to walk along non-existing spline chain with DB Id: {}.", _owner->GetGUID(), dbChainId);
981 return;
982 }
983 MoveAlongSplineChain(pointId, *chain, walk);
984}
985
986void MotionMaster::MoveAlongSplineChain(uint32 pointId, std::vector<SplineChainLink> const& chain, bool walk)
987{
988 Add(new SplineChainMovementGenerator(pointId, chain, walk));
989}
990
992{
993 if (info.Empty())
994 {
995 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::ResumeSplineChain: '{}', tried to resume a spline chain from empty info.", _owner->GetGUID());
996 return;
997 }
999}
1000
1003{
1005 {
1006 if (bool(*opt))
1008 });
1009
1010 // Use larger distance for vmap height search than in most other cases
1012 if (tz <= INVALID_HEIGHT)
1013 {
1014 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveFall: '{}', unable to retrieve a proper height at map Id: {} (X: {}, Y: {}, Z: {})",
1016 return;
1017 }
1018
1019 // Abort too if the ground is very near
1020 if (std::fabs(_owner->GetPositionZ() - tz) < 0.1f)
1021 return;
1022
1023 // rooted units don't move (also setting falling+root flag causes client freezes)
1025 return;
1026
1027 _owner->SetFall(true);
1028
1029 // Don't run spline movement for players
1030 if (_owner->GetTypeId() == TYPEID_PLAYER)
1031 {
1033 return;
1034 }
1035
1036 std::function<void(Movement::MoveSplineInit&)> initializer = [this, tz](Movement::MoveSplineInit& init)
1037 {
1038 init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz + _owner->GetHoverOffset(), false);
1039 init.SetFall();
1040 };
1041
1042 GenericMovementGenerator* movement = new GenericMovementGenerator(std::move(initializer), EFFECT_MOTION_TYPE, id, { .ScriptResult = std::move(*setterScopeExit.release()) });
1044 Add(movement);
1045}
1046
1047void MotionMaster::MoveSeekAssistance(float x, float y, float z)
1048{
1049 if (Creature* creature = _owner->ToCreature())
1050 {
1051 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveSeekAssistance: '{}', seeks assistance (X: {}, Y: {}, Z: {})", creature->GetGUID(), x, y, z);
1052 creature->AttackStop();
1053 creature->CastStop();
1054 creature->DoNotReacquireSpellFocusTarget();
1055 creature->SetReactState(REACT_PASSIVE);
1057 }
1058 else
1059 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::MoveSeekAssistance: '{}', attempted to seek assistance.", _owner->GetGUID());
1060}
1061
1063{
1064 if (_owner->GetTypeId() == TYPEID_UNIT)
1065 {
1066 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveSeekAssistanceDistract: '{}', is distracted after assistance call (Time: {})", _owner->GetGUID(), time);
1068 }
1069 else
1070 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::MoveSeekAssistanceDistract: '{}', attempted to call distract assistance.", _owner->GetGUID());
1071}
1072
1073void MotionMaster::MoveTaxiFlight(uint32 path, uint32 pathnode, Optional<float> speed /*= {}*/,
1075{
1076 if (_owner->GetTypeId() == TYPEID_PLAYER)
1077 {
1078 if (path < sTaxiPathNodesByPath.size())
1079 {
1080 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveTaxiFlight: '{}', taxi to path Id: {} (node {})", _owner->GetGUID(), path, pathnode);
1081
1082 // Only one FLIGHT_MOTION_TYPE is allowed
1083 bool hasExisting = HasMovementGenerator([](MovementGenerator const* gen) { return gen->GetMovementGeneratorType() == FLIGHT_MOTION_TYPE; });
1084 ASSERT(!hasExisting, "Duplicate flight path movement generator");
1085
1086 FlightPathMovementGenerator* movement = new FlightPathMovementGenerator(speed, std::move(scriptResult));
1087 movement->LoadPath(_owner->ToPlayer(), pathnode);
1088 Add(movement);
1089 }
1090 else
1091 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::MoveTaxiFlight: '{}', attempted taxi to non-existing path Id: {} (node: {})", _owner->GetGUID(), path, pathnode);
1092 }
1093 else
1094 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::MoveTaxiFlight: '{}', attempted taxi to path Id: {} (node: {})", _owner->GetGUID(), path, pathnode);
1095}
1096
1097void MotionMaster::MoveDistract(uint32 timer, float orientation)
1098{
1099/*
1100 if (_slot[MOTION_SLOT_CONTROLLED])
1101 return;
1102*/
1103 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveDistract: '{}', distracted (timer: {}, orientation: {})", _owner->GetGUID(), timer, orientation);
1104 Add(new DistractMovementGenerator(timer, orientation));
1105}
1106
1107void MotionMaster::MovePath(uint32 pathId, bool repeatable, Optional<Milliseconds> duration /*= {}*/, Optional<float> speed /*= {}*/,
1108 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/,
1109 Optional<std::pair<Milliseconds, Milliseconds>> waitTimeRangeAtPathEnd /*= {}*/,
1110 Optional<float> wanderDistanceAtPathEnds /*= {}*/, Optional<bool> followPathBackwardsFromEndToStart /*= {}*/,
1111 Optional<bool> exactSplinePath /*= {}*/, bool generatePath /*= true*/, Optional<MovementFadeObject> fadeObject /*= {}*/,
1113{
1114 if (!pathId)
1115 {
1116 if (scriptResult)
1117 scriptResult.SetResult(MovementStopReason::Interrupted);
1118 return;
1119 }
1120
1121 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MovePath: '{}', starts moving over path Id: {} (repeatable: {})",
1122 _owner->GetGUID(), pathId, repeatable ? "YES" : "NO");
1123
1124 if (_owner->GetTypeId() == TYPEID_UNIT)
1125 Add(new WaypointMovementGenerator<Creature>(pathId, repeatable, duration, speed, speedSelectionMode, std::move(waitTimeRangeAtPathEnd),
1126 wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, exactSplinePath, generatePath, fadeObject, std::move(scriptResult)), MOTION_SLOT_DEFAULT);
1127 else
1128 Add(new WaypointMovementGenerator<Player>(pathId, repeatable, duration, speed, speedSelectionMode, std::move(waitTimeRangeAtPathEnd),
1129 wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, exactSplinePath, generatePath, {}, std::move(scriptResult)));
1130}
1131
1132void MotionMaster::MovePath(WaypointPath const& path, bool repeatable, Optional<Milliseconds> duration /*= {}*/, Optional<float> speed /*= {}*/,
1133 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/,
1134 Optional<std::pair<Milliseconds, Milliseconds>> waitTimeRangeAtPathEnd /*= {}*/,
1135 Optional<float> wanderDistanceAtPathEnds /*= {}*/, Optional<bool> followPathBackwardsFromEndToStart /*= {}*/,
1136 Optional<bool> exactSplinePath /*= {}*/, bool generatePath /*= true*/, Optional<MovementFadeObject> fadeObject /*= {}*/,
1138{
1139 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MovePath: '{}', starts moving over path Id: {} (repeatable: {})",
1140 _owner->GetGUID(), path.Id, repeatable ? "YES" : "NO");
1141
1142 if (_owner->GetTypeId() == TYPEID_UNIT)
1143 Add(new WaypointMovementGenerator<Creature>(path, repeatable, duration, speed, speedSelectionMode, std::move(waitTimeRangeAtPathEnd),
1144 wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, exactSplinePath, generatePath, fadeObject, std::move(scriptResult)), MOTION_SLOT_DEFAULT);
1145 else
1146 Add(new WaypointMovementGenerator<Player>(path, repeatable, duration, speed, speedSelectionMode, std::move(waitTimeRangeAtPathEnd),
1147 wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, exactSplinePath, generatePath, {}, std::move(scriptResult)));
1148}
1149
1151 Optional<float> turnSpeed /*= {}*/, Optional<float> totalTurnAngle /*= {}*/,
1153{
1154 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveRotate: '{}', starts rotate (time: {}ms, turnSpeed: {}, totalTurnAngle: {}, direction: {})",
1155 _owner->GetGUID(), time.value_or(0ms).count(), turnSpeed, totalTurnAngle, direction);
1156
1157 Add(new RotateMovementGenerator(id, direction, time, turnSpeed, totalTurnAngle, std::move(scriptResult)));
1158}
1159
1160void MotionMaster::MoveFormation(Unit* leader, float range, float angle, uint32 point1, uint32 point2)
1161{
1162 if (_owner->GetTypeId() == TYPEID_UNIT && leader)
1163 {
1164 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveFormation: '{}', started to move in a formation with leader {}", _owner->GetGUID(), leader->GetGUID());
1165 Add(new FormationMovementGenerator(leader, range, angle, point1, point2), MOTION_SLOT_DEFAULT);
1166 }
1167}
1168
1169void MotionMaster::MoveFace(float orientation, uint32 id /*= EVENT_FACE*/)
1170{
1171 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveFace: '{}', faces '{}'", _owner->GetGUID(), orientation);
1172
1173 std::function<void(Movement::MoveSplineInit&)> initializer = [owner = _owner, orientation](Movement::MoveSplineInit& init)
1174 {
1175 init.MoveTo(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ(), false);
1176 if (owner->GetTransport())
1177 init.DisableTransportPathTransformations(); // It makes no sense to target global orientation
1178 init.SetFacing(orientation);
1179 };
1180
1181 Add(new ImmediateMovementGenerator(std::move(initializer), FACE_MOTION_TYPE, id));
1182}
1183
1184void MotionMaster::MoveFace(WorldObject const* object, uint32 id /*= EVENT_FACE*/)
1185{
1186 if (!object)
1187 return;
1188
1189 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveFace: '{}', faces '{}'", _owner->GetGUID(), object->GetGUID());
1190
1191 std::function<void(Movement::MoveSplineInit&)> initializer = [owner = _owner, object](Movement::MoveSplineInit& init)
1192 {
1193 init.MoveTo(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ(), false);
1194 init.SetFacing(owner->GetAbsoluteAngle(object)); // when on transport, GetAbsoluteAngle will still return global coordinates (and angle) that needs transforming
1195 };
1196
1197 Add(new ImmediateMovementGenerator(std::move(initializer), FACE_MOTION_TYPE, id));
1198}
1199
1200void MotionMaster::LaunchMoveSpline(std::function<void(Movement::MoveSplineInit& init)>&& initializer, uint32 id/*= 0*/, MovementGeneratorPriority priority/* = MOTION_PRIORITY_NORMAL*/, MovementGeneratorType type/*= EFFECT_MOTION_TYPE*/, Scripting::v2::ActionResultSetter<MovementStopReason>&& scriptResult /*= {}*/)
1201{
1203 {
1204 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::LaunchMoveSpline: '{}', tried to launch a spline with an invalid MovementGeneratorType: {} (Id: {}, Priority: {})", _owner->GetGUID(), type, id, priority);
1205 return;
1206 }
1207
1208 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::LaunchMoveSpline: '{}', initiates spline Id: {} (Type: {}, Priority: {})", _owner->GetGUID(), id, type, priority);
1209
1210 GenericMovementGenerator* movement = new GenericMovementGenerator(std::move(initializer), type, id, { .ScriptResult = scriptResult });
1211 movement->Priority = priority;
1212 Add(movement);
1213}
1214
1215/******************** Private methods ********************/
1216
1218{
1219 while (!_delayedActions.empty())
1220 {
1221 _delayedActions.front().Resolve();
1222 _delayedActions.pop_front();
1223 }
1224}
1225
1226void MotionMaster::Remove(MotionMasterContainer::iterator& iterator, bool active, bool movementInform)
1227{
1228 MovementGenerator* pointer = *iterator;
1229 iterator = _generators.erase(iterator);
1230 Delete(pointer, active, movementInform);
1231}
1232
1233void MotionMaster::Pop(bool active, bool movementInform)
1234{
1235 auto itr = _generators.begin();
1236 if (itr != _generators.end())
1237 Remove(itr, active, movementInform);
1238}
1239
1241{
1242 // Clear ALL movement generators (including default)
1244 DirectClear();
1245
1247}
1248
1250{
1251 // First delete Top
1252 if (!_generators.empty())
1253 Pop(true, false);
1254
1255 // Then the rest
1256 while (!_generators.empty())
1257 Pop(false, false);
1258
1259 // Make sure the storage is empty
1261}
1262
1264{
1266 DeleteDefault(_generators.empty(), false);
1267}
1268
1269void MotionMaster::DirectClear(std::function<bool(MovementGenerator*)> const& filter)
1270{
1272 for (auto itr = _generators.begin(); itr != _generators.end();)
1273 {
1274 if (filter(*itr))
1275 Remove(itr, *itr == top, false);
1276 else
1277 ++itr;
1278 }
1279}
1280
1281void MotionMaster::DirectAdd(MovementGenerator* movement, MovementSlot slot/* = MOTION_SLOT_ACTIVE*/)
1282{
1283 switch (slot)
1284 {
1287 _defaultGenerator->Finalize(_owner, _generators.empty(), false);
1288
1290 if (IsStatic(movement))
1292 break;
1293 case MOTION_SLOT_ACTIVE:
1294 {
1295 if (!_generators.empty())
1296 {
1297 auto where = _generators.lower_bound(movement); // find movement with equal priority
1298 bool replacesExisting = !movement->HasFlag(MOVEMENTGENERATOR_FLAG_IMMEDIATE)
1299 && where != _generators.end() && !_generators.key_comp()(movement, *where);
1300 auto top = _generators.begin();
1301 if (replacesExisting)
1302 Remove(where, where == top, false);
1303 else if (where == top)
1304 (*top)->Deactivate(_owner);
1305 }
1306 else
1307 _defaultGenerator->Deactivate(_owner);
1308
1310 {
1311 _generators.insert(movement);
1312 AddBaseUnitState(movement);
1313 }
1314 else
1315 {
1316 movement->Finalize(_owner, true, true);
1317 delete movement;
1318 }
1319 break;
1320 }
1321 default:
1322 break;
1323 }
1324}
1325
1326void MotionMaster::Delete(MovementGenerator* movement, bool active, bool movementInform)
1327{
1328 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::Delete: deleting generator (Priority: {}, Flags: {}, BaseUnitState: {}, Type: {}), owner: '{}'",
1329 movement->Priority, movement->Flags, movement->BaseUnitState, movement->GetMovementGeneratorType(), _owner->GetGUID());
1330
1331 movement->Finalize(_owner, active, movementInform);
1332 ClearBaseUnitState(movement);
1334}
1335
1336void MotionMaster::DeleteDefault(bool active, bool movementInform)
1337{
1338 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::DeleteDefault: deleting generator (Priority: {}, Flags: {}, BaseUnitState: {}, Type: {}), owner: '{}'",
1339 _defaultGenerator->Priority, _defaultGenerator->Flags, _defaultGenerator->BaseUnitState, _defaultGenerator->GetMovementGeneratorType(), _owner->GetGUID());
1340
1341 _defaultGenerator->Finalize(_owner, active, movementInform);
1344}
1345
1347{
1348 if (!movement || !movement->BaseUnitState)
1349 return;
1350
1351 _baseUnitStatesMap.emplace(movement->BaseUnitState, movement);
1352 _owner->AddUnitState(movement->BaseUnitState);
1353}
1354
1356{
1357 if (!movement || !movement->BaseUnitState)
1358 return;
1359
1361 if (_baseUnitStatesMap.count(movement->BaseUnitState) == 0)
1363}
1364
1366{
1367 uint32 unitState = 0;
1368 for (auto itr = _baseUnitStatesMap.begin(); itr != _baseUnitStatesMap.end(); ++itr)
1369 unitState |= itr->first;
1370
1371 _owner->ClearUnitState(unitState);
1372 _baseUnitStatesMap.clear();
1373}
#define M_PI
Definition Common.h:118
TaxiPathNodesByPath sTaxiPathNodesByPath
uint8_t uint8
Definition Define.h:156
uint16_t uint16
Definition Define.h:155
uint32_t uint32
Definition Define.h:154
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition Duration.h:24
#define ASSERT
Definition Errors.h:72
#define MAX_FALL_DISTANCE
Definition GridDefines.h:62
#define INVALID_HEIGHT
Definition GridDefines.h:61
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:173
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:182
void MovementGeneratorPointerDeleter(MovementGenerator *a)
MovementGenerator * GetIdleMovementGenerator()
bool IsStatic(MovementGenerator *movement)
@ MOTIONMASTER_DELAYED_ADD
@ MOTIONMASTER_DELAYED_REMOVE
@ MOTIONMASTER_DELAYED_REMOVE_TYPE
@ MOTIONMASTER_DELAYED_INITIALIZE
@ MOTIONMASTER_DELAYED_CLEAR_PRIORITY
@ MOTIONMASTER_DELAYED_CLEAR
@ MOTIONMASTER_DELAYED_CLEAR_SLOT
@ MOTIONMASTER_FLAG_DELAYED
@ MOTIONMASTER_FLAG_INITIALIZING
@ MOTIONMASTER_FLAG_UPDATE
@ MOTIONMASTER_FLAG_INITIALIZATION_PENDING
@ MOTIONMASTER_FLAG_STATIC_INITIALIZATION_PENDING
bool IsInvalidMovementSlot(uint8 const slot)
RotateDirection
MovementGeneratorPriority
@ MOTION_PRIORITY_HIGHEST
MovementSlot
@ MOTION_SLOT_ACTIVE
@ MAX_MOTION_SLOT
@ MOTION_SLOT_DEFAULT
MovementWalkRunSpeedSelectionMode
MovementGeneratorType
@ IDLE_MOTION_TYPE
@ CHASE_MOTION_TYPE
@ MAX_MOTION_TYPE
@ FACE_MOTION_TYPE
@ FLIGHT_MOTION_TYPE
@ FOLLOW_MOTION_TYPE
@ EFFECT_MOTION_TYPE
std::variant< std::monostate, Position, Unit const *, float > MovementFacingTarget
bool IsInvalidMovementGeneratorType(uint8 const type)
@ MOVEMENTGENERATOR_FLAG_INITIALIZATION_PENDING
@ MOVEMENTGENERATOR_FLAG_DEACTIVATED
@ MOVEMENTGENERATOR_FLAG_FINALIZED
@ MOVEMENTGENERATOR_FLAG_IMMEDIATE
@ MOVEMENTGENERATOR_FLAG_PERSIST_ON_DEATH
#define sMovementGeneratorRegistry
@ TYPEID_UNIT
Definition ObjectGuid.h:43
@ TYPEID_PLAYER
Definition ObjectGuid.h:44
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
#define PET_FOLLOW_ANGLE
Definition PetDefines.h:99
#define PET_FOLLOW_DIST
Definition PetDefines.h:98
#define sScriptSystemMgr
@ EVENT_ASSIST_MOVE
@ EVENT_CHARGE_PREPATH
@ REACT_PASSIVE
AnimTier
Definition UnitDefines.h:69
@ UNIT_STATE_ROOT
Definition Unit.h:285
@ UNIT_STATE_CHARGING
Definition Unit.h:292
@ UNIT_STATE_STUNNED
Definition Unit.h:278
@ UNIT_STATE_JUMPING
Definition Unit.h:293
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
bool IsInWorld() const
Definition BaseEntity.h:158
TypeID GetTypeId() const
Definition BaseEntity.h:166
void LoadPath(Player *owner, uint32 startNode=0)
void MoveTierTransition(uint32 id, Position const &pos, AnimTier newAnimTier, Optional< int32 > tierTransitionId={}, Optional< float > velocity={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::Default, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
uint32 Size() const
bool HasMovementGenerator(std::function< bool(MovementGenerator const *)> const &filter, MovementSlot slot=MOTION_SLOT_ACTIVE) const
void ClearBaseUnitStates()
void DeleteDefault(bool active, bool movementInform)
bool HasFlag(uint8 const flag) const
void ClearBaseUnitState(MovementGenerator const *movement)
void LaunchMoveSpline(std::function< void(Movement::MoveSplineInit &init)> &&initializer, uint32 id=0, MovementGeneratorPriority priority=MOTION_PRIORITY_NORMAL, MovementGeneratorType type=EFFECT_MOTION_TYPE, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
void ResolveDelayedActions()
void MoveTakeoff(uint32 id, Position const &pos, Optional< int32 > tierTransitionId={}, Optional< float > velocity={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::Default, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
void MoveFall(uint32 id=0, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
MovementGeneratorType GetCurrentMovementGeneratorType() const
std::vector< MovementGeneratorInformation > GetMovementGeneratorsInformation() const
void Pop(bool active, bool movementInform)
void PropagateSpeedChange()
void Delete(MovementGenerator *movement, bool active, bool movementInform)
void MoveFollow(Unit *target, float dist, Optional< ChaseAngle > angle={}, Optional< Milliseconds > duration={}, bool ignoreTargetWalk=false, MovementSlot slot=MOTION_SLOT_ACTIVE, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
void MoveChase(Unit *target, Optional< ChaseRange > dist={}, Optional< ChaseAngle > angle={})
void MoveFormation(Unit *leader, float range, float angle, uint32 point1, uint32 point2)
void MoveJump(uint32 id, Position const &pos, std::variant< std::monostate, float, Milliseconds > speedOrTime={}, Optional< float > minHeight={}, Optional< float > maxHeight={}, MovementFacingTarget const &facing={}, bool orientationFixed=false, bool unlimitedSpeed=false, Optional< float > speedMultiplier={}, JumpArrivalCastArgs const *arrivalCast=nullptr, Movement::SpellEffectExtraData const *spellEffectExtraData=nullptr, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
void InterruptOnTeleport()
void MoveAlongSplineChain(uint32 pointId, uint16 dbChainId, bool walk)
void MoveRotate(uint32 id, RotateDirection direction, Optional< Milliseconds > time={}, Optional< float > turnSpeed={}, Optional< float > totalTurnAngle={}, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
Makes the Unit turn in place.
void DirectAdd(MovementGenerator *movement, MovementSlot slot)
void MoveTaxiFlight(uint32 path, uint32 pathnode, Optional< float > speed={}, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
std::unique_ptr< MovementGenerator, MovementGeneratorDeleter > MovementGeneratorPointer
void AddFlag(uint8 const flag)
void MoveSeekAssistance(float x, float y, float z)
void MoveRandom(float wanderDistance=0.0f, Optional< Milliseconds > duration={}, Optional< float > speed={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::ForceWalk, MovementSlot slot=MOTION_SLOT_DEFAULT, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
void MoveSeekAssistanceDistract(uint32 timer)
void MoveFleeing(Unit *enemy, Milliseconds time=0ms, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
void DirectClearDefault()
MotionMasterUnitStatesContainer _baseUnitStatesMap
void ResumeSplineChain(SplineChainResumeInfo const &info)
void RemoveFlag(uint8 const flag)
void Add(MovementGenerator *movement, MovementSlot slot=MOTION_SLOT_ACTIVE)
bool GetDestination(float &x, float &y, float &z)
std::function< void()> DelayedActionDefine
void Update(uint32 diff)
void MovePoint(uint32 id, Position const &pos, bool generatePath=true, Optional< float > finalOrient={}, Optional< float > speed={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::Default, Optional< float > closeEnoughDistance={}, Optional< MovementFadeObject > fadeObject={}, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
MovementGenerator * GetCurrentMovementGenerator() const
std::deque< DelayedAction > _delayedActions
void MoveCharge(float x, float y, float z, float speed=SPEED_CHARGE, uint32 id=EVENT_CHARGE, bool generatePath=false, Unit const *target=nullptr, Movement::SpellEffectExtraData const *spellEffectExtraData=nullptr)
void MoveTargetedHome()
MovementSlot GetCurrentSlot() const
MovementGeneratorPointer _defaultGenerator
MotionMaster(Unit *unit)
void MoveCloserAndStop(uint32 id, Unit *target, float distance)
void MoveCirclePath(float x, float y, float z, float radius, bool clockwise, uint8 stepCount, Optional< Milliseconds > duration={}, Optional< float > speed={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::Default, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
void DirectInitialize()
void MoveDistract(uint32 time, float orientation)
void MovePath(uint32 pathId, bool repeatable, 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={})
MovementGenerator * GetMovementGenerator(std::function< bool(MovementGenerator const *)> const &filter, MovementSlot slot=MOTION_SLOT_ACTIVE) const
bool Empty() const
void Remove(MovementGenerator *movement, MovementSlot slot=MOTION_SLOT_ACTIVE)
void InitializeDefault()
void MoveKnockbackFrom(Position const &origin, float speedXY, float speedZ, float angle=M_PI, Movement::SpellEffectExtraData const *spellEffectExtraData=nullptr)
void MoveFace(float orientation, uint32 id=EVENT_FACE)
MotionMasterContainer _generators
void AddBaseUnitState(MovementGenerator const *movement)
void MoveLand(uint32 id, Position const &pos, Optional< int32 > tierTransitionId={}, Optional< float > velocity={}, MovementWalkRunSpeedSelectionMode speedSelectionMode=MovementWalkRunSpeedSelectionMode::Default, Scripting::v2::ActionResultSetter< MovementStopReason > &&scriptResult={})
virtual bool Update(Unit *owner, uint32 diff)=0
virtual bool Initialize(Unit *owner)=0
void AddFlag(uint16 const flag)
virtual void Finalize(Unit *owner, bool active, bool movementInform)=0
bool HasFlag(uint16 const flag) const
virtual void UnitSpeedChanged()
virtual MovementGeneratorType GetMovementGeneratorType() const =0
virtual bool Reset(Unit *owner)=0
void MovebyPath(std::span< Vector3 const > path, int32 pointId=0)
void SetVelocity(float velocity)
void SetFacing(float angle)
void SetSpellEffectExtraData(SpellEffectExtraData const &spellEffectExtraData)
void SetParabolic(float amplitude, int32 start_point)
void SetOrientationFixed(bool enable)
static ObjectGuid const Empty
Definition ObjectGuid.h:314
std::string ToString() const
Player * ToPlayer()
Definition Object.h:126
Creature * ToCreature()
Definition Object.h:121
Movement::PointsArray const & GetPath() const
G3D::Vector3 const & GetActualEndPosition() const
void SetFallInformation(uint32 time, float z)
Definition Player.cpp:27437
Definition Unit.h:647
void ClearUnitState(uint32 f)
Definition Unit.h:756
bool SetFall(bool enable)
Definition Unit.cpp:13503
void StopMoving(bool force=false)
Definition Unit.cpp:10753
AnimTier GetAnimTier() const
Definition Unit.h:924
void AddUnitState(uint32 f)
Definition Unit.h:754
Unit * GetCharmerOrOwner() const
Definition Unit.h:1241
float GetHoverOffset() const
Definition Unit.h:1828
bool HasUnitState(const uint32 f) const
Definition Unit.h:755
std::unique_ptr< Movement::MoveSpline > movespline
Definition Unit.h:1854
bool IsFlying() const
Definition Unit.h:1823
ObjectGuid GetTarget() const
Definition Unit.h:1847
constexpr uint32 GetMapId() const
Definition Position.h:216
float GetMapHeight(float x, float y, float z, bool vmap=true, float distanceToSearch=50.0f) const
Definition Object.cpp:3131
void MovePositionToFirstCollision(Position &pos, float dist, float angle) const
Definition Object.cpp:2833
MovementGenerator * SelectMovementGenerator(Unit *unit)
float constexpr gravity
float computeFallElevation(float t_passed, bool isSafeFall, float start_velocity=0.0f)
TC_GAME_API Unit * GetUnit(WorldObject const &, ObjectGuid const &guid)
void MultimapErasePair(M &multimap, typename M::key_type const &key, typename M::mapped_type const &value)
Definition MapUtils.h:57
std::unique_ptr< T, Impl::stateful_unique_ptr_deleter< Ptr, Del > > make_unique_ptr_with_deleter(Ptr ptr, Del deleter)
Definition Memory.h:133
bool operator()(MovementGenerator const *a, MovementGenerator const *b) const
void operator()(MovementGenerator *a)
MovementGeneratorInformation(MovementGeneratorType type, ObjectGuid targetGUID, std::string const &targetName)
constexpr float GetPositionX() const
Definition Position.h:87
float m_positionZ
Definition Position.h:66
constexpr float GetPositionY() const
Definition Position.h:88
float GetExactDist2d(const float x, const float y) const
Definition Position.h:117
float GetRelativeAngle(float x, float y) const
Definition Position.h:147
float GetExactDist(float x, float y, float z) const
Definition Position.h:129
Position GetPositionWithOffset(Position const &offset) const
Definition Position.cpp:61
float m_positionX
Definition Position.h:64
float m_positionY
Definition Position.h:65
float GetAbsoluteAngle(float x, float y) const
Definition Position.h:136
constexpr void GetPosition(float &x, float &y) const
Definition Position.h:92
constexpr float GetOrientation() const
Definition Position.h:90
constexpr float GetPositionZ() const
Definition Position.h:89