2015-11-20 03:03:20 -05:00
|
|
|
#include "Globals.h"
|
|
|
|
#include "PathFinder.h"
|
|
|
|
#include "../Chunk.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cPathFinder::cPathFinder(double a_MobWidth, double a_MobHeight) :
|
|
|
|
m_Path(),
|
|
|
|
m_GiveUpCounter(0),
|
|
|
|
m_NotFoundCooldown(0)
|
|
|
|
{
|
|
|
|
m_Width = a_MobWidth;
|
|
|
|
m_Height = a_MobHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ePathFinderStatus cPathFinder::GetNextWayPoint(cChunk & a_Chunk, const Vector3d & a_Source, Vector3d * a_Destination, Vector3d * a_OutputWaypoint, bool a_DontCare)
|
|
|
|
{
|
|
|
|
m_FinalDestination = *a_Destination;
|
|
|
|
m_Source = a_Source;
|
|
|
|
|
|
|
|
// If a recent PATH_NOT_FOUND was returned, we rest for a few ticks.
|
|
|
|
if (m_NotFoundCooldown > 0)
|
|
|
|
{
|
|
|
|
m_NotFoundCooldown -= 1;
|
|
|
|
return ePathFinderStatus::CALCULATING;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tweak the destination. If something is wrong with the destination or the chunk, rest for a while.
|
2015-12-22 00:43:50 -05:00
|
|
|
if (!(EnsureProperPoint(m_FinalDestination, a_Chunk) && EnsureProperPoint(m_Source, a_Chunk)))
|
2015-11-20 03:03:20 -05:00
|
|
|
{
|
|
|
|
m_NotFoundCooldown = 20;
|
|
|
|
return ePathFinderStatus::PATH_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
2015-12-22 00:43:50 -05:00
|
|
|
/* printf("%d %d %d -> %d %d %d\n",
|
|
|
|
static_cast<int>(m_Source.x),
|
|
|
|
static_cast<int>(m_Source.y),
|
|
|
|
static_cast<int>(m_Source.z),
|
|
|
|
static_cast<int>(m_FinalDestination.x),
|
|
|
|
static_cast<int>(m_FinalDestination.y),
|
|
|
|
static_cast<int>(m_FinalDestination.z)); */
|
|
|
|
|
2015-11-20 03:03:20 -05:00
|
|
|
// Rest is over. Prepare m_Path by calling ResetPathFinding.
|
|
|
|
if (m_NotFoundCooldown == 0)
|
|
|
|
{
|
|
|
|
m_NotFoundCooldown = -1;
|
|
|
|
ResetPathFinding(a_Chunk);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If m_Path has not been initialized yet, initialize it.
|
2015-12-21 09:47:43 -05:00
|
|
|
if (!m_Path->IsValid())
|
2015-11-20 03:03:20 -05:00
|
|
|
{
|
|
|
|
ResetPathFinding(a_Chunk);
|
|
|
|
}
|
|
|
|
|
2015-12-21 09:47:43 -05:00
|
|
|
switch (m_Path->CalculationStep(a_Chunk))
|
2015-11-20 03:03:20 -05:00
|
|
|
{
|
|
|
|
case ePathFinderStatus::NEARBY_FOUND:
|
|
|
|
{
|
|
|
|
m_NoPathToTarget = true;
|
2015-12-21 09:47:43 -05:00
|
|
|
m_PathDestination = m_Path->AcceptNearbyPath();
|
2015-11-20 03:03:20 -05:00
|
|
|
if (a_DontCare)
|
|
|
|
{
|
|
|
|
m_FinalDestination = m_PathDestination;
|
|
|
|
*a_Destination = m_FinalDestination; // Modify the mob's final destination because it doesn't care about reaching an exact spot
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_DeviationOrigin = m_FinalDestination; // This is the only case in which m_DeviationOrigin != m_PathDestination
|
|
|
|
}
|
|
|
|
return ePathFinderStatus::CALCULATING;
|
|
|
|
// The next call will trigger the PATH_FOUND case
|
|
|
|
}
|
|
|
|
|
|
|
|
case ePathFinderStatus::PATH_NOT_FOUND:
|
|
|
|
{
|
|
|
|
m_NotFoundCooldown = 20;
|
|
|
|
return ePathFinderStatus::PATH_NOT_FOUND;
|
|
|
|
}
|
|
|
|
case ePathFinderStatus::CALCULATING:
|
|
|
|
{
|
|
|
|
return ePathFinderStatus::CALCULATING;
|
|
|
|
}
|
|
|
|
case ePathFinderStatus::PATH_FOUND:
|
|
|
|
{
|
|
|
|
m_GiveUpCounter -= 1;
|
|
|
|
|
2015-12-22 00:43:50 -05:00
|
|
|
if (m_GiveUpCounter == 0)
|
|
|
|
{
|
|
|
|
if (a_DontCare)
|
|
|
|
{
|
|
|
|
// We're having trouble reaching the next waypoint but the mob
|
|
|
|
// Doesn't care where to go, just tell him we got there ;)
|
|
|
|
m_FinalDestination = m_Source;
|
|
|
|
*a_Destination = m_FinalDestination;
|
|
|
|
ResetPathFinding(a_Chunk);
|
|
|
|
return ePathFinderStatus::CALCULATING;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ResetPathFinding(a_Chunk);
|
|
|
|
return ePathFinderStatus::CALCULATING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PathIsTooOld())
|
2015-11-20 03:03:20 -05:00
|
|
|
{
|
|
|
|
ResetPathFinding(a_Chunk);
|
|
|
|
return ePathFinderStatus::CALCULATING;
|
|
|
|
}
|
|
|
|
|
2015-12-21 09:47:43 -05:00
|
|
|
if (m_Path->NoMoreWayPoints())
|
2015-11-20 03:03:20 -05:00
|
|
|
{
|
|
|
|
// We're always heading towards m_PathDestination.
|
|
|
|
// If m_PathDestination is exactly m_FinalDestination, then we're about to reach the destination.
|
|
|
|
if (m_PathDestination == m_FinalDestination)
|
|
|
|
{
|
|
|
|
*a_OutputWaypoint = m_FinalDestination;
|
|
|
|
return ePathFinderStatus::PATH_FOUND;
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Otherwise, we've finished our approximate path and time to recalc.
|
|
|
|
ResetPathFinding(a_Chunk);
|
|
|
|
return ePathFinderStatus::CALCULATING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-21 08:51:12 -05:00
|
|
|
Vector3d Waypoint(m_WayPoint);
|
|
|
|
Vector3d Source(m_Source);
|
|
|
|
Waypoint.y = 0;
|
|
|
|
Source.y = 0;
|
2015-11-20 03:03:20 -05:00
|
|
|
|
2015-12-21 08:51:12 -05:00
|
|
|
if (m_Path->IsFirstPoint() || (((Waypoint - Source).SqrLength() < WAYPOINT_RADIUS) && (m_Source.y >= m_WayPoint.y)))
|
2015-11-20 03:03:20 -05:00
|
|
|
{
|
|
|
|
// if the mob has just started or if the mob reached a waypoint, give them a new waypoint.
|
2015-12-21 09:47:43 -05:00
|
|
|
m_WayPoint = m_Path->GetNextPoint();
|
2015-11-20 03:03:20 -05:00
|
|
|
m_GiveUpCounter = 40;
|
|
|
|
return ePathFinderStatus::PATH_FOUND;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Otherwise, the mob is still walking towards its waypoint, we'll patiently wait. We won't update m_WayPoint.
|
|
|
|
*a_OutputWaypoint = m_WayPoint;
|
|
|
|
return ePathFinderStatus::PATH_FOUND;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifndef __clang__
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
return ePathFinderStatus::PATH_FOUND;
|
|
|
|
// Fixes GCC warning: "control reaches end of non-void function".
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cPathFinder::ResetPathFinding(cChunk &a_Chunk)
|
|
|
|
{
|
|
|
|
m_GiveUpCounter = 40;
|
|
|
|
m_NoPathToTarget = false;
|
|
|
|
m_PathDestination = m_FinalDestination;
|
|
|
|
m_DeviationOrigin = m_PathDestination;
|
2015-12-21 09:47:43 -05:00
|
|
|
m_Path.reset(new cPath(a_Chunk, m_Source, m_PathDestination, 20, m_Width, m_Height));
|
2015-11-20 03:03:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-12-22 00:43:50 -05:00
|
|
|
bool cPathFinder::EnsureProperPoint(Vector3d & a_Vector, cChunk & a_Chunk)
|
2015-11-20 03:03:20 -05:00
|
|
|
{
|
2015-12-22 00:43:50 -05:00
|
|
|
cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Vector.x), FloorC(a_Vector.z));
|
2015-11-20 03:03:20 -05:00
|
|
|
BLOCKTYPE BlockType;
|
|
|
|
NIBBLETYPE BlockMeta;
|
|
|
|
|
|
|
|
if ((Chunk == nullptr) || !Chunk->IsValid())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-12-22 00:43:50 -05:00
|
|
|
int RelX = FloorC(a_Vector.x) - Chunk->GetPosX() * cChunkDef::Width;
|
|
|
|
int RelZ = FloorC(a_Vector.z) - Chunk->GetPosZ() * cChunkDef::Width;
|
2015-11-20 03:03:20 -05:00
|
|
|
|
|
|
|
// If destination in the air, first try to go 1 block north, or east, or west.
|
|
|
|
// This fixes the player leaning issue.
|
|
|
|
// If that failed, we instead go down to the lowest air block.
|
2016-04-18 14:58:57 -04:00
|
|
|
int YBelowUs = FloorC(a_Vector.y) - 1;
|
|
|
|
if (YBelowUs < 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
Chunk->GetBlockTypeMeta(RelX, YBelowUs, RelZ, BlockType, BlockMeta);
|
2015-12-22 00:43:50 -05:00
|
|
|
if (!(IsWaterOrSolid(BlockType)))
|
2015-11-20 03:03:20 -05:00
|
|
|
{
|
|
|
|
bool InTheAir = true;
|
|
|
|
int x, z;
|
|
|
|
for (z = -1; z <= 1; ++z)
|
|
|
|
{
|
|
|
|
for (x = -1; x <= 1; ++x)
|
|
|
|
{
|
|
|
|
if ((x == 0) && (z == 0))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2015-12-22 00:43:50 -05:00
|
|
|
Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Vector.x+x), FloorC(a_Vector.z+z));
|
2015-11-20 03:03:20 -05:00
|
|
|
if ((Chunk == nullptr) || !Chunk->IsValid())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2015-12-22 00:43:50 -05:00
|
|
|
RelX = FloorC(a_Vector.x+x) - Chunk->GetPosX() * cChunkDef::Width;
|
|
|
|
RelZ = FloorC(a_Vector.z+z) - Chunk->GetPosZ() * cChunkDef::Width;
|
2016-04-18 14:58:57 -04:00
|
|
|
Chunk->GetBlockTypeMeta(RelX, YBelowUs, RelZ, BlockType, BlockMeta);
|
2015-12-22 00:43:50 -05:00
|
|
|
if (IsWaterOrSolid((BlockType)))
|
2015-11-20 03:03:20 -05:00
|
|
|
{
|
2015-12-22 00:43:50 -05:00
|
|
|
a_Vector.x += x;
|
|
|
|
a_Vector.z += z;
|
2015-11-20 03:03:20 -05:00
|
|
|
InTheAir = false;
|
|
|
|
goto breakBothLoops;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
breakBothLoops:
|
|
|
|
|
|
|
|
// Go down to the lowest air block.
|
|
|
|
if (InTheAir)
|
|
|
|
{
|
2015-12-22 00:43:50 -05:00
|
|
|
while (a_Vector.y > 0)
|
2015-11-20 03:03:20 -05:00
|
|
|
{
|
2015-12-22 00:43:50 -05:00
|
|
|
Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y) - 1, RelZ, BlockType, BlockMeta);
|
|
|
|
if (IsWaterOrSolid(BlockType))
|
2015-11-20 03:03:20 -05:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2015-12-22 00:43:50 -05:00
|
|
|
a_Vector.y -= 1;
|
2015-11-20 03:03:20 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-22 00:43:50 -05:00
|
|
|
// If destination in water or solid, go up to the first air block.
|
|
|
|
while (a_Vector.y < cChunkDef::Height)
|
2015-11-20 03:03:20 -05:00
|
|
|
{
|
2015-12-22 00:43:50 -05:00
|
|
|
Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y), RelZ, BlockType, BlockMeta);
|
|
|
|
if (!IsWaterOrSolid(BlockType))
|
2015-11-20 03:03:20 -05:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2015-12-22 00:43:50 -05:00
|
|
|
a_Vector.y += 1;
|
2015-11-20 03:03:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-12-22 00:43:50 -05:00
|
|
|
bool cPathFinder::IsWaterOrSolid(BLOCKTYPE a_BlockType)
|
|
|
|
{
|
|
|
|
return ((a_BlockType == E_BLOCK_STATIONARY_WATER) || cBlockInfo::IsSolid(a_BlockType));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-11-20 03:03:20 -05:00
|
|
|
bool cPathFinder::PathIsTooOld() const
|
|
|
|
{
|
2015-12-21 09:47:43 -05:00
|
|
|
size_t acceptableDeviation = m_Path->WayPointsLeft() / 2;
|
2015-11-20 03:03:20 -05:00
|
|
|
if (acceptableDeviation == 0)
|
|
|
|
{
|
|
|
|
acceptableDeviation = 1;
|
|
|
|
}
|
|
|
|
if ((m_FinalDestination - m_DeviationOrigin).SqrLength() > acceptableDeviation * acceptableDeviation)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|