AzerothCore 3.3.5a
OpenSource WoW Emulator
Loading...
Searching...
No Matches
PathGenerator Class Reference

#include "PathGenerator.h"

Public Member Functions

 PathGenerator (WorldObject const *owner)
 
 ~PathGenerator ()
 
bool CalculatePath (float destX, float destY, float destZ, bool forceDest=false)
 
bool CalculatePath (float x, float y, float z, float destX, float destY, float destZ, bool forceDest)
 
bool IsInvalidDestinationZ (Unit const *target) const
 
bool IsWalkableClimb (float const *v1, float const *v2) const
 
bool IsWalkableClimb (float x, float y, float z, float destX, float destY, float destZ) const
 
bool IsWaterPath (Movement::PointsArray pathPoints) const
 
bool IsSwimmableSegment (float const *v1, float const *v2, bool checkSwim=true) const
 predict if a certain segment is underwater and the unit can swim Must only be used for very short segments since this check doesn't work on long paths that alternate terrain and water.
 
bool IsSwimmableSegment (float x, float y, float z, float destX, float destY, float destZ, bool checkSwim=true) const
 predict if a certain segment is underwater and the unit can swim Must only be used for very short segments since this check doesn't work on long paths that alternate terrain and water.
 
void SetSlopeCheck (bool checkSlope)
 
void SetUseStraightPath (bool useStraightPath)
 
void SetPathLengthLimit (float distance)
 
void SetUseRaycast (bool useRaycast)
 
G3D::Vector3 const & GetStartPosition () const
 
G3D::Vector3 const & GetEndPosition () const
 
G3D::Vector3 const & GetActualEndPosition () const
 
Movement::PointsArray const & GetPath () const
 
PathType GetPathType () const
 
void ShortenPathUntilDist (G3D::Vector3 const &point, float dist)
 
float getPathLength () const
 
void Clear ()
 

Static Public Member Functions

static bool IsWalkableClimb (float x, float y, float z, float destX, float destY, float destZ, float sourceHeight)
 Check if a slope can be climbed based on source height This method is meant for short distances or linear paths.
 
static float GetRequiredHeightToClimb (float x, float y, float z, float destX, float destY, float destZ, float sourceHeight)
 Return the height of a slope that can be climbed based on source height This method is meant for short distances or linear paths.
 

Private Member Functions

void SetStartPosition (G3D::Vector3 const &point)
 
void SetEndPosition (G3D::Vector3 const &point)
 
void SetActualEndPosition (G3D::Vector3 const &point)
 
void NormalizePath ()
 
bool InRange (G3D::Vector3 const &p1, G3D::Vector3 const &p2, float r, float h) const
 
float Dist3DSqr (G3D::Vector3 const &p1, G3D::Vector3 const &p2) const
 
bool InRangeYZX (float const *v1, float const *v2, float r, float h) const
 
dtPolyRef GetPathPolyByPosition (dtPolyRef const *polyPath, uint32 polyPathSize, float const *Point, float *Distance=nullptr) const
 
dtPolyRef GetPolyByLocation (float const *Point, float *Distance) const
 
bool HaveTile (G3D::Vector3 const &p) const
 
void BuildPolyPath (G3D::Vector3 const &startPos, G3D::Vector3 const &endPos)
 
void BuildPointPath (float const *startPoint, float const *endPoint)
 
void BuildShortcut ()
 
NavTerrain GetNavTerrain (float x, float y, float z) const
 
void CreateFilter ()
 
void UpdateFilter ()
 
uint32 FixupCorridor (dtPolyRef *path, uint32 npath, uint32 maxPath, dtPolyRef const *visited, uint32 nvisited)
 
bool GetSteerTarget (float const *startPos, float const *endPos, float minTargetDist, dtPolyRef const *path, uint32 pathSize, float *steerPos, unsigned char &steerPosFlag, dtPolyRef &steerPosRef)
 
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)
 

Private Attributes

dtPolyRef _pathPolyRefs [MAX_PATH_LENGTH]
 
uint32 _polyLength
 
Movement::PointsArray _pathPoints
 
PathType _type
 
bool _useStraightPath
 
bool _forceDestination
 
bool _slopeCheck
 
uint32 _pointPathLimit
 
bool _useRaycast
 
G3D::Vector3 _startPosition
 
G3D::Vector3 _endPosition
 
G3D::Vector3 _actualEndPosition
 
WorldObject const *const _source
 
dtNavMesh const * _navMesh
 
dtNavMeshQuery const * _navMeshQuery
 
dtQueryFilterExt _filter
 

Detailed Description

Constructor & Destructor Documentation

◆ PathGenerator()

PathGenerator::PathGenerator ( WorldObject const *  owner)
explicit
28 :
31 _endPosition(G3D::Vector3::zero()), _source(owner), _navMesh(nullptr),
32 _navMeshQuery(nullptr)
33{
34 memset(_pathPolyRefs, 0, sizeof(_pathPolyRefs));
35
36 //if (sDisableMgr->IsPathfindingEnabled(_sourceUnit->FindMap()))
37 {
40 }
41
43}
#define MAX_POINT_PATH_LENGTH
Definition PathGenerator.h:36
@ PATHFIND_BLANK
Definition PathGenerator.h:46
dtNavMesh const * GetNavMesh() const
Definition MapCollisionData.h:62
dtNavMeshQuery const * GetNavMeshQuery()
Definition MapCollisionData.cpp:177
MMapData & GetMMapData()
Definition MapCollisionData.h:86
MapCollisionData & GetMapCollisionData()
Definition Map.h:525
PathType _type
Definition PathGenerator.h:135
bool _useRaycast
Definition PathGenerator.h:141
bool _slopeCheck
Definition PathGenerator.h:139
uint32 _pointPathLimit
Definition PathGenerator.h:140
bool _useStraightPath
Definition PathGenerator.h:137
dtNavMeshQuery const * _navMeshQuery
Definition PathGenerator.h:149
WorldObject const *const _source
Definition PathGenerator.h:147
uint32 _polyLength
Definition PathGenerator.h:132
bool _forceDestination
Definition PathGenerator.h:138
void CreateFilter()
Definition PathGenerator.cpp:647
dtPolyRef _pathPolyRefs[MAX_PATH_LENGTH]
Definition PathGenerator.h:131
dtNavMesh const * _navMesh
Definition PathGenerator.h:148
G3D::Vector3 _endPosition
Definition PathGenerator.h:144
Map * GetMap() const
Definition Object.h:625

References _navMesh, _navMeshQuery, _pathPolyRefs, _source, CreateFilter(), WorldObject::GetMap(), Map::GetMapCollisionData(), MapCollisionData::GetMMapData(), MMapData::GetNavMesh(), and MMapData::GetNavMeshQuery().

◆ ~PathGenerator()

PathGenerator::~PathGenerator ( )
46{
47}

Member Function Documentation

◆ AddFarFromPolyFlags()

void PathGenerator::AddFarFromPolyFlags ( bool  startFarFromPoly,
bool  endFarFromPoly 
)
private
1104{
1105 if (startFarFromPoly)
1106 {
1108 }
1109 if (endFarFromPoly)
1110 {
1112 }
1113}
PathType
Definition PathGenerator.h:45
@ PATHFIND_FARFROMPOLY_END
Definition PathGenerator.h:54
@ PATHFIND_FARFROMPOLY_START
Definition PathGenerator.h:53

References _type, PATHFIND_FARFROMPOLY_END, and PATHFIND_FARFROMPOLY_START.

Referenced by BuildPolyPath().

◆ BuildPointPath()

void PathGenerator::BuildPointPath ( float const *  startPoint,
float const *  endPoint 
)
private
Todo:
check the exact cases
518{
519 float pathPoints[MAX_POINT_PATH_LENGTH * VERTEX_SIZE];
520 uint32 pointCount = 0;
521 dtStatus dtResult = DT_FAILURE;
522 if (_useRaycast)
523 {
524 // _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
525 LOG_ERROR("movement", "PathGenerator::BuildPointPath() called with _useRaycast for unit {}", _source->GetGUID().ToString());
528 return;
529 }
530 else if (_useStraightPath)
531 {
532 dtResult = _navMeshQuery->findStraightPath(
533 startPoint, // start position
534 endPoint, // end position
535 _pathPolyRefs, // current path
536 _polyLength, // lenth of current path
537 pathPoints, // [out] path corner points
538 nullptr, // [out] flags
539 nullptr, // [out] shortened path
540 (int*)&pointCount,
541 _pointPathLimit); // maximum number of points/polygons to use
542 }
543 else
544 {
545 dtResult = FindSmoothPath(
546 startPoint, // start position
547 endPoint, // end position
548 _pathPolyRefs, // current path
549 _polyLength, // length of current path
550 pathPoints, // [out] path corner points
551 (int*)&pointCount,
552 _pointPathLimit); // maximum number of points
553 }
554
555 // Special case with start and end positions very close to each other
556 if (_polyLength == 1 && pointCount == 1 && !(dtResult & DT_SLOPE_TOO_STEEP))
557 {
558 // First point is start position, append end position
559 dtVcopy(&pathPoints[1 * VERTEX_SIZE], endPoint);
560 pointCount++;
561 }
562 else if (pointCount < 2 || dtStatusFailed(dtResult))
563 {
564 // If its too steep, just return incomplete path.
565 if (pointCount > 0 && dtResult & DT_SLOPE_TOO_STEEP)
566 {
567 _pathPoints.resize(pointCount);
568 for (uint32 i = 0; i < pointCount; ++i)
569 _pathPoints[i] = G3D::Vector3(pathPoints[i * VERTEX_SIZE + 2], pathPoints[i * VERTEX_SIZE], pathPoints[i * VERTEX_SIZE + 1]);
570
572
573 // first point is always our current location - we need the next one
574 SetActualEndPosition(_pathPoints[pointCount - 1]);
575
577 return;
578 }
579
580 // only happens if pass bad data to findStraightPath or navmesh is broken
581 // single point paths can be generated here
585 return;
586 }
587 else if (pointCount >= _pointPathLimit)
588 {
591 return;
592 }
593
594 _pathPoints.resize(pointCount);
595 for (uint32 i = 0; i < pointCount; ++i)
596 _pathPoints[i] = G3D::Vector3(pathPoints[i * VERTEX_SIZE + 2], pathPoints[i * VERTEX_SIZE], pathPoints[i * VERTEX_SIZE + 1]);
597
599
600 // first point is always our current location - we need the next one
601 SetActualEndPosition(_pathPoints[pointCount - 1]);
602
603 // force the given destination, if needed
604 if (_forceDestination &&
606 {
607 // we may want to keep partial subpath
609 {
611 _pathPoints[_pathPoints.size() - 1] = GetEndPosition();
612 }
613 else
614 {
617 }
618
620 }
621}
std::uint32_t uint32
Definition Define.h:107
#define LOG_ERROR(filterType__,...)
Definition Log.h:158
#define VERTEX_SIZE
Definition PathGenerator.h:41
@ PATHFIND_NOT_USING_PATH
Definition PathGenerator.h:51
@ PATHFIND_NORMAL
Definition PathGenerator.h:47
@ PATHFIND_NOPATH
Definition PathGenerator.h:50
@ PATHFIND_SHORT
Definition PathGenerator.h:52
@ PATHFIND_INCOMPLETE
Definition PathGenerator.h:49
std::string ToString() const
Definition ObjectGuid.cpp:47
static ObjectGuid GetGUID(Object const *o)
Definition Object.h:114
void SetActualEndPosition(G3D::Vector3 const &point)
Definition PathGenerator.h:155
G3D::Vector3 const & GetStartPosition() const
Definition PathGenerator.h:86
float Dist3DSqr(G3D::Vector3 const &p1, G3D::Vector3 const &p2) const
Definition PathGenerator.cpp:1032
dtStatus FindSmoothPath(float const *startPos, float const *endPos, dtPolyRef const *polyPath, uint32 polyPathSize, float *smoothPath, int *smoothPathSize, uint32 smoothPathMaxSize)
Definition PathGenerator.cpp:814
G3D::Vector3 const & GetEndPosition() const
Definition PathGenerator.h:87
void BuildShortcut()
Definition PathGenerator.cpp:631
void NormalizePath()
Definition PathGenerator.cpp:623
Movement::PointsArray _pathPoints
Definition PathGenerator.h:134
bool InRange(G3D::Vector3 const &p1, G3D::Vector3 const &p2, float r, float h) const
Definition PathGenerator.cpp:1026
G3D::Vector3 const & GetActualEndPosition() const
Definition PathGenerator.h:88

References _forceDestination, _navMeshQuery, _pathPoints, _pathPolyRefs, _pointPathLimit, _polyLength, _source, _type, _useRaycast, _useStraightPath, BuildShortcut(), Dist3DSqr(), FindSmoothPath(), GetActualEndPosition(), GetEndPosition(), Object::GetGUID(), GetStartPosition(), InRange(), LOG_ERROR, MAX_POINT_PATH_LENGTH, NormalizePath(), PATHFIND_INCOMPLETE, PATHFIND_NOPATH, PATHFIND_NORMAL, PATHFIND_NOT_USING_PATH, PATHFIND_SHORT, SetActualEndPosition(), ObjectGuid::ToString(), and VERTEX_SIZE.

Referenced by BuildPolyPath().

◆ BuildPolyPath()

void PathGenerator::BuildPolyPath ( G3D::Vector3 const &  startPos,
G3D::Vector3 const &  endPos 
)
private
Todo:
we can merge it with getPathPolyByPosition() loop
Todo:
play with the values here
160{
161 // *** getting start/end poly logic ***
162
163 float distToStartPoly, distToEndPoly;
164 float startPoint[VERTEX_SIZE] = { startPos.y, startPos.z, startPos.x };
165 float endPoint[VERTEX_SIZE] = { endPos.y, endPos.z, endPos.x };
166
167 dtPolyRef startPoly = GetPolyByLocation(startPoint, &distToStartPoly);
168 dtPolyRef endPoly = GetPolyByLocation(endPoint, &distToEndPoly);
169
171
172 Creature const* creature = _source->ToCreature();
173
174 // we have a hole in our mesh
175 // make shortcut path and mark it as NOPATH ( with flying and swimming exception )
176 // its up to caller how he will use this info
177 if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF)
178 {
180
181 bool canSwim = creature ? creature->CanSwim() : true;
182 bool path = creature ? creature->CanFly() : true;
183 bool waterPath = IsWaterPath(_pathPoints);
184 if (path || (waterPath && canSwim))
185 {
187 return;
188 }
189
190 // raycast doesn't need endPoly to be valid
191 if (!_useRaycast)
192 {
194 return;
195 }
196 }
197
198 // we may need a better number here
199 bool startFarFromPoly = distToStartPoly > 7.0f;
200 bool endFarFromPoly = distToEndPoly > 7.0f;
201
202 // create a shortcut if the path begins or end too far
203 // away from the desired path points.
204 // swimming creatures should not use a shortcut
205 // because exiting the water must be done following a proper path
206 // we just need to remove/normalize paths between 2 adjacent points
207 if (startFarFromPoly || endFarFromPoly)
208 {
209 bool buildShortcut = false;
210
211 auto liquidDataStart = _source->GetMap()->GetLiquidData(_source->GetPhaseMask(), startPos.x, startPos.y, startPos.z, _source->GetCollisionHeight(), {});
212 auto liquidDataEnd = _source->GetMap()->GetLiquidData(_source->GetPhaseMask(), endPos.x, endPos.y, endPos.z, _source->GetCollisionHeight(), {});
213
214 bool startUnderWaterEndInWater = liquidDataStart.Status == LIQUID_MAP_UNDER_WATER &&
215 (liquidDataEnd.Status & MAP_LIQUID_STATUS_IN_CONTACT) != 0;
216 bool startInWaterEndUnderWater = (liquidDataStart.Status & MAP_LIQUID_STATUS_IN_CONTACT) != 0 &&
217 liquidDataEnd.Status == LIQUID_MAP_UNDER_WATER;
218 bool waterPath = startUnderWaterEndInWater || startInWaterEndUnderWater;
219 Unit const* _sourceUnit = _source->ToUnit();
220
221 if (_sourceUnit)
222 {
223 bool isWater = (_sourceUnit->CanSwim() && waterPath);
224
225 if (isWater || _sourceUnit->CanFly() || (_sourceUnit->IsFalling() && endPos.z < startPos.z))
226 {
227 buildShortcut = true;
228 }
229 }
230
231 if (buildShortcut)
232 {
235
236 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
237
238 return;
239 }
240 else
241 {
242 float closestPoint[VERTEX_SIZE];
243 // we may want to use closestPointOnPolyBoundary instead
244 if (dtStatusSucceed(_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint, nullptr)))
245 {
246 dtVcopy(endPoint, closestPoint);
247 SetActualEndPosition(G3D::Vector3(endPoint[2], endPoint[0], endPoint[1]));
248 }
249
251
252 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
253 }
254 }
255
256 // *** poly path generating logic ***
257
258 // start and end are on same polygon
259 // handle this case as if they were 2 different polygons, building a line path split in some few points
260 if (startPoly == endPoly && !_useRaycast)
261 {
262 _pathPolyRefs[0] = startPoly;
263 _polyLength = 1;
264
265 if (startFarFromPoly || endFarFromPoly)
266 {
268
269 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
270 }
271 else
273
274 BuildPointPath(startPoint, endPoint);
275 return;
276 }
277
278 // look for startPoly/endPoly in current path
280 bool startPolyFound = false;
281 bool endPolyFound = false;
282 uint32 pathStartIndex = 0;
283 uint32 pathEndIndex = 0;
284
285 if (_polyLength)
286 {
287 for (; pathStartIndex < _polyLength; ++pathStartIndex)
288 {
289 // here to catch few bugs
290 if (_pathPolyRefs[pathStartIndex] == INVALID_POLYREF)
291 {
292 break;
293 }
294
295 if (_pathPolyRefs[pathStartIndex] == startPoly)
296 {
297 startPolyFound = true;
298 break;
299 }
300 }
301
302 for (pathEndIndex = _polyLength - 1; pathEndIndex > pathStartIndex; --pathEndIndex)
303 {
304 if (_pathPolyRefs[pathEndIndex] == endPoly)
305 {
306 endPolyFound = true;
307 break;
308 }
309 }
310 }
311
312 if (startPolyFound && endPolyFound)
313 {
314 // we moved along the path and the target did not move out of our old poly-path
315 // our path is a simple subpath case, we have all the data we need
316 // just "cut" it out
317
318 _polyLength = pathEndIndex - pathStartIndex + 1;
319 memmove(_pathPolyRefs, _pathPolyRefs + pathStartIndex, _polyLength * sizeof(dtPolyRef));
320 }
321 else if (startPolyFound && !endPolyFound)
322 {
323 // we are moving on the old path but target moved out
324 // so we have atleast part of poly-path ready
325
326 _polyLength -= pathStartIndex;
327
328 // try to adjust the suffix of the path instead of recalculating entire length
329 // at given interval the target cannot get too far from its last location
330 // thus we have less poly to cover
331 // sub-path of optimal path is optimal
332
333 // take ~80% of the original length
335 uint32 prefixPolyLength = uint32(_polyLength * 0.8f + 0.5f);
336 memmove(_pathPolyRefs, _pathPolyRefs + pathStartIndex, prefixPolyLength * sizeof(dtPolyRef));
337
338 dtPolyRef suffixStartPoly = _pathPolyRefs[prefixPolyLength - 1];
339
340 // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data
341 float suffixEndPoint[VERTEX_SIZE];
342 if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, nullptr)))
343 {
344 // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that
345 // try to recover by using prev polyref
346 --prefixPolyLength;
347 suffixStartPoly = _pathPolyRefs[prefixPolyLength - 1];
348 if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, nullptr)))
349 {
350 // suffixStartPoly is still invalid, error state
353 return;
354 }
355 }
356
357 // generate suffix
358 uint32 suffixPolyLength = 0;
359
360 dtStatus dtResult;
361 if (_useRaycast)
362 {
365 return;
366 }
367 else
368 {
369 dtResult = _navMeshQuery->findPath(
370 suffixStartPoly, // start polygon
371 endPoly, // end polygon
372 suffixEndPoint, // start position
373 endPoint, // end position
374 &_filter, // polygon search filter
375 _pathPolyRefs + prefixPolyLength - 1, // [out] path
376 (int*)&suffixPolyLength,
377 MAX_PATH_LENGTH - prefixPolyLength); // max number of polygons in output path
378 }
379
380 if (!suffixPolyLength || dtStatusFailed(dtResult))
381 {
382 // this is probably an error state, but we'll leave it
383 // and hopefully recover on the next Update
384 // we still need to copy our preffix
385 LOG_ERROR("movement", "PathGenerator::BuildPolyPath: Path Build failed {}", _source->GetGUID().ToString());
386 }
387
388 // new path = prefix + suffix - overlap
389 _polyLength = prefixPolyLength + suffixPolyLength - 1;
390 }
391 else
392 {
393 // either we have no path at all -> first run
394 // or something went really wrong -> we aren't moving along the path to the target
395 // just generate new path
396
397 // free and invalidate old path data
398 Clear();
399
400 dtStatus dtResult;
401 if (_useRaycast)
402 {
403 float hit = 0;
404 float hitNormal[3];
405 memset(hitNormal, 0, sizeof(hitNormal));
406
407 dtResult = _navMeshQuery->raycast(
408 startPoly,
409 startPoint,
410 endPoint,
411 &_filter,
412 &hit,
413 hitNormal,
415 (int*)&_polyLength,
417
418 if (!_polyLength || dtStatusFailed(dtResult))
419 {
422 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
423 return;
424 }
425
426 // raycast() sets hit to FLT_MAX if there is a ray between start and end
427 if (hit != FLT_MAX)
428 {
429 float hitPos[3];
430
431 // 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)
432 hit *= 0.99f;
433 dtVlerp(hitPos, startPoint, endPoint, hit);
434
435 // if it fails again, clamp to poly boundary
436 if (dtStatusFailed(_navMeshQuery->getPolyHeight(_pathPolyRefs[_polyLength - 1], hitPos, &hitPos[1])))
437 _navMeshQuery->closestPointOnPolyBoundary(_pathPolyRefs[_polyLength - 1], hitPos, hitPos);
438
439 _pathPoints.resize(2);
441 _pathPoints[1] = G3D::Vector3(hitPos[2], hitPos[0], hitPos[1]);
442
445 AddFarFromPolyFlags(startFarFromPoly, false);
446 return;
447 }
448 else
449 {
450 // clamp to poly boundary if we fail to get the height
451 if (dtStatusFailed(_navMeshQuery->getPolyHeight(_pathPolyRefs[_polyLength - 1], endPoint, &endPoint[1])))
452 _navMeshQuery->closestPointOnPolyBoundary(_pathPolyRefs[_polyLength - 1], endPoint, endPoint);
453
454 _pathPoints.resize(2);
456 _pathPoints[1] = G3D::Vector3(endPoint[2], endPoint[0], endPoint[1]);
457
459 if (startFarFromPoly || endFarFromPoly)
460 {
462
463 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
464 }
465 else
467 return;
468 }
469 }
470 else
471 {
472 dtResult = _navMeshQuery->findPath(
473 startPoly, // start polygon
474 endPoly, // end polygon
475 startPoint, // start position
476 endPoint, // end position
477 &_filter, // polygon search filter
478 _pathPolyRefs, // [out] path
479 (int*)&_polyLength,
480 MAX_PATH_LENGTH); // max number of polygons in output path
481 }
482
483 if (!_polyLength || dtStatusFailed(dtResult))
484 {
485 // only happens if we passed bad data to findPath(), or navmesh is messed up
486 LOG_ERROR("movement", "PathGenerator::BuildPolyPath: {} Path Build failed: 0 length path", _source->GetGUID().ToString());
489 return;
490 }
491 }
492
493 if (!_polyLength)
494 {
495 LOG_ERROR("movement", "PathGenerator::BuildPolyPath: {} Path Build failed: 0 length path", _source->GetGUID().ToString());
498 return;
499 }
500
501 // by now we know what type of path we can get
502 if (_pathPolyRefs[_polyLength - 1] == endPoly && !(_type & PATHFIND_INCOMPLETE))
503 {
505 }
506 else
507 {
509 }
510
511 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
512
513 // generate the point-path out of our up-to-date poly-path
514 BuildPointPath(startPoint, endPoint);
515}
#define MAP_LIQUID_STATUS_IN_CONTACT
Definition GridTerrainData.h:32
@ LIQUID_MAP_UNDER_WATER
Definition GridTerrainData.h:195
#define INVALID_POLYREF
Definition PathGenerator.h:42
#define MAX_PATH_LENGTH
Definition PathGenerator.h:35
Definition Creature.h:47
bool CanSwim() const override
This method check the current flag/status of a creature and its inhabit type.
Definition Creature.cpp:3276
bool CanFly() const override
Definition Creature.h:86
LiquidData const GetLiquidData(uint32 phaseMask, float x, float y, float z, float collisionHeight, Optional< uint8 > ReqLiquidType)
Definition Map.cpp:1292
Creature * ToCreature()
Definition Object.h:206
Unit * ToUnit()
Definition Object.h:210
void AddFarFromPolyFlags(bool startFarFromPoly, bool endFarFromPoly)
Definition PathGenerator.cpp:1103
dtQueryFilterExt _filter
Definition PathGenerator.h:151
dtPolyRef GetPolyByLocation(float const *Point, float *Distance) const
Definition PathGenerator.cpp:124
bool IsWaterPath(Movement::PointsArray pathPoints) const
Definition PathGenerator.cpp:1153
void BuildPointPath(float const *startPoint, float const *endPoint)
Definition PathGenerator.cpp:517
void Clear()
Definition PathGenerator.h:124
Definition Unit.h:664
virtual bool CanFly() const =0
virtual bool CanSwim() const
this method checks the current flag of a unit
Definition Unit.cpp:15907
bool IsFalling() const
Definition Unit.cpp:15894
uint32 GetPhaseMask() const
Definition Object.h:516
virtual float GetCollisionHeight() const
Definition Object.h:715
LiquidStatus Status
Definition GridTerrainData.h:206

References _filter, _navMeshQuery, _pathPoints, _pathPolyRefs, _polyLength, _source, _type, _useRaycast, AddFarFromPolyFlags(), BuildPointPath(), BuildShortcut(), Unit::CanFly(), Creature::CanFly(), Unit::CanSwim(), Creature::CanSwim(), Clear(), WorldObject::GetCollisionHeight(), Object::GetGUID(), Map::GetLiquidData(), WorldObject::GetMap(), WorldObject::GetPhaseMask(), GetPolyByLocation(), GetStartPosition(), INVALID_POLYREF, Unit::IsFalling(), IsWaterPath(), LIQUID_MAP_UNDER_WATER, LOG_ERROR, MAP_LIQUID_STATUS_IN_CONTACT, MAX_PATH_LENGTH, NormalizePath(), PATHFIND_INCOMPLETE, PATHFIND_NOPATH, PATHFIND_NORMAL, PATHFIND_NOT_USING_PATH, SetActualEndPosition(), LiquidData::Status, Object::ToCreature(), ObjectGuid::ToString(), Object::ToUnit(), and VERTEX_SIZE.

Referenced by CalculatePath().

◆ BuildShortcut()

void PathGenerator::BuildShortcut ( )
private
632{
633 Clear();
634
635 // make two point path, our curr pos is the start, and dest is the end
636 _pathPoints.resize(2);
637
638 // set start and a default next position
641
643
645}
@ PATHFIND_SHORTCUT
Definition PathGenerator.h:48

References _pathPoints, _type, Clear(), GetActualEndPosition(), GetStartPosition(), NormalizePath(), and PATHFIND_SHORTCUT.

Referenced by BuildPointPath(), BuildPolyPath(), and CalculatePath().

◆ CalculatePath() [1/2]

bool PathGenerator::CalculatePath ( float  destX,
float  destY,
float  destZ,
bool  forceDest = false 
)
50{
51 float x, y, z;
52 _source->GetPosition(x, y, z);
53
54 return CalculatePath(x, y, z, destX, destY, destZ, forceDest);
55}
bool CalculatePath(float destX, float destY, float destZ, bool forceDest=false)
Definition PathGenerator.cpp:49
void GetPosition(float &x, float &y) const
Definition Position.h:126

References _source, CalculatePath(), and Position::GetPosition().

Referenced by CalculatePath(), Map::CheckCollisionAndGetValidCoords(), mmaps_commandscript::HandleMmapPathCommand(), mmaps_commandscript::HandleMmapTestArea(), Movement::MoveSplineInit::MoveTo(), and Movement::MoveSplineInit::MoveTo().

◆ CalculatePath() [2/2]

bool PathGenerator::CalculatePath ( float  x,
float  y,
float  z,
float  destX,
float  destY,
float  destZ,
bool  forceDest 
)
58{
59 if (!Acore::IsValidMapCoord(destX, destY, destZ) || !Acore::IsValidMapCoord(x, y, z))
60 return false;
61
62 METRIC_DETAILED_EVENT("mmap_events", "CalculatePath", "");
63
64 G3D::Vector3 dest(destX, destY, destZ);
65 SetEndPosition(dest);
66
67 G3D::Vector3 start(x, y, z);
68 SetStartPosition(start);
69
70 _forceDestination = forceDest;
71
72 // make sure navMesh works - we can run on map w/o mmap
73 // check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?)
74 Unit const* _sourceUnit = _source->ToUnit();
75 if (!_navMesh || !_navMeshQuery || (_sourceUnit && _sourceUnit->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING)) ||
76 !HaveTile(start) || !HaveTile(dest))
77 {
80 return true;
81 }
82
84
85 BuildPolyPath(start, dest);
86 return true;
87}
#define METRIC_DETAILED_EVENT(category, title, description)
Definition Metric.h:222
@ UNIT_STATE_IGNORE_PATHFINDING
Definition UnitDefines.h:200
bool HaveTile(G3D::Vector3 const &p) const
Definition PathGenerator.cpp:715
void BuildPolyPath(G3D::Vector3 const &startPos, G3D::Vector3 const &endPos)
Definition PathGenerator.cpp:159
void SetStartPosition(G3D::Vector3 const &point)
Definition PathGenerator.h:153
void UpdateFilter()
Definition PathGenerator.cpp:674
void SetEndPosition(G3D::Vector3 const &point)
Definition PathGenerator.h:154
bool HasUnitState(const uint32 f) const
Definition Unit.h:736
bool IsValidMapCoord(float c)
Definition GridDefines.h:210

References _forceDestination, _navMesh, _navMeshQuery, _source, _type, BuildPolyPath(), BuildShortcut(), Unit::HasUnitState(), HaveTile(), Acore::IsValidMapCoord(), METRIC_DETAILED_EVENT, PATHFIND_NORMAL, PATHFIND_NOT_USING_PATH, SetEndPosition(), SetStartPosition(), Object::ToUnit(), UNIT_STATE_IGNORE_PATHFINDING, and UpdateFilter().

◆ Clear()

void PathGenerator::Clear ( )
inline
125 {
126 _polyLength = 0;
127 _pathPoints.clear();
128 }

References _pathPoints, and _polyLength.

Referenced by BuildPolyPath(), and BuildShortcut().

◆ CreateFilter()

void PathGenerator::CreateFilter ( )
private
648{
649 uint16 includeFlags = 0;
650 uint16 excludeFlags = 0;
651
652 if (_source->IsCreature())
653 {
654 Creature* creature = (Creature*)_source;
655 if (creature->CanWalk())
656 includeFlags |= NAV_GROUND; // walk
657
658 // creatures don't take environmental damage
659 if (creature->CanEnterWater())
660 includeFlags |= (NAV_WATER | NAV_MAGMA);
661 }
662 else // assume Player
663 {
664 // perfect support not possible, just stay 'safe'
665 includeFlags |= (NAV_GROUND | NAV_WATER | NAV_MAGMA);
666 }
667
668 _filter.setIncludeFlags(includeFlags);
669 _filter.setExcludeFlags(excludeFlags);
670
671 UpdateFilter();
672}
std::uint16_t uint16
Definition Define.h:108
@ NAV_MAGMA
Definition MapDefines.h:92
@ NAV_GROUND
Definition MapDefines.h:91
@ NAV_WATER
Definition MapDefines.h:94
bool CanWalk() const
Definition Creature.h:83
bool CanEnterWater() const override
Definition Creature.cpp:3287
bool IsCreature() const
Definition Object.h:205

References _filter, _source, Creature::CanEnterWater(), Creature::CanWalk(), Object::IsCreature(), NAV_GROUND, NAV_MAGMA, NAV_WATER, and UpdateFilter().

Referenced by PathGenerator().

◆ Dist3DSqr()

float PathGenerator::Dist3DSqr ( G3D::Vector3 const &  p1,
G3D::Vector3 const &  p2 
) const
private
1033{
1034 return (p1 - p2).squaredLength();
1035}

Referenced by BuildPointPath().

◆ FindSmoothPath()

dtStatus PathGenerator::FindSmoothPath ( float const *  startPos,
float const *  endPos,
dtPolyRef const *  polyPath,
uint32  polyPathSize,
float *  smoothPath,
int *  smoothPathSize,
uint32  smoothPathMaxSize 
)
private
817{
818 *smoothPathSize = 0;
819 uint32 nsmoothPath = 0;
820
821 dtPolyRef polys[MAX_PATH_LENGTH];
822 memcpy(polys, polyPath, sizeof(dtPolyRef) * polyPathSize);
823 uint32 npolys = polyPathSize;
824
825 float iterPos[VERTEX_SIZE], targetPos[VERTEX_SIZE];
826
827 if (polyPathSize > 1)
828 {
829 // Pick the closest points on poly border
830 if (dtStatusFailed(_navMeshQuery->closestPointOnPolyBoundary(polys[0], startPos, iterPos)))
831 {
832 return DT_FAILURE;
833 }
834
835 if (dtStatusFailed(_navMeshQuery->closestPointOnPolyBoundary(polys[npolys - 1], endPos, targetPos)))
836 {
837 return DT_FAILURE;
838 }
839 }
840 else
841 {
842 // Case where the path is on the same poly
843 dtVcopy(iterPos, startPos);
844 dtVcopy(targetPos, endPos);
845 }
846
847 dtVcopy(&smoothPath[nsmoothPath * VERTEX_SIZE], iterPos);
848 nsmoothPath++;
849
850 // Move towards target a small advancement at a time until target reached or
851 // when ran out of memory to store the path.
852 while (npolys && nsmoothPath < maxSmoothPathSize)
853 {
854 // Find location to steer towards.
855 float steerPos[VERTEX_SIZE];
856 unsigned char steerPosFlag;
857 dtPolyRef steerPosRef = INVALID_POLYREF;
858
859 if (!GetSteerTarget(iterPos, targetPos, SMOOTH_PATH_SLOP, polys, npolys, steerPos, steerPosFlag, steerPosRef))
860 break;
861
862 bool endOfPath = (steerPosFlag & DT_STRAIGHTPATH_END) != 0;
863 bool offMeshConnection = (steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0;
864
865 // Find movement delta.
866 float delta[VERTEX_SIZE];
867 dtVsub(delta, steerPos, iterPos);
868 float len = dtMathSqrtf(dtVdot(delta, delta));
869 // If the steer target is end of path or off-mesh link, do not move past the location.
870 if ((endOfPath || offMeshConnection) && len < SMOOTH_PATH_STEP_SIZE)
871 len = 1.0f;
872 else
873 len = SMOOTH_PATH_STEP_SIZE / len;
874
875 float moveTgt[VERTEX_SIZE];
876 dtVmad(moveTgt, iterPos, delta, len);
877
878 // Move
879 float result[VERTEX_SIZE];
880 const static uint32 MAX_VISIT_POLY = 16;
881 dtPolyRef visited[MAX_VISIT_POLY];
882
883 uint32 nvisited = 0;
884 if (dtStatusFailed(_navMeshQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &_filter, result, visited, (int*)&nvisited, MAX_VISIT_POLY)))
885 {
886 return DT_FAILURE;
887 }
888 npolys = FixupCorridor(polys, npolys, MAX_PATH_LENGTH, visited, nvisited);
889
890 if (dtStatusFailed(_navMeshQuery->getPolyHeight(polys[0], result, &result[1])))
891 LOG_DEBUG("maps", "PathGenerator::FindSmoothPath: Cannot find height at position X: {} Y: {} Z: {} for {}",
892 result[2], result[0], result[1], _source->GetGUID().ToString());
893 result[1] += 0.5f;
894 dtVcopy(iterPos, result);
895
896 bool canCheckSlope = _slopeCheck && (GetPathType() & ~(PATHFIND_NOT_USING_PATH));
897
898 if (canCheckSlope && !IsSwimmableSegment(iterPos, steerPos) && !IsWalkableClimb(iterPos, steerPos))
899 {
900 nsmoothPath--;
901 *smoothPathSize = nsmoothPath;
902 return DT_FAILURE | DT_SLOPE_TOO_STEEP;
903 }
904
905 // Handle end of path and off-mesh links when close enough.
906 if (endOfPath && InRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
907 {
908 // Reached end of path.
909 dtVcopy(iterPos, targetPos);
910 if (nsmoothPath < maxSmoothPathSize)
911 {
912 dtVcopy(&smoothPath[nsmoothPath * VERTEX_SIZE], iterPos);
913 nsmoothPath++;
914 }
915 break;
916 }
917 else if (offMeshConnection && InRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
918 {
919 // Advance the path up to and over the off-mesh connection.
920 dtPolyRef prevRef = INVALID_POLYREF;
921 dtPolyRef polyRef = polys[0];
922 uint32 npos = 0;
923 while (npos < npolys && polyRef != steerPosRef)
924 {
925 prevRef = polyRef;
926 polyRef = polys[npos];
927 npos++;
928 }
929
930 for (uint32 i = npos; i < npolys; ++i)
931 polys[i - npos] = polys[i];
932
933 npolys -= npos;
934
935 // Handle the connection.
936 float connectionStartPos[VERTEX_SIZE], connectionEndPos[VERTEX_SIZE];
937 if (dtStatusSucceed(_navMesh->getOffMeshConnectionPolyEndPoints(prevRef, polyRef, connectionStartPos, connectionEndPos)))
938 {
939 if (nsmoothPath < maxSmoothPathSize)
940 {
941 dtVcopy(&smoothPath[nsmoothPath * VERTEX_SIZE], connectionStartPos);
942 nsmoothPath++;
943 }
944 // Move position at the other side of the off-mesh link.
945 dtVcopy(iterPos, connectionEndPos);
946 if (dtStatusFailed(_navMeshQuery->getPolyHeight(polys[0], iterPos, &iterPos[1])))
947 return DT_FAILURE;
948 iterPos[1] += 0.5f;
949 }
950 }
951
952 // Store results.
953 if (nsmoothPath < maxSmoothPathSize)
954 {
955 dtVcopy(&smoothPath[nsmoothPath * VERTEX_SIZE], iterPos);
956 nsmoothPath++;
957 }
958 }
959
960 *smoothPathSize = nsmoothPath;
961
962 // this is most likely a loop
963 return nsmoothPath < MAX_POINT_PATH_LENGTH ? DT_SUCCESS : DT_FAILURE;
964}
#define LOG_DEBUG(filterType__,...)
Definition Log.h:170
#define SMOOTH_PATH_SLOP
Definition PathGenerator.h:39
#define SMOOTH_PATH_STEP_SIZE
Definition PathGenerator.h:38
PathType GetPathType() const
Definition PathGenerator.h:92
bool IsWalkableClimb(float const *v1, float const *v2) const
Definition PathGenerator.cpp:966
bool InRangeYZX(float const *v1, float const *v2, float r, float h) const
Definition PathGenerator.cpp:1018
uint32 FixupCorridor(dtPolyRef *path, uint32 npath, uint32 maxPath, dtPolyRef const *visited, uint32 nvisited)
Definition PathGenerator.cpp:731
bool IsSwimmableSegment(float const *v1, float const *v2, bool checkSwim=true) const
predict if a certain segment is underwater and the unit can swim Must only be used for very short seg...
Definition PathGenerator.cpp:1125
bool GetSteerTarget(float const *startPos, float const *endPos, float minTargetDist, dtPolyRef const *path, uint32 pathSize, float *steerPos, unsigned char &steerPosFlag, dtPolyRef &steerPosRef)
Definition PathGenerator.cpp:776

References _filter, _navMesh, _navMeshQuery, _slopeCheck, _source, FixupCorridor(), Object::GetGUID(), GetPathType(), GetSteerTarget(), InRangeYZX(), INVALID_POLYREF, IsSwimmableSegment(), IsWalkableClimb(), LOG_DEBUG, MAX_PATH_LENGTH, MAX_POINT_PATH_LENGTH, PATHFIND_NOT_USING_PATH, SMOOTH_PATH_SLOP, SMOOTH_PATH_STEP_SIZE, ObjectGuid::ToString(), and VERTEX_SIZE.

Referenced by BuildPointPath().

◆ FixupCorridor()

uint32 PathGenerator::FixupCorridor ( dtPolyRef *  path,
uint32  npath,
uint32  maxPath,
dtPolyRef const *  visited,
uint32  nvisited 
)
private
732{
733 int32 furthestPath = -1;
734 int32 furthestVisited = -1;
735
736 // Find furthest common polygon.
737 for (int32 i = npath - 1; i >= 0; --i)
738 {
739 bool found = false;
740 for (int32 j = nvisited - 1; j >= 0; --j)
741 {
742 if (path[i] == visited[j])
743 {
744 furthestPath = i;
745 furthestVisited = j;
746 found = true;
747 }
748 }
749 if (found)
750 break;
751 }
752
753 // If no intersection found just return current path.
754 if (furthestPath == -1 || furthestVisited == -1)
755 return npath;
756
757 // Concatenate paths.
758
759 // Adjust beginning of the buffer to include the visited.
760 uint32 req = nvisited - furthestVisited;
761 uint32 orig = uint32(furthestPath + 1) < npath ? furthestPath + 1 : npath;
762 uint32 size = npath > orig ? npath - orig : 0;
763 if (req + size > maxPath)
764 size = maxPath - req;
765
766 if (size)
767 memmove(path + req, path + orig, size * sizeof(dtPolyRef));
768
769 // Store visited
770 for (uint32 i = 0; i < req; ++i)
771 path[i] = visited[(nvisited - 1) - i];
772
773 return req + size;
774}
std::int32_t int32
Definition Define.h:103

Referenced by FindSmoothPath().

◆ GetActualEndPosition()

G3D::Vector3 const & PathGenerator::GetActualEndPosition ( ) const
inline

◆ GetEndPosition()

G3D::Vector3 const & PathGenerator::GetEndPosition ( ) const
inline

◆ GetNavTerrain()

NavTerrain PathGenerator::GetNavTerrain ( float  x,
float  y,
float  z 
) const
private
697{
698 LiquidData const& liquidData = _source->GetMap()->GetLiquidData(_source->GetPhaseMask(), x, y, z, _source->GetCollisionHeight(), {});
699 if (liquidData.Status == LIQUID_MAP_NO_WATER)
700 return NAV_GROUND;
701
702 switch (liquidData.Flags)
703 {
706 return NAV_WATER;
709 return NAV_MAGMA;
710 default:
711 return NAV_GROUND;
712 }
713}
#define MAP_LIQUID_TYPE_MAGMA
Definition GridTerrainData.h:37
#define MAP_LIQUID_TYPE_WATER
Definition GridTerrainData.h:35
#define MAP_LIQUID_TYPE_OCEAN
Definition GridTerrainData.h:36
#define MAP_LIQUID_TYPE_SLIME
Definition GridTerrainData.h:38
@ LIQUID_MAP_NO_WATER
Definition GridTerrainData.h:191
Definition GridTerrainData.h:199
uint32 Flags
Definition GridTerrainData.h:203

References _source, LiquidData::Flags, WorldObject::GetCollisionHeight(), Map::GetLiquidData(), WorldObject::GetMap(), WorldObject::GetPhaseMask(), LIQUID_MAP_NO_WATER, MAP_LIQUID_TYPE_MAGMA, MAP_LIQUID_TYPE_OCEAN, MAP_LIQUID_TYPE_SLIME, MAP_LIQUID_TYPE_WATER, NAV_GROUND, NAV_MAGMA, NAV_WATER, and LiquidData::Status.

Referenced by IsWaterPath(), and UpdateFilter().

◆ GetPath()

◆ getPathLength()

float PathGenerator::getPathLength ( ) const
inline
98 {
99 float len = 0.0f;
100 float dx, dy, dz;
101 uint32 size = _pathPoints.size();
102 if (size)
103 {
104 dx = _pathPoints[0].x - _startPosition.x;
105 dy = _pathPoints[0].y - _startPosition.y;
106 dz = _pathPoints[0].z - _startPosition.z;
107 len += std::sqrt( dx * dx + dy * dy + dz * dz );
108 }
109 else
110 {
111 return len;
112 }
113
114 for (uint32 i = 1; i < size; ++i)
115 {
116 dx = _pathPoints[i].x - _pathPoints[i - 1].x;
117 dy = _pathPoints[i].y - _pathPoints[i - 1].y;
118 dz = _pathPoints[i].z - _pathPoints[i - 1].z;
119 len += std::sqrt( dx * dx + dy * dy + dz * dz );
120 }
121 return len;
122 }
G3D::Vector3 _startPosition
Definition PathGenerator.h:143

References _pathPoints, and _startPosition.

◆ GetPathPolyByPosition()

dtPolyRef PathGenerator::GetPathPolyByPosition ( dtPolyRef const *  polyPath,
uint32  polyPathSize,
float const *  Point,
float *  Distance = nullptr 
) const
private
90{
91 if (!polyPath || !polyPathSize)
92 return INVALID_POLYREF;
93
94 dtPolyRef nearestPoly = INVALID_POLYREF;
95 float minDist = FLT_MAX;
96
97 for (uint32 i = 0; i < polyPathSize; ++i)
98 {
99 float closestPoint[VERTEX_SIZE];
100 if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(polyPath[i], point, closestPoint, nullptr)))
101 continue;
102
103 float d = dtVdistSqr(point, closestPoint);
104 if (d < minDist)
105 {
106 minDist = d;
107 nearestPoly = polyPath[i];
108 }
109
110 if (minDist < 1.0f) // shortcut out - close enough for us
111 {
112 break;
113 }
114 }
115
116 if (distance)
117 {
118 *distance = dtMathSqrtf(minDist);
119 }
120
121 return (minDist < 3.0f) ? nearestPoly : INVALID_POLYREF;
122}

References _navMeshQuery, INVALID_POLYREF, and VERTEX_SIZE.

Referenced by GetPolyByLocation().

◆ GetPathType()

◆ GetPolyByLocation()

dtPolyRef PathGenerator::GetPolyByLocation ( float const *  Point,
float *  Distance 
) const
private
125{
126 // first we check the current path
127 // if the current path doesn't contain the current poly,
128 // we need to use the expensive navMesh.findNearestPoly
129 dtPolyRef polyRef = GetPathPolyByPosition(_pathPolyRefs, _polyLength, point, distance);
130 if (polyRef != INVALID_POLYREF)
131 return polyRef;
132
133 // we don't have it in our old path
134 // try to get it by findNearestPoly()
135 // first try with low search box
136 float extents[VERTEX_SIZE] = { 3.0f, 5.0f, 3.0f }; // bounds of poly search area
137 float closestPoint[VERTEX_SIZE] = { 0.0f, 0.0f, 0.0f };
138 if (dtStatusSucceed(_navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint)) && polyRef != INVALID_POLYREF)
139 {
140 *distance = dtVdist(closestPoint, point);
141 return polyRef;
142 }
143
144 // still nothing ..
145 // try with bigger search box
146 // Note that the extent should not overlap more than 128 polygons in the navmesh (see dtNavMeshQuery::findNearestPoly)
147 extents[1] = 50.0f;
148
149 if (dtStatusSucceed(_navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint)) && polyRef != INVALID_POLYREF)
150 {
151 *distance = dtVdist(closestPoint, point);
152 return polyRef;
153 }
154
155 *distance = FLT_MAX;
156 return INVALID_POLYREF;
157}
dtPolyRef GetPathPolyByPosition(dtPolyRef const *polyPath, uint32 polyPathSize, float const *Point, float *Distance=nullptr) const
Definition PathGenerator.cpp:89

References _filter, _navMeshQuery, _pathPolyRefs, _polyLength, GetPathPolyByPosition(), INVALID_POLYREF, and VERTEX_SIZE.

Referenced by BuildPolyPath().

◆ GetRequiredHeightToClimb()

float PathGenerator::GetRequiredHeightToClimb ( float  x,
float  y,
float  z,
float  destX,
float  destY,
float  destZ,
float  sourceHeight 
)
static

Return the height of a slope that can be climbed based on source height This method is meant for short distances or linear paths.

Parameters
xstart x coord
ystart y coord
zstart z coord
destXdestination x coord
destYdestination y coord
destZdestination z coord
sourceHeightheight of the source
Returns
float the maximum height that a source can climb based on slope angle
1011{
1012 float slopeAngle = getSlopeAngleAbs(x, y, z, destX, destY, destZ);
1013 float slopeAngleDegree = (slopeAngle * 180.0f / M_PI);
1014 float climbableHeight = sourceHeight - (sourceHeight * (slopeAngleDegree / 100));
1015 return climbableHeight;
1016}
float getSlopeAngleAbs(float startX, float startY, float startZ, float destX, float destY, float destZ)
Definition Geometry.h:46

References getSlopeAngleAbs().

Referenced by IsWalkableClimb().

◆ GetStartPosition()

G3D::Vector3 const & PathGenerator::GetStartPosition ( ) const
inline

◆ GetSteerTarget()

bool PathGenerator::GetSteerTarget ( float const *  startPos,
float const *  endPos,
float  minTargetDist,
dtPolyRef const *  path,
uint32  pathSize,
float *  steerPos,
unsigned char &  steerPosFlag,
dtPolyRef &  steerPosRef 
)
private
779{
780 // Find steer target.
781 static const uint32 MAX_STEER_POINTS = 3;
782 float steerPath[MAX_STEER_POINTS * VERTEX_SIZE];
783 unsigned char steerPathFlags[MAX_STEER_POINTS];
784 dtPolyRef steerPathPolys[MAX_STEER_POINTS];
785 uint32 nsteerPath = 0;
786 dtStatus dtResult = _navMeshQuery->findStraightPath(startPos, endPos, path, pathSize,
787 steerPath, steerPathFlags, steerPathPolys, (int*)&nsteerPath, MAX_STEER_POINTS);
788 if (!nsteerPath || dtStatusFailed(dtResult))
789 return false;
790
791 // Find vertex far enough to steer to.
792 uint32 ns = 0;
793 while (ns < nsteerPath)
794 {
795 // Stop at Off-Mesh link or when point is further than slop away.
796 if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
797 !InRangeYZX(&steerPath[ns * VERTEX_SIZE], startPos, minTargetDist, 1000.0f))
798 break;
799
800 ns++;
801 }
802 // Failed to find good point to steer to.
803 if (ns >= nsteerPath)
804 return false;
805
806 dtVcopy(steerPos, &steerPath[ns * VERTEX_SIZE]);
807 steerPos[1] = startPos[1]; // keep Z value
808 steerPosFlag = steerPathFlags[ns];
809 steerPosRef = steerPathPolys[ns];
810
811 return true;
812}

References _navMeshQuery, InRangeYZX(), and VERTEX_SIZE.

Referenced by FindSmoothPath().

◆ HaveTile()

bool PathGenerator::HaveTile ( G3D::Vector3 const &  p) const
private

Workaround For some reason, often the tx and ty variables wont get a valid value Use this check to prevent getting negative tile coords and crashing on getTileAt

716{
717 int tx = -1, ty = -1;
718 float point[VERTEX_SIZE] = { p.y, p.z, p.x };
719
720 _navMesh->calcTileLoc(point, &tx, &ty);
721
725 if (tx < 0 || ty < 0)
726 return false;
727
728 return (_navMesh->getTileAt(tx, ty, 0) != nullptr);
729}

References _navMesh, and VERTEX_SIZE.

Referenced by CalculatePath().

◆ InRange()

bool PathGenerator::InRange ( G3D::Vector3 const &  p1,
G3D::Vector3 const &  p2,
float  r,
float  h 
) const
private
1027{
1028 G3D::Vector3 d = p1 - p2;
1029 return (d.x * d.x + d.y * d.y) < r * r && fabsf(d.z) < h;
1030}

Referenced by BuildPointPath().

◆ InRangeYZX()

bool PathGenerator::InRangeYZX ( float const *  v1,
float const *  v2,
float  r,
float  h 
) const
private
1019{
1020 const float dx = v2[0] - v1[0];
1021 const float dy = v2[1] - v1[1]; // elevation
1022 const float dz = v2[2] - v1[2];
1023 return (dx * dx + dz * dz) < r * r && fabsf(dy) < h;
1024}

Referenced by FindSmoothPath(), and GetSteerTarget().

◆ IsInvalidDestinationZ()

bool PathGenerator::IsInvalidDestinationZ ( Unit const *  target) const
1099{
1100 return (target->GetPositionZ() - GetActualEndPosition().z) > 5.0f;
1101}

References GetActualEndPosition(), and Position::GetPositionZ().

◆ IsSwimmableSegment() [1/2]

bool PathGenerator::IsSwimmableSegment ( float const *  v1,
float const *  v2,
bool  checkSwim = true 
) const

predict if a certain segment is underwater and the unit can swim Must only be used for very short segments since this check doesn't work on long paths that alternate terrain and water.

Parameters
v1
v2
Returns
true
false
1126{
1127 return IsSwimmableSegment(v1[2], v1[0], v1[1], v2[2], v2[0], v2[1], checkSwim);
1128}

References IsSwimmableSegment().

Referenced by FindSmoothPath(), IsSwimmableSegment(), and ShortenPathUntilDist().

◆ IsSwimmableSegment() [2/2]

bool PathGenerator::IsSwimmableSegment ( float  x,
float  y,
float  z,
float  destX,
float  destY,
float  destZ,
bool  checkSwim = true 
) const

predict if a certain segment is underwater and the unit can swim Must only be used for very short segments since this check doesn't work on long paths that alternate terrain and water.

Parameters
x
y
z
destX
destY
destZ
checkSwimalso check if the unit can swim
Returns
true if there's water at the end AND at the start of the segment
false if there's no water at the end OR at the start of the segment
1146{
1147 Creature const* _sourceCreature = _source->ToCreature();
1149 _source->GetMap()->IsInWater(_source->GetPhaseMask(), destX, destY, destZ, _source->GetCollisionHeight()) &&
1150 (!checkSwim || !_sourceCreature || _sourceCreature->CanSwim());
1151}
bool IsInWater(uint32 phaseMask, float x, float y, float z, float collisionHeight) const
Definition Map.cpp:1573

References _source, Creature::CanSwim(), WorldObject::GetCollisionHeight(), WorldObject::GetMap(), WorldObject::GetPhaseMask(), Map::IsInWater(), and Object::ToCreature().

◆ IsWalkableClimb() [1/3]

bool PathGenerator::IsWalkableClimb ( float const *  v1,
float const *  v2 
) const
967{
968 return IsWalkableClimb(v1[2], v1[0], v1[1], v2[2], v2[0], v2[1]);
969}

References IsWalkableClimb().

Referenced by Map::CanReachPositionAndGetValidCoords(), FindSmoothPath(), IsWalkableClimb(), IsWalkableClimb(), and ShortenPathUntilDist().

◆ IsWalkableClimb() [2/3]

bool PathGenerator::IsWalkableClimb ( float  x,
float  y,
float  z,
float  destX,
float  destY,
float  destZ 
) const
972{
973 return IsWalkableClimb(x, y, z, destX, destY, destZ, _source->GetCollisionHeight());
974}

References _source, WorldObject::GetCollisionHeight(), and IsWalkableClimb().

◆ IsWalkableClimb() [3/3]

bool PathGenerator::IsWalkableClimb ( float  x,
float  y,
float  z,
float  destX,
float  destY,
float  destZ,
float  sourceHeight 
)
static

Check if a slope can be climbed based on source height This method is meant for short distances or linear paths.

Parameters
xstart x coord
ystart y coord
zstart z coord
destXdestination x coord
destYdestination y coord
destZdestination z coord
sourceHeightheight of the source
Returns
bool check if you can climb the path
990{
991 float diffHeight = std::abs(destZ - z);
992 float reqHeight = GetRequiredHeightToClimb(x, y, z, destX, destY, destZ, sourceHeight);
993 // check walkable slopes, based on unit height
994 return diffHeight <= reqHeight;
995}
static float GetRequiredHeightToClimb(float x, float y, float z, float destX, float destY, float destZ, float sourceHeight)
Return the height of a slope that can be climbed based on source height This method is meant for shor...
Definition PathGenerator.cpp:1010

References GetRequiredHeightToClimb().

◆ IsWaterPath()

bool PathGenerator::IsWaterPath ( Movement::PointsArray  pathPoints) const
1154{
1155 bool waterPath = true;
1156 // Check both start and end points, if they're both in water, then we can *safely* let the creature move
1157 for (uint32 i = 0; i < pathPoints.size(); ++i)
1158 {
1159 NavTerrain terrain = GetNavTerrain(pathPoints[i].x, pathPoints[i].y, pathPoints[i].z);
1160 // One of the points is not in the water
1161 if (terrain != NAV_MAGMA && terrain != NAV_WATER)
1162 {
1163 waterPath = false;
1164 break;
1165 }
1166 }
1167
1168 return waterPath;
1169}
NavTerrain
Definition MapDefines.h:89
NavTerrain GetNavTerrain(float x, float y, float z) const
Definition PathGenerator.cpp:696

References GetNavTerrain(), NAV_MAGMA, and NAV_WATER.

Referenced by BuildPolyPath().

◆ NormalizePath()

void PathGenerator::NormalizePath ( )
private
624{
625 for (uint32 i = 0; i < _pathPoints.size(); ++i)
626 {
628 }
629}
void UpdateAllowedPositionZ(float x, float y, float &z, float *groundZ=nullptr) const
Definition Object.cpp:1610

References _pathPoints, _source, and WorldObject::UpdateAllowedPositionZ().

Referenced by BuildPointPath(), BuildPolyPath(), and BuildShortcut().

◆ SetActualEndPosition()

void PathGenerator::SetActualEndPosition ( G3D::Vector3 const &  point)
inlineprivate
155{ _actualEndPosition = point; }

References _actualEndPosition.

Referenced by BuildPointPath(), and BuildPolyPath().

◆ SetEndPosition()

void PathGenerator::SetEndPosition ( G3D::Vector3 const &  point)
inlineprivate
154{ _actualEndPosition = point; _endPosition = point; }

References _actualEndPosition, and _endPosition.

Referenced by CalculatePath().

◆ SetPathLengthLimit()

void PathGenerator::SetPathLengthLimit ( float  distance)
inline

◆ SetSlopeCheck()

void PathGenerator::SetSlopeCheck ( bool  checkSlope)
inline
80{ _slopeCheck = checkSlope; }

References _slopeCheck.

◆ SetStartPosition()

void PathGenerator::SetStartPosition ( G3D::Vector3 const &  point)
inlineprivate
153{ _startPosition = point; }

References _startPosition.

Referenced by CalculatePath().

◆ SetUseRaycast()

void PathGenerator::SetUseRaycast ( bool  useRaycast)
inline

◆ SetUseStraightPath()

void PathGenerator::SetUseStraightPath ( bool  useStraightPath)
inline
81{ _useStraightPath = useStraightPath; }

References _useStraightPath.

Referenced by mmaps_commandscript::HandleMmapPathCommand().

◆ ShortenPathUntilDist()

void PathGenerator::ShortenPathUntilDist ( G3D::Vector3 const &  point,
float  dist 
)
1038{
1039 if (GetPathType() == PATHFIND_BLANK || _pathPoints.size() < 2)
1040 {
1041 LOG_ERROR("movement", "PathGenerator::ReducePathLengthByDist called before path was successfully built");
1042 return;
1043 }
1044
1045 float const distSq = dist * dist;
1046
1047 // the first point of the path must be outside the specified range
1048 // (this should have really been checked by the caller...)
1049 if ((_pathPoints[0] - target).squaredLength() < distSq)
1050 return;
1051
1052 // check if we even need to do anything
1053 if ((*_pathPoints.rbegin() - target).squaredLength() >= distSq)
1054 return;
1055
1056 std::size_t i = _pathPoints.size() - 1;
1057 float x, y, z, collisionHeight = _source->GetCollisionHeight();
1058 // find the first i s.t.:
1059 // - _pathPoints[i] is still too close
1060 // - _pathPoints[i-1] is too far away
1061 // => the end point is somewhere on the line between the two
1062 while (1)
1063 {
1064 // we know that pathPoints[i] is too close already (from the previous iteration)
1065 if ((_pathPoints[i - 1] - target).squaredLength() >= distSq)
1066 break; // bingo!
1067
1068 bool canCheckSlope = _slopeCheck && (GetPathType() & ~(PATHFIND_NOT_USING_PATH));
1069
1070 // check if the shortened path is still in LoS with the target and it is walkable
1071 _source->GetHitSpherePointFor({ _pathPoints[i - 1].x, _pathPoints[i - 1].y, _pathPoints[i - 1].z + collisionHeight }, x, y, z);
1072 if (!_source->GetMap()->isInLineOfSight(x, y, z, _pathPoints[i - 1].x, _pathPoints[i - 1].y, _pathPoints[i - 1].z + collisionHeight,
1076 {
1077 // whenver we find a point that is not valid anymore, simply use last valid path
1078 _pathPoints.resize(i + 1);
1079 return;
1080 }
1081
1082 if (!--i)
1083 {
1084 // no point found that fulfills the condition
1085 _pathPoints[0] = _pathPoints[1];
1086 _pathPoints.resize(2);
1087 return;
1088 }
1089 }
1090
1091 // ok, _pathPoints[i] is too close, _pathPoints[i-1] is not, so our target point is somewhere between the two...
1092 // ... settle for a guesstimate since i'm not confident in doing trig on every chase motion tick...
1093 // (@todo review this)
1094 _pathPoints[i] += (_pathPoints[i - 1] - _pathPoints[i]).direction() * (dist - (_pathPoints[i] - target).length());
1095 _pathPoints.resize(i + 1);
1096}
@ LINEOFSIGHT_ALL_CHECKS
Definition Map.h:111
bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const
Definition Map.cpp:1525
Position GetHitSpherePointFor(Position const &dest, Optional< float > collisionHeight={ }, Optional< float > combatReach={ }) const
Definition Object.cpp:1286
float GetPositionZ() const
Definition Position.h:123
float GetPositionX() const
Definition Position.h:121
float GetPositionY() const
Definition Position.h:122

References _pathPoints, _slopeCheck, _source, WorldObject::GetCollisionHeight(), WorldObject::GetHitSpherePointFor(), WorldObject::GetMap(), GetPathType(), WorldObject::GetPhaseMask(), Position::GetPositionX(), Position::GetPositionY(), Position::GetPositionZ(), Map::isInLineOfSight(), IsSwimmableSegment(), IsWalkableClimb(), LINEOFSIGHT_ALL_CHECKS, LOG_ERROR, VMAP::Nothing, PATHFIND_BLANK, and PATHFIND_NOT_USING_PATH.

◆ UpdateFilter()

void PathGenerator::UpdateFilter ( )
private
675{
676 // allow creatures to cheat and use different movement types if they are moved
677 // forcefully into terrain they can't normally move in
678 if (Unit const* _sourceUnit = _source->ToUnit())
679 {
680 if (_sourceUnit->IsInWater() || _sourceUnit->IsUnderWater())
681 {
682 uint16 includedFlags = _filter.getIncludeFlags();
683 includedFlags |= GetNavTerrain(_source->GetPositionX(),
686
687 _filter.setIncludeFlags(includedFlags);
688 }
689
690 /*if (Creature const* _sourceCreature = _source->ToCreature())
691 if (_sourceCreature->IsInCombat() || _sourceCreature->IsInEvadeMode())
692 _filter.setIncludeFlags(_filter.getIncludeFlags() | NAV_GROUND_STEEP);*/
693 }
694}

References _filter, _source, GetNavTerrain(), Position::GetPositionX(), Position::GetPositionY(), Position::GetPositionZ(), and Object::ToUnit().

Referenced by CalculatePath(), and CreateFilter().

Member Data Documentation

◆ _actualEndPosition

G3D::Vector3 PathGenerator::_actualEndPosition
private

◆ _endPosition

G3D::Vector3 PathGenerator::_endPosition
private

Referenced by GetEndPosition(), and SetEndPosition().

◆ _filter

◆ _forceDestination

bool PathGenerator::_forceDestination
private

Referenced by BuildPointPath(), and CalculatePath().

◆ _navMesh

dtNavMesh const* PathGenerator::_navMesh
private

◆ _navMeshQuery

dtNavMeshQuery const* PathGenerator::_navMeshQuery
private

◆ _pathPoints

◆ _pathPolyRefs

dtPolyRef PathGenerator::_pathPolyRefs[MAX_PATH_LENGTH]
private

◆ _pointPathLimit

uint32 PathGenerator::_pointPathLimit
private

◆ _polyLength

uint32 PathGenerator::_polyLength
private

◆ _slopeCheck

bool PathGenerator::_slopeCheck
private

◆ _source

◆ _startPosition

G3D::Vector3 PathGenerator::_startPosition
private

◆ _type

◆ _useRaycast

bool PathGenerator::_useRaycast
private

◆ _useStraightPath

bool PathGenerator::_useStraightPath
private

The documentation for this class was generated from the following files: