Core/PathFinding: Add support to WorldObject pathfinding

Port From (https://github.com/TrinityCore/TrinityCore/commit/3729c76523f42831a0bbe9c53d9594004860001a)
This commit is contained in:
hondacrx
2022-01-07 11:09:08 -05:00
parent e71512b36e
commit 69da866100
@@ -25,7 +25,7 @@ namespace Game.Movement
{ {
public class PathGenerator public class PathGenerator
{ {
public PathGenerator(Unit owner) public PathGenerator(WorldObject owner)
{ {
_polyLength = 0; _polyLength = 0;
pathType = PathType.Blank; pathType = PathType.Blank;
@@ -33,16 +33,16 @@ namespace Game.Movement
_forceDestination = false; _forceDestination = false;
_pointPathLimit = 74; _pointPathLimit = 74;
_endPosition = Vector3.Zero; _endPosition = Vector3.Zero;
_sourceUnit = owner; _source = owner;
_navMesh = null; _navMesh = null;
_navMeshQuery = null; _navMeshQuery = null;
Log.outDebug(LogFilter.Maps, "PathGenerator:PathGenerator for {0}", _sourceUnit.GetGUID().ToString()); Log.outDebug(LogFilter.Maps, "PathGenerator:PathGenerator for {0}", _source.GetGUID().ToString());
uint mapId = PhasingHandler.GetTerrainMapId(_sourceUnit.GetPhaseShift(), _sourceUnit.GetMap(), _sourceUnit.GetPositionX(), _sourceUnit.GetPositionY()); uint mapId = PhasingHandler.GetTerrainMapId(_source.GetPhaseShift(), _source.GetMap(), _source.GetPositionX(), _source.GetPositionY());
if (Global.DisableMgr.IsPathfindingEnabled(_sourceUnit.GetMapId())) if (Global.DisableMgr.IsPathfindingEnabled(_source.GetMapId()))
{ {
_navMesh = Global.MMapMgr.GetNavMesh(mapId); _navMesh = Global.MMapMgr.GetNavMesh(mapId);
_navMeshQuery = Global.MMapMgr.GetNavMeshQuery(mapId, _sourceUnit.GetInstanceId()); _navMeshQuery = Global.MMapMgr.GetNavMeshQuery(mapId, _source.GetInstanceId());
} }
CreateFilter(); CreateFilter();
} }
@@ -50,7 +50,7 @@ namespace Game.Movement
public bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false, bool straightLine = false) public bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false, bool straightLine = false)
{ {
float x, y, z; float x, y, z;
_sourceUnit.GetPosition(out x, out y, out z); _source.GetPosition(out x, out y, out z);
if (!GridDefines.IsValidMapCoord(destX, destY, destZ) || !GridDefines.IsValidMapCoord(x, y, z)) if (!GridDefines.IsValidMapCoord(destX, destY, destZ) || !GridDefines.IsValidMapCoord(x, y, z))
return false; return false;
@@ -64,11 +64,12 @@ namespace Game.Movement
_forceDestination = forceDest; _forceDestination = forceDest;
_straightLine = straightLine; _straightLine = straightLine;
Log.outDebug(LogFilter.Maps, "PathGenerator.CalculatePath() for {0} \n", _sourceUnit.GetGUID().ToString()); Log.outDebug(LogFilter.Maps, "PathGenerator.CalculatePath() for {0} \n", _source.GetGUID().ToString());
// make sure navMesh works - we can run on map w/o mmap // make sure navMesh works - we can run on map w/o mmap
// check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?) // check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?)
if (_navMesh == null || _navMeshQuery == null || _sourceUnit.HasUnitState(UnitState.IgnorePathfinding) Unit _sourceUnit = _source.ToUnit();
if (_navMesh == null || _navMeshQuery == null || (_sourceUnit != null && _sourceUnit.HasUnitState(UnitState.IgnorePathfinding))
|| !HaveTile(start) || !HaveTile(dest)) || !HaveTile(start) || !HaveTile(dest))
{ {
BuildShortcut(); BuildShortcut();
@@ -166,15 +167,15 @@ namespace Game.Movement
{ {
Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . (startPoly == 0 || endPoly == 0)\n"); Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . (startPoly == 0 || endPoly == 0)\n");
BuildShortcut(); BuildShortcut();
bool path = _sourceUnit.IsTypeId(TypeId.Unit) && _sourceUnit.ToCreature().CanFly(); bool path = _source.IsTypeId(TypeId.Unit) && _source.ToCreature().CanFly();
bool waterPath = _sourceUnit.IsTypeId(TypeId.Unit) && _sourceUnit.ToCreature().CanSwim(); bool waterPath = _source.IsTypeId(TypeId.Unit) && _source.ToCreature().CanSwim();
if (waterPath) if (waterPath)
{ {
// Check both start and end points, if they're both in water, then we can *safely* let the creature move // Check both start and end points, if they're both in water, then we can *safely* let the creature move
for (uint i = 0; i < _pathPoints.Length; ++i) for (uint i = 0; i < _pathPoints.Length; ++i)
{ {
ZLiquidStatus status = _sourceUnit.GetMap().GetLiquidStatus(_sourceUnit.GetPhaseShift(), _pathPoints[i].X, _pathPoints[i].Y, _pathPoints[i].Z, LiquidHeaderTypeFlags.AllLiquids, _sourceUnit.GetCollisionHeight()); ZLiquidStatus status = _source.GetMap().GetLiquidStatus(_source.GetPhaseShift(), _pathPoints[i].X, _pathPoints[i].Y, _pathPoints[i].Z, LiquidHeaderTypeFlags.AllLiquids, _source.GetCollisionHeight());
// One of the points is not in the water, cancel movement. // One of the points is not in the water, cancel movement.
if (status == ZLiquidStatus.NoWater) if (status == ZLiquidStatus.NoWater)
{ {
@@ -196,20 +197,26 @@ namespace Game.Movement
bool buildShotrcut = false; bool buildShotrcut = false;
var p = (distToStartPoly > 7.0f) ? startPos : endPos; var p = (distToStartPoly > 7.0f) ? startPos : endPos;
if (_sourceUnit.GetMap().IsUnderWater(_sourceUnit.GetPhaseShift(), p.X, p.Y, p.Z)) if (_source.GetMap().IsUnderWater(_source.GetPhaseShift(), p.X, p.Y, p.Z))
{ {
Log.outDebug(LogFilter.Maps, "++ BuildPolyPath :: underWater case"); Log.outDebug(LogFilter.Maps, "++ BuildPolyPath :: underWater case");
if (_sourceUnit.CanSwim()) Unit _sourceUnit = _source.ToUnit();
buildShotrcut = true; if (_sourceUnit != null)
if (_sourceUnit.CanSwim())
buildShotrcut = true;
} }
else else
{ {
Log.outDebug(LogFilter.Maps, "++ BuildPolyPath :: flying case"); Log.outDebug(LogFilter.Maps, "++ BuildPolyPath :: flying case");
if (_sourceUnit.CanFly()) Unit _sourceUnit = _source.ToUnit();
buildShotrcut = true; if (_sourceUnit != null)
// Allow to build a shortcut if the unit is falling and it's trying to move downwards towards a target (i.e. charging) {
else if (_sourceUnit.IsFalling() && endPos.Z < startPos.Z) if (_sourceUnit.CanFly())
buildShotrcut = true; buildShotrcut = true;
// Allow to build a shortcut if the unit is falling and it's trying to move downwards towards a target (i.e. charging)
else if (_sourceUnit.IsFalling() && endPos.Z < startPos.Z)
buildShotrcut = true;
}
} }
if (buildShotrcut) if (buildShotrcut)
@@ -265,7 +272,7 @@ namespace Game.Movement
if (_pathPolyRefs[pathStartIndex] == 0) if (_pathPolyRefs[pathStartIndex] == 0)
{ {
Log.outError(LogFilter.Maps, "Invalid poly ref in BuildPolyPath. _polyLength: {0}, pathStartIndex: {1}," + Log.outError(LogFilter.Maps, "Invalid poly ref in BuildPolyPath. _polyLength: {0}, pathStartIndex: {1}," +
" startPos: {2}, endPos: {3}, mapid: {4}", _polyLength, pathStartIndex, startPos, endPos, _sourceUnit.GetMapId()); " startPos: {2}, endPos: {3}, mapid: {4}", _polyLength, pathStartIndex, startPos, endPos, _source.GetMapId());
break; break;
} }
@@ -389,7 +396,7 @@ namespace Game.Movement
// this is probably an error state, but we'll leave it // this is probably an error state, but we'll leave it
// and hopefully recover on the next Update // and hopefully recover on the next Update
// we still need to copy our preffix // we still need to copy our preffix
Log.outError(LogFilter.Maps, $"Path Build failed\n{_sourceUnit.GetDebugInfo()}"); Log.outError(LogFilter.Maps, $"Path Build failed\n{_source.GetDebugInfo()}");
} }
Log.outDebug(LogFilter.Maps, "m_polyLength={0} prefixPolyLength={1} suffixPolyLength={2} \n", _polyLength, prefixPolyLength, suffixPolyLength); Log.outDebug(LogFilter.Maps, "m_polyLength={0} prefixPolyLength={1} suffixPolyLength={2} \n", _polyLength, prefixPolyLength, suffixPolyLength);
@@ -460,7 +467,7 @@ namespace Game.Movement
if (_polyLength == 0 || Detour.dtStatusFailed(dtResult)) if (_polyLength == 0 || Detour.dtStatusFailed(dtResult))
{ {
// only happens if we passed bad data to findPath(), or navmesh is messed up // only happens if we passed bad data to findPath(), or navmesh is messed up
Log.outError(LogFilter.Maps, "{0}'s Path Build failed: 0 length path", _sourceUnit.GetGUID().ToString()); Log.outError(LogFilter.Maps, "{0}'s Path Build failed: 0 length path", _source.GetGUID().ToString());
BuildShortcut(); BuildShortcut();
pathType = PathType.NoPath; pathType = PathType.NoPath;
return; return;
@@ -742,7 +749,7 @@ namespace Game.Movement
npolys = FixupCorridor(polys, npolys, 74, visited, nvisited); npolys = FixupCorridor(polys, npolys, 74, visited, nvisited);
if (Detour.dtStatusFailed(_navMeshQuery.getPolyHeight(polys[0], result, ref result[1]))) if (Detour.dtStatusFailed(_navMeshQuery.getPolyHeight(polys[0], result, ref result[1])))
Log.outDebug(LogFilter.Maps, $"Cannot find height at position X: {result[2]} Y: {result[0]} Z: {result[1]} for {_sourceUnit.GetDebugInfo()}"); Log.outDebug(LogFilter.Maps, $"Cannot find height at position X: {result[2]} Y: {result[0]} Z: {result[1]} for {_source.GetDebugInfo()}");
result[1] += 0.5f; result[1] += 0.5f;
Detour.dtVcopy(iterPos, result); Detour.dtVcopy(iterPos, result);
@@ -813,7 +820,7 @@ namespace Game.Movement
void NormalizePath() void NormalizePath()
{ {
for (uint i = 0; i < _pathPoints.Length; ++i) for (uint i = 0; i < _pathPoints.Length; ++i)
_sourceUnit.UpdateAllowedPositionZ(_pathPoints[i].X, _pathPoints[i].Y, ref _pathPoints[i].Z); _source.UpdateAllowedPositionZ(_pathPoints[i].X, _pathPoints[i].Y, ref _pathPoints[i].Z);
} }
void BuildShortcut() void BuildShortcut()
@@ -839,9 +846,9 @@ namespace Game.Movement
NavTerrainFlag includeFlags = 0; NavTerrainFlag includeFlags = 0;
NavTerrainFlag excludeFlags = 0; NavTerrainFlag excludeFlags = 0;
if (_sourceUnit.IsTypeId(TypeId.Unit)) if (_source.IsTypeId(TypeId.Unit))
{ {
Creature creature = _sourceUnit.ToCreature(); Creature creature = _source.ToCreature();
if (creature.CanWalk()) if (creature.CanWalk())
includeFlags |= NavTerrainFlag.Ground; includeFlags |= NavTerrainFlag.Ground;
@@ -862,19 +869,23 @@ namespace Game.Movement
{ {
// allow creatures to cheat and use different movement types if they are moved // allow creatures to cheat and use different movement types if they are moved
// forcefully into terrain they can't normally move in // forcefully into terrain they can't normally move in
if (_sourceUnit.IsInWater() || _sourceUnit.IsUnderWater()) Unit _sourceUnit = _source.ToUnit();
if (_sourceUnit != null)
{ {
NavTerrainFlag includedFlags = (NavTerrainFlag)_filter.getIncludeFlags(); if (_sourceUnit.IsInWater() || _sourceUnit.IsUnderWater())
includedFlags |= GetNavTerrain(_sourceUnit.GetPositionX(), _sourceUnit.GetPositionY(), _sourceUnit.GetPositionZ()); {
NavTerrainFlag includedFlags = (NavTerrainFlag)_filter.getIncludeFlags();
includedFlags |= GetNavTerrain(_source.GetPositionX(), _source.GetPositionY(), _source.GetPositionZ());
_filter.setIncludeFlags((ushort)includedFlags); _filter.setIncludeFlags((ushort)includedFlags);
}
} }
} }
NavTerrainFlag GetNavTerrain(float x, float y, float z) NavTerrainFlag GetNavTerrain(float x, float y, float z)
{ {
LiquidData data; LiquidData data;
ZLiquidStatus liquidStatus = _sourceUnit.GetMap().GetLiquidStatus(_sourceUnit.GetPhaseShift(), x, y, z, LiquidHeaderTypeFlags.AllLiquids, out data, _sourceUnit.GetCollisionHeight()); ZLiquidStatus liquidStatus = _source.GetMap().GetLiquidStatus(_source.GetPhaseShift(), x, y, z, LiquidHeaderTypeFlags.AllLiquids, out data, _source.GetCollisionHeight());
if (liquidStatus == ZLiquidStatus.NoWater) if (liquidStatus == ZLiquidStatus.NoWater)
return NavTerrainFlag.Ground; return NavTerrainFlag.Ground;
@@ -928,7 +939,7 @@ namespace Game.Movement
float x; float x;
float y; float y;
float z; float z;
float collisionHeight = _sourceUnit.GetCollisionHeight(); float collisionHeight = _source.GetCollisionHeight();
// find the first i s.t.: // find the first i s.t.:
// - _pathPoints[i] is still too close // - _pathPoints[i] is still too close
// - _pathPoints[i-1] is too far away // - _pathPoints[i-1] is too far away
@@ -940,8 +951,8 @@ namespace Game.Movement
break; // bingo! break; // bingo!
// check if the shortened path is still in LoS with the target // check if the shortened path is still in LoS with the target
_sourceUnit.GetHitSpherePointFor(new Position(_pathPoints[i - 1].X, _pathPoints[i - 1].Y, _pathPoints[i - 1].Z + collisionHeight), out x, out y, out z); _source.GetHitSpherePointFor(new Position(_pathPoints[i - 1].X, _pathPoints[i - 1].Y, _pathPoints[i - 1].Z + collisionHeight), out x, out y, out z);
if (!_sourceUnit.GetMap().IsInLineOfSight(_sourceUnit.GetPhaseShift(), x, y, z, _pathPoints[i - 1].X, _pathPoints[i - 1].Y, _pathPoints[i - 1].Z + collisionHeight, LineOfSightChecks.All, ModelIgnoreFlags.Nothing)) if (!_source.GetMap().IsInLineOfSight(_source.GetPhaseShift(), x, y, z, _pathPoints[i - 1].X, _pathPoints[i - 1].Y, _pathPoints[i - 1].Z + collisionHeight, LineOfSightChecks.All, ModelIgnoreFlags.Nothing))
{ {
// whenver we find a point that is not in LoS anymore, simply use last valid path // whenver we find a point that is not in LoS anymore, simply use last valid path
Array.Resize(ref _pathPoints, i + 1); Array.Resize(ref _pathPoints, i + 1);
@@ -964,7 +975,7 @@ namespace Game.Movement
Array.Resize(ref _pathPoints, i + 1); Array.Resize(ref _pathPoints, i + 1);
} }
public bool IsInvalidDestinationZ(Unit target) public bool IsInvalidDestinationZ(WorldObject target)
{ {
return (target.GetPositionZ() - GetActualEndPosition().Z) > 5.0f; return (target.GetPositionZ() - GetActualEndPosition().Z) > 5.0f;
} }
@@ -1023,7 +1034,7 @@ namespace Game.Movement
uint _polyLength; uint _polyLength;
uint _pointPathLimit; uint _pointPathLimit;
bool _straightLine; // use raycast if true for a straight line path bool _straightLine; // use raycast if true for a straight line path
Unit _sourceUnit; WorldObject _source;
bool _forceDestination; bool _forceDestination;
bool _useStraightPath; bool _useStraightPath;
Vector3[] _pathPoints; Vector3[] _pathPoints;