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 if (a->Mode > b->Mode)
75 return true;
76 else if (a->Mode == b->Mode)
77 return a->Priority > b->Priority;
78
79 return false;
80}
81
82MovementGeneratorInformation::MovementGeneratorInformation(MovementGeneratorType type, ObjectGuid targetGUID, std::string const& targetName) : Type(type), TargetGUID(targetGUID), TargetName(targetName) { }
83
84MotionMaster::MotionMaster(Unit* unit) : _owner(unit), _defaultGenerator(nullptr), _flags(MOTIONMASTER_FLAG_INITIALIZATION_PENDING) { }
85
87{
88 _delayedActions.clear();
89
90 for (auto itr = _generators.begin(); itr != _generators.end(); itr = _generators.erase(itr))
92}
93
95{
97 return;
98
100 {
101 DelayedActionDefine action = [this]()
102 {
103 Initialize();
104 };
105 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_INITIALIZE);
106 return;
107 }
108
110}
111
116
130
132{
133 return !_defaultGenerator && _generators.empty();
134}
135
137{
138 return (_defaultGenerator ? 1 : 0) + uint32(_generators.size());
139}
140
141std::vector<MovementGeneratorInformation> MotionMaster::GetMovementGeneratorsInformation() const
142{
143 std::vector<MovementGeneratorInformation> list;
144
146 list.emplace_back(_defaultGenerator->GetMovementGeneratorType(), ObjectGuid::Empty, std::string());
147
148 for (auto itr = _generators.begin(); itr != _generators.end(); ++itr)
149 {
150 MovementGenerator* movement = *itr;
151 MovementGeneratorType const type = movement->GetMovementGeneratorType();
152 switch (type)
153 {
156 if (AbstractFollower* followInformation = dynamic_cast<AbstractFollower*>(movement))
157 {
158 if (Unit* target = followInformation->GetTarget())
159 list.emplace_back(type, target->GetGUID(), target->GetName());
160 else
161 list.emplace_back(type, ObjectGuid::Empty, std::string());
162 }
163 else
164 list.emplace_back(type, ObjectGuid::Empty, std::string());
165 break;
166 default:
167 list.emplace_back(type, ObjectGuid::Empty, std::string());
168 break;
169 }
170 }
171
172 return list;
173}
174
176{
177 if (!_generators.empty())
178 return MOTION_SLOT_ACTIVE;
179
181 return MOTION_SLOT_DEFAULT;
182
183 return MAX_MOTION_SLOT;
184}
185
187{
188 if (!_generators.empty())
189 return *_generators.begin();
190
192 return _defaultGenerator.get();
193
194 return nullptr;
195}
196
198{
199 if (Empty())
200 return MAX_MOTION_TYPE;
201
203 if (!movement)
204 return MAX_MOTION_TYPE;
205
206 return movement->GetMovementGeneratorType();
207}
208
210{
211 if (Empty() || IsInvalidMovementSlot(slot))
212 return MAX_MOTION_TYPE;
213
214 if (slot == MOTION_SLOT_ACTIVE && !_generators.empty())
215 return (*_generators.begin())->GetMovementGeneratorType();
216
218 return _defaultGenerator->GetMovementGeneratorType();
219
220 return MAX_MOTION_TYPE;
221}
222
224{
225 if (Empty() || IsInvalidMovementSlot(slot))
226 return nullptr;
227
228 if (slot == MOTION_SLOT_ACTIVE && !_generators.empty())
229 return *_generators.begin();
230
232 return _defaultGenerator.get();
233
234 return nullptr;
235}
236
237MovementGenerator* MotionMaster::GetMovementGenerator(std::function<bool(MovementGenerator const*)> const& filter, MovementSlot slot) const
238{
239 if (Empty() || IsInvalidMovementSlot(slot))
240 return nullptr;
241
242 MovementGenerator* movement = nullptr;
243 switch (slot)
244 {
246 if (_defaultGenerator && filter(_defaultGenerator.get()))
247 movement = _defaultGenerator.get();
248 break;
250 if (!_generators.empty())
251 {
252 auto itr = std::find_if(_generators.begin(), _generators.end(), std::ref(filter));
253 if (itr != _generators.end())
254 movement = *itr;
255 }
256 break;
257 default:
258 break;
259 }
260
261 return movement;
262}
263
264bool MotionMaster::HasMovementGenerator(std::function<bool(MovementGenerator const*)> const& filter, MovementSlot slot) const
265{
266 if (Empty() || IsInvalidMovementSlot(slot))
267 return false;
268
269 bool value = false;
270 switch (slot)
271 {
273 if (_defaultGenerator && filter(_defaultGenerator.get()))
274 value = true;
275 break;
277 if (!_generators.empty())
278 {
279 auto itr = std::find_if(_generators.begin(), _generators.end(), std::ref(filter));
280 value = itr != _generators.end();
281 }
282 break;
283 default:
284 break;
285 }
286
287 return value;
288}
289
291{
292 if (!_owner)
293 return;
294
296 return;
297
298 ASSERT(!Empty(), "MotionMaster:Update: update called without Initializing! (%s)", _owner->GetGUID().ToString().c_str());
299
301
304 {
306 top->Initialize(_owner);
307 }
309 top->Initialize(_owner);
311 top->Reset(_owner);
312
313 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);
314
315 if (!top->Update(_owner, diff))
316 {
317 ASSERT(top == GetCurrentMovementGenerator(), "MotionMaster::Update: top was modified while updating! (%s)", _owner->GetGUID().ToString().c_str());
318
319 // Since all the actions that modify any slot are delayed, this movement is guaranteed to be top
320 Pop(true, true); // Natural, and only, call to MovementInform
321 }
322
324
326}
327
328void MotionMaster::Add(MovementGenerator* movement, MovementSlot slot/* = MOTION_SLOT_ACTIVE*/)
329{
330 if (!movement)
331 return;
332
333 if (IsInvalidMovementSlot(slot))
334 {
335 delete movement;
336 return;
337 }
338
340 {
341 DelayedActionDefine action = [this, movement, slot]()
342 {
343 Add(movement, slot);
344 };
345 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_ADD);
346 }
347 else
348 DirectAdd(movement, slot);
349}
350
351void MotionMaster::Remove(MovementGenerator* movement, MovementSlot slot/* = MOTION_SLOT_ACTIVE*/)
352{
353 if (!movement || IsInvalidMovementSlot(slot))
354 return;
355
357 {
358 DelayedActionDefine action = [this, movement, slot]()
359 {
360 Remove(movement, slot);
361 };
362 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_REMOVE);
363 return;
364 }
365
366 if (Empty())
367 return;
368
369 switch (slot)
370 {
372 if (_defaultGenerator && _defaultGenerator.get() == movement)
374 break;
376 if (!_generators.empty())
377 {
378 auto bounds = _generators.equal_range(movement);
379 auto itr = std::find(bounds.first, bounds.second, movement);
380 if (itr != _generators.end())
381 Remove(itr, GetCurrentMovementGenerator() == *itr, false);
382 }
383 break;
384 default:
385 break;
386 }
387}
388
389void MotionMaster::Remove(MovementGeneratorType type, MovementSlot slot/* = MOTION_SLOT_ACTIVE*/)
390{
392 return;
393
395 {
396 DelayedActionDefine action = [this, type, slot]()
397 {
398 Remove(type, slot);
399 };
400 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_REMOVE_TYPE);
401 return;
402 }
403
404 if (Empty())
405 return;
406
407 switch (slot)
408 {
410 if (_defaultGenerator && _defaultGenerator->GetMovementGeneratorType() == type)
412 break;
414 if (!_generators.empty())
415 {
416 auto itr = std::find_if(_generators.begin(), _generators.end(), [type](MovementGenerator const* a) -> bool
417 {
418 return a->GetMovementGeneratorType() == type;
419 });
420
421 if (itr != _generators.end())
422 Remove(itr, GetCurrentMovementGenerator() == *itr, false);
423 }
424 break;
425 default:
426 break;
427 }
428}
429
431{
433 {
434 DelayedActionDefine action = [this]()
435 {
436 Clear();
437 };
438 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_CLEAR);
439 return;
440 }
441
442 if (!Empty())
443 DirectClear();
444}
445
447{
448 if (IsInvalidMovementSlot(slot))
449 return;
450
452 {
453 DelayedActionDefine action = [this, slot]()
454 {
455 Clear(slot);
456 };
457 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_CLEAR_SLOT);
458 return;
459 }
460
461 if (Empty())
462 return;
463
464 switch (slot)
465 {
468 break;
470 DirectClear();
471 break;
472 default:
473 break;
474 }
475}
476
478{
480 {
481 DelayedActionDefine action = [this, mode]()
482 {
483 Clear(mode);
484 };
485 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_CLEAR_MODE);
486 return;
487 }
488
489 if (Empty())
490 return;
491
492 std::function<bool(MovementGenerator*)> criteria = [mode](MovementGenerator* a) -> bool
493 {
494 return a->Mode == mode;
495 };
496 DirectClear(criteria);
497}
498
500{
502 {
503 DelayedActionDefine action = [this, priority]()
504 {
505 Clear(priority);
506 };
507 _delayedActions.emplace_back(std::move(action), MOTIONMASTER_DELAYED_CLEAR_PRIORITY);
508 return;
509 }
510
511 if (Empty())
512 return;
513
514 std::function<bool(MovementGenerator*)> criteria = [priority](MovementGenerator* a) -> bool
515 {
516 return a->Priority == priority;
517 };
518 DirectClear(criteria);
519}
520
522{
523 if (Empty())
524 return;
525
527 if (!movement)
528 return;
529
530 movement->UnitSpeedChanged();
531}
532
533bool MotionMaster::GetDestination(float &x, float &y, float &z)
534{
535 if (_owner->movespline->Finalized())
536 return false;
537
538 G3D::Vector3 const& dest = _owner->movespline->FinalDestination();
539 x = dest.x;
540 y = dest.y;
541 z = dest.z;
542 return true;
543}
544
546{
547 if (MovementGenerator* movementGenerator = GetCurrentMovementGenerator())
548 if (movementGenerator->HasFlag(MOVEMENTGENERATOR_FLAG_PERSIST_ON_DEATH))
549 return false;
550
551 if (_owner->IsInWorld())
552 {
553 // Only clear MotionMaster for entities that exists in world
554 // Avoids crashes in the following conditions :
555 // * Using 'call pet' on dead pets
556 // * Using 'call stabled pet'
557 // * Logging in with dead pets
558 Clear();
559 MoveIdle();
560 }
561
563
564 return true;
565}
566
571
573{
574 Creature* owner = _owner->ToCreature();
575 if (!owner)
576 {
577 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::MoveTargetedHome: '{}', attempted to move towards target home.", _owner->GetGUID());
578 return;
579 }
580
581 Clear();
582
583 Unit* target = owner->GetCharmerOrOwner();
584 if (!target)
585 {
586 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveTargetedHome: '{}', targeted home.", _owner->GetGUID());
588 }
589 else
590 {
591 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveTargetedHome: '{}', starts following '{}'", _owner->GetGUID(), target->GetGUID());
593 }
594}
595
596void MotionMaster::MoveRandom(float wanderDistance /*= 0.0f*/, Optional<Milliseconds> duration /*= {}*/, Optional<float> speed /*= {}*/,
597 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::ForceWalk*/, MovementSlot slot /*= MOTION_SLOT_DEFAULT*/,
599{
600 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveRandom: '{}', started random movement (spawnDist: {})", _owner->GetGUID(), wanderDistance);
601 if (_owner->GetTypeId() == TYPEID_UNIT)
602 Add(new RandomMovementGenerator<Creature>(wanderDistance, duration, speed, speedSelectionMode, std::move(scriptResult)), slot);
603 else
604 Add(new RandomMovementGenerator<Player>(wanderDistance, duration, speed, speedSelectionMode, std::move(scriptResult)));
605}
606
607void MotionMaster::MoveFollow(Unit* target, float dist, Optional<ChaseAngle> angle /*= {}*/, Optional<Milliseconds> duration /*= {}*/, bool ignoreTargetWalk /*= false*/, MovementSlot slot/* = MOTION_SLOT_ACTIVE*/,
609{
610 // Ignore movement request if target not exist
611 if (!target || target == _owner)
612 {
613 if (scriptResult)
614 scriptResult.SetResult(MovementStopReason::Interrupted);
615 return;
616 }
617
618 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveFollow: '{}', starts following '{}'", _owner->GetGUID(), target->GetGUID());
619 Add(new FollowMovementGenerator(target, dist, angle, duration, ignoreTargetWalk, std::move(scriptResult)), slot);
620}
621
623{
624 // Ignore movement request if target not exist
625 if (!target || target == _owner)
626 return;
627
628 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveChase: '{}', starts chasing '{}'", _owner->GetGUID(), target->GetGUID());
629 Add(new ChaseMovementGenerator(target, dist, angle));
630}
631
633{
635 {
636 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveConfused: '{}', started confused movement.", _owner->GetGUID());
638 }
639 else
640 {
641 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveConfused: '{}', started confused movement.", _owner->GetGUID());
643 }
644}
645
646void MotionMaster::MoveFleeing(Unit* enemy, Milliseconds time /*= 0ms*/,
648{
649 if (!enemy)
650 {
651 if (scriptResult)
652 scriptResult.SetResult(MovementStopReason::Interrupted);
653 return;
654 }
655
656 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveFleeing: '{}', flees from '{}' (time: {}ms)", _owner->GetGUID(), enemy->GetGUID(), time.count());
657 if (_owner->GetTypeId() == TYPEID_UNIT && time > 0ms)
658 Add(new TimedFleeingMovementGenerator(enemy->GetGUID(), time, std::move(scriptResult)));
659 else
660 Add(new FleeingMovementGenerator(enemy->GetGUID(), std::move(scriptResult)));
661}
662
663void MotionMaster::MovePoint(uint32 id, Position const& pos, bool generatePath/* = true*/, Optional<float> finalOrient/* = {}*/, Optional<float> speed /*= {}*/,
664 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/, Optional<float> closeEnoughDistance /*= {}*/,
665 Optional<MovementFadeObject> fadeObject /*= {}*/,
667{
668 MovePoint(id, pos.m_positionX, pos.m_positionY, pos.m_positionZ, generatePath, finalOrient, speed, speedSelectionMode, closeEnoughDistance,
669 fadeObject, std::move(scriptResult));
670}
671
672void MotionMaster::MovePoint(uint32 id, float x, float y, float z, bool generatePath /*= true*/, Optional<float> finalOrient /*= {}*/, Optional<float> speed /*= {}*/,
673 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/, Optional<float> closeEnoughDistance /*= {}*/,
674 Optional<MovementFadeObject> fadeObject /*= {}*/,
676{
677 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MovePoint: '{}', targeted point Id: {} (X: {}, Y: {}, Z: {})", _owner->GetGUID(), id, x, y, z);
678 Add(new PointMovementGenerator(id, x, y, z, generatePath, speed, finalOrient, nullptr, nullptr, speedSelectionMode, closeEnoughDistance,
679 fadeObject, std::move(scriptResult)));
680}
681
682void MotionMaster::MoveCloserAndStop(uint32 id, Unit* target, float distance)
683{
684 float distanceToTravel = _owner->GetExactDist2d(target) - distance;
685 if (distanceToTravel > 0.0f)
686 {
687 float angle = _owner->GetAbsoluteAngle(target);
688 float destx = _owner->GetPositionX() + distanceToTravel * std::cos(angle);
689 float desty = _owner->GetPositionY() + distanceToTravel * std::sin(angle);
690 MovePoint(id, destx, desty, target->GetPositionZ());
691 }
692 else
693 {
694 // We are already close enough. We just need to turn toward the target without changing position.
695 std::function<void(Movement::MoveSplineInit&)> initializer = [=, this, target = target->GetGUID()](Movement::MoveSplineInit& init)
696 {
698 if (Unit const* refreshedTarget = ObjectAccessor::GetUnit(*_owner, target))
699 init.SetFacing(refreshedTarget);
700 };
701 Add(new GenericMovementGenerator(std::move(initializer), EFFECT_MOTION_TYPE, id));
702 }
703}
704
705void MotionMaster::MoveLand(uint32 id, Position const& pos, Optional<int32> tierTransitionId /*= {}*/, Optional<float> velocity /*= {}*/,
706 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/,
708{
709 MoveTierTransition(id, pos, AnimTier::Ground, tierTransitionId, velocity, speedSelectionMode, std::move(scriptResult));
710}
711
712void MotionMaster::MoveTakeoff(uint32 id, Position const& pos, Optional<int32> tierTransitionId /*= {}*/, Optional<float> velocity /*= {}*/,
713 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/,
715{
716 MoveTierTransition(id, pos, AnimTier::Fly, tierTransitionId, velocity, speedSelectionMode, std::move(scriptResult));
717}
718
719void MotionMaster::MoveTierTransition(uint32 id, Position const& pos, AnimTier newAnimTier, Optional<int32> tierTransitionId /*= {}*/, Optional<float> velocity /*= {}*/,
720 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/,
722{
723 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveTierTransition: '{}', anim tier transition to {} Id: {} (X: {}, Y: {}, Z: {})",
724 _owner->GetGUID(), newAnimTier, id, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
725
726 bool flyingTransition = newAnimTier == AnimTier::Fly || _owner->GetAnimTier() == AnimTier::Fly;
727 if (!tierTransitionId)
728 {
729 switch (newAnimTier)
730 {
731 case AnimTier::Ground:
732 tierTransitionId = 208;
733 break;
734 case AnimTier::Swim:
735 tierTransitionId = 149;
736 break;
737 case AnimTier::Hover:
738 tierTransitionId = 25;
739 break;
740 case AnimTier::Fly:
741 tierTransitionId = 17;
742 break;
743 default:
744 break;
745 }
746 }
747
748 std::function<void(Movement::MoveSplineInit&)> initializer = [=, tierTransitionId = tierTransitionId.value_or(0)](Movement::MoveSplineInit& init)
749 {
750 init.MoveTo(PositionToVector3(pos), false);
751 init.SetAnimation(newAnimTier, tierTransitionId);
752 if (flyingTransition)
753 init.SetFly(); // ensure smooth animation even if gravity is disabled after/enabled before calling this function
754 init.SetSmooth();
755 switch (speedSelectionMode)
756 {
758 init.SetWalk(false);
759 break;
761 init.SetWalk(true);
762 break;
764 default:
765 break;
766 }
767 if (velocity)
768 init.SetVelocity(*velocity);
769 };
770
771 Add(new GenericMovementGenerator(std::move(initializer), EFFECT_MOTION_TYPE, id, { .ScriptResult = std::move(scriptResult) }));
772}
773
774void MotionMaster::MoveCharge(float x, float y, float z, float speed /*= SPEED_CHARGE*/, uint32 id /*= EVENT_CHARGE*/, bool generatePath /*= false*/,
775 Unit const* target /*= nullptr*/, Movement::SpellEffectExtraData const* spellEffectExtraData /*= nullptr*/)
776{
777/*
778 if (_slot[MOTION_SLOT_CONTROLLED] && _slot[MOTION_SLOT_CONTROLLED]->GetMovementGeneratorType() != DISTRACT_MOTION_TYPE)
779 return;
780*/
781 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveCharge: '{}', charging point Id: {} (X: {}, Y: {}, Z: {})", _owner->GetGUID(), id, x, y, z);
782 PointMovementGenerator* movement = new PointMovementGenerator(id, x, y, z, generatePath, speed, {}, target, spellEffectExtraData);
785 Add(movement);
786}
787
788void MotionMaster::MoveCharge(PathGenerator const& path, float speed /*= SPEED_CHARGE*/, Unit const* target /*= nullptr*/,
789 Movement::SpellEffectExtraData const* spellEffectExtraData /*= nullptr*/)
790{
791 G3D::Vector3 dest = path.GetActualEndPosition();
792
793 MoveCharge(dest.x, dest.y, dest.z, speed, EVENT_CHARGE_PREPATH);
794
795 // If this is ever changed to not happen immediately then all spell effect handlers that use this must be updated
796
797 // Charge movement is not started when using EVENT_CHARGE_PREPATH
799 init.MovebyPath(path.GetPath());
800 init.SetVelocity(speed);
801 if (target)
802 init.SetFacing(target);
803 if (spellEffectExtraData)
804 init.SetSpellEffectExtraData(*spellEffectExtraData);
805 init.Launch();
806}
807
808void MotionMaster::MoveKnockbackFrom(Position const& origin, float speedXY, float speedZ, float angle /*= M_PI*/, Movement::SpellEffectExtraData const* spellEffectExtraData /*= nullptr*/)
809{
810 if (std::abs(speedXY) < 0.01f && std::abs(speedZ) < 0.01f)
811 return;
812
813 Position dest = _owner->GetPosition();
814 float o = (dest == origin ? 0.0f : _owner->GetRelativeAngle(origin)) + angle;
815 if (speedXY < 0)
816 {
817 speedXY = -speedXY;
818 o = o - float(M_PI);
819 }
820
821 if (speedZ < 0)
822 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
823
824 float moveTimeHalf = speedZ / Movement::gravity;
825 float dist = 2 * moveTimeHalf * speedXY;
826 float max_height = -Movement::computeFallElevation(moveTimeHalf, false, -speedZ);
827
828 boost::container::static_vector<G3D::Vector3, 3> path;
829 path.push_back(PositionToVector3(dest));
830
831 if (dist > 0.01f)
832 {
833 // Use a mmap raycast to get a valid destination.
834 _owner->MovePositionToFirstCollision(dest, dist, o);
835 path.push_back(PositionToVector3(dest));
836 }
837 else
838 {
839 // vertical knockbacks get a fake 0.5 higher additional point to avoid clientside spline length checks
840 // sniffs confirmed that it is always 0.5, no matter what the max height is
841 path.push_back(PositionToVector3(dest.GetPositionWithOffset({ 0.0f, 0.0f, 0.5f })));
842 path.push_back(PositionToVector3(dest));
843 }
844
845 std::function<void(Movement::MoveSplineInit&)> initializer = [=, effect = (spellEffectExtraData ? Optional<Movement::SpellEffectExtraData>(*spellEffectExtraData) : Optional<Movement::SpellEffectExtraData>())](Movement::MoveSplineInit& init)
846 {
847 init.MovebyPath({ path.data(), path.size() });
848 init.SetParabolic(max_height, 0);
849 init.SetOrientationFixed(true);
850 init.SetVelocity(speedXY);
851 if (effect)
852 init.SetSpellEffectExtraData(*effect);
853 };
854
855 GenericMovementGenerator* movement = new GenericMovementGenerator(std::move(initializer), EFFECT_MOTION_TYPE, 0);
858 Add(movement);
859}
860
861void MotionMaster::MoveJump(uint32 id, Position const& pos, std::variant<std::monostate, float, Milliseconds> speedOrTime /*= {}*/,
862 Optional<float> minHeight /*= {}*/, Optional<float> maxHeight /*= {}*/,
863 MovementFacingTarget const& facing /*= {}*/, bool orientationFixed, bool unlimitedSpeed /*= false*/, Optional<float> speedMultiplier /*= {}*/,
864 JumpArrivalCastArgs const* arrivalCast /*= nullptr*/, Movement::SpellEffectExtraData const* spellEffectExtraData /*= nullptr*/,
866{
867 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveJump: '{}', jumps to point Id: {} ({})", _owner->GetGUID(), id, pos);
868
869 float dist = _owner->GetExactDist(pos);
870
871 float speedXY = std::visit([owner = _owner, speedMultiplier = speedMultiplier.value_or(1.0f), dist]<typename T>(T speedOrTime) noexcept -> float
872 {
873 if constexpr (std::is_same_v<T, std::monostate>)
874 {
875 float baseSpeed = owner->IsControlledByPlayer() ? playerBaseMoveSpeed[MOVE_RUN] : baseMoveSpeed[MOVE_RUN];
876 if (Creature* creature = owner->ToCreature())
877 baseSpeed *= creature->GetCreatureTemplate()->speed_run;
878
879 return baseSpeed * 3.0f * speedMultiplier;
880 }
881 else if constexpr (std::is_same_v<T, float>)
882 {
883 return speedOrTime;
884 }
885 else if constexpr (std::is_same_v<T, Milliseconds>)
886 {
887 return dist / duration_cast<FloatSeconds>(speedOrTime).count();
888 }
889 else
890 {
891 static_assert(Trinity::dependant_false_v<T>, "Unhandled type");
892 }
893 }, speedOrTime);
894
895 if (!unlimitedSpeed)
896 speedXY = std::min(speedXY, 50.0f);
897
898 if (speedXY < 0.01f)
899 {
900 if (scriptResult)
901 scriptResult.SetResult(MovementStopReason::Interrupted);
902 return;
903 }
904
905 float duration = dist / speedXY;
906 float durationSqr = duration * duration;
907 float height = std::clamp(Movement::gravity * durationSqr / 8, minHeight.value_or(0.5f), maxHeight.value_or(1000.0f));
908
909 std::function<void(Movement::MoveSplineInit&)> initializer = [=, effect = (spellEffectExtraData ? Optional<Movement::SpellEffectExtraData>(*spellEffectExtraData) : Optional<Movement::SpellEffectExtraData>())](Movement::MoveSplineInit& init)
910 {
911 init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), false);
912 init.SetParabolic(height, 0);
913 init.SetVelocity(speedXY);
914 std::visit(Movement::MoveSplineInitFacingVisitor(init), facing);
915 init.SetJumpOrientationFixed(orientationFixed);
916 if (unlimitedSpeed)
917 init.SetUnlimitedSpeed();
918 if (effect)
919 init.SetSpellEffectExtraData(*effect);
920 };
921
922 uint32 arrivalSpellId = 0;
923 ObjectGuid arrivalSpellTargetGuid;
924 if (arrivalCast)
925 {
926 arrivalSpellId = arrivalCast->SpellId;
927 arrivalSpellTargetGuid = arrivalCast->Target;
928 }
929
930 GenericMovementGenerator* movement = new GenericMovementGenerator(std::move(initializer), EFFECT_MOTION_TYPE, id,
931 { .ArrivalSpellId = arrivalSpellId, .ArrivalSpellTarget = arrivalSpellTargetGuid, .ScriptResult = std::move(scriptResult) });
934 Add(movement);
935}
936
937void MotionMaster::MoveCirclePath(float x, float y, float z, float radius, bool clockwise, uint8 stepCount,
938 Optional<Milliseconds> duration /*= {}*/, Optional<float> speed /*= {}*/,
939 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/,
941{
942 std::function<void(Movement::MoveSplineInit&)> initializer = [=, this](Movement::MoveSplineInit& init)
943 {
944 float step = 2 * float(M_PI) / stepCount * (clockwise ? -1.0f : 1.0f);
945 Position const& pos = { x, y, z, 0.0f };
946 float angle = pos.GetAbsoluteAngle(_owner->GetPositionX(), _owner->GetPositionY());
947
948 init.Path().reserve(stepCount + 1);
949
950 // add the owner's current position as starting point as it gets removed after entering the cycle
951 init.Path().emplace_back(_owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ());
952
953 for (uint8 i = 0; i < stepCount; angle += step, ++i)
954 {
955 G3D::Vector3& point = init.Path().emplace_back();
956 point.x = x + radius * cosf(angle);
957 point.y = y + radius * sinf(angle);
958
959 if (_owner->IsFlying())
960 point.z = z;
961 else
962 point.z = _owner->GetMapHeight(point.x, point.y, z) + _owner->GetHoverOffset();
963 }
964
965 init.SetCyclic();
966 if (_owner->IsFlying())
967 {
968 init.SetFly();
969 init.SetAnimation(AnimTier::Hover);
970 }
971 else
972 init.SetWalk(true);
973
974 switch (speedSelectionMode)
975 {
977 init.SetWalk(false);
978 break;
980 init.SetWalk(true);
981 break;
983 default:
984 break;
985 }
986 if (speed)
987 init.SetVelocity(*speed);
988 };
989
990 Add(new GenericMovementGenerator(std::move(initializer), EFFECT_MOTION_TYPE, 0, { .Duration = duration, .ScriptResult = std::move(scriptResult) }));
991}
992
993void MotionMaster::MoveAlongSplineChain(uint32 pointId, uint16 dbChainId, bool walk)
994{
995 Creature* owner = _owner->ToCreature();
996 if (!owner)
997 {
998 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::MoveAlongSplineChain: '{}', tried to walk along DB spline chain. Ignoring.", _owner->GetGUID());
999 return;
1000 }
1001 std::vector<SplineChainLink> const* chain = sScriptSystemMgr->GetSplineChain(owner, dbChainId);
1002 if (!chain)
1003 {
1004 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::MoveAlongSplineChain: '{}', tried to walk along non-existing spline chain with DB Id: {}.", _owner->GetGUID(), dbChainId);
1005 return;
1006 }
1007 MoveAlongSplineChain(pointId, *chain, walk);
1008}
1009
1010void MotionMaster::MoveAlongSplineChain(uint32 pointId, std::vector<SplineChainLink> const& chain, bool walk)
1011{
1012 Add(new SplineChainMovementGenerator(pointId, chain, walk));
1013}
1014
1016{
1017 if (info.Empty())
1018 {
1019 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::ResumeSplineChain: '{}', tried to resume a spline chain from empty info.", _owner->GetGUID());
1020 return;
1021 }
1023}
1024
1027{
1029 {
1030 if (bool(*opt))
1032 });
1033
1034 // Use larger distance for vmap height search than in most other cases
1036 if (tz <= INVALID_HEIGHT)
1037 {
1038 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveFall: '{}', unable to retrieve a proper height at map Id: {} (X: {}, Y: {}, Z: {})",
1040 return;
1041 }
1042
1043 // Abort too if the ground is very near
1044 if (std::fabs(_owner->GetPositionZ() - tz) < 0.1f)
1045 return;
1046
1047 // rooted units don't move (also setting falling+root flag causes client freezes)
1049 return;
1050
1051 _owner->SetFall(true);
1052
1053 // Don't run spline movement for players
1054 if (_owner->GetTypeId() == TYPEID_PLAYER)
1055 {
1057 return;
1058 }
1059
1060 std::function<void(Movement::MoveSplineInit&)> initializer = [this, tz](Movement::MoveSplineInit& init)
1061 {
1062 init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz + _owner->GetHoverOffset(), false);
1063 init.SetFall();
1064 };
1065
1066 GenericMovementGenerator* movement = new GenericMovementGenerator(std::move(initializer), EFFECT_MOTION_TYPE, id, { .ScriptResult = std::move(*setterScopeExit.release()) });
1068 Add(movement);
1069}
1070
1071void MotionMaster::MoveSeekAssistance(float x, float y, float z)
1072{
1073 if (Creature* creature = _owner->ToCreature())
1074 {
1075 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveSeekAssistance: '{}', seeks assistance (X: {}, Y: {}, Z: {})", creature->GetGUID(), x, y, z);
1076 creature->AttackStop();
1077 creature->CastStop();
1078 creature->DoNotReacquireSpellFocusTarget();
1079 creature->SetReactState(REACT_PASSIVE);
1081 }
1082 else
1083 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::MoveSeekAssistance: '{}', attempted to seek assistance.", _owner->GetGUID());
1084}
1085
1087{
1088 if (_owner->GetTypeId() == TYPEID_UNIT)
1089 {
1090 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveSeekAssistanceDistract: '{}', is distracted after assistance call (Time: {})", _owner->GetGUID(), time);
1092 }
1093 else
1094 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::MoveSeekAssistanceDistract: '{}', attempted to call distract assistance.", _owner->GetGUID());
1095}
1096
1097void MotionMaster::MoveTaxiFlight(uint32 path, uint32 pathnode, Optional<float> speed /*= {}*/,
1099{
1100 if (_owner->GetTypeId() == TYPEID_PLAYER)
1101 {
1102 if (path < sTaxiPathNodesByPath.size())
1103 {
1104 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveTaxiFlight: '{}', taxi to path Id: {} (node {})", _owner->GetGUID(), path, pathnode);
1105
1106 // Only one FLIGHT_MOTION_TYPE is allowed
1107 bool hasExisting = HasMovementGenerator([](MovementGenerator const* gen) { return gen->GetMovementGeneratorType() == FLIGHT_MOTION_TYPE; });
1108 ASSERT(!hasExisting, "Duplicate flight path movement generator");
1109
1110 FlightPathMovementGenerator* movement = new FlightPathMovementGenerator(speed, std::move(scriptResult));
1111 movement->LoadPath(_owner->ToPlayer(), pathnode);
1112 Add(movement);
1113 }
1114 else
1115 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::MoveTaxiFlight: '{}', attempted taxi to non-existing path Id: {} (node: {})", _owner->GetGUID(), path, pathnode);
1116 }
1117 else
1118 TC_LOG_ERROR("movement.motionmaster", "MotionMaster::MoveTaxiFlight: '{}', attempted taxi to path Id: {} (node: {})", _owner->GetGUID(), path, pathnode);
1119}
1120
1121void MotionMaster::MoveDistract(uint32 timer, float orientation)
1122{
1123/*
1124 if (_slot[MOTION_SLOT_CONTROLLED])
1125 return;
1126*/
1127 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveDistract: '{}', distracted (timer: {}, orientation: {})", _owner->GetGUID(), timer, orientation);
1128 Add(new DistractMovementGenerator(timer, orientation));
1129}
1130
1131void MotionMaster::MovePath(uint32 pathId, bool repeatable, Optional<Milliseconds> duration /*= {}*/, Optional<float> speed /*= {}*/,
1132 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/,
1133 Optional<std::pair<Milliseconds, Milliseconds>> waitTimeRangeAtPathEnd /*= {}*/,
1134 Optional<float> wanderDistanceAtPathEnds /*= {}*/, Optional<bool> followPathBackwardsFromEndToStart /*= {}*/,
1135 Optional<bool> exactSplinePath /*= {}*/, bool generatePath /*= true*/, Optional<MovementFadeObject> fadeObject /*= {}*/,
1137{
1138 if (!pathId)
1139 {
1140 if (scriptResult)
1141 scriptResult.SetResult(MovementStopReason::Interrupted);
1142 return;
1143 }
1144
1145 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MovePath: '{}', starts moving over path Id: {} (repeatable: {})",
1146 _owner->GetGUID(), pathId, repeatable ? "YES" : "NO");
1147
1148 if (_owner->GetTypeId() == TYPEID_UNIT)
1149 Add(new WaypointMovementGenerator<Creature>(pathId, repeatable, duration, speed, speedSelectionMode, std::move(waitTimeRangeAtPathEnd),
1150 wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, exactSplinePath, generatePath, fadeObject, std::move(scriptResult)), MOTION_SLOT_DEFAULT);
1151 else
1152 Add(new WaypointMovementGenerator<Player>(pathId, repeatable, duration, speed, speedSelectionMode, std::move(waitTimeRangeAtPathEnd),
1153 wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, exactSplinePath, generatePath, {}, std::move(scriptResult)));
1154}
1155
1156void MotionMaster::MovePath(WaypointPath const& path, bool repeatable, Optional<Milliseconds> duration /*= {}*/, Optional<float> speed /*= {}*/,
1157 MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::Default*/,
1158 Optional<std::pair<Milliseconds, Milliseconds>> waitTimeRangeAtPathEnd /*= {}*/,
1159 Optional<float> wanderDistanceAtPathEnds /*= {}*/, Optional<bool> followPathBackwardsFromEndToStart /*= {}*/,
1160 Optional<bool> exactSplinePath /*= {}*/, bool generatePath /*= true*/, Optional<MovementFadeObject> fadeObject /*= {}*/,
1162{
1163 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MovePath: '{}', starts moving over path Id: {} (repeatable: {})",
1164 _owner->GetGUID(), path.Id, repeatable ? "YES" : "NO");
1165
1166 if (_owner->GetTypeId() == TYPEID_UNIT)
1167 Add(new WaypointMovementGenerator<Creature>(path, repeatable, duration, speed, speedSelectionMode, std::move(waitTimeRangeAtPathEnd),
1168 wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, exactSplinePath, generatePath, fadeObject, std::move(scriptResult)), MOTION_SLOT_DEFAULT);
1169 else
1170 Add(new WaypointMovementGenerator<Player>(path, repeatable, duration, speed, speedSelectionMode, std::move(waitTimeRangeAtPathEnd),
1171 wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, exactSplinePath, generatePath, {}, std::move(scriptResult)));
1172}
1173
1175 Optional<float> turnSpeed /*= {}*/, Optional<float> totalTurnAngle /*= {}*/,
1177{
1178 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveRotate: '{}', starts rotate (time: {}ms, turnSpeed: {}, totalTurnAngle: {}, direction: {})",
1179 _owner->GetGUID(), time.value_or(0ms).count(), turnSpeed, totalTurnAngle, direction);
1180
1181 Add(new RotateMovementGenerator(id, direction, time, turnSpeed, totalTurnAngle, std::move(scriptResult)));
1182}
1183
1184void MotionMaster::MoveFormation(Unit* leader, float range, float angle, uint32 point1, uint32 point2)
1185{
1186 if (_owner->GetTypeId() == TYPEID_UNIT && leader)
1187 {
1188 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveFormation: '{}', started to move in a formation with leader {}", _owner->GetGUID(), leader->GetGUID());
1189 Add(new FormationMovementGenerator(leader, range, angle, point1, point2), MOTION_SLOT_DEFAULT);
1190 }
1191}
1192
1193void 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 /*= {}*/)
1194{
1196 {
1197 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::LaunchMoveSpline: '{}', tried to launch a spline with an invalid MovementGeneratorType: {} (Id: {}, Priority: {})", _owner->GetGUID(), type, id, priority);
1198 return;
1199 }
1200
1201 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::LaunchMoveSpline: '{}', initiates spline Id: {} (Type: {}, Priority: {})", _owner->GetGUID(), id, type, priority);
1202
1203 GenericMovementGenerator* movement = new GenericMovementGenerator(std::move(initializer), type, id, { .ScriptResult = scriptResult });
1204 movement->Priority = priority;
1205 Add(movement);
1206}
1207
1208/******************** Private methods ********************/
1209
1211{
1212 while (!_delayedActions.empty())
1213 {
1214 _delayedActions.front().Resolve();
1215 _delayedActions.pop_front();
1216 }
1217}
1218
1219void MotionMaster::Remove(MotionMasterContainer::iterator iterator, bool active, bool movementInform)
1220{
1221 MovementGenerator* pointer = *iterator;
1222 _generators.erase(iterator);
1223 Delete(pointer, active, movementInform);
1224}
1225
1226void MotionMaster::Pop(bool active, bool movementInform)
1227{
1228 if (!_generators.empty())
1229 Remove(_generators.begin(), active, movementInform);
1230}
1231
1233{
1234 // Clear ALL movement generators (including default)
1236 DirectClear();
1237
1239}
1240
1242{
1243 // First delete Top
1244 if (!_generators.empty())
1245 Pop(true, false);
1246
1247 // Then the rest
1248 while (!_generators.empty())
1249 Pop(false, false);
1250
1251 // Make sure the storage is empty
1253}
1254
1256{
1258 DeleteDefault(_generators.empty(), false);
1259}
1260
1261void MotionMaster::DirectClear(std::function<bool(MovementGenerator*)> const& filter)
1262{
1263 if (_generators.empty())
1264 return;
1265
1267 for (auto itr = _generators.begin(); itr != _generators.end();)
1268 {
1269 if (filter(*itr))
1270 {
1271 MovementGenerator* movement = *itr;
1272 itr = _generators.erase(itr);
1273 Delete(movement, movement == top, false);
1274 }
1275 else
1276 ++itr;
1277 }
1278}
1279
1280void MotionMaster::DirectAdd(MovementGenerator* movement, MovementSlot slot/* = MOTION_SLOT_ACTIVE*/)
1281{
1282/*
1283 if (MovementGenerator* curr = _slot[slot])
1284 {
1285 _slot[slot] = nullptr; // in case a new one is generated in this slot during directdelete
1286 if (_top == slot && (_cleanFlag & MOTIONMMASTER_CLEANFLAG_UPDATE))
1287 DelayedDelete(curr);
1288 else
1289 DirectDelete(curr);
1290 }
1291 else if (_top < slot)
1292 {
1293 _top = slot;
1294 }
1295
1296 _slot[slot] = m;
1297 if (_top > slot)
1298 _initialize[slot] = true;
1299 else
1300 {
1301 _initialize[slot] = false;
1302 m->Initialize(_owner);
1303 }
1304*/
1305
1306 /*
1307 * NOTE: This mimics old behaviour: only one MOTION_SLOT_IDLE, MOTION_SLOT_ACTIVE, MOTION_SLOT_CONTROLLED
1308 * On future changes support for multiple will be added
1309 */
1310
1311 switch (slot)
1312 {
1315 _defaultGenerator->Finalize(_owner, _generators.empty(), false);
1316
1318 if (IsStatic(movement))
1320 break;
1321 case MOTION_SLOT_ACTIVE:
1322 if (!_generators.empty())
1323 {
1324 if (movement->Priority >= (*_generators.begin())->Priority)
1325 {
1326 auto itr = _generators.begin();
1327 if (movement->Priority == (*itr)->Priority)
1328 Remove(itr, true, false);
1329 else
1330 (*itr)->Deactivate(_owner);
1331 }
1332 else
1333 {
1334 auto itr = std::find_if(_generators.begin(), _generators.end(), [movement](MovementGenerator const* a) -> bool
1335 {
1336 return a->Priority == movement->Priority;
1337 });
1338
1339 if (itr != _generators.end())
1340 Remove(itr, false, false);
1341 }
1342 }
1343 else
1344 _defaultGenerator->Deactivate(_owner);
1345
1346 _generators.emplace(movement);
1347 AddBaseUnitState(movement);
1348 break;
1349 default:
1350 break;
1351 }
1352}
1353
1354void MotionMaster::Delete(MovementGenerator* movement, bool active, bool movementInform)
1355{
1356 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::Delete: deleting generator (Priority: {}, Flags: {}, BaseUnitState: {}, Type: {}), owner: '{}'",
1357 movement->Priority, movement->Flags, movement->BaseUnitState, movement->GetMovementGeneratorType(), _owner->GetGUID());
1358
1359 movement->Finalize(_owner, active, movementInform);
1360 ClearBaseUnitState(movement);
1362}
1363
1364void MotionMaster::DeleteDefault(bool active, bool movementInform)
1365{
1366 TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::DeleteDefault: deleting generator (Priority: {}, Flags: {}, BaseUnitState: {}, Type: {}), owner: '{}'",
1367 _defaultGenerator->Priority, _defaultGenerator->Flags, _defaultGenerator->BaseUnitState, _defaultGenerator->GetMovementGeneratorType(), _owner->GetGUID());
1368
1369 _defaultGenerator->Finalize(_owner, active, movementInform);
1372}
1373
1375{
1376 if (!movement || !movement->BaseUnitState)
1377 return;
1378
1379 _baseUnitStatesMap.emplace(movement->BaseUnitState, movement);
1380 _owner->AddUnitState(movement->BaseUnitState);
1381}
1382
1384{
1385 if (!movement || !movement->BaseUnitState)
1386 return;
1387
1389 if (_baseUnitStatesMap.count(movement->BaseUnitState) == 0)
1391}
1392
1394{
1395 uint32 unitState = 0;
1396 for (auto itr = _baseUnitStatesMap.begin(); itr != _baseUnitStatesMap.end(); ++itr)
1397 unitState |= itr->first;
1398
1399 _owner->ClearUnitState(unitState);
1400 _baseUnitStatesMap.clear();
1401}
#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:80
#define MAX_FALL_DISTANCE
Definition GridDefines.h:62
#define INVALID_HEIGHT
Definition GridDefines.h:61
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
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_DELAYED_CLEAR_MODE
@ MOTIONMASTER_FLAG_DELAYED
@ MOTIONMASTER_FLAG_INITIALIZING
@ MOTIONMASTER_FLAG_UPDATE
@ MOTIONMASTER_FLAG_INITIALIZATION_PENDING
@ MOTIONMASTER_FLAG_STATIC_INITIALIZATION_PENDING
bool IsInvalidMovementSlot(uint8 const slot)
MovementGeneratorMode
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
@ 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_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:271
@ UNIT_STATE_CHARGING
Definition Unit.h:278
@ UNIT_STATE_STUNNED
Definition Unit.h:264
@ UNIT_STATE_JUMPING
Definition Unit.h:279
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 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)
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 void Initialize(Unit *owner)=0
virtual bool Update(Unit *owner, uint32 diff)=0
void AddFlag(uint16 const flag)
virtual void Finalize(Unit *owner, bool active, bool movementInform)=0
bool HasFlag(uint16 const flag) const
virtual void Reset(Unit *owner)=0
virtual void UnitSpeedChanged()
virtual MovementGeneratorType GetMovementGeneratorType() const =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:27493
Definition Unit.h:635
void ClearUnitState(uint32 f)
Definition Unit.h:744
bool SetFall(bool enable)
Definition Unit.cpp:13424
void StopMoving()
Definition Unit.cpp:10680
AnimTier GetAnimTier() const
Definition Unit.h:909
void AddUnitState(uint32 f)
Definition Unit.h:742
Unit * GetCharmerOrOwner() const
Definition Unit.h:1221
float GetHoverOffset() const
Definition Unit.h:1812
bool HasUnitState(const uint32 f) const
Definition Unit.h:743
std::unique_ptr< Movement::MoveSpline > movespline
Definition Unit.h:1838
bool IsFlying() const
Definition Unit.h:1807
ObjectGuid GetTarget() const
Definition Unit.h:1831
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:3128
void MovePositionToFirstCollision(Position &pos, float dist, float angle) const
Definition Object.cpp:2828
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