diff options
author | Shauren <shauren.trinity@gmail.com> | 2024-04-20 00:22:34 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2024-04-20 00:22:34 +0200 |
commit | 204f65684c2a027baa7a3c2d647f0e735a368c62 (patch) | |
tree | 0162aa60518576af4477963a83a8b5e8b7f757a5 /src | |
parent | cf4c3b5467fde04f53636443a7a8b392aa527cf5 (diff) |
Core/Movement: Merge waypoints without delay into a single movement packet
Diffstat (limited to 'src')
4 files changed, 129 insertions, 6 deletions
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index c8545090156..fa87733d6f9 100644 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -24,8 +24,10 @@ #include "MoveSpline.h" #include "MoveSplineInit.h" #include "MovementDefines.h" +#include "PathGenerator.h" #include "Transport.h" #include "WaypointManager.h" +#include <span> WaypointMovementGenerator<Creature>::WaypointMovementGenerator(uint32 pathId, bool repeating, Optional<Milliseconds> duration, Optional<float> speed, MovementWalkRunSpeedSelectionMode speedSelectionMode, Optional<std::pair<Milliseconds, Milliseconds>> waitTimeRangeAtPathEnd, @@ -61,6 +63,8 @@ WaypointMovementGenerator<Creature>::WaypointMovementGenerator(WaypointPath cons ScriptResult = std::move(scriptResult); if (duration) _duration.emplace(*duration); + + std::get<std::unique_ptr<WaypointPath>>(_path)->BuildSegments(); } WaypointMovementGenerator<Creature>::~WaypointMovementGenerator() = default; @@ -215,6 +219,16 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* owner, uint32 diff) if (owner->GetTransGUID().IsEmpty()) owner->SetHomePosition(owner->GetPosition()); + // handle switching points in continuous segments + if (_waypointTransitionSplinePoints.size() > 1 && owner->movespline->currentPathIdx() >= _waypointTransitionSplinePoints.front()) + { + OnArrived(owner); + _waypointTransitionSplinePoints.erase(_waypointTransitionSplinePoints.begin()); + if (ComputeNextNode()) + if (CreatureAI* ai = owner->AI()) + ai->WaypointStarted(path->Nodes[_currentNode].Id, path->Id); + } + // relaunch movement if its speed has changed if (HasFlag(MOVEMENTGENERATOR_FLAG_SPEED_UPDATE_PENDING)) StartMove(owner, true); @@ -336,6 +350,7 @@ void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaun bool const transportPath = !owner->GetTransGUID().IsEmpty(); + uint32 previousNode = _currentNode; if (HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED) && HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED)) { if (ComputeNextNode()) @@ -388,7 +403,69 @@ void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaun } ASSERT(_currentNode < path->Nodes.size(), "WaypointMovementGenerator::StartMove: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, path->Id); - WaypointNode const &waypoint = path->Nodes[_currentNode]; + + std::span<WaypointNode const> segment = [&] + { + // find the continuous segment that our destination waypoint is on + auto segmentItr = std::ranges::find_if(path->ContinuousSegments, [&](std::pair<std::size_t, std::size_t> segmentRange) + { + auto isInSegmentRange = [&](uint32 node) { return node >= segmentRange.first && node < segmentRange.first + segmentRange.second; }; + return isInSegmentRange(_currentNode) && isInSegmentRange(previousNode); + }); + + // handle path returning directly from last point to first + if (segmentItr == path->ContinuousSegments.end()) + { + if (_currentNode != 0 || previousNode != path->Nodes.size() - 1) + return std::span(&path->Nodes[_currentNode], 1); + + segmentItr = path->ContinuousSegments.begin(); + } + + if (!_isReturningToStart) + return std::span(&path->Nodes[_currentNode], segmentItr->second - (_currentNode - segmentItr->first)); + + return std::span(&path->Nodes[segmentItr->first], _currentNode - segmentItr->first + 1); + }(); + + WaypointNode const& lastWaypointForSegment = !_isReturningToStart ? segment.back() : segment.front(); + + _waypointTransitionSplinePoints.clear(); + Movement::PointsArray points; + auto fillPath = [this, owner, &points]<typename iterator>(iterator itr, iterator end) + { + Optional<PathGenerator> generator; + if (_generatePath) + generator.emplace(owner); + + Position source = owner->GetPosition(); + points.emplace_back(source.GetPositionX(), source.GetPositionY(), source.GetPositionZ()); + + while (itr != end) + { + if (generator) + { + bool result = generator->CalculatePath(source.GetPositionX(), source.GetPositionY(), source.GetPositionZ(), itr->X, itr->Y, itr->Z); + if (result && !(generator->GetPathType() & PATHFIND_NOPATH)) + points.insert(points.end(), generator->GetPath().begin() + 1, generator->GetPath().end()); + else + generator.reset(); // when path generation to a waypoint fails, add all remaining points without pathfinding (preserve legacy behavior of MoveSplineInit::MoveTo) + } + + if (!generator) + points.emplace_back(itr->X, itr->Y, itr->Z); + + _waypointTransitionSplinePoints.push_back(points.size() - 1); + + source.Relocate(itr->X, itr->Y, itr->Z); + ++itr; + } + }; + + if (!_isReturningToStart) + fillPath(segment.begin(), segment.end()); + else + fillPath(segment.rbegin(), segment.rend()); RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_INFORM_ENABLED | MOVEMENTGENERATOR_FLAG_TIMED_PAUSED); @@ -396,22 +473,28 @@ void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaun Movement::MoveSplineInit init(owner); + // because steering flag can cause position on client to not be perfectly accurate, dont do it in combat + if (!owner->IsInCombat()) + init.SetSteering(); + //! If creature is on transport, we assume waypoints set in DB are already transport offsets if (transportPath) init.DisableTransportPathTransformations(); - init.MoveTo(waypoint.X, waypoint.Y, waypoint.Z, _generatePath); + init.MovebyPath(points); - if (waypoint.Orientation.has_value() && (waypoint.Delay || _currentNode == path->Nodes.size() - 1)) - init.SetFacing(*waypoint.Orientation); + if (lastWaypointForSegment.Orientation.has_value() && (lastWaypointForSegment.Delay || _currentNode == path->Nodes.size() - 1)) + init.SetFacing(*lastWaypointForSegment.Orientation); switch (path->MoveType) { case WaypointMoveType::Land: init.SetAnimation(AnimTier::Ground); + init.SetFly(); break; case WaypointMoveType::TakeOff: init.SetAnimation(AnimTier::Fly); + init.SetFly(); break; case WaypointMoveType::Run: init.SetWalk(false); @@ -443,6 +526,25 @@ void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaun if (_speed) init.SetVelocity(*_speed); + if (init.Path().size() > 2) + { + G3D::Vector3 mid = (init.Path().front() + init.Path().back()) / 2.0f; + auto itr = init.Path().begin() + 1; + auto end = itr + (init.Path().size() - 2); + while (itr != end) + { + G3D::Vector3 offset = *itr - mid; + if (std::fabs(offset.x) >= 128.0f || std::fabs(offset.y) >= 128.0f || std::fabs(offset.z) >= 64.0f) + { + // when distance is too great, send path in uncompressed state otherwise too much precision is lost on each point + init.SetUncompressed(); + break; + } + + ++itr; + } + } + init.Launch(); // inform formation diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h index f6f3380cbe2..29b1b48354a 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h @@ -84,6 +84,7 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium<Creat bool IsLoadedFromDB() const { return std::holds_alternative<WaypointPath const*>(_path); } + std::vector<int32> _waypointTransitionSplinePoints; TimeTracker _nextMoveTime; uint32 _pathId; bool _repeating; diff --git a/src/server/game/Movement/Waypoints/WaypointDefines.h b/src/server/game/Movement/Waypoints/WaypointDefines.h index 2d897c0eb09..cf5c85a1161 100644 --- a/src/server/game/Movement/Waypoints/WaypointDefines.h +++ b/src/server/game/Movement/Waypoints/WaypointDefines.h @@ -79,10 +79,13 @@ struct WaypointPath } std::vector<WaypointNode> Nodes; + std::vector<std::pair<std::size_t, std::size_t>> ContinuousSegments; uint32 Id = 0; WaypointMoveType MoveType = WaypointMoveType::Walk; EnumFlag<WaypointPathFlags> Flags = WaypointPathFlags::None; Optional<float> Velocity; + + void BuildSegments(); }; #endif diff --git a/src/server/game/Movement/Waypoints/WaypointManager.cpp b/src/server/game/Movement/Waypoints/WaypointManager.cpp index 40106ecb2e4..ffb984fb4af 100644 --- a/src/server/game/Movement/Waypoints/WaypointManager.cpp +++ b/src/server/game/Movement/Waypoints/WaypointManager.cpp @@ -139,9 +139,10 @@ void WaypointMgr::LoadPathNodesFromDB(Field* fields) void WaypointMgr::DoPostLoadingChecks() { - for (auto const& path : _pathStore) + for (auto& [pathId, pathInfo] : _pathStore) { - WaypointPath pathInfo = path.second; + pathInfo.BuildSegments(); + if (pathInfo.Nodes.empty()) TC_LOG_ERROR("sql.sql", "PathId {} in `waypoint_path` has no assigned nodes in `waypoint_path_node`", pathInfo.Id); @@ -194,6 +195,9 @@ void WaypointMgr::ReloadPath(uint32 pathId) { LoadPathNodesFromDB(result->Fetch()); } while (result->NextRow()); + + if (WaypointPath* path = Trinity::Containers::MapGetValuePtr(_pathStore, pathId)) + path->BuildSegments(); } } @@ -330,3 +334,16 @@ ObjectGuid const& WaypointMgr::GetVisualGUIDByNode(uint32 pathId, uint32 nodeId) return itr->second; } + +void WaypointPath::BuildSegments() +{ + ContinuousSegments.assign(1, { 0, 0 }); + for (std::size_t i = 0; i < Nodes.size(); ++i) + { + ++ContinuousSegments.back().second; + + // split on delay + if (i + 1 != Nodes.size() && Nodes[i].Delay) + ContinuousSegments.emplace_back(i, 1); + } +} |