From 366615bcb9a36f187334f82884c81cceb23ff311 Mon Sep 17 00:00:00 2001 From: nico Date: Sun, 18 Oct 2026 13:39:31 +0000 Subject: [PATCH] implemented usage of virtual entry points when planning the travleroute --- src/server/game/Movement/PathGenerator.cpp | 115 +++++++++++++++++++++ src/server/game/Movement/PathGenerator.h | 37 ++++++- 2 files changed, 151 insertions(+), 1 deletion(-) diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp index f97f0860e7..47fc35869a 100644 --- a/src/server/game/Movement/PathGenerator.cpp +++ b/src/server/game/Movement/PathGenerator.cpp @@ -30,6 +30,121 @@ #include "Metric.h" #include "PhasingHandler.h" +// VCORE_POSITION_MMAP_BEGIN +VcorePositionPathResult PathGenerator::AnalyzePositions(uint32 meshMapId, uint32 instanceMapId, + uint32 instanceId, Position const& start, Position const& destination, uint16 includeFlags) +{ + VcorePositionPathResult result; + G3D::Vector3 const startPos(start.GetPositionX(), start.GetPositionY(), start.GetPositionZ()); + G3D::Vector3 const endPos(destination.GetPositionX(), destination.GetPositionY(), destination.GetPositionZ()); + result.DirectDistance = (endPos - startPos).length(); + result.FinalDistance = result.DirectDistance; + + if (!Trinity::IsValidMapCoord(startPos.x, startPos.y, startPos.z) || + !Trinity::IsValidMapCoord(endPos.x, endPos.y, endPos.z) || !includeFlags) + { + result.Result = VcorePositionPathResult::Status::InvalidCoordinates; + return result; + } + + if (!DisableMgr::IsPathfindingEnabled(meshMapId)) + return result; // NavMeshUnavailable + + MMAP::MMapManager* manager = MMAP::MMapFactory::createOrGetMMapManager(); + dtNavMeshQuery const* query = manager->GetNavMeshQuery(meshMapId, instanceMapId, instanceId); + dtNavMesh const* mesh = query ? query->getAttachedNavMesh() : nullptr; + if (!query || !mesh) + return result; + + // Detour uses Y,Z,X; world coordinates use X,Y,Z. + float const from[3] = {startPos.y, startPos.z, startPos.x}; + float const to[3] = {endPos.y, endPos.z, endPos.x}; + for (float const* point : {from, to}) + { + int tileX = -1, tileY = -1; + mesh->calcTileLoc(point, &tileX, &tileY); + if (tileX < 0 || tileY < 0 || !mesh->getTileAt(tileX, tileY, 0)) + { + result.Result = VcorePositionPathResult::Status::TileUnavailable; + return result; + } + } + + dtQueryFilter filter; + filter.setIncludeFlags(includeFlags); + filter.setExcludeFlags(0); + float const extents[3] = {3.0f, 5.0f, 3.0f}; + float nearestStart[3] = {}, nearestEnd[3] = {}; + dtPolyRef startPoly = 0, endPoly = 0; + if (dtStatusFailed(query->findNearestPoly(from, extents, &filter, &startPoly, nearestStart)) || + dtStatusFailed(query->findNearestPoly(to, extents, &filter, &endPoly, nearestEnd)) || + !startPoly || !endPoly) + { + result.Result = VcorePositionPathResult::Status::NoPolygon; + return result; + } + + // A distant projection is not proof that the requested world point is walkable. + if (dtVdist(from, nearestStart) > 3.0f || dtVdist(to, nearestEnd) > 3.0f) + { + result.Result = VcorePositionPathResult::Status::NoPolygon; + return result; + } + + dtPolyRef polys[MAX_PATH_LENGTH] = {}; + int polyCount = 0; + dtStatus const pathStatus = query->findPath(startPoly, endPoly, nearestStart, nearestEnd, + &filter, polys, &polyCount, MAX_PATH_LENGTH); + if (dtStatusFailed(pathStatus) || polyCount <= 0) + { + result.Result = VcorePositionPathResult::Status::NoPath; + return result; + } + + bool const reachedEndPoly = polys[polyCount - 1] == endPoly; + float reachableEnd[3] = {nearestEnd[0], nearestEnd[1], nearestEnd[2]}; + if (!reachedEndPoly && + dtStatusFailed(query->closestPointOnPoly(polys[polyCount - 1], nearestEnd, reachableEnd, nullptr))) + { + result.Result = VcorePositionPathResult::Status::NoPath; + return result; + } + + // Use an explicit bounded straight-path representation, not a forced shortcut. + constexpr int MaxPoints = 256; + float straight[MaxPoints * 3] = {}; + int pointCount = 0; + dtStatus const straightStatus = query->findStraightPath(nearestStart, reachableEnd, polys, + polyCount, straight, nullptr, nullptr, &pointCount, MaxPoints); + if (dtStatusFailed(straightStatus) || pointCount <= 0) + { + result.Result = VcorePositionPathResult::Status::NoPath; + return result; + } + + result.Points.reserve(pointCount); + for (int i = 0; i < pointCount; ++i) + { + G3D::Vector3 const p(straight[i * 3 + 2], straight[i * 3], straight[i * 3 + 1]); + if (!result.Points.empty()) + result.PathLength += (p - result.Points.back()).length(); + result.Points.push_back(p); + } + result.FinalDistance = (endPos - result.Points.back()).length(); + + bool const truncated = (pathStatus & DT_BUFFER_TOO_SMALL) || + (straightStatus & DT_BUFFER_TOO_SMALL) || pointCount >= MaxPoints || + polyCount >= MAX_PATH_LENGTH; + if (truncated) + result.Result = VcorePositionPathResult::Status::Truncated; + else if (reachedEndPoly && result.FinalDistance <= 1.5f) + result.Result = VcorePositionPathResult::Status::Complete; + else + result.Result = VcorePositionPathResult::Status::Partial; + return result; +} +// VCORE_POSITION_MMAP_END + ////////////////// PathGenerator ////////////////// PathGenerator::PathGenerator(WorldObject const* owner) : _polyLength(0), _type(PATHFIND_BLANK), _useStraightPath(false), diff --git a/src/server/game/Movement/PathGenerator.h b/src/server/game/Movement/PathGenerator.h index e3d75bdc37..cde10b64fb 100644 --- a/src/server/game/Movement/PathGenerator.h +++ b/src/server/game/Movement/PathGenerator.h @@ -23,6 +23,7 @@ #include "MMapDefines.h" #include "MoveSplineInitArgs.h" #include +#include class Unit; class WorldObject; @@ -54,6 +55,34 @@ enum PathType PATHFIND_FARFROMPOLY = PATHFIND_FARFROMPOLY_START | PATHFIND_FARFROMPOLY_END, // start or end positions are far from the mmap poligon }; +// VCORE_POSITION_MMAP_BEGIN +// Ground-only navmesh probe without a Player or WorldObject. +// meshMapId may differ from instanceMapId for phased/terrain-swapped maps. +// The caller must run this on the map/world thread that owns the navmesh query. +struct VcorePositionPathResult +{ + enum class Status : uint8 + { + InvalidCoordinates, + NavMeshUnavailable, + TileUnavailable, + NoPolygon, + NoPath, + Partial, + Complete, + Truncated + }; + + Status Result = Status::NavMeshUnavailable; + float DirectDistance = 0.0f; + float PathLength = 0.0f; + float FinalDistance = 0.0f; + std::vector Points; + + bool IsComplete() const { return Result == Status::Complete; } +}; +// VCORE_POSITION_MMAP_END + class TC_GAME_API PathGenerator { public: @@ -65,7 +94,13 @@ class TC_GAME_API PathGenerator PathGenerator& operator=(PathGenerator const& right) = delete; PathGenerator& operator=(PathGenerator&& right) = delete; - // Calculate the path from owner to given destination + // Vcore: query loaded MMAP data between arbitrary coordinates; no character required. + // The result describes ground navmesh reachability, not dynamic world collision. + static VcorePositionPathResult AnalyzePositions(uint32 meshMapId, uint32 instanceMapId, + uint32 instanceId, Position const& start, Position const& destination, + uint16 includeFlags = NAV_GROUND); + + // Calculate the path from owner to given destination // return: true if new path was calculated, false otherwise (no change needed) bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false); bool CalculatePath(Position const& destination, bool forceDest = false);