diff --git a/src/common/Collision/Maps/MapDefines.h b/src/common/Collision/Maps/MapDefines.h index 4935e99125..db0035c05f 100644 --- a/src/common/Collision/Maps/MapDefines.h +++ b/src/common/Collision/Maps/MapDefines.h @@ -5,7 +5,7 @@ #include "DetourNavMesh.h" const uint32 MMAP_MAGIC = 0x4d4d4150; // 'MMAP' -#define MMAP_VERSION 9 +#define MMAP_VERSION 10 struct MmapTileHeader { diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index a62d6e713c..2fe57bff74 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -2517,7 +2517,8 @@ void WorldObject::MovePositionToFirstCollision(Position &pos, float dist, float // Use a detour raycast to get our first collision point PathGenerator path(this); - path.CalculatePath(destx, desty, destz, false, true); + path.SetUseRaycast(true); + path.CalculatePath(destx, desty, destz, false); // We have a invalid path result. Skip further processing. if (path.GetPathType() & ~(PATHFIND_NORMAL | PATHFIND_SHORTCUT | PATHFIND_INCOMPLETE | PATHFIND_FARFROMPOLY_END | PATHFIND_NOT_USING_PATH)) @@ -2530,7 +2531,8 @@ void WorldObject::MovePositionToFirstCollision(Position &pos, float dist, float // Object is using a shortcut. Check static LOS float halfHeight = GetCollisionHeight() * 0.5f; - + bool col; + /* uint32 terrainMapId = PhasingHandler::GetTerrainMapId(GetPhaseShift(), GetMap(), pos.m_positionX, pos.m_positionY); bool col = VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(terrainMapId, pos.m_positionX, pos.m_positionY, pos.m_positionZ + halfHeight, @@ -2546,6 +2548,7 @@ void WorldObject::MovePositionToFirstCollision(Position &pos, float dist, float desty -= CONTACT_DISTANCE * std::sin(angle); dist = std::sqrt((pos.m_positionX - destx) * (pos.m_positionX - destx) + (pos.m_positionY - desty) * (pos.m_positionY - desty)); } + */ // check dynamic collision col = GetMap()->getObjectHitPos(GetPhaseShift(), diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp index 798a77f49c..33000f7144 100644 --- a/src/server/game/Movement/PathGenerator.cpp +++ b/src/server/game/Movement/PathGenerator.cpp @@ -31,7 +31,7 @@ ////////////////// PathGenerator ////////////////// PathGenerator::PathGenerator(WorldObject const* owner) : _polyLength(0), _type(PATHFIND_BLANK), _useStraightPath(false), - _forceDestination(false), _pointPathLimit(MAX_POINT_PATH_LENGTH), _straightLine(false), + _forceDestination(false), _pointPathLimit(MAX_POINT_PATH_LENGTH), _useRaycast(false), _endPosition(G3D::Vector3::zero()), _source(owner), _navMesh(nullptr), _navMeshQuery(nullptr) { @@ -55,7 +55,7 @@ PathGenerator::~PathGenerator() TC_LOG_DEBUG("maps.mmaps", "++ PathGenerator::~PathGenerator() for %u", _source->GetGUID().GetCounter()); } -bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool forceDest, bool straightLine) +bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool forceDest) { float x, y, z; _source->GetPosition(x, y, z); @@ -72,7 +72,6 @@ bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool fo SetStartPosition(start); _forceDestination = forceDest; - _straightLine = straightLine; TC_LOG_DEBUG("maps.mmaps", "++ PathGenerator::CalculatePath() for %u", _source->GetGUID().GetCounter()); @@ -157,6 +156,7 @@ dtPolyRef PathGenerator::GetPolyByLocation(float const* point, float* distance) return polyRef; } + *distance = FLT_MAX; return INVALID_POLYREF; } @@ -171,6 +171,8 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con dtPolyRef startPoly = GetPolyByLocation(startPoint, &distToStartPoly); dtPolyRef endPoly = GetPolyByLocation(endPoint, &distToEndPoly); + _type = PathType(PATHFIND_NORMAL); + // we have a hole in our mesh // make shortcut path and mark it as NOPATH ( with flying and swimming exception ) // its up to caller how he will use this info @@ -196,8 +198,18 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con } } - _type = (path || waterPath) ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH; - return; + if (path || waterPath) + { + _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); + return; + } + + // raycast doesn't need endPoly to be valid + if (!_useRaycast) + { + _type = PATHFIND_NOPATH; + return; + } } // we may need a better number here @@ -235,10 +247,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con BuildShortcut(); _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); - if (startFarFromPoly) - _type = PathType(_type | PATHFIND_FARFROMPOLY_START); - if (endFarFromPoly) - _type = PathType(_type | PATHFIND_FARFROMPOLY_END); + AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly); return; } @@ -254,10 +263,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con _type = PathType(PATHFIND_INCOMPLETE); - if (startFarFromPoly) - _type = PathType(_type | PATHFIND_FARFROMPOLY_START); - if (endFarFromPoly) - _type = PathType(_type | PATHFIND_FARFROMPOLY_END); + AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly); } } @@ -265,7 +271,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con // start and end are on same polygon // handle this case as if they were 2 different polygons, building a line path split in some few points - if (startPoly == endPoly) + if (startPoly == endPoly && !_useRaycast) { TC_LOG_DEBUG("maps.mmaps", "++ BuildPolyPath :: (startPoly == endPoly)"); @@ -276,10 +282,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con { _type = PathType(PATHFIND_INCOMPLETE); - if (startFarFromPoly) - _type = PathType(_type | PATHFIND_FARFROMPOLY_START); - if (endFarFromPoly) - _type = PathType(_type | PATHFIND_FARFROMPOLY_END); + AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly); } else _type = PATHFIND_NORMAL; @@ -378,39 +381,12 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con uint32 suffixPolyLength = 0; dtStatus dtResult; - if (_straightLine) + if (_useRaycast) { - float hit = 0; - float hitNormal[3]; - memset(hitNormal, 0, sizeof(hitNormal)); - - dtResult = _navMeshQuery->raycast( - suffixStartPoly, - suffixEndPoint, - endPoint, - &_filter, - &hit, - hitNormal, - _pathPolyRefs + prefixPolyLength - 1, - (int*)&suffixPolyLength, - MAX_PATH_LENGTH - prefixPolyLength); - - // raycast() sets hit to FLT_MAX if there is a ray between start and end - if (hit != FLT_MAX) - { - // the ray hit something, return no path instead of the incomplete one - Clear(); - _polyLength = 2; - _pathPoints.resize(2); - _pathPoints[0] = GetStartPosition(); - float hitPos[3]; - dtVlerp(hitPos, startPoint, endPoint, hit); - _pathPoints[1] = G3D::Vector3(hitPos[2], hitPos[0], hitPos[1]); - - NormalizePath(); - _type = PATHFIND_INCOMPLETE; - return; - } + TC_LOG_ERROR("maps.mmaps", "PathGenerator::BuildPolyPath() called with _useRaycast with a previous path for unit %s", _source->GetGUID().ToString().c_str()); + BuildShortcut(); + _type = PATHFIND_NOPATH; + return; } else { @@ -450,7 +426,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con Clear(); dtStatus dtResult; - if (_straightLine) + if (_useRaycast) { float hit = 0; float hitNormal[3]; @@ -467,24 +443,57 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con (int*)&_polyLength, MAX_PATH_LENGTH); + if (!_polyLength || dtStatusFailed(dtResult)) + { + BuildShortcut(); + _type = PATHFIND_NOPATH; + AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly); + return; + } + // raycast() sets hit to FLT_MAX if there is a ray between start and end if (hit != FLT_MAX) { - // the ray hit something, return no path instead of the incomplete one - Clear(); - _polyLength = 2; + float hitPos[3]; + + // Walk back a bit from the hit point to make sure it's in the mesh (sometimes the point is actually outside of the polygons due to float precision issues) + hit *= 0.99f; + dtVlerp(hitPos, startPoint, endPoint, hit); + + // if it fails again, clamp to poly boundary + if (dtStatusFailed(_navMeshQuery->getPolyHeight(_pathPolyRefs[_polyLength - 1], hitPos, &hitPos[1]))) + _navMeshQuery->closestPointOnPolyBoundary(_pathPolyRefs[_polyLength - 1], hitPos, hitPos); + _pathPoints.resize(2); _pathPoints[0] = GetStartPosition(); - float hitPos[3]; - dtVlerp(hitPos, startPoint, endPoint, hit); _pathPoints[1] = G3D::Vector3(hitPos[2], hitPos[0], hitPos[1]); NormalizePath(); _type = PATHFIND_INCOMPLETE; + AddFarFromPolyFlags(startFarFromPoly, false); return; } else - _navMeshQuery->getPolyHeight(_pathPolyRefs[_polyLength - 1], endPoint, &endPoint[1]); + { + // clamp to poly boundary if we fail to get the height + if (dtStatusFailed(_navMeshQuery->getPolyHeight(_pathPolyRefs[_polyLength - 1], endPoint, &endPoint[1]))) + _navMeshQuery->closestPointOnPolyBoundary(_pathPolyRefs[_polyLength - 1], endPoint, endPoint); + + _pathPoints.resize(2); + _pathPoints[0] = GetStartPosition(); + _pathPoints[1] = G3D::Vector3(endPoint[2], endPoint[0], endPoint[1]); + + NormalizePath(); + if (startFarFromPoly || endFarFromPoly) + { + _type = PathType(PATHFIND_INCOMPLETE); + + AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly); + } + else + _type = PATHFIND_NORMAL; + return; + } } else { @@ -515,10 +524,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con else _type = PATHFIND_INCOMPLETE; - if (startFarFromPoly) - _type = PathType(_type | PATHFIND_FARFROMPOLY_START); - if (endFarFromPoly) - _type = PathType(_type | PATHFIND_FARFROMPOLY_END); + AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly); // generate the point-path out of our up-to-date poly-path BuildPointPath(startPoint, endPoint); @@ -529,37 +535,13 @@ void PathGenerator::BuildPointPath(const float *startPoint, const float *endPoin float pathPoints[MAX_POINT_PATH_LENGTH*VERTEX_SIZE]; uint32 pointCount = 0; dtStatus dtResult = DT_FAILURE; - if (_straightLine) + if (_useRaycast) { - dtResult = DT_SUCCESS; - pointCount = 1; - memcpy(&pathPoints[VERTEX_SIZE * 0], startPoint, sizeof(float)* 3); // first point - - // path has to be split into polygons with dist SMOOTH_PATH_STEP_SIZE between them - G3D::Vector3 startVec = G3D::Vector3(startPoint[0], startPoint[1], startPoint[2]); - G3D::Vector3 endVec = G3D::Vector3(endPoint[0], endPoint[1], endPoint[2]); - G3D::Vector3 diffVec = (endVec - startVec); - G3D::Vector3 prevVec = startVec; - float len = diffVec.length(); - diffVec *= SMOOTH_PATH_STEP_SIZE / len; - - // If the path is short PATHFIND_SHORT will be set as type - while (len > SMOOTH_PATH_STEP_SIZE && pointCount < MAX_POINT_PATH_LENGTH) - { - len -= SMOOTH_PATH_STEP_SIZE; - prevVec += diffVec; - pathPoints[VERTEX_SIZE * pointCount + 0] = prevVec.x; - pathPoints[VERTEX_SIZE * pointCount + 1] = prevVec.y; - pathPoints[VERTEX_SIZE * pointCount + 2] = prevVec.z; - ++pointCount; - } - - // If the path is short PATHFIND_SHORT will be set as type - if (pointCount < MAX_POINT_PATH_LENGTH) - { - memcpy(&pathPoints[VERTEX_SIZE * pointCount], endPoint, sizeof(float) * 3); // last point - ++pointCount; - } + // _straightLine uses raycast and it currently doesn't support building a point path, only a 2-point path with start and hitpoint/end is returned + TC_LOG_ERROR("maps.mmaps", "PathGenerator::BuildPointPath() called with _useRaycast for unit %s", _source->GetGUID().ToString().c_str()); + BuildShortcut(); + _type = PATHFIND_NOPATH; + return; } else if (_useStraightPath) { @@ -842,7 +824,7 @@ dtStatus PathGenerator::FindSmoothPath(float const* startPos, float const* endPo if (polyPathSize > 1) { - // Pick the closest poitns on poly border + // Pick the closest points on poly border if (dtStatusFailed(_navMeshQuery->closestPointOnPolyBoundary(polys[0], startPos, iterPos))) return DT_FAILURE; @@ -1042,3 +1024,11 @@ bool PathGenerator::IsInvalidDestinationZ(Unit const* target) const { return (target->GetPositionZ() - GetActualEndPosition().z) > 5.0f; } + +void PathGenerator::AddFarFromPolyFlags(bool startFarFromPoly, bool endFarFromPoly) +{ + if (startFarFromPoly) + _type = PathType(_type | PATHFIND_FARFROMPOLY_START); + if (endFarFromPoly) + _type = PathType(_type | PATHFIND_FARFROMPOLY_END); +} diff --git a/src/server/game/Movement/PathGenerator.h b/src/server/game/Movement/PathGenerator.h index cbdfa17cec..b785c2739d 100644 --- a/src/server/game/Movement/PathGenerator.h +++ b/src/server/game/Movement/PathGenerator.h @@ -61,12 +61,13 @@ class TC_GAME_API PathGenerator // 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 straightLine = false); + bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false); bool IsInvalidDestinationZ(Unit const* target) const; // option setters - use optional void SetUseStraightPath(bool useStraightPath) { _useStraightPath = useStraightPath; } void SetPathLengthLimit(float distance) { _pointPathLimit = std::min(uint32(distance/SMOOTH_PATH_STEP_SIZE), MAX_POINT_PATH_LENGTH); } + void SetUseRaycast(bool useRaycast) { _useRaycast = useRaycast; } // result getters G3D::Vector3 const& GetStartPosition() const { return _startPosition; } @@ -91,7 +92,7 @@ class TC_GAME_API PathGenerator bool _useStraightPath; // type of path will be generated bool _forceDestination; // when set, we will always arrive at given point uint32 _pointPathLimit; // limit point path size; min(this, MAX_POINT_PATH_LENGTH) - bool _straightLine; // use raycast if true for a straight line path + bool _useRaycast; // use raycast if true for a straight line path G3D::Vector3 _startPosition; // {x, y, z} of current location G3D::Vector3 _endPosition; // {x, y, z} of the destination @@ -137,6 +138,8 @@ class TC_GAME_API PathGenerator dtStatus FindSmoothPath(float const* startPos, float const* endPos, dtPolyRef const* polyPath, uint32 polyPathSize, float* smoothPath, int* smoothPathSize, uint32 smoothPathMaxSize); + + void AddFarFromPolyFlags(bool startFarFromPoly, bool endFarFromPoly); }; #endif diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 8211f7a681..4e9381d0a8 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -5804,7 +5804,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint m_preGeneratedPath->SetPathLengthLimit(range); // first try with raycast, if it fails fall back to normal path - bool result = m_preGeneratedPath->CalculatePath(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), false, false); + bool result = m_preGeneratedPath->CalculatePath(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), false); if (m_preGeneratedPath->GetPathType() & PATHFIND_SHORT) return SPELL_FAILED_NOPATH; else if (!result || m_preGeneratedPath->GetPathType() & (PATHFIND_NOPATH | PATHFIND_INCOMPLETE)) diff --git a/src/server/scripts/Commands/cs_mmaps.cpp b/src/server/scripts/Commands/cs_mmaps.cpp index c092b7badd..dcd6bb0b71 100644 --- a/src/server/scripts/Commands/cs_mmaps.cpp +++ b/src/server/scripts/Commands/cs_mmaps.cpp @@ -86,9 +86,9 @@ public: if (para && strcmp(para, "true") == 0) useStraightPath = true; - bool useStraightLine = false; - if (para && strcmp(para, "line") == 0) - useStraightLine = true; + bool useRaycast = false; + if (para && (strcmp(para, "line") == 0 || strcmp(para, "ray") == 0 || strcmp(para, "raycast") == 0)) + useRaycast = true; // unit locations float x, y, z; @@ -97,11 +97,12 @@ public: // path PathGenerator path(target); path.SetUseStraightPath(useStraightPath); - bool result = path.CalculatePath(x, y, z, false, useStraightLine); + path.SetUseRaycast(useRaycast); + bool result = path.CalculatePath(x, y, z, false); Movement::PointsArray const& pointPath = path.GetPath(); handler->PSendSysMessage("%s's path to %s:", target->GetName().c_str(), player->GetName().c_str()); - handler->PSendSysMessage("Building: %s", useStraightPath ? "StraightPath" : useStraightLine ? "Raycast" : "SmoothPath"); + handler->PSendSysMessage("Building: %s", useStraightPath ? "StraightPath" : useRaycast ? "Raycast" : "SmoothPath"); handler->PSendSysMessage("Result: %s - Length: %zu - Type: %u", (result ? "true" : "false"), pointPath.size(), path.GetPathType()); G3D::Vector3 const& start = path.GetStartPosition(); diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp index d5a74180e1..0ab7932872 100644 --- a/src/tools/mmaps_generator/MapBuilder.cpp +++ b/src/tools/mmaps_generator/MapBuilder.cpp @@ -1041,7 +1041,7 @@ namespace MMAP config.walkableHeight = m_bigBaseUnit ? 3 : 6; // a value >= 3|6 allows npcs to walk over some fences // a value >= 4|8 allows npcs to walk over all fences - config.walkableClimb = m_bigBaseUnit ? 4 : 8; + config.walkableClimb = m_bigBaseUnit ? 3 : 6; config.minRegionArea = rcSqr(60); config.mergeRegionArea = rcSqr(50); config.maxSimplificationError = 1.8f; // eliminates most jagged edges (tiny polygons)