diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 694c19287..90352ae04 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -22,6 +22,7 @@ #include "CreatureAI.h" #include "CreatureAISelector.h" #include "CreatureGroups.h" +#include "MoveSpline.h" #include "DatabaseEnv.h" #include "Formulas.h" #include "GameEventMgr.h" @@ -1093,8 +1094,7 @@ bool Creature::AIM_Initialize(CreatureAI* ai) UnitAI* oldAI = i_AI; - // Xinef: called in add to world - //Motion_Initialize(); + Motion_Initialize(); i_AI = ai ? ai : FactorySelector::SelectAI(this); delete oldAI; @@ -1117,7 +1117,19 @@ void Creature::Motion_Initialize() GetMotionMaster()->Initialize(); } else if (m_formation->IsFormed()) + { + // If the leader is already moving, start following immediately + // instead of waiting for the next waypoint signal. + if (Creature* leader = m_formation->GetLeader()) + { + if (leader->IsAlive() && !leader->movespline->Finalized()) + { + m_formation->LeaderStartedMoving(); + return; + } + } GetMotionMaster()->MoveIdle(); //wait the order of leader + } else GetMotionMaster()->Initialize(); } diff --git a/src/server/game/Entities/Creature/CreatureGroups.cpp b/src/server/game/Entities/Creature/CreatureGroups.cpp index b26ef4d9a..e3cb1fd3c 100644 --- a/src/server/game/Entities/Creature/CreatureGroups.cpp +++ b/src/server/game/Entities/Creature/CreatureGroups.cpp @@ -186,6 +186,7 @@ void CreatureGroup::RemoveMember(Creature* member) { if (m_leader == member) { + RemoveFormationMovement(); m_leader = nullptr; } @@ -333,7 +334,7 @@ void CreatureGroup::FormationReset(bool dismiss, bool initMotionMaster) if (initMotionMaster) { if (dismiss) - member->GetMotionMaster()->Initialize(); + member->GetMotionMaster()->MovementExpiredOnSlot(MOTION_SLOT_IDLE, false); else member->GetMotionMaster()->MoveIdle(); @@ -344,16 +345,11 @@ void CreatureGroup::FormationReset(bool dismiss, bool initMotionMaster) m_Formed = !dismiss; } -void CreatureGroup::LeaderMoveTo(float x, float y, float z, uint32 move_type) +void CreatureGroup::LeaderStartedMoving() { - //! To do: This should probably get its own movement generator or use WaypointMovementGenerator. - //! If the leader's path is known, member's path can be plotted as well using formation offsets. if (!m_leader) return; - float pathDist = m_leader->GetExactDist(x, y, z); - float pathAngle = std::atan2(m_leader->GetPositionY() - y, m_leader->GetPositionX() - x); - for (auto const& itr : m_members) { Creature* member = itr.first; @@ -361,59 +357,39 @@ void CreatureGroup::LeaderMoveTo(float x, float y, float z, uint32 move_type) if (member == m_leader || !member->IsAlive() || member->GetVictim() || !pFormationInfo.HasGroupFlag(std::underlying_type_t(GroupAIFlags::GROUP_AI_FLAG_FOLLOW_LEADER))) continue; - // If member is stunned / rooted etc don't allow to move him - // Or if charmed/controlled if (member->HasUnitState(UNIT_STATE_NOT_MOVE) || member->isPossessed() || member->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) continue; - // Xinef: this should be automatized, if turn angle is greater than PI/2 (90�) we should swap formation angle - float followAngle = pFormationInfo.follow_angle; - if (static_cast(M_PI) - std::fabs(std::fabs(m_leader->GetOrientation() - pathAngle) - static_cast(M_PI)) > static_cast(M_PI)* 0.5f) - { - // pussywizard: in both cases should be 2*M_PI - follow_angle - // pussywizard: also, GetCurrentWaypointID() returns 0..n-1, while point_1 must be > 0, so +1 - // pussywizard: db table waypoint_data shouldn't have point id 0 and shouldn't have any gaps for this to work! - // if (m_leader->GetCurrentWaypointID()+1 == pFormationInfo->point_1 || m_leader->GetCurrentWaypointID()+1 == itr->second->point_2) - followAngle = Position::NormalizeOrientation(pFormationInfo.follow_angle + static_cast(M_PI)); //(2 * M_PI) - itr->second->follow_angle; - } - + float const followAngle = pFormationInfo.follow_angle; float const followDist = pFormationInfo.follow_dist; - float dx = x + std::cos(followAngle + pathAngle) * followDist; - float dy = y + std::sin(followAngle + pathAngle) * followDist; - float dz = z; + if (!member->HasUnitState(UNIT_STATE_FOLLOW_MOVE)) + member->GetMotionMaster()->MoveFormation(m_leader, followDist, followAngle, pFormationInfo.point_1, pFormationInfo.point_2); + } +} - Acore::NormalizeMapCoord(dx); - Acore::NormalizeMapCoord(dy); - if (move_type < 2) - member->UpdateGroundPositionZ(dx, dy, dz); +bool CreatureGroup::CanLeaderStartMoving() const +{ + for (auto const& itr : m_members) + { + if (itr.first && itr.first != m_leader && itr.first->IsAlive()) + if (itr.first->IsEngaged() || itr.first->IsInEvadeMode()) + return false; + } - // pussywizard: setting the same movementflags is not enough, spline decides whether leader walks/runs, so spline param is now passed as "run" parameter to this function - member->SetUnitMovementFlags(m_leader->GetUnitMovementFlags()); - switch (move_type) - { - case WAYPOINT_MOVE_TYPE_WALK: - member->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); - break; - case WAYPOINT_MOVE_TYPE_RUN: - member->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - break; - case WAYPOINT_MOVE_TYPE_LAND: - member->AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); - break; - } + return true; +} - // xinef: if we move members to position without taking care of sizes, we should compare distance without sizes - // xinef: change members speed basing on distance - if too far speed up, if too close slow down - UnitMoveType const mtype = Movement::SelectSpeedType(member->GetUnitMovementFlags()); - float const speedRate = m_leader->GetSpeedRate(mtype) * member->GetExactDist(dx, dy, dz) / pathDist; +void CreatureGroup::RemoveFormationMovement() +{ + for (auto const& itr : m_members) + { + Creature* member = itr.first; + if (!member || member == m_leader) + continue; - if (speedRate > 0.01f) // don't move if speed rate is too low - { - member->SetSpeedRate(mtype, speedRate); - member->GetMotionMaster()->MovePoint(0, dx, dy, dz); - member->SetHomePosition(dx, dy, dz, pathAngle); - } + if (member->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_IDLE) == FORMATION_MOTION_TYPE) + member->GetMotionMaster()->MovementExpiredOnSlot(MOTION_SLOT_IDLE, false); } } diff --git a/src/server/game/Entities/Creature/CreatureGroups.h b/src/server/game/Entities/Creature/CreatureGroups.h index 0ce3a7c90..a397a5b3c 100644 --- a/src/server/game/Entities/Creature/CreatureGroups.h +++ b/src/server/game/Entities/Creature/CreatureGroups.h @@ -108,7 +108,9 @@ public: void RemoveMember(Creature* member); void FormationReset(bool dismiss, bool initMotionMaster); - void LeaderMoveTo(float x, float y, float z, uint32 move_type); + void LeaderStartedMoving(); + [[nodiscard]] bool CanLeaderStartMoving() const; + void RemoveFormationMovement(); void MemberEngagingTarget(Creature* member, Unit* target); Unit* GetNewTargetForMember(Creature* member); void MemberEvaded(Creature* member); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 11065a615..beea748b1 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -44,6 +44,7 @@ #include "MoveSpline.h" #include "MoveSplineInit.h" #include "MovementGenerator.h" +#include "AbstractFollower.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "OutdoorPvP.h" @@ -5528,6 +5529,16 @@ void Unit::RemoveAreaAurasDueToLeaveWorld() } } +void Unit::RemoveAllFollowers() +{ + while (auto* ref = m_FollowingRefMgr.getFirst()) + { + auto* source = ref->GetSource(); + ref->delink(); + source->SetTarget(nullptr); + } +} + void Unit::RemoveAllAuras() { // this may be a dead loop if some events on aura remove will continiously apply aura on remove @@ -12700,6 +12711,8 @@ void Unit::RemoveFromWorld() RemoveAreaAurasDueToLeaveWorld(); + RemoveAllFollowers(); + if (GetCharmerGUID()) { LOG_FATAL("entities.unit", "Unit {} has charmer guid when removed from world", GetEntry()); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 6be903f8b..94ec2e962 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1881,9 +1881,10 @@ public: [[nodiscard]] bool IsInDisallowedMountForm() const; // Followers - void addFollower(FollowerReference* pRef) { m_FollowingRefMgr.insertFirst(pRef); } - void removeFollower(FollowerReference* /*pRef*/) { /* nothing to do yet */ } + void AddFollower(FollowerReference* ref) { m_FollowingRefMgr.insertFirst(ref); } [[nodiscard]] virtual float GetFollowAngle() const { return static_cast(M_PI / 2); } + void RemoveFollower(FollowerReference* /*ref*/ ) { /* nothing to do yet */ } + void RemoveAllFollowers(); // Pets, guardians, minions... [[nodiscard]] Guardian* GetGuardianPet() const; diff --git a/src/server/game/Movement/AbstractFollower.cpp b/src/server/game/Movement/AbstractFollower.cpp new file mode 100644 index 000000000..a2d60f393 --- /dev/null +++ b/src/server/game/Movement/AbstractFollower.cpp @@ -0,0 +1,28 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "AbstractFollower.h" +#include "Unit.h" + +void AbstractFollower::SetTarget(Unit* unit) +{ + if (unit == GetTarget()) + return; + + i_target.link(unit, this); + _target = i_target.getTarget(); +} diff --git a/src/server/game/Movement/AbstractFollower.h b/src/server/game/Movement/AbstractFollower.h new file mode 100644 index 000000000..ae4d2bd73 --- /dev/null +++ b/src/server/game/Movement/AbstractFollower.h @@ -0,0 +1,43 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef ACORE_ABSTRACTFOLLOWER_H +#define ACORE_ABSTRACTFOLLOWER_H + +#include "FollowerReference.h" + +class Unit; + +class AbstractFollower +{ +public: + explicit AbstractFollower(Unit* target = nullptr) { SetTarget(target); } + virtual ~AbstractFollower() { SetTarget(nullptr); } + + void SetTarget(Unit* unit); + Unit* GetTarget() const { return _target; } + bool IsTargetValid() const { return i_target.isValid(); } + + virtual void stopFollowing() { } + + Unit* _target = nullptr; + +protected: + FollowerReference i_target; +}; + +#endif diff --git a/src/server/game/Movement/FollowerRefMgr.h b/src/server/game/Movement/FollowerRefMgr.h index 6c2e6de43..acece2dbb 100644 --- a/src/server/game/Movement/FollowerRefMgr.h +++ b/src/server/game/Movement/FollowerRefMgr.h @@ -21,9 +21,9 @@ #include "RefMgr.h" class Unit; -class TargetedMovementGeneratorBase; +class AbstractFollower; -class FollowerRefMgr : public RefMgr +class FollowerRefMgr : public RefMgr { }; #endif diff --git a/src/server/game/Movement/FollowerReference.cpp b/src/server/game/Movement/FollowerReference.cpp index 287a0d9c7..21e565726 100644 --- a/src/server/game/Movement/FollowerReference.cpp +++ b/src/server/game/Movement/FollowerReference.cpp @@ -16,17 +16,17 @@ */ #include "FollowerReference.h" -#include "TargetedMovementGenerator.h" +#include "AbstractFollower.h" #include "Unit.h" void FollowerReference::targetObjectBuildLink() { - getTarget()->addFollower(this); + getTarget()->AddFollower(this); } void FollowerReference::targetObjectDestroyLink() { - getTarget()->removeFollower(this); + getTarget()->RemoveFollower(this); } void FollowerReference::sourceObjectDestroyLink() diff --git a/src/server/game/Movement/FollowerReference.h b/src/server/game/Movement/FollowerReference.h index 2d1c77f46..c31f42850 100644 --- a/src/server/game/Movement/FollowerReference.h +++ b/src/server/game/Movement/FollowerReference.h @@ -20,10 +20,10 @@ #include "Reference.h" -class TargetedMovementGeneratorBase; +class AbstractFollower; class Unit; -class FollowerReference : public Reference +class FollowerReference : public Reference { protected: void targetObjectBuildLink() override; diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 8e4f91c16..7625a6b21 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -21,6 +21,7 @@ #include "CreatureAISelector.h" #include "EscortMovementGenerator.h" #include "FleeingMovementGenerator.h" +#include "FormationMovementGenerator.h" #include "GameTime.h" #include "HomeMovementGenerator.h" #include "IdleMovementGenerator.h" @@ -466,6 +467,17 @@ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlo } } +void MotionMaster::MoveFormation(Unit* leader, float dist, float angle, uint32 point1, uint32 point2) +{ + if (!leader || leader == _owner || _owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) + return; + + if (!_owner->IsCreature()) + return; + + Mutate(new FormationMovementGenerator(leader, dist, angle, point1, point2), MOTION_SLOT_IDLE); +} + /** * @brief The unit will move to a specific point. Doesn't work with UNIT_FLAG_DISABLE_MOVE * diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index 4ac140260..c33c29473 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -55,7 +55,8 @@ enum MovementGeneratorType ROTATE_MOTION_TYPE = 15, EFFECT_MOTION_TYPE = 16, ESCORT_MOTION_TYPE = 17, // xinef: EscortMovementGenerator.h - NULL_MOTION_TYPE = 18 + FORMATION_MOTION_TYPE = 18, // FormationMovementGenerator.h + NULL_MOTION_TYPE = 19 }; enum MovementSlot @@ -227,6 +228,7 @@ public: void MoveTargetedHome(bool walk = false); void MoveRandom(float wanderDistance = 0.0f); void MoveFollow(Unit* target, float dist, float angle, MovementSlot slot = MOTION_SLOT_ACTIVE, bool inheritWalkState = true, bool inheritSpeed = true); + void MoveFormation(Unit* leader, float dist, float angle, uint32 point1, uint32 point2); void MoveChase(Unit* target, std::optional dist = {}, std::optional angle = {}); void MoveChase(Unit* target, float dist, float angle) { MoveChase(target, ChaseRange(dist), ChaseAngle(angle)); } void MoveChase(Unit* target, float dist) { MoveChase(target, ChaseRange(dist)); } diff --git a/src/server/game/Movement/MovementGenerators/FormationMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FormationMovementGenerator.cpp new file mode 100644 index 000000000..1a968603c --- /dev/null +++ b/src/server/game/Movement/MovementGenerators/FormationMovementGenerator.cpp @@ -0,0 +1,173 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "FormationMovementGenerator.h" +#include "Creature.h" +#include "CreatureAI.h" +#include "CreatureGroups.h" +#include "MoveSpline.h" +#include "MoveSplineInit.h" + +FormationMovementGenerator::FormationMovementGenerator(Unit* leader, float range, float angle, uint32 point1, uint32 point2) + : AbstractFollower(leader), _range(range), _angle(angle), _point1(point1), _point2(point2), _lastLeaderSplineID(0), + _hasPredictedDestination(false), _isMoving(false), _nextMoveTimer(0) +{ +} + +void FormationMovementGenerator::DoInitialize(Creature* owner) +{ + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) + { + owner->StopMoving(); + return; + } + + _nextMoveTimer.Reset(0); +} + +void FormationMovementGenerator::DoFinalize(Creature* owner) +{ + owner->ClearUnitState(UNIT_STATE_FOLLOW_MOVE); +} + +void FormationMovementGenerator::DoReset(Creature* owner) +{ + DoInitialize(owner); +} + +bool FormationMovementGenerator::DoUpdate(Creature* owner, uint32 diff) +{ + Unit* target = GetTarget(); + if (!owner || !target) + return false; + + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) + { + owner->StopMoving(); + _nextMoveTimer.Reset(0); + _hasPredictedDestination = false; + _isMoving = false; + return true; + } + + if (target->movespline->Finalized() && target->movespline->GetId() == _lastLeaderSplineID && _hasPredictedDestination) + { + owner->StopMoving(); + _nextMoveTimer.Reset(0); + _hasPredictedDestination = false; + _isMoving = false; + return true; + } + + if (!owner->movespline->Finalized()) + owner->SetHomePosition(owner->GetPosition()); + + if (!target->movespline->Finalized() && target->movespline->GetId() != _lastLeaderSplineID) + { + if (_point1 && target->IsCreature()) + { + if (CreatureGroup* formation = target->ToCreature()->GetFormation()) + { + if (Creature* leader = formation->GetLeader()) + { + uint32 currentWaypoint = leader->GetCurrentWaypointID() + 1; + if (currentWaypoint == _point1 || currentWaypoint == _point2) + _angle = Position::NormalizeOrientation(float(2 * M_PI) - _angle); + } + } + } + + LaunchMovement(owner, target); + _lastLeaderSplineID = target->movespline->GetId(); + return true; + } + + _nextMoveTimer.Update(diff); + if (_nextMoveTimer.Passed()) + { + _nextMoveTimer.Reset(FORMATION_MOVEMENT_INTERVAL); + + if (_lastLeaderPosition != target->GetPosition()) + { + LaunchMovement(owner, target); + return true; + } + } + + if (_isMoving && owner->movespline->Finalized()) + { + _isMoving = false; + owner->SetFacingTo(target->GetOrientation()); + MovementInform(owner); + } + + return true; +} + +void FormationMovementGenerator::LaunchMovement(Creature* owner, Unit* target) +{ + float relativeAngle = 0.0f; + + if (!target->movespline->Finalized()) + { + G3D::Vector3 const leaderDestination = target->movespline->CurrentDestination(); + relativeAngle = target->GetRelativeAngle(leaderDestination.x, leaderDestination.y); + } + + Position dest = target->GetPosition(); + float velocity = 0.0f; + + if (!target->movespline->Finalized()) + { + // Pick up leader's spline velocity + velocity = target->movespline->Velocity(); + + // Calculate travel distance to get a 1650ms result + float travelDist = velocity * 1.65f; + target->MovePositionToFirstCollision(dest, travelDist, relativeAngle); + target->MovePositionToFirstCollision(dest, _range, _angle + relativeAngle); + + float distance = owner->GetExactDist(dest); + float velocityMod = std::min(distance / travelDist, 1.5f); + + velocity *= velocityMod; + _hasPredictedDestination = true; + } + else + { + target->MovePositionToFirstCollision(dest, _range, _angle + relativeAngle); + _hasPredictedDestination = false; + } + + if (velocity == 0.0f) + velocity = target->GetSpeed(MOVE_WALK); + + Movement::MoveSplineInit init(owner); + init.MoveTo(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ()); + init.SetVelocity(velocity); + init.Launch(); + + _lastLeaderPosition.Relocate(target->GetPosition()); + _isMoving = true; + owner->AddUnitState(UNIT_STATE_FOLLOW_MOVE); +} + +void FormationMovementGenerator::MovementInform(Creature* owner) +{ + if (owner->AI()) + owner->AI()->MovementInform(FORMATION_MOTION_TYPE, 0); +} diff --git a/src/server/game/Movement/MovementGenerators/FormationMovementGenerator.h b/src/server/game/Movement/MovementGenerators/FormationMovementGenerator.h new file mode 100644 index 000000000..c859a8da3 --- /dev/null +++ b/src/server/game/Movement/MovementGenerators/FormationMovementGenerator.h @@ -0,0 +1,58 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef ACORE_FORMATIONMOVEMENTGENERATOR_H +#define ACORE_FORMATIONMOVEMENTGENERATOR_H + +#include "AbstractFollower.h" +#include "MovementGenerator.h" +#include "Position.h" +#include "Timer.h" + +class Creature; + +class FormationMovementGenerator : public MovementGeneratorMedium, public AbstractFollower +{ +public: + explicit FormationMovementGenerator(Unit* leader, float range, float angle, uint32 point1, uint32 point2); + + MovementGeneratorType GetMovementGeneratorType() { return FORMATION_MOTION_TYPE; } + + void DoInitialize(Creature*); + void DoFinalize(Creature*); + void DoReset(Creature*); + bool DoUpdate(Creature*, uint32); + +private: + void MovementInform(Creature* owner); + void LaunchMovement(Creature* owner, Unit* target); + + static constexpr uint32 FORMATION_MOVEMENT_INTERVAL = 1200; + + float const _range; + float _angle; + uint32 const _point1; + uint32 const _point2; + uint32 _lastLeaderSplineID; + bool _hasPredictedDestination; + bool _isMoving; + + Position _lastLeaderPosition; + TimeTracker _nextMoveTimer; +}; + +#endif diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp index 996544943..f32542c7d 100644 --- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp @@ -67,8 +67,8 @@ void RandomMovementGenerator::_setRandomLocation(Creature* creature) init.SetWalk(walk); init.Launch(); - if (creature->GetFormation() && creature->GetFormation()->GetLeader() == creature) - creature->GetFormation()->LeaderMoveTo(_currDestPosition.GetPositionX(), _currDestPosition.GetPositionY(), _currDestPosition.GetPositionZ(), 0); + if (creature->GetFormation() && creature->GetFormation()->GetLeader() == creature && creature->GetFormation()->CanLeaderStartMoving()) + creature->GetFormation()->LeaderStartedMoving(); return; } @@ -242,8 +242,8 @@ void RandomMovementGenerator::_setRandomLocation(Creature* creature) } //Call for creature group update - if (creature->GetFormation() && creature->GetFormation()->GetLeader() == creature) - creature->GetFormation()->LeaderMoveTo(finalPoint.x, finalPoint.y, finalPoint.z, 0); + if (creature->GetFormation() && creature->GetFormation()->GetLeader() == creature && creature->GetFormation()->CanLeaderStartMoving()) + creature->GetFormation()->LeaderStartedMoving(); if (sWorld->getBoolConfig(CONFIG_DONT_CACHE_RANDOM_MOVEMENT_PATHS)) _preComputedPaths.erase(pathIdx); diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h index 913738a92..c8485e130 100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h @@ -18,21 +18,14 @@ #ifndef ACORE_TARGETEDMOVEMENTGENERATOR_H #define ACORE_TARGETEDMOVEMENTGENERATOR_H -#include "FollowerReference.h" +#include "AbstractFollower.h" #include "MovementGenerator.h" #include "Optional.h" #include "PathGenerator.h" #include "Timer.h" #include "Unit.h" -class TargetedMovementGeneratorBase -{ -public: - TargetedMovementGeneratorBase(Unit* target) { i_target.link(target, this); } - void stopFollowing() { } -protected: - FollowerReference i_target; -}; +using TargetedMovementGeneratorBase = AbstractFollower; enum ChaseMovementMode { @@ -43,11 +36,11 @@ enum ChaseMovementMode }; template -class ChaseMovementGenerator : public MovementGeneratorMedium>, public TargetedMovementGeneratorBase +class ChaseMovementGenerator : public MovementGeneratorMedium>, public AbstractFollower { public: ChaseMovementGenerator(Unit* target, Optional range = {}, Optional angle = {}) - : TargetedMovementGeneratorBase(target), i_leashExtensionTimer(5000), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle), m_currentMode(CHASE_MODE_NORMAL) {} + : AbstractFollower(target), i_leashExtensionTimer(5000), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle), m_currentMode(CHASE_MODE_NORMAL) {} ~ChaseMovementGenerator() { } MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; } @@ -61,7 +54,7 @@ public: bool PositionOkay(T* owner, Unit* target, Optional maxDistance, Optional angle); void unitSpeedChanged() { _lastTargetPosition.reset(); } - Unit* GetTarget() const { return i_target.getTarget(); } + Unit* GetTarget() const { return AbstractFollower::GetTarget(); } bool EnableWalking() const { return false; } bool HasLostTarget(Unit* unit) const { return unit->GetVictim() != this->GetTarget(); } @@ -87,11 +80,11 @@ private: }; template -class FollowMovementGenerator : public MovementGeneratorMedium>, public TargetedMovementGeneratorBase +class FollowMovementGenerator : public MovementGeneratorMedium>, public AbstractFollower { public: FollowMovementGenerator(Unit* target, float range, ChaseAngle angle, bool inheritWalkState, bool inheritSpeed) - : TargetedMovementGeneratorBase(target), i_path(nullptr), i_recheckPredictedDistanceTimer(0), i_recheckPredictedDistance(false), _range(range), _angle(angle), _inheritWalkState(inheritWalkState), _inheritSpeed(inheritSpeed) {} + : AbstractFollower(target), i_path(nullptr), i_recheckPredictedDistanceTimer(0), i_recheckPredictedDistance(false), _range(range), _angle(angle), _inheritWalkState(inheritWalkState), _inheritSpeed(inheritSpeed) {} ~FollowMovementGenerator() { } MovementGeneratorType GetMovementGeneratorType() { return FOLLOW_MOTION_TYPE; } @@ -102,7 +95,7 @@ public: void DoReset(T*); void MovementInform(T*); - Unit* GetTarget() const { return i_target.getTarget(); } + Unit* GetTarget() const { return AbstractFollower::GetTarget(); } void unitSpeedChanged() { _lastTargetPosition.reset(); } diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index bee97062a..a8da788e5 100644 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -222,8 +222,8 @@ bool WaypointMovementGenerator::StartMove(Creature* creature) init.Launch(); //Call for creature group update - if (creature->GetFormation() && creature->GetFormation()->GetLeader() == creature) - creature->GetFormation()->LeaderMoveTo(formationDest.x, formationDest.y, formationDest.z, node.move_type); + if (creature->GetFormation() && creature->GetFormation()->GetLeader() == creature && creature->GetFormation()->CanLeaderStartMoving()) + creature->GetFormation()->LeaderStartedMoving(); return true; } diff --git a/src/server/game/Movement/Spline/MoveSpline.cpp b/src/server/game/Movement/Spline/MoveSpline.cpp index 0d9581a2e..f10f055be 100644 --- a/src/server/game/Movement/Spline/MoveSpline.cpp +++ b/src/server/game/Movement/Spline/MoveSpline.cpp @@ -166,6 +166,7 @@ namespace Movement time_passed = 0; vertical_acceleration = 0.f; effect_start_time = 0; + velocity = args.velocity; // Check if its a stop spline if (args.flags.done) @@ -190,7 +191,7 @@ namespace Movement } MoveSpline::MoveSpline() : m_Id(0), time_passed(0), - vertical_acceleration(0.f), initialOrientation(0.f), effect_start_time(0), point_Idx(0), point_Idx_offset(0), + vertical_acceleration(0.f), initialOrientation(0.f), velocity(0.f), effect_start_time(0), point_Idx(0), point_Idx_offset(0), onTransport(false) { splineflags.done = true; diff --git a/src/server/game/Movement/Spline/MoveSpline.h b/src/server/game/Movement/Spline/MoveSpline.h index b74d0d244..3b134d44e 100644 --- a/src/server/game/Movement/Spline/MoveSpline.h +++ b/src/server/game/Movement/Spline/MoveSpline.h @@ -64,6 +64,7 @@ namespace Movement //float duration_mod_next; float vertical_acceleration; float initialOrientation; + float velocity; int32 effect_start_time; int32 point_Idx; int32 point_Idx_offset; @@ -85,6 +86,7 @@ namespace Movement [[nodiscard]] int32 Duration() const { return spline.length(); } [[nodiscard]] MySpline const& _Spline() const { return spline; } [[nodiscard]] int32 _currentSplineIdx() const { return point_Idx; } + [[nodiscard]] float Velocity() const { return velocity; } void _Finalize(); void _Interrupt() { splineflags.done = true; }