aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/Entities/Object/Object.cpp10
-rw-r--r--src/server/game/Movement/PathGenerator.cpp175
-rw-r--r--src/server/game/Movement/PathGenerator.h7
-rw-r--r--src/server/game/Spells/Spell.cpp18
-rw-r--r--src/server/scripts/Commands/cs_mmaps.cpp11
-rw-r--r--src/server/scripts/Spells/spell_warrior.cpp10
-rw-r--r--src/tools/mmaps_generator/MapBuilder.cpp2
7 files changed, 106 insertions, 127 deletions
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 5e24ce2d606..0026d42a968 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -3211,10 +3211,11 @@ 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))
+ if (path.GetPathType() & ~(PATHFIND_NORMAL | PATHFIND_SHORTCUT | PATHFIND_INCOMPLETE | PATHFIND_FARFROMPOLY_END | PATHFIND_NOT_USING_PATH))
return;
G3D::Vector3 result = path.GetPath().back();
@@ -3224,7 +3225,9 @@ void WorldObject::MovePositionToFirstCollision(Position &pos, float dist, float
// check static LOS
float halfHeight = GetCollisionHeight() * 0.5f;
- bool col = VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(PhasingHandler::GetTerrainMapId(GetPhaseShift(), GetMap(), pos.m_positionX, pos.m_positionY),
+ bool col;
+ /*
+ col = VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(PhasingHandler::GetTerrainMapId(GetPhaseShift(), GetMap(), pos.m_positionX, pos.m_positionY),
pos.m_positionX, pos.m_positionY, pos.m_positionZ + halfHeight,
destx, desty, destz + halfHeight,
destx, desty, destz, -0.5f);
@@ -3238,6 +3241,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 593a347f424..77d59afdbad 100644
--- a/src/server/game/Movement/PathGenerator.cpp
+++ b/src/server/game/Movement/PathGenerator.cpp
@@ -30,7 +30,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)
{
@@ -54,7 +54,7 @@ PathGenerator::~PathGenerator()
TC_LOG_DEBUG("maps.mmaps", "++ PathGenerator::~PathGenerator() for %s", _source->GetGUID().ToString().c_str());
}
-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);
@@ -71,7 +71,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 %s", _source->GetGUID().ToString().c_str());
@@ -156,6 +155,7 @@ dtPolyRef PathGenerator::GetPolyByLocation(float const* point, float* distance)
return polyRef;
}
+ *distance = FLT_MAX;
return INVALID_POLYREF;
}
@@ -170,6 +170,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
@@ -195,8 +197,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
@@ -234,10 +246,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;
}
@@ -253,10 +262,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);
}
}
@@ -264,9 +270,10 @@ 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)");
+
_pathPolyRefs[0] = startPoly;
_polyLength = 1;
@@ -274,10 +281,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;
@@ -376,39 +380,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
{
@@ -448,7 +425,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con
Clear();
dtStatus dtResult;
- if (_straightLine)
+ if (_useRaycast)
{
float hit = 0;
float hitNormal[3];
@@ -465,24 +442,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;
- _pathPoints.resize(2);
- _pathPoints[0] = GetStartPosition();
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();
_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
{
@@ -513,10 +523,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);
@@ -527,37 +534,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)
{
@@ -841,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;
@@ -1041,3 +1024,11 @@ bool PathGenerator::IsInvalidDestinationZ(WorldObject 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 769e396c3a1..64ad8393436 100644
--- a/src/server/game/Movement/PathGenerator.h
+++ b/src/server/game/Movement/PathGenerator.h
@@ -60,12 +60,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(WorldObject const* target) const;
// option setters - use optional
void SetUseStraightPath(bool useStraightPath) { _useStraightPath = useStraightPath; }
void SetPathLengthLimit(float distance) { _pointPathLimit = std::min<uint32>(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; }
@@ -90,7 +91,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
@@ -136,6 +137,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 66ec7cbad4d..7632a0c6993 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -1422,21 +1422,7 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffectInfo const& spellEffectIn
Position pos = dest._position;
unitCaster->MovePositionToFirstCollision(pos, dist, angle);
- // Generate path to that point.
- if (!m_preGeneratedPath)
- m_preGeneratedPath = std::make_unique<PathGenerator>(unitCaster);
-
- m_preGeneratedPath->SetPathLengthLimit(dist);
-
- // Should we use straightline here ? What do we do when we don't have a full path ?
- bool pathResult = m_preGeneratedPath->CalculatePath(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), false, true);
- if (pathResult && m_preGeneratedPath->GetPathType() & (PATHFIND_NORMAL | PATHFIND_SHORTCUT))
- {
- pos.m_positionX = m_preGeneratedPath->GetActualEndPosition().x;
- pos.m_positionY = m_preGeneratedPath->GetActualEndPosition().y;
- pos.m_positionZ = m_preGeneratedPath->GetActualEndPosition().z;
- dest.Relocate(pos);
- }
+ dest.Relocate(pos);
break;
}
case TARGET_DEST_CASTER_GROUND:
@@ -5757,7 +5743,7 @@ SpellCastResult Spell::CheckCast(bool strict, int32* param1 /*= nullptr*/, int32
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 20213f392d2..04d14913170 100644
--- a/src/server/scripts/Commands/cs_mmaps.cpp
+++ b/src/server/scripts/Commands/cs_mmaps.cpp
@@ -85,9 +85,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;
@@ -96,11 +96,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/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp
index 2376a3d13fe..d008fec0db8 100644
--- a/src/server/scripts/Spells/spell_warrior.cpp
+++ b/src/server/scripts/Spells/spell_warrior.cpp
@@ -283,17 +283,11 @@ public:
PathGenerator generatedPath(GetCaster());
generatedPath.SetPathLengthLimit(range);
- bool result = generatedPath.CalculatePath(dest->GetPositionX(), dest->GetPositionY(), dest->GetPositionZ(), false, true);
+ bool result = generatedPath.CalculatePath(dest->GetPositionX(), dest->GetPositionY(), dest->GetPositionZ(), false);
if (generatedPath.GetPathType() & PATHFIND_SHORT)
return SPELL_FAILED_OUT_OF_RANGE;
else if (!result || generatedPath.GetPathType() & PATHFIND_NOPATH)
- {
- result = generatedPath.CalculatePath(dest->GetPositionX(), dest->GetPositionY(), dest->GetPositionZ(), false, false);
- if (generatedPath.GetPathType() & PATHFIND_SHORT)
- return SPELL_FAILED_OUT_OF_RANGE;
- else if (!result || generatedPath.GetPathType() & PATHFIND_NOPATH)
- return SPELL_FAILED_NOPATH;
- }
+ return SPELL_FAILED_NOPATH;
}
else if (dest->GetPositionZ() > GetCaster()->GetPositionZ() + 4.0f)
return SPELL_FAILED_NOPATH;
diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp
index f75d571046c..6e3adc79a04 100644
--- a/src/tools/mmaps_generator/MapBuilder.cpp
+++ b/src/tools/mmaps_generator/MapBuilder.cpp
@@ -986,7 +986,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)