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
126 :
129 _endPosition(G3D::Vector3::zero()), _source(owner), _navMesh(nullptr),
130 _navMeshQuery(nullptr)
131{
132 memset(_pathPolyRefs, 0, sizeof(_pathPolyRefs));
133
134 //if (sDisableMgr->IsPathfindingEnabled(_sourceUnit->FindMap()))
135 {
138 }
139
140 CreateFilter();
141}
#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:573
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:749
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:631

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

◆ ~PathGenerator()

PathGenerator::~PathGenerator ( )
144{
145}

Member Function Documentation

◆ AddFarFromPolyFlags()

void PathGenerator::AddFarFromPolyFlags ( bool  startFarFromPoly,
bool  endFarFromPoly 
)
private
1206{
1207 if (startFarFromPoly)
1208 {
1210 }
1211 if (endFarFromPoly)
1212 {
1214 }
1215}
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
616{
617 float pathPoints[MAX_POINT_PATH_LENGTH * VERTEX_SIZE];
618 uint32 pointCount = 0;
619 dtStatus dtResult = DT_FAILURE;
620 if (_useRaycast)
621 {
622 // _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
623 LOG_ERROR("movement", "PathGenerator::BuildPointPath() called with _useRaycast for unit {}", _source->GetGUID().ToString());
626 return;
627 }
628 else if (_useStraightPath)
629 {
630 dtResult = _navMeshQuery->findStraightPath(
631 startPoint, // start position
632 endPoint, // end position
633 _pathPolyRefs, // current path
634 _polyLength, // lenth of current path
635 pathPoints, // [out] path corner points
636 nullptr, // [out] flags
637 nullptr, // [out] shortened path
638 (int*)&pointCount,
639 _pointPathLimit); // maximum number of points/polygons to use
640 }
641 else
642 {
643 dtResult = FindSmoothPath(
644 startPoint, // start position
645 endPoint, // end position
646 _pathPolyRefs, // current path
647 _polyLength, // length of current path
648 pathPoints, // [out] path corner points
649 (int*)&pointCount,
650 _pointPathLimit); // maximum number of points
651 }
652
653 // Special case with start and end positions very close to each other
654 if (_polyLength == 1 && pointCount == 1 && !(dtResult & DT_SLOPE_TOO_STEEP))
655 {
656 // First point is start position, append end position
657 dtVcopy(&pathPoints[1 * VERTEX_SIZE], endPoint);
658 pointCount++;
659 }
660 else if (pointCount < 2 || dtStatusFailed(dtResult))
661 {
662 // If its too steep, just return incomplete path.
663 if (pointCount > 0 && dtResult & DT_SLOPE_TOO_STEEP)
664 {
665 _pathPoints.resize(pointCount);
666 for (uint32 i = 0; i < pointCount; ++i)
667 _pathPoints[i] = G3D::Vector3(pathPoints[i * VERTEX_SIZE + 2], pathPoints[i * VERTEX_SIZE], pathPoints[i * VERTEX_SIZE + 1]);
668
670
671 // first point is always our current location - we need the next one
672 SetActualEndPosition(_pathPoints[pointCount - 1]);
673
675 return;
676 }
677
678 // only happens if pass bad data to findStraightPath or navmesh is broken
679 // single point paths can be generated here
683 return;
684 }
685 else if (pointCount >= _pointPathLimit)
686 {
689 return;
690 }
691
692 _pathPoints.resize(pointCount);
693 for (uint32 i = 0; i < pointCount; ++i)
694 _pathPoints[i] = G3D::Vector3(pathPoints[i * VERTEX_SIZE + 2], pathPoints[i * VERTEX_SIZE], pathPoints[i * VERTEX_SIZE + 1]);
695
697
698 // first point is always our current location - we need the next one
699 SetActualEndPosition(_pathPoints[pointCount - 1]);
700
701 // force the given destination, if needed
702 if (_forceDestination &&
704 {
705 // we may want to keep partial subpath
707 {
709 _pathPoints[_pathPoints.size() - 1] = GetEndPosition();
710 }
711 else
712 {
715 }
716
718 }
719}
std::uint32_t uint32
Definition Define.h:107
#define LOG_ERROR(filterType__,...)
Definition Log.h:145
#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:48
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:1134
dtStatus FindSmoothPath(float const *startPos, float const *endPos, dtPolyRef const *polyPath, uint32 polyPathSize, float *smoothPath, int *smoothPathSize, uint32 smoothPathMaxSize)
Definition PathGenerator.cpp:916
G3D::Vector3 const & GetEndPosition() const
Definition PathGenerator.h:87
void BuildShortcut()
Definition PathGenerator.cpp:733
void NormalizePath()
Definition PathGenerator.cpp:721
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:1128
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
258{
259 // *** getting start/end poly logic ***
260
261 float distToStartPoly, distToEndPoly;
262 float startPoint[VERTEX_SIZE] = { startPos.y, startPos.z, startPos.x };
263 float endPoint[VERTEX_SIZE] = { endPos.y, endPos.z, endPos.x };
264
265 dtPolyRef startPoly = GetPolyByLocation(startPoint, &distToStartPoly);
266 dtPolyRef endPoly = GetPolyByLocation(endPoint, &distToEndPoly);
267
269
270 Creature const* creature = _source->ToCreature();
271
272 // we have a hole in our mesh
273 // make shortcut path and mark it as NOPATH ( with flying and swimming exception )
274 // its up to caller how he will use this info
275 if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF)
276 {
278
279 bool canSwim = creature ? creature->CanSwim() : true;
280 bool path = creature ? creature->CanFly() : true;
281 bool waterPath = IsWaterPath(_pathPoints);
282 if (path || (waterPath && canSwim))
283 {
285 return;
286 }
287
288 // raycast doesn't need endPoly to be valid
289 if (!_useRaycast)
290 {
292 return;
293 }
294 }
295
296 // we may need a better number here
297 bool startFarFromPoly = distToStartPoly > 7.0f;
298 bool endFarFromPoly = distToEndPoly > 7.0f;
299
300 // create a shortcut if the path begins or end too far
301 // away from the desired path points.
302 // swimming creatures should not use a shortcut
303 // because exiting the water must be done following a proper path
304 // we just need to remove/normalize paths between 2 adjacent points
305 if (startFarFromPoly || endFarFromPoly)
306 {
307 bool buildShortcut = false;
308
309 auto liquidDataStart = _source->GetMap()->GetLiquidData(_source->GetPhaseMask(), startPos.x, startPos.y, startPos.z, _source->GetCollisionHeight(), {});
310 auto liquidDataEnd = _source->GetMap()->GetLiquidData(_source->GetPhaseMask(), endPos.x, endPos.y, endPos.z, _source->GetCollisionHeight(), {});
311
312 bool startUnderWaterEndInWater = liquidDataStart.Status == LIQUID_MAP_UNDER_WATER &&
313 (liquidDataEnd.Status & MAP_LIQUID_STATUS_IN_CONTACT) != 0;
314 bool startInWaterEndUnderWater = (liquidDataStart.Status & MAP_LIQUID_STATUS_IN_CONTACT) != 0 &&
315 liquidDataEnd.Status == LIQUID_MAP_UNDER_WATER;
316 bool waterPath = startUnderWaterEndInWater || startInWaterEndUnderWater;
317 Unit const* _sourceUnit = _source->ToUnit();
318
319 if (_sourceUnit)
320 {
321 bool isWater = (_sourceUnit->CanSwim() && waterPath);
322
323 if (isWater || _sourceUnit->CanFly() || (_sourceUnit->IsFalling() && endPos.z < startPos.z))
324 {
325 buildShortcut = true;
326 }
327 }
328
329 if (buildShortcut)
330 {
333
334 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
335
336 return;
337 }
338 else
339 {
340 float closestPoint[VERTEX_SIZE];
341 // we may want to use closestPointOnPolyBoundary instead
342 if (dtStatusSucceed(_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint, nullptr)))
343 {
344 dtVcopy(endPoint, closestPoint);
345 SetActualEndPosition(G3D::Vector3(endPoint[2], endPoint[0], endPoint[1]));
346 }
347
349
350 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
351 }
352 }
353
354 // *** poly path generating logic ***
355
356 // start and end are on same polygon
357 // handle this case as if they were 2 different polygons, building a line path split in some few points
358 if (startPoly == endPoly && !_useRaycast)
359 {
360 _pathPolyRefs[0] = startPoly;
361 _polyLength = 1;
362
363 if (startFarFromPoly || endFarFromPoly)
364 {
366
367 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
368 }
369 else
371
372 BuildPointPath(startPoint, endPoint);
373 return;
374 }
375
376 // look for startPoly/endPoly in current path
378 bool startPolyFound = false;
379 bool endPolyFound = false;
380 uint32 pathStartIndex = 0;
381 uint32 pathEndIndex = 0;
382
383 if (_polyLength)
384 {
385 for (; pathStartIndex < _polyLength; ++pathStartIndex)
386 {
387 // here to catch few bugs
388 if (_pathPolyRefs[pathStartIndex] == INVALID_POLYREF)
389 {
390 break;
391 }
392
393 if (_pathPolyRefs[pathStartIndex] == startPoly)
394 {
395 startPolyFound = true;
396 break;
397 }
398 }
399
400 for (pathEndIndex = _polyLength - 1; pathEndIndex > pathStartIndex; --pathEndIndex)
401 {
402 if (_pathPolyRefs[pathEndIndex] == endPoly)
403 {
404 endPolyFound = true;
405 break;
406 }
407 }
408 }
409
410 if (startPolyFound && endPolyFound)
411 {
412 // we moved along the path and the target did not move out of our old poly-path
413 // our path is a simple subpath case, we have all the data we need
414 // just "cut" it out
415
416 _polyLength = pathEndIndex - pathStartIndex + 1;
417 memmove(_pathPolyRefs, _pathPolyRefs + pathStartIndex, _polyLength * sizeof(dtPolyRef));
418 }
419 else if (startPolyFound && !endPolyFound)
420 {
421 // we are moving on the old path but target moved out
422 // so we have atleast part of poly-path ready
423
424 _polyLength -= pathStartIndex;
425
426 // try to adjust the suffix of the path instead of recalculating entire length
427 // at given interval the target cannot get too far from its last location
428 // thus we have less poly to cover
429 // sub-path of optimal path is optimal
430
431 // take ~80% of the original length
433 uint32 prefixPolyLength = uint32(_polyLength * 0.8f + 0.5f);
434 memmove(_pathPolyRefs, _pathPolyRefs + pathStartIndex, prefixPolyLength * sizeof(dtPolyRef));
435
436 dtPolyRef suffixStartPoly = _pathPolyRefs[prefixPolyLength - 1];
437
438 // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data
439 float suffixEndPoint[VERTEX_SIZE];
440 if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, nullptr)))
441 {
442 // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that
443 // try to recover by using prev polyref
444 --prefixPolyLength;
445 suffixStartPoly = _pathPolyRefs[prefixPolyLength - 1];
446 if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, nullptr)))
447 {
448 // suffixStartPoly is still invalid, error state
451 return;
452 }
453 }
454
455 // generate suffix
456 uint32 suffixPolyLength = 0;
457
458 dtStatus dtResult;
459 if (_useRaycast)
460 {
463 return;
464 }
465 else
466 {
467 dtResult = _navMeshQuery->findPath(
468 suffixStartPoly, // start polygon
469 endPoly, // end polygon
470 suffixEndPoint, // start position
471 endPoint, // end position
472 &_filter, // polygon search filter
473 _pathPolyRefs + prefixPolyLength - 1, // [out] path
474 (int*)&suffixPolyLength,
475 MAX_PATH_LENGTH - prefixPolyLength); // max number of polygons in output path
476 }
477
478 if (!suffixPolyLength || dtStatusFailed(dtResult))
479 {
480 // this is probably an error state, but we'll leave it
481 // and hopefully recover on the next Update
482 // we still need to copy our preffix
483 LOG_ERROR("movement", "PathGenerator::BuildPolyPath: Path Build failed {}", _source->GetGUID().ToString());
484 }
485
486 // new path = prefix + suffix - overlap
487 _polyLength = prefixPolyLength + suffixPolyLength - 1;
488 }
489 else
490 {
491 // either we have no path at all -> first run
492 // or something went really wrong -> we aren't moving along the path to the target
493 // just generate new path
494
495 // free and invalidate old path data
496 Clear();
497
498 dtStatus dtResult;
499 if (_useRaycast)
500 {
501 float hit = 0;
502 float hitNormal[3];
503 memset(hitNormal, 0, sizeof(hitNormal));
504
505 dtResult = _navMeshQuery->raycast(
506 startPoly,
507 startPoint,
508 endPoint,
509 &_filter,
510 &hit,
511 hitNormal,
513 (int*)&_polyLength,
515
516 if (!_polyLength || dtStatusFailed(dtResult))
517 {
520 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
521 return;
522 }
523
524 // raycast() sets hit to FLT_MAX if there is a ray between start and end
525 if (hit != FLT_MAX)
526 {
527 float hitPos[3];
528
529 // 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)
530 hit *= 0.99f;
531 dtVlerp(hitPos, startPoint, endPoint, hit);
532
533 // if it fails again, clamp to poly boundary
534 if (dtStatusFailed(_navMeshQuery->getPolyHeight(_pathPolyRefs[_polyLength - 1], hitPos, &hitPos[1])))
535 _navMeshQuery->closestPointOnPolyBoundary(_pathPolyRefs[_polyLength - 1], hitPos, hitPos);
536
537 _pathPoints.resize(2);
539 _pathPoints[1] = G3D::Vector3(hitPos[2], hitPos[0], hitPos[1]);
540
543 AddFarFromPolyFlags(startFarFromPoly, false);
544 return;
545 }
546 else
547 {
548 // clamp to poly boundary if we fail to get the height
549 if (dtStatusFailed(_navMeshQuery->getPolyHeight(_pathPolyRefs[_polyLength - 1], endPoint, &endPoint[1])))
550 _navMeshQuery->closestPointOnPolyBoundary(_pathPolyRefs[_polyLength - 1], endPoint, endPoint);
551
552 _pathPoints.resize(2);
554 _pathPoints[1] = G3D::Vector3(endPoint[2], endPoint[0], endPoint[1]);
555
557 if (startFarFromPoly || endFarFromPoly)
558 {
560
561 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
562 }
563 else
565 return;
566 }
567 }
568 else
569 {
570 dtResult = _navMeshQuery->findPath(
571 startPoly, // start polygon
572 endPoly, // end polygon
573 startPoint, // start position
574 endPoint, // end position
575 &_filter, // polygon search filter
576 _pathPolyRefs, // [out] path
577 (int*)&_polyLength,
578 MAX_PATH_LENGTH); // max number of polygons in output path
579 }
580
581 if (!_polyLength || dtStatusFailed(dtResult))
582 {
583 // only happens if we passed bad data to findPath(), or navmesh is messed up
584 LOG_ERROR("movement", "PathGenerator::BuildPolyPath: {} Path Build failed: 0 length path", _source->GetGUID().ToString());
587 return;
588 }
589 }
590
591 if (!_polyLength)
592 {
593 LOG_ERROR("movement", "PathGenerator::BuildPolyPath: {} Path Build failed: 0 length path", _source->GetGUID().ToString());
596 return;
597 }
598
599 // by now we know what type of path we can get
600 if (_pathPolyRefs[_polyLength - 1] == endPoly && !(_type & PATHFIND_INCOMPLETE))
601 {
603 }
604 else
605 {
607 }
608
609 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
610
611 // generate the point-path out of our up-to-date poly-path
612 BuildPointPath(startPoint, endPoint);
613}
#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:3372
bool CanFly() const override
Definition Creature.h:87
LiquidData const GetLiquidData(uint32 phaseMask, float x, float y, float z, float collisionHeight, Optional< uint8 > ReqLiquidType)
Definition Map.cpp:1314
Creature * ToCreature()
Definition Object.h:206
Unit * ToUnit()
Definition Object.h:210
void AddFarFromPolyFlags(bool startFarFromPoly, bool endFarFromPoly)
Definition PathGenerator.cpp:1205
dtQueryFilterExt _filter
Definition PathGenerator.h:151
dtPolyRef GetPolyByLocation(float const *Point, float *Distance) const
Definition PathGenerator.cpp:222
bool IsWaterPath(Movement::PointsArray pathPoints) const
Definition PathGenerator.cpp:1255
void BuildPointPath(float const *startPoint, float const *endPoint)
Definition PathGenerator.cpp:615
void Clear()
Definition PathGenerator.h:124
Definition Unit.h:665
virtual bool CanFly() const =0
virtual bool CanSwim() const
this method checks the current flag of a unit
Definition Unit.cpp:15945
bool IsFalling() const
Definition Unit.cpp:15932
uint32 GetPhaseMask() const
Definition Object.h:517
virtual float GetCollisionHeight() const
Definition Object.h:721
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
734{
735 Clear();
736
737 // make two point path, our curr pos is the start, and dest is the end
738 _pathPoints.resize(2);
739
740 // set start and a default next position
743
745
747}
@ 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 
)
148{
149 float x, y, z;
150 _source->GetPosition(x, y, z);
151
152 return CalculatePath(x, y, z, destX, destY, destZ, forceDest);
153}
bool CalculatePath(float destX, float destY, float destZ, bool forceDest=false)
Definition PathGenerator.cpp:147
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 
)
156{
157 if (!Acore::IsValidMapCoord(destX, destY, destZ) || !Acore::IsValidMapCoord(x, y, z))
158 return false;
159
160 METRIC_DETAILED_EVENT("mmap_events", "CalculatePath", "");
161
162 G3D::Vector3 dest(destX, destY, destZ);
163 SetEndPosition(dest);
164
165 G3D::Vector3 start(x, y, z);
166 SetStartPosition(start);
167
168 _forceDestination = forceDest;
169
170 // make sure navMesh works - we can run on map w/o mmap
171 // check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?)
172 Unit const* _sourceUnit = _source->ToUnit();
173 if (!_navMesh || !_navMeshQuery || (_sourceUnit && _sourceUnit->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING)) ||
174 !HaveTile(start) || !HaveTile(dest))
175 {
178 return true;
179 }
180
181 UpdateFilter();
182
183 BuildPolyPath(start, dest);
184 return true;
185}
#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:817
void BuildPolyPath(G3D::Vector3 const &startPos, G3D::Vector3 const &endPos)
Definition PathGenerator.cpp:257
void SetStartPosition(G3D::Vector3 const &point)
Definition PathGenerator.h:153
void UpdateFilter()
Definition PathGenerator.cpp:776
void SetEndPosition(G3D::Vector3 const &point)
Definition PathGenerator.h:154
bool HasUnitState(const uint32 f) const
Definition Unit.h:737
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
750{
751 uint16 includeFlags = 0;
752 uint16 excludeFlags = 0;
753
754 if (_source->IsCreature())
755 {
756 Creature* creature = (Creature*)_source;
757 if (creature->CanWalk())
758 includeFlags |= NAV_GROUND; // walk
759
760 // creatures don't take environmental damage
761 if (creature->CanEnterWater())
762 includeFlags |= (NAV_WATER | NAV_MAGMA);
763 }
764 else // assume Player
765 {
766 // perfect support not possible, just stay 'safe'
767 includeFlags |= (NAV_GROUND | NAV_WATER | NAV_MAGMA);
768 }
769
770 _filter.setIncludeFlags(includeFlags);
771 _filter.setExcludeFlags(excludeFlags);
772
773 UpdateFilter();
774}
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:84
bool CanEnterWater() const override
Definition Creature.cpp:3383
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
1135{
1136 return (p1 - p2).squaredLength();
1137}

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
919{
920 *smoothPathSize = 0;
921 uint32 nsmoothPath = 0;
922
923 dtPolyRef polys[MAX_PATH_LENGTH];
924 memcpy(polys, polyPath, sizeof(dtPolyRef) * polyPathSize);
925 uint32 npolys = polyPathSize;
926
927 float iterPos[VERTEX_SIZE], targetPos[VERTEX_SIZE];
928
929 if (polyPathSize > 1)
930 {
931 // Pick the closest points on poly border
932 if (dtStatusFailed(_navMeshQuery->closestPointOnPolyBoundary(polys[0], startPos, iterPos)))
933 {
934 return DT_FAILURE;
935 }
936
937 if (dtStatusFailed(_navMeshQuery->closestPointOnPolyBoundary(polys[npolys - 1], endPos, targetPos)))
938 {
939 return DT_FAILURE;
940 }
941 }
942 else
943 {
944 // Case where the path is on the same poly
945 dtVcopy(iterPos, startPos);
946 dtVcopy(targetPos, endPos);
947 }
948
949 dtVcopy(&smoothPath[nsmoothPath * VERTEX_SIZE], iterPos);
950 nsmoothPath++;
951
952 // Move towards target a small advancement at a time until target reached or
953 // when ran out of memory to store the path.
954 while (npolys && nsmoothPath < maxSmoothPathSize)
955 {
956 // Find location to steer towards.
957 float steerPos[VERTEX_SIZE];
958 unsigned char steerPosFlag;
959 dtPolyRef steerPosRef = INVALID_POLYREF;
960
961 if (!GetSteerTarget(iterPos, targetPos, SMOOTH_PATH_SLOP, polys, npolys, steerPos, steerPosFlag, steerPosRef))
962 break;
963
964 bool endOfPath = (steerPosFlag & DT_STRAIGHTPATH_END) != 0;
965 bool offMeshConnection = (steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0;
966
967 // Find movement delta.
968 float delta[VERTEX_SIZE];
969 dtVsub(delta, steerPos, iterPos);
970 float len = dtMathSqrtf(dtVdot(delta, delta));
971 // If the steer target is end of path or off-mesh link, do not move past the location.
972 if ((endOfPath || offMeshConnection) && len < SMOOTH_PATH_STEP_SIZE)
973 len = 1.0f;
974 else
975 len = SMOOTH_PATH_STEP_SIZE / len;
976
977 float moveTgt[VERTEX_SIZE];
978 dtVmad(moveTgt, iterPos, delta, len);
979
980 // Move
981 float result[VERTEX_SIZE];
982 const static uint32 MAX_VISIT_POLY = 16;
983 dtPolyRef visited[MAX_VISIT_POLY];
984
985 uint32 nvisited = 0;
986 if (dtStatusFailed(_navMeshQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &_filter, result, visited, (int*)&nvisited, MAX_VISIT_POLY)))
987 {
988 return DT_FAILURE;
989 }
990 npolys = FixupCorridor(polys, npolys, MAX_PATH_LENGTH, visited, nvisited);
991
992 if (dtStatusFailed(_navMeshQuery->getPolyHeight(polys[0], result, &result[1])))
993 LOG_DEBUG("maps", "PathGenerator::FindSmoothPath: Cannot find height at position X: {} Y: {} Z: {} for {}",
994 result[2], result[0], result[1], _source->GetGUID().ToString());
995 result[1] += 0.5f;
996 dtVcopy(iterPos, result);
997
998 bool canCheckSlope = _slopeCheck && (GetPathType() & ~(PATHFIND_NOT_USING_PATH));
999
1000 if (canCheckSlope && !IsSwimmableSegment(iterPos, steerPos) && !IsWalkableClimb(iterPos, steerPos))
1001 {
1002 nsmoothPath--;
1003 *smoothPathSize = nsmoothPath;
1004 return DT_FAILURE | DT_SLOPE_TOO_STEEP;
1005 }
1006
1007 // Handle end of path and off-mesh links when close enough.
1008 if (endOfPath && InRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
1009 {
1010 // Reached end of path.
1011 dtVcopy(iterPos, targetPos);
1012 if (nsmoothPath < maxSmoothPathSize)
1013 {
1014 dtVcopy(&smoothPath[nsmoothPath * VERTEX_SIZE], iterPos);
1015 nsmoothPath++;
1016 }
1017 break;
1018 }
1019 else if (offMeshConnection && InRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
1020 {
1021 // Advance the path up to and over the off-mesh connection.
1022 dtPolyRef prevRef = INVALID_POLYREF;
1023 dtPolyRef polyRef = polys[0];
1024 uint32 npos = 0;
1025 while (npos < npolys && polyRef != steerPosRef)
1026 {
1027 prevRef = polyRef;
1028 polyRef = polys[npos];
1029 npos++;
1030 }
1031
1032 for (uint32 i = npos; i < npolys; ++i)
1033 polys[i - npos] = polys[i];
1034
1035 npolys -= npos;
1036
1037 // Handle the connection.
1038 float connectionStartPos[VERTEX_SIZE], connectionEndPos[VERTEX_SIZE];
1039 if (dtStatusSucceed(_navMesh->getOffMeshConnectionPolyEndPoints(prevRef, polyRef, connectionStartPos, connectionEndPos)))
1040 {
1041 if (nsmoothPath < maxSmoothPathSize)
1042 {
1043 dtVcopy(&smoothPath[nsmoothPath * VERTEX_SIZE], connectionStartPos);
1044 nsmoothPath++;
1045 }
1046 // Move position at the other side of the off-mesh link.
1047 dtVcopy(iterPos, connectionEndPos);
1048 if (dtStatusFailed(_navMeshQuery->getPolyHeight(polys[0], iterPos, &iterPos[1])))
1049 return DT_FAILURE;
1050 iterPos[1] += 0.5f;
1051 }
1052 }
1053
1054 // Store results.
1055 if (nsmoothPath < maxSmoothPathSize)
1056 {
1057 dtVcopy(&smoothPath[nsmoothPath * VERTEX_SIZE], iterPos);
1058 nsmoothPath++;
1059 }
1060 }
1061
1062 *smoothPathSize = nsmoothPath;
1063
1064 // this is most likely a loop
1065 return nsmoothPath < MAX_POINT_PATH_LENGTH ? DT_SUCCESS : DT_FAILURE;
1066}
#define LOG_DEBUG(filterType__,...)
Definition Log.h:157
#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:1068
bool InRangeYZX(float const *v1, float const *v2, float r, float h) const
Definition PathGenerator.cpp:1120
uint32 FixupCorridor(dtPolyRef *path, uint32 npath, uint32 maxPath, dtPolyRef const *visited, uint32 nvisited)
Definition PathGenerator.cpp:833
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:1227
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:878

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
834{
835 int32 furthestPath = -1;
836 int32 furthestVisited = -1;
837
838 // Find furthest common polygon.
839 for (int32 i = npath - 1; i >= 0; --i)
840 {
841 bool found = false;
842 for (int32 j = nvisited - 1; j >= 0; --j)
843 {
844 if (path[i] == visited[j])
845 {
846 furthestPath = i;
847 furthestVisited = j;
848 found = true;
849 }
850 }
851 if (found)
852 break;
853 }
854
855 // If no intersection found just return current path.
856 if (furthestPath == -1 || furthestVisited == -1)
857 return npath;
858
859 // Concatenate paths.
860
861 // Adjust beginning of the buffer to include the visited.
862 uint32 req = nvisited - furthestVisited;
863 uint32 orig = uint32(furthestPath + 1) < npath ? furthestPath + 1 : npath;
864 uint32 size = npath > orig ? npath - orig : 0;
865 if (req + size > maxPath)
866 size = maxPath - req;
867
868 if (size)
869 memmove(path + req, path + orig, size * sizeof(dtPolyRef));
870
871 // Store visited
872 for (uint32 i = 0; i < req; ++i)
873 path[i] = visited[(nvisited - 1) - i];
874
875 return req + size;
876}
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
799{
800 LiquidData const& liquidData = _source->GetMap()->GetLiquidData(_source->GetPhaseMask(), x, y, z, _source->GetCollisionHeight(), {});
801 if (liquidData.Status == LIQUID_MAP_NO_WATER)
802 return NAV_GROUND;
803
804 switch (liquidData.Flags)
805 {
808 return NAV_WATER;
811 return NAV_MAGMA;
812 default:
813 return NAV_GROUND;
814 }
815}
#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
188{
189 if (!polyPath || !polyPathSize)
190 return INVALID_POLYREF;
191
192 dtPolyRef nearestPoly = INVALID_POLYREF;
193 float minDist = FLT_MAX;
194
195 for (uint32 i = 0; i < polyPathSize; ++i)
196 {
197 float closestPoint[VERTEX_SIZE];
198 if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(polyPath[i], point, closestPoint, nullptr)))
199 continue;
200
201 float d = dtVdistSqr(point, closestPoint);
202 if (d < minDist)
203 {
204 minDist = d;
205 nearestPoly = polyPath[i];
206 }
207
208 if (minDist < 1.0f) // shortcut out - close enough for us
209 {
210 break;
211 }
212 }
213
214 if (distance)
215 {
216 *distance = dtMathSqrtf(minDist);
217 }
218
219 return (minDist < 3.0f) ? nearestPoly : INVALID_POLYREF;
220}

References _navMeshQuery, INVALID_POLYREF, and VERTEX_SIZE.

Referenced by GetPolyByLocation().

◆ GetPathType()

◆ GetPolyByLocation()

dtPolyRef PathGenerator::GetPolyByLocation ( float const *  Point,
float *  Distance 
) const
private
223{
224 // first we check the current path
225 // if the current path doesn't contain the current poly,
226 // we need to use the expensive navMesh.findNearestPoly
227 dtPolyRef polyRef = GetPathPolyByPosition(_pathPolyRefs, _polyLength, point, distance);
228 if (polyRef != INVALID_POLYREF)
229 return polyRef;
230
231 // we don't have it in our old path
232 // try to get it by findNearestPoly()
233 // first try with low search box
234 float extents[VERTEX_SIZE] = { 3.0f, 5.0f, 3.0f }; // bounds of poly search area
235 float closestPoint[VERTEX_SIZE] = { 0.0f, 0.0f, 0.0f };
236 if (dtStatusSucceed(_navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint)) && polyRef != INVALID_POLYREF)
237 {
238 *distance = dtVdist(closestPoint, point);
239 return polyRef;
240 }
241
242 // still nothing ..
243 // try with bigger search box
244 // Note that the extent should not overlap more than 128 polygons in the navmesh (see dtNavMeshQuery::findNearestPoly)
245 extents[1] = 50.0f;
246
247 if (dtStatusSucceed(_navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint)) && polyRef != INVALID_POLYREF)
248 {
249 *distance = dtVdist(closestPoint, point);
250 return polyRef;
251 }
252
253 *distance = FLT_MAX;
254 return INVALID_POLYREF;
255}
dtPolyRef GetPathPolyByPosition(dtPolyRef const *polyPath, uint32 polyPathSize, float const *Point, float *Distance=nullptr) const
Definition PathGenerator.cpp:187

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
1113{
1114 float slopeAngle = getSlopeAngleAbs(x, y, z, destX, destY, destZ);
1115 float slopeAngleDegree = (slopeAngle * 180.0f / M_PI);
1116 float climbableHeight = sourceHeight - (sourceHeight * (slopeAngleDegree / 100));
1117 return climbableHeight;
1118}
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
881{
882 // Find steer target.
883 static const uint32 MAX_STEER_POINTS = 3;
884 float steerPath[MAX_STEER_POINTS * VERTEX_SIZE];
885 unsigned char steerPathFlags[MAX_STEER_POINTS];
886 dtPolyRef steerPathPolys[MAX_STEER_POINTS];
887 uint32 nsteerPath = 0;
888 dtStatus dtResult = _navMeshQuery->findStraightPath(startPos, endPos, path, pathSize,
889 steerPath, steerPathFlags, steerPathPolys, (int*)&nsteerPath, MAX_STEER_POINTS);
890 if (!nsteerPath || dtStatusFailed(dtResult))
891 return false;
892
893 // Find vertex far enough to steer to.
894 uint32 ns = 0;
895 while (ns < nsteerPath)
896 {
897 // Stop at Off-Mesh link or when point is further than slop away.
898 if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
899 !InRangeYZX(&steerPath[ns * VERTEX_SIZE], startPos, minTargetDist, 1000.0f))
900 break;
901
902 ns++;
903 }
904 // Failed to find good point to steer to.
905 if (ns >= nsteerPath)
906 return false;
907
908 dtVcopy(steerPos, &steerPath[ns * VERTEX_SIZE]);
909 steerPos[1] = startPos[1]; // keep Z value
910 steerPosFlag = steerPathFlags[ns];
911 steerPosRef = steerPathPolys[ns];
912
913 return true;
914}

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

818{
819 int tx = -1, ty = -1;
820 float point[VERTEX_SIZE] = { p.y, p.z, p.x };
821
822 _navMesh->calcTileLoc(point, &tx, &ty);
823
827 if (tx < 0 || ty < 0)
828 return false;
829
830 return (_navMesh->getTileAt(tx, ty, 0) != nullptr);
831}

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
1129{
1130 G3D::Vector3 d = p1 - p2;
1131 return (d.x * d.x + d.y * d.y) < r * r && fabsf(d.z) < h;
1132}

Referenced by BuildPointPath().

◆ InRangeYZX()

bool PathGenerator::InRangeYZX ( float const *  v1,
float const *  v2,
float  r,
float  h 
) const
private
1121{
1122 const float dx = v2[0] - v1[0];
1123 const float dy = v2[1] - v1[1]; // elevation
1124 const float dz = v2[2] - v1[2];
1125 return (dx * dx + dz * dz) < r * r && fabsf(dy) < h;
1126}

Referenced by FindSmoothPath(), and GetSteerTarget().

◆ IsInvalidDestinationZ()

bool PathGenerator::IsInvalidDestinationZ ( Unit const *  target) const
1201{
1202 return (target->GetPositionZ() - GetActualEndPosition().z) > 5.0f;
1203}

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
1228{
1229 return IsSwimmableSegment(v1[2], v1[0], v1[1], v2[2], v2[0], v2[1], checkSwim);
1230}

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
1248{
1249 Creature const* _sourceCreature = _source->ToCreature();
1251 _source->GetMap()->IsInWater(_source->GetPhaseMask(), destX, destY, destZ, _source->GetCollisionHeight()) &&
1252 (!checkSwim || !_sourceCreature || _sourceCreature->CanSwim());
1253}
bool IsInWater(uint32 phaseMask, float x, float y, float z, float collisionHeight) const
Definition Map.cpp:1593

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
1069{
1070 return IsWalkableClimb(v1[2], v1[0], v1[1], v2[2], v2[0], v2[1]);
1071}

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
1074{
1075 return IsWalkableClimb(x, y, z, destX, destY, destZ, _source->GetCollisionHeight());
1076}

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
1092{
1093 float diffHeight = std::abs(destZ - z);
1094 float reqHeight = GetRequiredHeightToClimb(x, y, z, destX, destY, destZ, sourceHeight);
1095 // check walkable slopes, based on unit height
1096 return diffHeight <= reqHeight;
1097}
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:1112

References GetRequiredHeightToClimb().

◆ IsWaterPath()

bool PathGenerator::IsWaterPath ( Movement::PointsArray  pathPoints) const
1256{
1257 bool waterPath = true;
1258 // Check both start and end points, if they're both in water, then we can *safely* let the creature move
1259 for (uint32 i = 0; i < pathPoints.size(); ++i)
1260 {
1261 NavTerrain terrain = GetNavTerrain(pathPoints[i].x, pathPoints[i].y, pathPoints[i].z);
1262 // One of the points is not in the water
1263 if (terrain != NAV_MAGMA && terrain != NAV_WATER)
1264 {
1265 waterPath = false;
1266 break;
1267 }
1268 }
1269
1270 return waterPath;
1271}
NavTerrain
Definition MapDefines.h:89
NavTerrain GetNavTerrain(float x, float y, float z) const
Definition PathGenerator.cpp:798

References GetNavTerrain(), NAV_MAGMA, and NAV_WATER.

Referenced by BuildPolyPath().

◆ NormalizePath()

void PathGenerator::NormalizePath ( )
private
722{
723 bool const snapBladeEdgeArenaRopes = _source->GetMapId() == MAP_BLADES_EDGE_ARENA;
724 for (G3D::Vector3& point : _pathPoints)
725 {
726 if (snapBladeEdgeArenaRopes && TrySnapToBladeEdgeArenaRope(point))
727 continue;
728
729 _source->UpdateAllowedPositionZ(point.x, point.y, point.z);
730 }
731}
@ MAP_BLADES_EDGE_ARENA
Definition AreaDefines.h:239
uint32 GetMapId() const
Definition Position.h:281
void UpdateAllowedPositionZ(float x, float y, float &z, float *groundZ=nullptr) const
Definition Object.cpp:1614

References _pathPoints, _source, WorldLocation::GetMapId(), MAP_BLADES_EDGE_ARENA, 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 
)
1140{
1141 if (GetPathType() == PATHFIND_BLANK || _pathPoints.size() < 2)
1142 {
1143 LOG_ERROR("movement", "PathGenerator::ReducePathLengthByDist called before path was successfully built");
1144 return;
1145 }
1146
1147 float const distSq = dist * dist;
1148
1149 // the first point of the path must be outside the specified range
1150 // (this should have really been checked by the caller...)
1151 if ((_pathPoints[0] - target).squaredLength() < distSq)
1152 return;
1153
1154 // check if we even need to do anything
1155 if ((*_pathPoints.rbegin() - target).squaredLength() >= distSq)
1156 return;
1157
1158 std::size_t i = _pathPoints.size() - 1;
1159 float x, y, z, collisionHeight = _source->GetCollisionHeight();
1160 // find the first i s.t.:
1161 // - _pathPoints[i] is still too close
1162 // - _pathPoints[i-1] is too far away
1163 // => the end point is somewhere on the line between the two
1164 while (1)
1165 {
1166 // we know that pathPoints[i] is too close already (from the previous iteration)
1167 if ((_pathPoints[i - 1] - target).squaredLength() >= distSq)
1168 break; // bingo!
1169
1170 bool canCheckSlope = _slopeCheck && (GetPathType() & ~(PATHFIND_NOT_USING_PATH));
1171
1172 // check if the shortened path is still in LoS with the target and it is walkable
1173 _source->GetHitSpherePointFor({ _pathPoints[i - 1].x, _pathPoints[i - 1].y, _pathPoints[i - 1].z + collisionHeight }, x, y, z);
1174 if (!_source->GetMap()->isInLineOfSight(x, y, z, _pathPoints[i - 1].x, _pathPoints[i - 1].y, _pathPoints[i - 1].z + collisionHeight,
1178 {
1179 // whenver we find a point that is not valid anymore, simply use last valid path
1180 _pathPoints.resize(i + 1);
1181 return;
1182 }
1183
1184 if (!--i)
1185 {
1186 // no point found that fulfills the condition
1187 _pathPoints[0] = _pathPoints[1];
1188 _pathPoints.resize(2);
1189 return;
1190 }
1191 }
1192
1193 // ok, _pathPoints[i] is too close, _pathPoints[i-1] is not, so our target point is somewhere between the two...
1194 // ... settle for a guesstimate since i'm not confident in doing trig on every chase motion tick...
1195 // (@todo review this)
1196 _pathPoints[i] += (_pathPoints[i - 1] - _pathPoints[i]).direction() * (dist - (_pathPoints[i] - target).length());
1197 _pathPoints.resize(i + 1);
1198}
@ LINEOFSIGHT_ALL_CHECKS
Definition Map.h:115
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:1545
Position GetHitSpherePointFor(Position const &dest, Optional< float > collisionHeight={ }, Optional< float > combatReach={ }) const
Definition Object.cpp:1290
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
777{
778 // allow creatures to cheat and use different movement types if they are moved
779 // forcefully into terrain they can't normally move in
780 if (Unit const* _sourceUnit = _source->ToUnit())
781 {
782 if (_sourceUnit->IsInWater() || _sourceUnit->IsUnderWater())
783 {
784 uint16 includedFlags = _filter.getIncludeFlags();
785 includedFlags |= GetNavTerrain(_source->GetPositionX(),
788
789 _filter.setIncludeFlags(includedFlags);
790 }
791
792 /*if (Creature const* _sourceCreature = _source->ToCreature())
793 if (_sourceCreature->IsInCombat() || _sourceCreature->IsInEvadeMode())
794 _filter.setIncludeFlags(_filter.getIncludeFlags() | NAV_GROUND_STEEP);*/
795 }
796}

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: