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
29 :
32 _endPosition(G3D::Vector3::zero()), _source(owner), _navMesh(nullptr),
33 _navMeshQuery(nullptr)
34{
35 memset(_pathPolyRefs, 0, sizeof(_pathPolyRefs));
36
37 uint32 mapId = _source->GetMapId();
38 //if (sDisableMgr->IsPathfindingEnabled(_sourceUnit->FindMap()))
39 {
41 _navMesh = mmap->GetNavMesh(mapId);
43 }
44
46}
std::uint32_t uint32
Definition Define.h:107
#define MAX_POINT_PATH_LENGTH
Definition PathGenerator.h:36
@ PATHFIND_BLANK
Definition PathGenerator.h:46
static MMapMgr * createOrGetMMapMgr()
Definition MMapFactory.cpp:27
Definition MMapMgr.h:74
dtNavMesh const * GetNavMesh(uint32 mapId)
Definition MMapMgr.cpp:306
dtNavMeshQuery const * GetNavMeshQuery(uint32 mapId, uint32 instanceId)
Definition MMapMgr.cpp:317
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:650
dtPolyRef _pathPolyRefs[MAX_PATH_LENGTH]
Definition PathGenerator.h:131
dtNavMesh const * _navMesh
Definition PathGenerator.h:148
G3D::Vector3 _endPosition
Definition PathGenerator.h:144
uint32 GetMapId() const
Definition Position.h:280
uint32 GetInstanceId() const
Definition Object.h:499

References _navMesh, _navMeshQuery, _pathPolyRefs, _source, CreateFilter(), MMAP::MMapFactory::createOrGetMMapMgr(), WorldObject::GetInstanceId(), WorldLocation::GetMapId(), MMAP::MMapMgr::GetNavMesh(), and MMAP::MMapMgr::GetNavMeshQuery().

◆ ~PathGenerator()

PathGenerator::~PathGenerator ( )
49{
50}

Member Function Documentation

◆ AddFarFromPolyFlags()

void PathGenerator::AddFarFromPolyFlags ( bool  startFarFromPoly,
bool  endFarFromPoly 
)
private
1107{
1108 if (startFarFromPoly)
1109 {
1111 }
1112 if (endFarFromPoly)
1113 {
1115 }
1116}
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
521{
522 float pathPoints[MAX_POINT_PATH_LENGTH * VERTEX_SIZE];
523 uint32 pointCount = 0;
524 dtStatus dtResult = DT_FAILURE;
525 if (_useRaycast)
526 {
527 // _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
528 LOG_ERROR("movement", "PathGenerator::BuildPointPath() called with _useRaycast for unit {}", _source->GetGUID().ToString());
531 return;
532 }
533 else if (_useStraightPath)
534 {
535 dtResult = _navMeshQuery->findStraightPath(
536 startPoint, // start position
537 endPoint, // end position
538 _pathPolyRefs, // current path
539 _polyLength, // lenth of current path
540 pathPoints, // [out] path corner points
541 nullptr, // [out] flags
542 nullptr, // [out] shortened path
543 (int*)&pointCount,
544 _pointPathLimit); // maximum number of points/polygons to use
545 }
546 else
547 {
548 dtResult = FindSmoothPath(
549 startPoint, // start position
550 endPoint, // end position
551 _pathPolyRefs, // current path
552 _polyLength, // length of current path
553 pathPoints, // [out] path corner points
554 (int*)&pointCount,
555 _pointPathLimit); // maximum number of points
556 }
557
558 // Special case with start and end positions very close to each other
559 if (_polyLength == 1 && pointCount == 1 && !(dtResult & DT_SLOPE_TOO_STEEP))
560 {
561 // First point is start position, append end position
562 dtVcopy(&pathPoints[1 * VERTEX_SIZE], endPoint);
563 pointCount++;
564 }
565 else if (pointCount < 2 || dtStatusFailed(dtResult))
566 {
567 // If its too steep, just return incomplete path.
568 if (pointCount > 0 && dtResult & DT_SLOPE_TOO_STEEP)
569 {
570 _pathPoints.resize(pointCount);
571 for (uint32 i = 0; i < pointCount; ++i)
572 _pathPoints[i] = G3D::Vector3(pathPoints[i * VERTEX_SIZE + 2], pathPoints[i * VERTEX_SIZE], pathPoints[i * VERTEX_SIZE + 1]);
573
575
576 // first point is always our current location - we need the next one
577 SetActualEndPosition(_pathPoints[pointCount - 1]);
578
580 return;
581 }
582
583 // only happens if pass bad data to findStraightPath or navmesh is broken
584 // single point paths can be generated here
588 return;
589 }
590 else if (pointCount >= _pointPathLimit)
591 {
594 return;
595 }
596
597 _pathPoints.resize(pointCount);
598 for (uint32 i = 0; i < pointCount; ++i)
599 _pathPoints[i] = G3D::Vector3(pathPoints[i * VERTEX_SIZE + 2], pathPoints[i * VERTEX_SIZE], pathPoints[i * VERTEX_SIZE + 1]);
600
602
603 // first point is always our current location - we need the next one
604 SetActualEndPosition(_pathPoints[pointCount - 1]);
605
606 // force the given destination, if needed
607 if (_forceDestination &&
609 {
610 // we may want to keep partial subpath
612 {
614 _pathPoints[_pathPoints.size() - 1] = GetEndPosition();
615 }
616 else
617 {
620 }
621
623 }
624}
#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:113
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:1035
dtStatus FindSmoothPath(float const *startPos, float const *endPos, dtPolyRef const *polyPath, uint32 polyPathSize, float *smoothPath, int *smoothPathSize, uint32 smoothPathMaxSize)
Definition PathGenerator.cpp:817
G3D::Vector3 const & GetEndPosition() const
Definition PathGenerator.h:87
void BuildShortcut()
Definition PathGenerator.cpp:634
void NormalizePath()
Definition PathGenerator.cpp:626
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:1029
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
163{
164 // *** getting start/end poly logic ***
165
166 float distToStartPoly, distToEndPoly;
167 float startPoint[VERTEX_SIZE] = { startPos.y, startPos.z, startPos.x };
168 float endPoint[VERTEX_SIZE] = { endPos.y, endPos.z, endPos.x };
169
170 dtPolyRef startPoly = GetPolyByLocation(startPoint, &distToStartPoly);
171 dtPolyRef endPoly = GetPolyByLocation(endPoint, &distToEndPoly);
172
174
175 Creature const* creature = _source->ToCreature();
176
177 // we have a hole in our mesh
178 // make shortcut path and mark it as NOPATH ( with flying and swimming exception )
179 // its up to caller how he will use this info
180 if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF)
181 {
183
184 bool canSwim = creature ? creature->CanSwim() : true;
185 bool path = creature ? creature->CanFly() : true;
186 bool waterPath = IsWaterPath(_pathPoints);
187 if (path || (waterPath && canSwim))
188 {
190 return;
191 }
192
193 // raycast doesn't need endPoly to be valid
194 if (!_useRaycast)
195 {
197 return;
198 }
199 }
200
201 // we may need a better number here
202 bool startFarFromPoly = distToStartPoly > 7.0f;
203 bool endFarFromPoly = distToEndPoly > 7.0f;
204
205 // create a shortcut if the path begins or end too far
206 // away from the desired path points.
207 // swimming creatures should not use a shortcut
208 // because exiting the water must be done following a proper path
209 // we just need to remove/normalize paths between 2 adjacent points
210 if (startFarFromPoly || endFarFromPoly)
211 {
212 bool buildShortcut = false;
213
214 auto liquidDataStart = _source->GetMap()->GetLiquidData(_source->GetPhaseMask(), startPos.x, startPos.y, startPos.z, _source->GetCollisionHeight(), MAP_ALL_LIQUIDS);
215 auto liquidDataEnd = _source->GetMap()->GetLiquidData(_source->GetPhaseMask(), endPos.x, endPos.y, endPos.z, _source->GetCollisionHeight(), MAP_ALL_LIQUIDS);
216
217 bool startUnderWaterEndInWater = liquidDataStart.Status == LIQUID_MAP_UNDER_WATER &&
218 (liquidDataEnd.Status & MAP_LIQUID_STATUS_IN_CONTACT) != 0;
219 bool startInWaterEndUnderWater = (liquidDataStart.Status & MAP_LIQUID_STATUS_IN_CONTACT) != 0 &&
220 liquidDataEnd.Status == LIQUID_MAP_UNDER_WATER;
221 bool waterPath = startUnderWaterEndInWater || startInWaterEndUnderWater;
222 Unit const* _sourceUnit = _source->ToUnit();
223
224 if (_sourceUnit)
225 {
226 bool isWater = (_sourceUnit->CanSwim() && waterPath);
227
228 if (isWater || _sourceUnit->CanFly() || (_sourceUnit->IsFalling() && endPos.z < startPos.z))
229 {
230 buildShortcut = true;
231 }
232 }
233
234 if (buildShortcut)
235 {
238
239 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
240
241 return;
242 }
243 else
244 {
245 float closestPoint[VERTEX_SIZE];
246 // we may want to use closestPointOnPolyBoundary instead
247 if (dtStatusSucceed(_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint, nullptr)))
248 {
249 dtVcopy(endPoint, closestPoint);
250 SetActualEndPosition(G3D::Vector3(endPoint[2], endPoint[0], endPoint[1]));
251 }
252
254
255 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
256 }
257 }
258
259 // *** poly path generating logic ***
260
261 // start and end are on same polygon
262 // handle this case as if they were 2 different polygons, building a line path split in some few points
263 if (startPoly == endPoly && !_useRaycast)
264 {
265 _pathPolyRefs[0] = startPoly;
266 _polyLength = 1;
267
268 if (startFarFromPoly || endFarFromPoly)
269 {
271
272 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
273 }
274 else
276
277 BuildPointPath(startPoint, endPoint);
278 return;
279 }
280
281 // look for startPoly/endPoly in current path
283 bool startPolyFound = false;
284 bool endPolyFound = false;
285 uint32 pathStartIndex = 0;
286 uint32 pathEndIndex = 0;
287
288 if (_polyLength)
289 {
290 for (; pathStartIndex < _polyLength; ++pathStartIndex)
291 {
292 // here to catch few bugs
293 if (_pathPolyRefs[pathStartIndex] == INVALID_POLYREF)
294 {
295 break;
296 }
297
298 if (_pathPolyRefs[pathStartIndex] == startPoly)
299 {
300 startPolyFound = true;
301 break;
302 }
303 }
304
305 for (pathEndIndex = _polyLength - 1; pathEndIndex > pathStartIndex; --pathEndIndex)
306 {
307 if (_pathPolyRefs[pathEndIndex] == endPoly)
308 {
309 endPolyFound = true;
310 break;
311 }
312 }
313 }
314
315 if (startPolyFound && endPolyFound)
316 {
317 // we moved along the path and the target did not move out of our old poly-path
318 // our path is a simple subpath case, we have all the data we need
319 // just "cut" it out
320
321 _polyLength = pathEndIndex - pathStartIndex + 1;
322 memmove(_pathPolyRefs, _pathPolyRefs + pathStartIndex, _polyLength * sizeof(dtPolyRef));
323 }
324 else if (startPolyFound && !endPolyFound)
325 {
326 // we are moving on the old path but target moved out
327 // so we have atleast part of poly-path ready
328
329 _polyLength -= pathStartIndex;
330
331 // try to adjust the suffix of the path instead of recalculating entire length
332 // at given interval the target cannot get too far from its last location
333 // thus we have less poly to cover
334 // sub-path of optimal path is optimal
335
336 // take ~80% of the original length
338 uint32 prefixPolyLength = uint32(_polyLength * 0.8f + 0.5f);
339 memmove(_pathPolyRefs, _pathPolyRefs + pathStartIndex, prefixPolyLength * sizeof(dtPolyRef));
340
341 dtPolyRef suffixStartPoly = _pathPolyRefs[prefixPolyLength - 1];
342
343 // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data
344 float suffixEndPoint[VERTEX_SIZE];
345 if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, nullptr)))
346 {
347 // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that
348 // try to recover by using prev polyref
349 --prefixPolyLength;
350 suffixStartPoly = _pathPolyRefs[prefixPolyLength - 1];
351 if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, nullptr)))
352 {
353 // suffixStartPoly is still invalid, error state
356 return;
357 }
358 }
359
360 // generate suffix
361 uint32 suffixPolyLength = 0;
362
363 dtStatus dtResult;
364 if (_useRaycast)
365 {
368 return;
369 }
370 else
371 {
372 dtResult = _navMeshQuery->findPath(
373 suffixStartPoly, // start polygon
374 endPoly, // end polygon
375 suffixEndPoint, // start position
376 endPoint, // end position
377 &_filter, // polygon search filter
378 _pathPolyRefs + prefixPolyLength - 1, // [out] path
379 (int*)&suffixPolyLength,
380 MAX_PATH_LENGTH - prefixPolyLength); // max number of polygons in output path
381 }
382
383 if (!suffixPolyLength || dtStatusFailed(dtResult))
384 {
385 // this is probably an error state, but we'll leave it
386 // and hopefully recover on the next Update
387 // we still need to copy our preffix
388 LOG_ERROR("movement", "PathGenerator::BuildPolyPath: Path Build failed {}", _source->GetGUID().ToString());
389 }
390
391 // new path = prefix + suffix - overlap
392 _polyLength = prefixPolyLength + suffixPolyLength - 1;
393 }
394 else
395 {
396 // either we have no path at all -> first run
397 // or something went really wrong -> we aren't moving along the path to the target
398 // just generate new path
399
400 // free and invalidate old path data
401 Clear();
402
403 dtStatus dtResult;
404 if (_useRaycast)
405 {
406 float hit = 0;
407 float hitNormal[3];
408 memset(hitNormal, 0, sizeof(hitNormal));
409
410 dtResult = _navMeshQuery->raycast(
411 startPoly,
412 startPoint,
413 endPoint,
414 &_filter,
415 &hit,
416 hitNormal,
418 (int*)&_polyLength,
420
421 if (!_polyLength || dtStatusFailed(dtResult))
422 {
425 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
426 return;
427 }
428
429 // raycast() sets hit to FLT_MAX if there is a ray between start and end
430 if (hit != FLT_MAX)
431 {
432 float hitPos[3];
433
434 // 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)
435 hit *= 0.99f;
436 dtVlerp(hitPos, startPoint, endPoint, hit);
437
438 // if it fails again, clamp to poly boundary
439 if (dtStatusFailed(_navMeshQuery->getPolyHeight(_pathPolyRefs[_polyLength - 1], hitPos, &hitPos[1])))
440 _navMeshQuery->closestPointOnPolyBoundary(_pathPolyRefs[_polyLength - 1], hitPos, hitPos);
441
442 _pathPoints.resize(2);
444 _pathPoints[1] = G3D::Vector3(hitPos[2], hitPos[0], hitPos[1]);
445
448 AddFarFromPolyFlags(startFarFromPoly, false);
449 return;
450 }
451 else
452 {
453 // clamp to poly boundary if we fail to get the height
454 if (dtStatusFailed(_navMeshQuery->getPolyHeight(_pathPolyRefs[_polyLength - 1], endPoint, &endPoint[1])))
455 _navMeshQuery->closestPointOnPolyBoundary(_pathPolyRefs[_polyLength - 1], endPoint, endPoint);
456
457 _pathPoints.resize(2);
459 _pathPoints[1] = G3D::Vector3(endPoint[2], endPoint[0], endPoint[1]);
460
462 if (startFarFromPoly || endFarFromPoly)
463 {
465
466 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
467 }
468 else
470 return;
471 }
472 }
473 else
474 {
475 dtResult = _navMeshQuery->findPath(
476 startPoly, // start polygon
477 endPoly, // end polygon
478 startPoint, // start position
479 endPoint, // end position
480 &_filter, // polygon search filter
481 _pathPolyRefs, // [out] path
482 (int*)&_polyLength,
483 MAX_PATH_LENGTH); // max number of polygons in output path
484 }
485
486 if (!_polyLength || dtStatusFailed(dtResult))
487 {
488 // only happens if we passed bad data to findPath(), or navmesh is messed up
489 LOG_ERROR("movement", "PathGenerator::BuildPolyPath: {} Path Build failed: 0 length path", _source->GetGUID().ToString());
492 return;
493 }
494 }
495
496 if (!_polyLength)
497 {
498 LOG_ERROR("movement", "PathGenerator::BuildPolyPath: {} Path Build failed: 0 length path", _source->GetGUID().ToString());
501 return;
502 }
503
504 // by now we know what type of path we can get
505 if (_pathPolyRefs[_polyLength - 1] == endPoly && !(_type & PATHFIND_INCOMPLETE))
506 {
508 }
509 else
510 {
512 }
513
514 AddFarFromPolyFlags(startFarFromPoly, endFarFromPoly);
515
516 // generate the point-path out of our up-to-date poly-path
517 BuildPointPath(startPoint, endPoint);
518}
@ LIQUID_MAP_UNDER_WATER
Definition GridTerrainData.h:195
#define MAP_LIQUID_STATUS_IN_CONTACT
Definition GridTerrainData.h:32
#define MAP_ALL_LIQUIDS
Definition GridTerrainData.h:40
#define INVALID_POLYREF
Definition PathGenerator.h:42
#define MAX_PATH_LENGTH
Definition PathGenerator.h:35
Definition Creature.h:43
bool CanSwim() const override
This method check the current flag/status of a creature and its inhabit type.
Definition Creature.cpp:3275
bool CanFly() const override
Definition Creature.h:82
LiquidData const GetLiquidData(uint32 phaseMask, float x, float y, float z, float collisionHeight, uint8 ReqLiquidType)
Definition Map.cpp:1283
Creature * ToCreature()
Definition Object.h:206
Unit * ToUnit()
Definition Object.h:210
void AddFarFromPolyFlags(bool startFarFromPoly, bool endFarFromPoly)
Definition PathGenerator.cpp:1106
dtQueryFilterExt _filter
Definition PathGenerator.h:151
dtPolyRef GetPolyByLocation(float const *Point, float *Distance) const
Definition PathGenerator.cpp:127
bool IsWaterPath(Movement::PointsArray pathPoints) const
Definition PathGenerator.cpp:1156
void BuildPointPath(float const *startPoint, float const *endPoint)
Definition PathGenerator.cpp:520
void Clear()
Definition PathGenerator.h:124
Definition Unit.h:620
virtual bool CanFly() const =0
virtual bool CanSwim() const
this method checks the current flag of a unit
Definition Unit.cpp:19786
bool IsFalling() const
Definition Unit.cpp:19773
uint32 GetPhaseMask() const
Definition Object.h:502
Map * GetMap() const
Definition Object.h:587
virtual float GetCollisionHeight() const
Definition Object.h:675
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_ALL_LIQUIDS, 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
635{
636 Clear();
637
638 // make two point path, our curr pos is the start, and dest is the end
639 _pathPoints.resize(2);
640
641 // set start and a default next position
644
646
648}
@ 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 
)
53{
54 float x, y, z;
55 _source->GetPosition(x, y, z);
56
57 return CalculatePath(x, y, z, destX, destY, destZ, forceDest);
58}
bool CalculatePath(float destX, float destY, float destZ, bool forceDest=false)
Definition PathGenerator.cpp:52
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(), and Movement::MoveSplineInit::MoveTo().

◆ CalculatePath() [2/2]

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

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
651{
652 uint16 includeFlags = 0;
653 uint16 excludeFlags = 0;
654
655 if (_source->IsCreature())
656 {
657 Creature* creature = (Creature*)_source;
658 if (creature->CanWalk())
659 includeFlags |= NAV_GROUND; // walk
660
661 // creatures don't take environmental damage
662 if (creature->CanEnterWater())
663 includeFlags |= (NAV_WATER | NAV_MAGMA);
664 }
665 else // assume Player
666 {
667 // perfect support not possible, just stay 'safe'
668 includeFlags |= (NAV_GROUND | NAV_WATER | NAV_MAGMA);
669 }
670
671 _filter.setIncludeFlags(includeFlags);
672 _filter.setExcludeFlags(excludeFlags);
673
674 UpdateFilter();
675}
std::uint16_t uint16
Definition Define.h:108
@ NAV_MAGMA
Definition MapDefines.h:56
@ NAV_GROUND
Definition MapDefines.h:55
@ NAV_WATER
Definition MapDefines.h:58
bool CanWalk() const
Definition Creature.h:79
bool CanEnterWater() const override
Definition Creature.cpp:3286
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
1036{
1037 return (p1 - p2).squaredLength();
1038}

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

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
735{
736 int32 furthestPath = -1;
737 int32 furthestVisited = -1;
738
739 // Find furthest common polygon.
740 for (int32 i = npath - 1; i >= 0; --i)
741 {
742 bool found = false;
743 for (int32 j = nvisited - 1; j >= 0; --j)
744 {
745 if (path[i] == visited[j])
746 {
747 furthestPath = i;
748 furthestVisited = j;
749 found = true;
750 }
751 }
752 if (found)
753 break;
754 }
755
756 // If no intersection found just return current path.
757 if (furthestPath == -1 || furthestVisited == -1)
758 return npath;
759
760 // Concatenate paths.
761
762 // Adjust beginning of the buffer to include the visited.
763 uint32 req = nvisited - furthestVisited;
764 uint32 orig = uint32(furthestPath + 1) < npath ? furthestPath + 1 : npath;
765 uint32 size = npath > orig ? npath - orig : 0;
766 if (req + size > maxPath)
767 size = maxPath - req;
768
769 if (size)
770 memmove(path + req, path + orig, size * sizeof(dtPolyRef));
771
772 // Store visited
773 for (uint32 i = 0; i < req; ++i)
774 path[i] = visited[(nvisited - 1) - i];
775
776 return req + size;
777}
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
700{
702 if (liquidData.Status == LIQUID_MAP_NO_WATER)
703 return NAV_GROUND;
704
705 switch (liquidData.Flags)
706 {
709 return NAV_WATER;
712 return NAV_MAGMA;
713 default:
714 return NAV_GROUND;
715 }
716}
#define MAP_LIQUID_TYPE_MAGMA
Definition GridTerrainData.h:37
@ LIQUID_MAP_NO_WATER
Definition GridTerrainData.h:191
#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
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_ALL_LIQUIDS, 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
93{
94 if (!polyPath || !polyPathSize)
95 return INVALID_POLYREF;
96
97 dtPolyRef nearestPoly = INVALID_POLYREF;
98 float minDist = FLT_MAX;
99
100 for (uint32 i = 0; i < polyPathSize; ++i)
101 {
102 float closestPoint[VERTEX_SIZE];
103 if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(polyPath[i], point, closestPoint, nullptr)))
104 continue;
105
106 float d = dtVdistSqr(point, closestPoint);
107 if (d < minDist)
108 {
109 minDist = d;
110 nearestPoly = polyPath[i];
111 }
112
113 if (minDist < 1.0f) // shortcut out - close enough for us
114 {
115 break;
116 }
117 }
118
119 if (distance)
120 {
121 *distance = dtMathSqrtf(minDist);
122 }
123
124 return (minDist < 3.0f) ? nearestPoly : INVALID_POLYREF;
125}

References _navMeshQuery, INVALID_POLYREF, and VERTEX_SIZE.

Referenced by GetPolyByLocation().

◆ GetPathType()

◆ GetPolyByLocation()

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

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
1014{
1015 float slopeAngle = getSlopeAngleAbs(x, y, z, destX, destY, destZ);
1016 float slopeAngleDegree = (slopeAngle * 180.0f / M_PI);
1017 float climbableHeight = sourceHeight - (sourceHeight * (slopeAngleDegree / 100));
1018 return climbableHeight;
1019}
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
782{
783 // Find steer target.
784 static const uint32 MAX_STEER_POINTS = 3;
785 float steerPath[MAX_STEER_POINTS * VERTEX_SIZE];
786 unsigned char steerPathFlags[MAX_STEER_POINTS];
787 dtPolyRef steerPathPolys[MAX_STEER_POINTS];
788 uint32 nsteerPath = 0;
789 dtStatus dtResult = _navMeshQuery->findStraightPath(startPos, endPos, path, pathSize,
790 steerPath, steerPathFlags, steerPathPolys, (int*)&nsteerPath, MAX_STEER_POINTS);
791 if (!nsteerPath || dtStatusFailed(dtResult))
792 return false;
793
794 // Find vertex far enough to steer to.
795 uint32 ns = 0;
796 while (ns < nsteerPath)
797 {
798 // Stop at Off-Mesh link or when point is further than slop away.
799 if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
800 !InRangeYZX(&steerPath[ns * VERTEX_SIZE], startPos, minTargetDist, 1000.0f))
801 break;
802
803 ns++;
804 }
805 // Failed to find good point to steer to.
806 if (ns >= nsteerPath)
807 return false;
808
809 dtVcopy(steerPos, &steerPath[ns * VERTEX_SIZE]);
810 steerPos[1] = startPos[1]; // keep Z value
811 steerPosFlag = steerPathFlags[ns];
812 steerPosRef = steerPathPolys[ns];
813
814 return true;
815}

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

719{
720 int tx = -1, ty = -1;
721 float point[VERTEX_SIZE] = { p.y, p.z, p.x };
722
723 _navMesh->calcTileLoc(point, &tx, &ty);
724
728 if (tx < 0 || ty < 0)
729 return false;
730
731 return (_navMesh->getTileAt(tx, ty, 0) != nullptr);
732}

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
1030{
1031 G3D::Vector3 d = p1 - p2;
1032 return (d.x * d.x + d.y * d.y) < r * r && fabsf(d.z) < h;
1033}

Referenced by BuildPointPath().

◆ InRangeYZX()

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

Referenced by FindSmoothPath(), and GetSteerTarget().

◆ IsInvalidDestinationZ()

bool PathGenerator::IsInvalidDestinationZ ( Unit const *  target) const
1102{
1103 return (target->GetPositionZ() - GetActualEndPosition().z) > 5.0f;
1104}

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

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
1149{
1150 Creature const* _sourceCreature = _source->ToCreature();
1152 _source->GetMap()->IsInWater(_source->GetPhaseMask(), destX, destY, destZ, _source->GetCollisionHeight()) &&
1153 (!checkSwim || !_sourceCreature || _sourceCreature->CanSwim());
1154}
bool IsInWater(uint32 phaseMask, float x, float y, float z, float collisionHeight) const
Definition Map.cpp:1582

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
970{
971 return IsWalkableClimb(v1[2], v1[0], v1[1], v2[2], v2[0], v2[1]);
972}

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

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
993{
994 float diffHeight = std::abs(destZ - z);
995 float reqHeight = GetRequiredHeightToClimb(x, y, z, destX, destY, destZ, sourceHeight);
996 // check walkable slopes, based on unit height
997 return diffHeight <= reqHeight;
998}
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:1013

References GetRequiredHeightToClimb().

◆ IsWaterPath()

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

References GetNavTerrain(), NAV_MAGMA, and NAV_WATER.

Referenced by BuildPolyPath().

◆ NormalizePath()

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

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 
)
1041{
1042 if (GetPathType() == PATHFIND_BLANK || _pathPoints.size() < 2)
1043 {
1044 LOG_ERROR("movement", "PathGenerator::ReducePathLengthByDist called before path was successfully built");
1045 return;
1046 }
1047
1048 float const distSq = dist * dist;
1049
1050 // the first point of the path must be outside the specified range
1051 // (this should have really been checked by the caller...)
1052 if ((_pathPoints[0] - target).squaredLength() < distSq)
1053 return;
1054
1055 // check if we even need to do anything
1056 if ((*_pathPoints.rbegin() - target).squaredLength() >= distSq)
1057 return;
1058
1059 std::size_t i = _pathPoints.size() - 1;
1060 float x, y, z, collisionHeight = _source->GetCollisionHeight();
1061 // find the first i s.t.:
1062 // - _pathPoints[i] is still too close
1063 // - _pathPoints[i-1] is too far away
1064 // => the end point is somewhere on the line between the two
1065 while (1)
1066 {
1067 // we know that pathPoints[i] is too close already (from the previous iteration)
1068 if ((_pathPoints[i - 1] - target).squaredLength() >= distSq)
1069 break; // bingo!
1070
1071 bool canCheckSlope = _slopeCheck && (GetPathType() & ~(PATHFIND_NOT_USING_PATH));
1072
1073 // check if the shortened path is still in LoS with the target and it is walkable
1074 _source->GetHitSpherePointFor({ _pathPoints[i - 1].x, _pathPoints[i - 1].y, _pathPoints[i - 1].z + collisionHeight }, x, y, z);
1075 if (!_source->GetMap()->isInLineOfSight(x, y, z, _pathPoints[i - 1].x, _pathPoints[i - 1].y, _pathPoints[i - 1].z + collisionHeight,
1079 {
1080 // whenver we find a point that is not valid anymore, simply use last valid path
1081 _pathPoints.resize(i + 1);
1082 return;
1083 }
1084
1085 if (!--i)
1086 {
1087 // no point found that fulfills the condition
1088 _pathPoints[0] = _pathPoints[1];
1089 _pathPoints.resize(2);
1090 return;
1091 }
1092 }
1093
1094 // ok, _pathPoints[i] is too close, _pathPoints[i-1] is not, so our target point is somewhere between the two...
1095 // ... settle for a guesstimate since i'm not confident in doing trig on every chase motion tick...
1096 // (@todo review this)
1097 _pathPoints[i] += (_pathPoints[i - 1] - _pathPoints[i]).direction() * (dist - (_pathPoints[i] - target).length());
1098 _pathPoints.resize(i + 1);
1099}
@ LINEOFSIGHT_ALL_CHECKS
Definition Map.h:108
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:1520
Position GetHitSpherePointFor(Position const &dest, Optional< float > collisionHeight={ }, Optional< float > combatReach={ }) const
Definition Object.cpp:1229
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
678{
679 // allow creatures to cheat and use different movement types if they are moved
680 // forcefully into terrain they can't normally move in
681 if (Unit const* _sourceUnit = _source->ToUnit())
682 {
683 if (_sourceUnit->IsInWater() || _sourceUnit->IsUnderWater())
684 {
685 uint16 includedFlags = _filter.getIncludeFlags();
686 includedFlags |= GetNavTerrain(_source->GetPositionX(),
689
690 _filter.setIncludeFlags(includedFlags);
691 }
692
693 /*if (Creature const* _sourceCreature = _source->ToCreature())
694 if (_sourceCreature->IsInCombat() || _sourceCreature->IsInEvadeMode())
695 _filter.setIncludeFlags(_filter.getIncludeFlags() | NAV_GROUND_STEEP);*/
696 }
697}

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: