From adb7434c1df214e6717c9250339935a01e3a421a Mon Sep 17 00:00:00 2001 From: Stoabrogga Date: Sun, 18 Jan 2026 22:45:30 +0100 Subject: [PATCH 1/2] Improve waypoint movement --- .../Movement/WaypointMovementGenerator.cpp | 243 ++++++++++++++---- src/game/Movement/WaypointMovementGenerator.h | 14 +- src/game/Movement/spline/MoveSplineInit.h | 2 + 3 files changed, 211 insertions(+), 48 deletions(-) diff --git a/src/game/Movement/WaypointMovementGenerator.cpp b/src/game/Movement/WaypointMovementGenerator.cpp index 3fba85d3e58..9c255b528bc 100644 --- a/src/game/Movement/WaypointMovementGenerator.cpp +++ b/src/game/Movement/WaypointMovementGenerator.cpp @@ -28,10 +28,31 @@ #include "MoveSpline.h" #include "CreatureGroups.h" #include "Map.h" - -#include +#include "Geometry.h" //-----------------------------------------------// +float BuildIntPath(PointsArray& dstPath, PointsArray const& srcPath) +{ + Vector3 prevPoint = dstPath.back(); + float distance = 0; + bool first = true; + + for (auto& point : srcPath) + { + if (first) // first point has to already be in dstPath + { + first = false; + continue; + } + + distance += Geometry::GetDistance3D(point, prevPoint); + prevPoint = point; + dstPath.push_back(point); + } + + return distance; +} + void WaypointMovementGenerator::LoadPath(uint32 guid, uint32 entry, WaypointPathOrigin wpOrigin) { DETAIL_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "LoadPath: loading waypoint path for GUID %u Entry %u", guid, entry); @@ -137,16 +158,17 @@ bool WaypointMovementGenerator::OnArrived(Creature& creature) return false; } - m_lastReachedWaypoint = i_currentNode; - - if (m_isArrivalDone) - return true; - creature.ClearUnitState(UNIT_STATE_ROAMING_MOVE); - m_isArrivalDone = true; WaypointPath::const_iterator currPoint = i_path->find(i_currentNode); - MANGOS_ASSERT(currPoint != i_path->end()); + if (currPoint == i_path->end()) + return false; + + m_lastReachedWaypoint = i_currentNode++; + + if (i_path->find(i_currentNode) == i_path->end() && m_repeating) + i_currentNode = i_path->begin()->first; + WaypointNode const& node = currPoint->second; if (node.script_id) @@ -181,64 +203,113 @@ bool WaypointMovementGenerator::OnArrived(Creature& creature) return true; } +// minimum time that will take the unit to travel the path (much higher value have been seen in sniff) +static const uint32 MinimumPathTime = 6000; +// client need to receive new path before the end of previous path (much higher value have been seen in sniff) +static const uint32 PreSendTime = 1500; + void WaypointMovementGenerator::StartMove(Creature &creature) { + m_nodeIndexes.clear(); // make sure to reset spline indexes as its a new path + if (!i_path || i_path->empty()) return; if (Stopped()) return; + m_creatureSpeed = creature.GetSpeed(Movement::SelectSpeedType(creature.m_movementInfo.GetMovementFlags())); WaypointPath::const_iterator currPoint = i_path->find(i_currentNode); - MANGOS_ASSERT(currPoint != i_path->end()); - if (m_isArrivalDone) + if (currPoint == i_path->end()) + { + creature.SetHomePosition(i_path->at(m_lastReachedWaypoint).x, i_path->at(m_lastReachedWaypoint).y, i_path->at(m_lastReachedWaypoint).z, i_path->at(m_lastReachedWaypoint).orientation); + creature.GetMotionMaster()->Initialize(); + return; + } + + creature.AddUnitState(UNIT_STATE_ROAMING_MOVE); + + Movement::MoveSplineInit init(creature, "WaypointMovementGenerator::StartMove"); + PointsArray genPath; // will contain the generated path + genPath.reserve(20); // little optimization + genPath.emplace_back(creature.GetPositionX(), creature.GetPositionY(), creature.GetPositionZ()); + Vector3 startPos, endPos; + uint32 travelTime = 0; + uint32 nextDelay = 0; + float nextOrientation = 0.f; + uint32 loops = 0; + bool pathFinished = false; + + do { - bool reachedLast = false; + WaypointNode const& currNode = currPoint->second; + nextDelay = currNode.delay; + nextOrientation = currNode.orientation; + startPos = genPath[genPath.size() - 1]; + endPos = Vector3(currNode.x, currNode.y, currNode.z); + + if (WaypointPath const* pSubpath = (currNode.path_id ? sWaypointMgr.GetPathFromOrigin(currNode.path_id, 0, 0, PATH_FROM_SPECIAL) : nullptr)) + { + PointsArray tmp; + tmp.push_back(startPos); + for (auto const& node : *pSubpath) + tmp.emplace_back(node.second.x, node.second.y, node.second.z); + + travelTime += BuildIntPath(genPath, tmp) / m_creatureSpeed * 1000.f; + + if (creature.CanFly()) + init.SetFly(); + } + else if (m_PathOrigin != PATH_FROM_SPECIAL) + { + PathFinder pathfinder(&creature); + pathfinder.calculate(startPos, endPos, true); + travelTime += BuildIntPath(genPath, pathfinder.getPath()) / m_creatureSpeed * 1000.f; + } + else + { + PointsArray tmp; + tmp.push_back(startPos); + tmp.push_back(endPos); + travelTime += BuildIntPath(genPath, tmp) / m_creatureSpeed * 1000.f; + } + + m_nodeIndexes.push_back(genPath.size()); + + if (nextDelay) + break; + ++currPoint; + if (currPoint == i_path->end()) { - reachedLast = true; - if (!m_repeating) { - creature.SetHomePosition(i_path->at(i_currentNode).x, i_path->at(i_currentNode).y, i_path->at(i_currentNode).z, i_path->at(i_currentNode).orientation); - creature.GetMotionMaster()->Initialize(); - return; + pathFinished = true; + break; } currPoint = i_path->begin(); + ++loops; } + } while (travelTime < MinimumPathTime && loops < 3); - i_currentNode = currPoint->first; - } - - m_isArrivalDone = false; - creature.AddUnitState(UNIT_STATE_ROAMING_MOVE); - - WaypointNode const& nextNode = currPoint->second; - Movement::MoveSplineInit init(creature, "WaypointMovementGenerator::StartMove"); - - if (WaypointPath const* pSubpath = (nextNode.path_id ? sWaypointMgr.GetPathFromOrigin(nextNode.path_id, 0, 0, PATH_FROM_SPECIAL) : nullptr)) - { - PointsArray genPath; - genPath.resize(pSubpath->size()); - for (auto const& node : *pSubpath) - genPath[node.first] = G3D::Vector3(node.second.x, node.second.y, node.second.z); + if (!travelTime) + return; - if (creature.CanFly()) - init.SetFly(); + init.MovebyPath(genPath); - init.MovebyPath(genPath); - } - else - init.MoveTo(nextNode.x, nextNode.y, nextNode.z, (m_PathOrigin == PATH_FROM_SPECIAL) ? MOVE_STRAIGHT_PATH : MOVE_PATHFINDING); - - if (nextNode.orientation != 100 && nextNode.delay != 0) - init.SetFacing(nextNode.orientation); + if (nextOrientation != 100 && nextDelay != 0) + init.SetFacing(nextOrientation); creature.SetWalk(!creature.HasUnitState(UNIT_STATE_RUNNING) && !creature.IsLevitating(), false); - init.Launch(); + m_pathDuration = init.Launch(); + m_lastSplineId = creature.movespline->GetId(); + + // remove presend time if next node have no delay and path is not finished + if (nextDelay == 0 && !pathFinished) + m_pathDuration -= PreSendTime; } bool WaypointMovementGenerator::Update(Creature &creature, uint32 const& diff) @@ -264,7 +335,6 @@ bool WaypointMovementGenerator::Update(Creature &creature, uint32 cons { creature.StopMoving(); i_nextMoveTime.Reset(1); - m_isArrivalDone = false; } creature.ClearUnitState(UNIT_STATE_ROAMING_MOVE); @@ -276,6 +346,12 @@ bool WaypointMovementGenerator::Update(Creature &creature, uint32 cons if (CanMove(diff)) StartMove(creature); } + else if (m_creatureSpeed != creature.GetSpeed(Movement::SelectSpeedType(creature.m_movementInfo.GetMovementFlags()))) + { + creature.StopMoving(); + if (CanMove(diff)) + StartMove(creature); + } else { if (creature.IsStopped()) @@ -285,6 +361,25 @@ bool WaypointMovementGenerator::Update(Creature &creature, uint32 cons if (OnArrived(creature)) // fire script events StartMove(creature); // restart movement if needed } + else + { + if (m_pathDuration <= 0) + { + WaypointPath::const_iterator currPoint = i_path->find(i_currentNode); + WaypointNode const& node = currPoint->second; + if (!node.delay) + StartMove(creature); + } + else + m_pathDuration -= diff; + + if (!m_nodeIndexes.empty() && creature.movespline->_currentSplineIdx() >= m_nodeIndexes.front()) + { + m_nodeIndexes.pop_front(); + if (OnArrived(creature)); + creature.AddUnitState(UNIT_STATE_ROAMING_MOVE); + } + } } return true; } @@ -321,7 +416,6 @@ bool WaypointMovementGenerator::SetNextWaypoint(uint32 pointId) // Handle allow movement this way to not interact with PAUSED state. // If this function is called while PAUSED, it will move properly when unpaused. i_nextMoveTime.Reset(1); - m_isArrivalDone = false; // Set the point i_currentNode = pointId; @@ -533,7 +627,8 @@ bool PatrolMovementGenerator::Update(Creature &creature, uint32 const& diff) return true; } - if (creature.movespline->Finalized()) + Creature* leader = creature.GetMap()->GetCreature(m_leaderGuid); + if (creature.movespline->Finalized() || (leader && !leader->movespline->Finalized() && leader->movespline->GetId() != m_leaderSplineId)) StartMove(creature); return true; @@ -559,6 +654,8 @@ void PatrolMovementGenerator::StartMove(Creature& creature) if (!leader || leader->movespline->Finalized()) return; + m_leaderSplineId = leader->movespline->GetId(); + switch (leader->GetMotionMaster()->GetCurrentMovementGeneratorType()) { case RANDOM_MOTION_TYPE: @@ -582,6 +679,62 @@ void PatrolMovementGenerator::StartMove(Creature& creature) if (!leaderTimeBeforeNextWP) return; + WaypointMovementGenerator const* wpMMGen = dynamic_cast const*>(leader->GetMotionMaster()->GetCurrent()); + if (wpMMGen && !wpMMGen->GetNodeIndexes().empty() && wpMMGen->GetLastSplineId() == leader->movespline->GetId()) + { + std::list nodeIndexes; + for (int32 idx : wpMMGen->GetNodeIndexes()) + nodeIndexes.push_back(idx); + + float angle = 0.f; + float distance = 0.f; + PointsArray genPath; + genPath.emplace_back(creature.GetPositionX(), creature.GetPositionY(), creature.GetPositionZ()); + Vector3 prevPoint = leader->movespline->GetPoint(0); + + while (!nodeIndexes.empty()) + { + Vector3 point = leader->movespline->GetPoint(nodeIndexes.front()); + nodeIndexes.pop_front(); + Vector3 direction = point - prevPoint; + if (direction.isZero()) + return; + + angle = atan2(direction.y, direction.x); + Vector3 startPos = genPath[genPath.size() - 1]; + float x, y, z; + m_groupMember.ComputeRelativePosition(angle, x, y); + x += point.x; + y += point.y; + z = point.z; + creature.UpdateGroundPositionZ(x, y, z); + creature.GetMap()->GetWalkHitPosition(creature.GetTransport(), point.x, point.y, point.z, x, y, z); + Vector3 endPos = Vector3(x, y, z); + PathFinder pathfinder(&creature); + pathfinder.calculate(startPos, endPos, true); + distance += BuildIntPath(genPath, pathfinder.getPath()); + prevPoint = point; + } + + creature.AddUnitState(UNIT_STATE_ROAMING_MOVE); + + if (distance < 0.2f) + return; + + // Increased speed if late, decreased if in a rotating ... + float speed = distance / float(leaderTimeBeforeNextWP) * 1000.0f; + if (speed > creature.GetSpeed(MOVE_RUN) * 1.3f) + speed = creature.GetSpeed(MOVE_RUN) * 1.3f; + + Movement::MoveSplineInit init(creature, "PatrolMovementGenerator::StartMove"); + init.MovebyPath(genPath); + init.SetWalk(creature.IsWalking() && !creature.IsLevitating()); + init.SetVelocity(speed); + init.SetFacing(angle); + init.Launch(); + return; + } + uint32 totalLeaderPoints = leader->movespline->CountSplinePoints(); Vector3 last = leader->movespline->GetPoint(totalLeaderPoints); Vector3 direction = last - leader->movespline->GetPoint(totalLeaderPoints - 1); diff --git a/src/game/Movement/WaypointMovementGenerator.h b/src/game/Movement/WaypointMovementGenerator.h index ec4ebb707bb..c224017139a 100644 --- a/src/game/Movement/WaypointMovementGenerator.h +++ b/src/game/Movement/WaypointMovementGenerator.h @@ -36,6 +36,8 @@ #define FLIGHT_TRAVEL_UPDATE 100 #define STOP_TIME_FOR_PLAYER (30 * IN_MILLISECONDS) +static float BuildIntPath(Movement::PointsArray& path, Movement::PointsArray const& genPath); + template class PathMovementBase { @@ -68,7 +70,7 @@ class WaypointMovementGenerator public PathMovementBase { public: - WaypointMovementGenerator(Creature &, bool repeating = true) : i_nextMoveTime(0), m_isArrivalDone(false), m_repeating(repeating), m_isWandering(false), m_lastReachedWaypoint(0) {} + WaypointMovementGenerator(Creature &, bool repeating = true) : i_nextMoveTime(0), m_repeating(repeating), m_isWandering(false), m_lastReachedWaypoint(0), m_pathDuration(0), m_creatureSpeed(0.f), m_lastSplineId(0) {} ~WaypointMovementGenerator() { i_path = nullptr; } void Initialize(Creature &u); void Interrupt(Creature &); @@ -85,6 +87,8 @@ class WaypointMovementGenerator void GetPathInformation(WaypointPathOrigin& wpOrigin) const { wpOrigin = m_PathOrigin; } void GetPathInformation(std::ostringstream& oss) const; bool SetNextWaypoint(uint32 pointId); + std::list const& GetNodeIndexes() const { return m_nodeIndexes; } + uint32 GetLastSplineId() const { return m_lastSplineId; } void AddPauseTime(int32 waitTimeDiff) { @@ -114,10 +118,13 @@ class WaypointMovementGenerator } ShortTimeTracker i_nextMoveTime; - bool m_isArrivalDone; bool m_repeating; bool m_isWandering; uint32 m_lastReachedWaypoint; + int32 m_pathDuration; + float m_creatureSpeed; + std::list m_nodeIndexes; + uint32 m_lastSplineId; WaypointPathOrigin m_PathOrigin; }; @@ -156,7 +163,7 @@ class PatrolMovementGenerator { public: explicit PatrolMovementGenerator(Creature& c) { ASSERT(InitPatrol(c)); } - explicit PatrolMovementGenerator(ObjectGuid leader, CreatureGroupMember const* member) : m_leaderGuid(leader), m_groupMember(*member) {} + explicit PatrolMovementGenerator(ObjectGuid leader, CreatureGroupMember const* member) : m_leaderGuid(leader), m_groupMember(*member), m_leaderSplineId(0) {} bool InitPatrol(Creature& c); void LoadPath(Creature &c); @@ -172,6 +179,7 @@ class PatrolMovementGenerator private: ObjectGuid m_leaderGuid; CreatureGroupMember m_groupMember; + uint32 m_leaderSplineId; }; #endif diff --git a/src/game/Movement/spline/MoveSplineInit.h b/src/game/Movement/spline/MoveSplineInit.h index c163f4ecefd..399982fc609 100644 --- a/src/game/Movement/spline/MoveSplineInit.h +++ b/src/game/Movement/spline/MoveSplineInit.h @@ -27,6 +27,8 @@ class Unit; namespace Movement { + UnitMoveType SelectSpeedType(uint32 moveFlags); + /* Initializes and launches spline movement */ class MoveSplineInit From 01f4689af457eb5a3fcebc404f5d88a311374ca5 Mon Sep 17 00:00:00 2001 From: Stoabrogga Date: Sun, 18 Jan 2026 23:04:56 +0100 Subject: [PATCH 2/2] Bugfix if condition Thanks to Gamemechanic --- src/game/Movement/WaypointMovementGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/Movement/WaypointMovementGenerator.cpp b/src/game/Movement/WaypointMovementGenerator.cpp index 9c255b528bc..7fb029f1ccd 100644 --- a/src/game/Movement/WaypointMovementGenerator.cpp +++ b/src/game/Movement/WaypointMovementGenerator.cpp @@ -376,7 +376,7 @@ bool WaypointMovementGenerator::Update(Creature &creature, uint32 cons if (!m_nodeIndexes.empty() && creature.movespline->_currentSplineIdx() >= m_nodeIndexes.front()) { m_nodeIndexes.pop_front(); - if (OnArrived(creature)); + if (OnArrived(creature)) creature.AddUnitState(UNIT_STATE_ROAMING_MOVE); } }