From 4811525de6214cb6062b83e6466fb1faa16c4ea5 Mon Sep 17 00:00:00 2001 From: H0zen Date: Fri, 24 Jun 2016 18:10:16 +0300 Subject: [PATCH] Fix a crash related to looting. (#142) Also a WIP implementation of pathfinding Z-patch --- src/game/MotionGenerators/PathFinder.cpp | 94 ++++++++++++++++-------- src/game/MotionGenerators/PathFinder.h | 2 + src/game/Object/LootMgr.cpp | 4 + 3 files changed, 69 insertions(+), 31 deletions(-) diff --git a/src/game/MotionGenerators/PathFinder.cpp b/src/game/MotionGenerators/PathFinder.cpp index 83f4d817..de2ab5c6 100644 --- a/src/game/MotionGenerators/PathFinder.cpp +++ b/src/game/MotionGenerators/PathFinder.cpp @@ -36,9 +36,12 @@ PathFinder::PathFinder(const Unit* owner) : m_useStraightPath(false), m_forceDestination(false), m_pointPathLimit(MAX_POINT_PATH_LENGTH), m_sourceUnit(owner), m_navMesh(NULL), m_navMeshQuery(NULL) { - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathFinder::PathInfo for %u \n", m_sourceUnit->GetGUIDLow()); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathFinder::PathFinder for %s \n", m_sourceUnit->GetGuidStr().c_str()); + + memset(m_pathPolyRefs, 0, sizeof(m_pathPolyRefs)); uint32 mapId = m_sourceUnit->GetMapId(); + if (MMAP::MMapFactory::IsPathfindingEnabled(mapId, owner)) { MMAP::MMapManager* mmap = MMAP::MMapFactory::createOrGetMMapManager(); @@ -51,23 +54,26 @@ PathFinder::PathFinder(const Unit* owner) : PathFinder::~PathFinder() { - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathFinder::~PathInfo() for %u \n", m_sourceUnit->GetGUIDLow()); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathFinder::~PathFinder() for %s \n", m_sourceUnit->GetGuidStr().c_str()); } bool PathFinder::calculate(float destX, float destY, float destZ, bool forceDest) { - // Vector3 oldDest = getEndPosition(); - Vector3 dest(destX, destY, destZ); - setEndPosition(dest); - float x, y, z; m_sourceUnit->GetPosition(x, y, z); + + if (!MaNGOS::IsValidMapCoord(x,y,z) || !MaNGOS::IsValidMapCoord(destX, destY, destZ)) + { return false; } + Vector3 start(x, y, z); setStartPosition(start); + Vector3 dest(destX, destY, destZ); + setEndPosition(dest); + m_forceDestination = forceDest; - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathFinder::calculate() for %u \n", m_sourceUnit->GetGUIDLow()); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathFinder::calculate() for %s \n", m_sourceUnit->GetGuidStr().c_str()); // make sure navMesh works - we can run on map w/o mmap // check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?) @@ -97,8 +103,8 @@ dtPolyRef PathFinder::getPathPolyByPosition(const dtPolyRef* polyPath, uint32 po for (uint32 i = 0; i < polyPathSize; ++i) { float closestPoint[VERTEX_SIZE]; - dtStatus dtResult = m_navMeshQuery->closestPointOnPoly(polyPath[i], point, closestPoint, NULL); - if (dtStatusFailed(dtResult)) + + if (dtStatusFailed(m_navMeshQuery->closestPointOnPoly(polyPath[i], point, closestPoint, NULL))) continue; float d = dtVdist2DSqr(point, closestPoint); @@ -133,8 +139,7 @@ dtPolyRef PathFinder::getPolyByLocation(const float* point, float* distance) con // first try with low search box float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f}; // bounds of poly search area float closestPoint[VERTEX_SIZE] = {0.0f, 0.0f, 0.0f}; - dtStatus dtResult = m_navMeshQuery->findNearestPoly(point, extents, &m_filter, &polyRef, closestPoint); - if (dtStatusSucceed(dtResult) && polyRef != INVALID_POLYREF) + if (dtStatusSucceed(m_navMeshQuery->findNearestPoly(point, extents, &m_filter, &polyRef, closestPoint)) && polyRef != INVALID_POLYREF) { *distance = dtVdist(closestPoint, point); return polyRef; @@ -143,8 +148,7 @@ dtPolyRef PathFinder::getPolyByLocation(const float* point, float* distance) con // still nothing .. // try with bigger search box extents[1] = 200.0f; - dtResult = m_navMeshQuery->findNearestPoly(point, extents, &m_filter, &polyRef, closestPoint); - if (dtStatusSucceed(dtResult) && polyRef != INVALID_POLYREF) + if (dtStatusSucceed(m_navMeshQuery->findNearestPoly(point, extents, &m_filter, &polyRef, closestPoint)) && polyRef != INVALID_POLYREF) { *distance = dtVdist(closestPoint, point); return polyRef; @@ -171,7 +175,7 @@ void PathFinder::BuildPolyPath(const Vector3& startPos, const Vector3& endPos) // its up to caller how he will use this info if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF) { - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPoly == 0 || endPoly == 0)\n"); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPoly == 0 || endPoly == 0) for %s\n", m_sourceUnit->GetGuidStr().c_str()); BuildShortcut(); if (m_sourceUnit->GetTypeId() == TYPEID_UNIT) @@ -179,9 +183,9 @@ void PathFinder::BuildPolyPath(const Vector3& startPos, const Vector3& endPos) // Check for swimming or flying shortcut if ((startPoly == INVALID_POLYREF && m_sourceUnit->GetTerrain()->IsUnderWater(startPos.x, startPos.y, startPos.z)) || (endPoly == INVALID_POLYREF && m_sourceUnit->GetTerrain()->IsUnderWater(endPos.x, endPos.y, endPos.z))) - { m_type = ((Creature*)m_sourceUnit)->CanSwim() ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH; } + { m_type = m_sourceUnit->ToCreature()->CanSwim() ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH; } else - { m_type = ((Creature*)m_sourceUnit)->CanFly() ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH; } + { m_type = m_sourceUnit->ToCreature()->CanFly() ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH; } } else { m_type = PATHFIND_NOPATH; } @@ -193,23 +197,24 @@ void PathFinder::BuildPolyPath(const Vector3& startPos, const Vector3& endPos) bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f); if (farFromPoly) { - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f\n", distToStartPoly, distToEndPoly); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f for %s\n", + distToStartPoly, distToEndPoly, m_sourceUnit->GetGuidStr().c_str()); bool buildShotrcut = false; if (m_sourceUnit->GetTypeId() == TYPEID_UNIT) { - Creature* owner = (Creature*)m_sourceUnit; + const Creature* owner = m_sourceUnit->ToCreature(); Vector3 p = (distToStartPoly > 7.0f) ? startPos : endPos; if (m_sourceUnit->GetTerrain()->IsUnderWater(p.x, p.y, p.z)) { - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: underWater case\n"); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: underWater case for %s\n", m_sourceUnit->GetGuidStr().c_str()); if (owner->CanSwim()) { buildShotrcut = true; } } else { - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: flying case\n"); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: flying case for %s\n", m_sourceUnit->GetGuidStr().c_str()); if (owner->CanFly()) { buildShotrcut = true; } } @@ -242,7 +247,7 @@ void PathFinder::BuildPolyPath(const Vector3& startPos, const Vector3& endPos) // just need to move in straight line if (startPoly == endPoly) { - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPoly == endPoly)\n"); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPoly == endPoly) for %s\n", m_sourceUnit->GetGuidStr().c_str()); BuildShortcut(); @@ -250,7 +255,7 @@ void PathFinder::BuildPolyPath(const Vector3& startPos, const Vector3& endPos) m_polyLength = 1; m_type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL; - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: path type %d\n", m_type); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: path type %d for %s\n", m_type, m_sourceUnit->GetGuidStr().c_str()); return; } @@ -284,7 +289,7 @@ void PathFinder::BuildPolyPath(const Vector3& startPos, const Vector3& endPos) if (startPolyFound && endPolyFound) { - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPolyFound && endPolyFound)\n"); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPolyFound && endPolyFound) for %s\n", m_sourceUnit->GetGuidStr().c_str()); // we moved along the path and the target did not move out of our old poly-path // our path is a simple subpath case, we have all the data we need @@ -295,7 +300,7 @@ void PathFinder::BuildPolyPath(const Vector3& startPos, const Vector3& endPos) } else if (startPolyFound && !endPolyFound) { - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPolyFound && !endPolyFound)\n"); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPolyFound && !endPolyFound) for %s\n", m_sourceUnit->GetGuidStr().c_str()); // we are moving on the old path but target moved out // so we have atleast part of poly-path ready @@ -353,14 +358,15 @@ void PathFinder::BuildPolyPath(const Vector3& startPos, const Vector3& endPos) sLog.outError("%u's Path Build failed: 0 length path", m_sourceUnit->GetGUIDLow()); } - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u \n", m_polyLength, prefixPolyLength, suffixPolyLength); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u for %s\n", + m_polyLength, prefixPolyLength, suffixPolyLength, m_sourceUnit->GetGuidStr().c_str()); // new path = prefix + suffix - overlap m_polyLength = prefixPolyLength + suffixPolyLength - 1; } else { - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (!startPolyFound && !endPolyFound)\n"); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (!startPolyFound && !endPolyFound) for %s\n", m_sourceUnit->GetGuidStr().c_str()); // either we have no path at all -> first run // or something went really wrong -> we aren't moving along the path to the target @@ -382,7 +388,7 @@ void PathFinder::BuildPolyPath(const Vector3& startPos, const Vector3& endPos) if (!m_polyLength || dtStatusFailed(dtResult)) { // only happens if we passed bad data to findPath(), or navmesh is messed up - sLog.outError("%u's Path Build failed: 0 length path", m_sourceUnit->GetGUIDLow()); + sLog.outError("Path Build failed: 0 length path for %s", m_sourceUnit->GetGuidStr().c_str()); BuildShortcut(); m_type = PATHFIND_NOPATH; return; @@ -434,7 +440,7 @@ void PathFinder::BuildPointPath(const float* startPoint, const float* endPoint) // only happens if pass bad data to findStraightPath or navmesh is broken // single point paths can be generated here // TODO : check the exact cases - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathFinder::BuildPointPath FAILED! path sized %d returned\n", pointCount); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathFinder::BuildPointPath FAILED! path sized %d returned for %s\n", pointCount, m_sourceUnit->GetGuidStr().c_str()); BuildShortcut(); m_type = PATHFIND_NOPATH; return; @@ -444,6 +450,8 @@ void PathFinder::BuildPointPath(const float* startPoint, const float* endPoint) for (uint32 i = 0; i < pointCount; ++i) { m_pathPoints[i] = Vector3(pathPoints[i * VERTEX_SIZE + 2], pathPoints[i * VERTEX_SIZE], pathPoints[i * VERTEX_SIZE + 1]); } + //NormalizePath(pointCount); + // first point is always our current location - we need the next one setActualEndPosition(m_pathPoints[pointCount - 1]); @@ -467,22 +475,26 @@ void PathFinder::BuildPointPath(const float* startPoint, const float* endPoint) m_type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); } - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathFinder::BuildPointPath path type %d size %d poly-size %d\n", m_type, pointCount, m_polyLength); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathFinder::BuildPointPath path type %d size %d poly-size %d for %s\n", + m_type, pointCount, m_polyLength, m_sourceUnit->GetGuidStr().c_str()); } void PathFinder::BuildShortcut() { - DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathFinder::BuildShortcut :: making shortcut\n"); + DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ PathFinder::BuildShortcut :: making shortcut for %s\n", m_sourceUnit->GetGuidStr().c_str()); clear(); // make two point path, our curr pos is the start, and dest is the end - m_pathPoints.resize(2); + uint32 size = 2; + m_pathPoints.resize(size); // set start and a default next position m_pathPoints[0] = getStartPosition(); m_pathPoints[1] = getActualEndPosition(); + //NormalizePath(size); + m_type = PATHFIND_SHORTCUT; } @@ -783,3 +795,23 @@ float PathFinder::dist3DSqr(const Vector3& p1, const Vector3& p2) const { return (p1 - p2).squaredLength(); } + +void PathFinder::NormalizePath(uint32& size) +{ + for (uint32 i = 0; i < m_pathPoints.size(); ++i) + { m_sourceUnit->UpdateAllowedPositionZ(m_pathPoints[i].x, m_pathPoints[i].y, m_pathPoints[i].z); } + + // check if the Z difference between each point is higher than SMOOTH_PATH_HEIGHT. + // add another point if that's the case and keep adding new midpoints till the Z difference is low enough + for (uint32 i = 1; i < m_pathPoints.size(); ++i) + { + if ((m_pathPoints[i - 1].z - m_pathPoints[i].z) > SMOOTH_PATH_HEIGHT) + { + auto midPoint = m_pathPoints[i - 1] + (m_pathPoints[i] - m_pathPoints[i - 1]) / 2.f; + m_sourceUnit->UpdateAllowedPositionZ(midPoint.x, midPoint.y, midPoint.z); + m_pathPoints.insert(m_pathPoints.begin() + i, midPoint); + --i; + } + } + size = m_pathPoints.size(); +} diff --git a/src/game/MotionGenerators/PathFinder.h b/src/game/MotionGenerators/PathFinder.h index c1139a84..f4c16b1f 100644 --- a/src/game/MotionGenerators/PathFinder.h +++ b/src/game/MotionGenerators/PathFinder.h @@ -44,6 +44,7 @@ class Unit; #define SMOOTH_PATH_STEP_SIZE 4.0f #define SMOOTH_PATH_SLOP 0.3f +#define SMOOTH_PATH_HEIGHT 1.0f #define VERTEX_SIZE 3 #define INVALID_POLYREF 0 @@ -76,6 +77,7 @@ class PathFinder Vector3 getStartPosition() const { return m_startPosition; } Vector3 getEndPosition() const { return m_endPosition; } Vector3 getActualEndPosition() const { return m_actualEndPosition; } + void NormalizePath(uint32& size); PointsArray& getPath() { return m_pathPoints; } PathType getPathType() const { return m_type; } diff --git a/src/game/Object/LootMgr.cpp b/src/game/Object/LootMgr.cpp index bd6088c3..6b8330ff 100644 --- a/src/game/Object/LootMgr.cpp +++ b/src/game/Object/LootMgr.cpp @@ -403,6 +403,10 @@ LootItem::LootItem(uint32 itemid_, uint32 count_, int32 randomPropertyId_) // Basic checks for player/item compatibility - if false no chance to see the item in the loot bool LootItem::AllowedForPlayer(Player const* player, WorldObject const* lootTarget) const { + // player check + if (!player || !player->IsInWorld()) + { return false; } + // DB conditions check if (conditionId && !sObjectMgr.IsPlayerMeetToCondition(conditionId, player, player->GetMap(), lootTarget, CONDITION_FROM_LOOT)) { return false; }