2015-05-08 10:07:36 -04:00
|
|
|
|
2015-04-29 12:24:14 -04:00
|
|
|
#pragma once
|
|
|
|
|
2015-05-18 23:54:20 -04:00
|
|
|
/*
|
|
|
|
// Needed Fwds: cPath
|
2015-04-29 12:24:14 -04:00
|
|
|
enum class ePathFinderStatus;
|
|
|
|
class cPath;
|
|
|
|
*/
|
|
|
|
|
2015-12-22 00:43:50 -05:00
|
|
|
|
2015-05-18 23:54:20 -04:00
|
|
|
#include "../FastRandom.h"
|
2015-04-29 12:24:14 -04:00
|
|
|
#ifdef COMPILING_PATHFIND_DEBUGGER
|
2015-05-09 03:25:09 -04:00
|
|
|
/* Note: the COMPILING_PATHFIND_DEBUGGER flag is used by Native / WiseOldMan95 to debug
|
2015-09-25 04:14:17 -04:00
|
|
|
this class outside of Cuberite. This preprocessor flag is never set when compiling Cuberite. */
|
2015-04-29 12:24:14 -04:00
|
|
|
#include "PathFinderIrrlicht_Head.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
2015-05-01 11:53:24 -04:00
|
|
|
//fwd: ../Chunk.h
|
|
|
|
class cChunk;
|
2015-04-29 12:24:14 -04:00
|
|
|
|
2015-12-22 00:43:50 -05:00
|
|
|
|
2015-04-29 12:24:14 -04:00
|
|
|
/* Various little structs and classes */
|
2015-05-16 07:05:33 -04:00
|
|
|
enum class ePathFinderStatus {CALCULATING, PATH_FOUND, PATH_NOT_FOUND, NEARBY_FOUND};
|
2015-05-19 14:07:05 -04:00
|
|
|
enum class eCellStatus {OPENLIST, CLOSEDLIST, NOLIST};
|
2015-12-22 00:43:50 -05:00
|
|
|
/** The pathfinder has 3 types of cells (cPathCell).
|
|
|
|
1 - empty. m_IsSolid is false, m_IsSpecial is false. Air cells are always traversable by A*.
|
|
|
|
2 - occupied / solid. m_IsSolid is true, m_IsSpecial is false. Air cells are never traversable by A*.
|
|
|
|
3 - Special. m_IsSolid is true, m_IsSpecial is true. These cells are special: They may either behave as empty
|
|
|
|
or as occupied / solid, depending on the mob's direction of movement. For instance, an airblock above a fence is a special cell,
|
|
|
|
because when mobs attempt to travel to it by jumping, it acts as a solid. But when mobs fall and land on top of a fence,
|
|
|
|
it acts as air. Special cells include: Doors, ladders, trapdoors, water, gates.
|
|
|
|
|
|
|
|
The main function which handles special blocks is SpecialIsSolidFromThisDirection.
|
|
|
|
This function receives a BlockType, a meta, and a direction of travel,
|
|
|
|
then it uses those 3 parameters to decide whether the special block should behave as a solid or as air in this
|
|
|
|
particular direction of travel.
|
|
|
|
|
|
|
|
Currently, only fences and water are handled properly. The function always returns "true" (meaning: treat as occuiped/solid) for
|
|
|
|
the rest of the blocks. This will be fixed once the physics engine issues are fixed. */
|
2015-05-19 14:07:05 -04:00
|
|
|
struct cPathCell
|
|
|
|
{
|
|
|
|
Vector3i m_Location; // Location of the cell in the world.
|
|
|
|
int m_F, m_G, m_H; // F, G, H as defined in regular A*.
|
|
|
|
eCellStatus m_Status; // Which list is the cell in? Either non, open, or closed.
|
|
|
|
cPathCell * m_Parent; // Cell's parent, as defined in regular A*.
|
2015-12-22 00:43:50 -05:00
|
|
|
bool m_IsSolid; // Is the cell an air or a solid? Partial solids are considered solids. If m_IsSpecial is true, this is always true.
|
|
|
|
bool m_IsSpecial; // The cell is special - it acts as "solid" or "air" depending on direction, e.g. door or top of fence.
|
|
|
|
BLOCKTYPE m_BlockType;
|
|
|
|
NIBBLETYPE m_BlockMeta;
|
2015-05-19 14:07:05 -04:00
|
|
|
};
|
2015-05-19 15:47:48 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-04-29 12:24:14 -04:00
|
|
|
class compareHeuristics
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
bool operator()(cPathCell * & a_V1, cPathCell * & a_V2);
|
|
|
|
};
|
|
|
|
|
2015-05-19 15:47:48 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-04-29 12:24:14 -04:00
|
|
|
class cPath
|
|
|
|
{
|
|
|
|
public:
|
2015-12-22 00:43:50 -05:00
|
|
|
/** Creates a pathfinder instance.
|
|
|
|
After calling this, you are expected to call CalculationStep() once per tick or once per several ticks
|
|
|
|
until it returns something other than CALCULATING.
|
2015-04-29 12:24:14 -04:00
|
|
|
|
|
|
|
@param a_StartingPoint The function expects this position to be the lowest block the mob is in, a rule of thumb: "The block where the Zombie's knees are at".
|
|
|
|
@param a_EndingPoint "The block where the Zombie's knees want to be".
|
2015-12-22 00:43:50 -05:00
|
|
|
@param a_MaxSteps The maximum steps before giving up.
|
|
|
|
@param a_BoundingBoxWidth the character's boundingbox width in blocks. Currently the parameter is ignored and 1 is assumed.
|
|
|
|
@param a_BoundingBoxHeight the character's boundingbox width in blocks. Currently the parameter is ignored and 2 is assumed. */
|
2015-04-29 12:24:14 -04:00
|
|
|
cPath(
|
2015-05-03 02:45:27 -04:00
|
|
|
cChunk & a_Chunk,
|
2015-05-23 10:41:29 -04:00
|
|
|
const Vector3d & a_StartingPoint, const Vector3d & a_EndingPoint, int a_MaxSteps,
|
2015-12-22 00:43:50 -05:00
|
|
|
double a_BoundingBoxWidth, double a_BoundingBoxHeight
|
2015-04-29 12:24:14 -04:00
|
|
|
);
|
2015-11-20 03:03:20 -05:00
|
|
|
|
2015-12-22 00:43:50 -05:00
|
|
|
/** Creates an invalid path which is not usable. You shouldn't call any method other than isValid on such a path. */
|
2015-11-20 03:03:20 -05:00
|
|
|
cPath();
|
|
|
|
|
2015-12-15 05:44:29 -05:00
|
|
|
/** delete default constructors */
|
|
|
|
cPath(const cPath & a_other) = delete;
|
|
|
|
cPath(cPath && a_other) = delete;
|
2015-12-21 08:51:12 -05:00
|
|
|
|
2015-12-15 05:44:29 -05:00
|
|
|
cPath & operator=(const cPath & a_other) = delete;
|
|
|
|
cPath & operator=(cPath && a_other) = delete;
|
|
|
|
|
2015-05-16 07:05:33 -04:00
|
|
|
/** Performs part of the path calculation and returns the appropriate status.
|
2015-12-22 00:43:50 -05:00
|
|
|
If PATH_FOUND is returned, the path was found, and you can call query the instance for waypoints via GetNextWayPoint, etc.
|
2015-05-16 07:05:33 -04:00
|
|
|
If NEARBY_FOUND is returned, it means that the destination is not reachable, but a nearby destination
|
|
|
|
is reachable. If the user likes the alternative destination, they can call AcceptNearbyPath to treat the path as found,
|
2015-12-22 00:43:50 -05:00
|
|
|
and to make consequent calls to step return PATH_FOUND
|
|
|
|
If PATH_NOT_FOUND is returned, then no path was found. */
|
2015-11-20 03:03:20 -05:00
|
|
|
ePathFinderStatus CalculationStep(cChunk & a_Chunk);
|
2015-04-29 12:24:14 -04:00
|
|
|
|
2015-05-16 07:05:33 -04:00
|
|
|
/** Called after the PathFinder's step returns NEARBY_FOUND.
|
|
|
|
Changes the PathFinder status from NEARBY_FOUND to PATH_FOUND, returns the nearby destination that
|
|
|
|
the PathFinder found a path to. */
|
|
|
|
Vector3i AcceptNearbyPath();
|
|
|
|
|
2015-07-31 10:49:10 -04:00
|
|
|
// Point retrieval functions, inlined for performance:
|
|
|
|
|
2015-04-29 12:24:14 -04:00
|
|
|
/** Returns the next point in the path. */
|
2015-05-23 10:41:29 -04:00
|
|
|
inline Vector3d GetNextPoint()
|
2015-04-29 12:24:14 -04:00
|
|
|
{
|
|
|
|
ASSERT(m_Status == ePathFinderStatus::PATH_FOUND);
|
2015-11-20 03:03:20 -05:00
|
|
|
ASSERT(m_CurrentPoint < m_PathPoints.size());
|
|
|
|
Vector3i Point = m_PathPoints[m_PathPoints.size() - 1 - m_CurrentPoint];
|
|
|
|
++m_CurrentPoint;
|
2015-05-23 10:41:29 -04:00
|
|
|
return Vector3d(Point.x + m_HalfWidth, Point.y, Point.z + m_HalfWidth);
|
2015-04-29 12:24:14 -04:00
|
|
|
}
|
2015-07-31 10:49:10 -04:00
|
|
|
|
|
|
|
|
2015-11-20 03:03:20 -05:00
|
|
|
/** Checks if we have no more waypoints to return. Never call getnextPoint when this is true. */
|
|
|
|
inline bool NoMoreWayPoints() const
|
2015-04-29 12:24:14 -04:00
|
|
|
{
|
|
|
|
ASSERT(m_Status == ePathFinderStatus::PATH_FOUND);
|
2015-11-20 03:03:20 -05:00
|
|
|
return (m_CurrentPoint == m_PathPoints.size());
|
2015-05-03 13:56:37 -04:00
|
|
|
}
|
2015-07-31 10:49:10 -04:00
|
|
|
|
2015-11-20 03:03:20 -05:00
|
|
|
/** Returns true if GetNextPoint() was never called for this Path. */
|
2015-07-11 15:40:03 -04:00
|
|
|
inline bool IsFirstPoint() const
|
2015-05-03 13:56:37 -04:00
|
|
|
{
|
|
|
|
ASSERT(m_Status == ePathFinderStatus::PATH_FOUND);
|
|
|
|
return (m_CurrentPoint == 0);
|
2015-04-29 12:24:14 -04:00
|
|
|
}
|
2015-07-31 10:49:10 -04:00
|
|
|
|
2015-11-20 03:03:20 -05:00
|
|
|
/** Returns true if this path is properly initialized.
|
|
|
|
Returns false if this path was initialized with an empty constructor.
|
|
|
|
If false, the path is unusable and you should not call any methods. */
|
|
|
|
inline bool IsValid() const
|
|
|
|
{
|
|
|
|
return m_IsValid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** The amount of waypoints left to return. */
|
|
|
|
inline size_t WayPointsLeft() const
|
|
|
|
{
|
|
|
|
ASSERT(m_Status == ePathFinderStatus::PATH_FOUND);
|
|
|
|
return m_PathPoints.size() - m_CurrentPoint;
|
|
|
|
}
|
2015-07-31 10:49:10 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
2015-04-29 12:24:14 -04:00
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
/* General */
|
2015-07-11 15:40:03 -04:00
|
|
|
bool StepOnce(); // The public version just calls this version * CALCULATIONS_PER_CALL times.
|
2015-04-29 12:24:14 -04:00
|
|
|
void FinishCalculation(); // Clears the memory used for calculating the path.
|
|
|
|
void FinishCalculation(ePathFinderStatus a_NewStatus); // Clears the memory used for calculating the path and changes the status.
|
2015-05-16 07:05:33 -04:00
|
|
|
void AttemptToFindAlternative();
|
|
|
|
void BuildPath();
|
2015-04-29 12:24:14 -04:00
|
|
|
|
|
|
|
/* Openlist and closedlist management */
|
|
|
|
void OpenListAdd(cPathCell * a_Cell);
|
|
|
|
cPathCell * OpenListPop();
|
2015-12-22 00:43:50 -05:00
|
|
|
bool ProcessIfWalkable(const Vector3i & a_Location, cPathCell * a_Source, int a_Cost);
|
2015-04-29 12:24:14 -04:00
|
|
|
|
|
|
|
/* Map management */
|
|
|
|
void ProcessCell(cPathCell * a_Cell, cPathCell * a_Caller, int a_GDelta);
|
2015-05-03 13:56:37 -04:00
|
|
|
cPathCell * GetCell(const Vector3i & a_location);
|
2015-04-29 12:24:14 -04:00
|
|
|
|
|
|
|
/* Pathfinding fields */
|
|
|
|
std::priority_queue<cPathCell *, std::vector<cPathCell *>, compareHeuristics> m_OpenList;
|
2015-06-07 06:52:14 -04:00
|
|
|
std::unordered_map<Vector3i, cPathCell, VectorHasher<int>> m_Map;
|
2015-05-03 13:56:37 -04:00
|
|
|
Vector3i m_Destination;
|
|
|
|
Vector3i m_Source;
|
2015-05-23 10:41:29 -04:00
|
|
|
int m_BoundingBoxWidth;
|
|
|
|
int m_BoundingBoxHeight;
|
|
|
|
double m_HalfWidth;
|
2015-04-29 12:24:14 -04:00
|
|
|
int m_StepsLeft;
|
2015-05-16 07:05:33 -04:00
|
|
|
cPathCell * m_NearestPointToTarget;
|
2015-04-29 12:24:14 -04:00
|
|
|
|
|
|
|
/* Control fields */
|
|
|
|
ePathFinderStatus m_Status;
|
2015-11-20 03:03:20 -05:00
|
|
|
bool m_IsValid;
|
2015-04-29 12:24:14 -04:00
|
|
|
|
|
|
|
/* Final path fields */
|
2015-05-03 13:56:37 -04:00
|
|
|
size_t m_CurrentPoint;
|
|
|
|
std::vector<Vector3i> m_PathPoints;
|
2015-04-29 12:24:14 -04:00
|
|
|
|
2015-05-01 11:53:24 -04:00
|
|
|
/* Interfacing with the world */
|
2015-12-22 00:43:50 -05:00
|
|
|
void FillCellAttributes(cPathCell & a_Cell); // Query our hosting world and fill the cell with info
|
2015-05-01 11:53:24 -04:00
|
|
|
cChunk * m_Chunk; // Only valid inside Step()!
|
2015-05-16 07:05:33 -04:00
|
|
|
bool m_BadChunkFound;
|
2015-12-21 08:51:12 -05:00
|
|
|
|
|
|
|
/* High level world queries */
|
2015-12-22 00:43:50 -05:00
|
|
|
bool IsWalkable(const Vector3i & a_Location, const Vector3i & a_Source);
|
|
|
|
bool BodyFitsIn(const Vector3i & a_Location, const Vector3i & a_Source);
|
|
|
|
bool BlockTypeIsSpecial(BLOCKTYPE a_Type);
|
|
|
|
bool SpecialIsSolidFromThisDirection(BLOCKTYPE a_Type, NIBBLETYPE a_Meta, const Vector3i & a_Direction);
|
2015-12-21 08:51:12 -05:00
|
|
|
bool HasSolidBelow(const Vector3i & a_Location);
|
2015-05-01 11:53:24 -04:00
|
|
|
#ifdef COMPILING_PATHFIND_DEBUGGER
|
2015-04-29 12:24:14 -04:00
|
|
|
#include "../path_irrlicht.cpp"
|
|
|
|
#endif
|
|
|
|
};
|