Merge pull request #2795 from SafwatHalaby/doors
"Special blocks" handling
This commit is contained in:
commit
6a22605fae
@ -22,9 +22,9 @@ cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eMonsterTyp
|
|||||||
|
|
||||||
|
|
||||||
// What to do if in Chasing State
|
// What to do if in Chasing State
|
||||||
void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt)
|
void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::InStateChasing(a_Dt);
|
super::InStateChasing(a_Dt, a_Chunk);
|
||||||
|
|
||||||
if (m_Target != nullptr)
|
if (m_Target != nullptr)
|
||||||
{
|
{
|
||||||
|
@ -17,7 +17,7 @@ public:
|
|||||||
cAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
|
cAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
|
||||||
|
|
||||||
virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
|
virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
|
||||||
virtual void InStateChasing(std::chrono::milliseconds a_Dt) override;
|
virtual void InStateChasing(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
|
||||||
|
|
||||||
virtual void EventSeePlayer(cEntity *) override;
|
virtual void EventSeePlayer(cEntity *) override;
|
||||||
|
|
||||||
|
@ -161,12 +161,12 @@ void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cHorse::InStateIdle(std::chrono::milliseconds a_Dt)
|
void cHorse::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
// If horse is tame and someone is sitting on it, don't walk around
|
// If horse is tame and someone is sitting on it, don't walk around
|
||||||
if ((!m_bIsTame) || (m_Attachee == nullptr))
|
if ((!m_bIsTame) || (m_Attachee == nullptr))
|
||||||
{
|
{
|
||||||
super::InStateIdle(a_Dt);
|
super::InStateIdle(a_Dt, a_Chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ public:
|
|||||||
CLASS_PROTODEF(cHorse)
|
CLASS_PROTODEF(cHorse)
|
||||||
|
|
||||||
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
|
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
|
||||||
virtual void InStateIdle(std::chrono::milliseconds a_Dt) override;
|
virtual void InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
|
||||||
virtual void HandleSpeedFromAttachee(float a_Forward, float a_Sideways) override;
|
virtual void HandleSpeedFromAttachee(float a_Forward, float a_Sideways) override;
|
||||||
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
|
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
|
||||||
virtual void OnRightClicked(cPlayer & a_Player) override;
|
virtual void OnRightClicked(cPlayer & a_Player) override;
|
||||||
|
@ -133,9 +133,8 @@ void cMonster::MoveToWayPoint(cChunk & a_Chunk)
|
|||||||
{
|
{
|
||||||
if (DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y)))
|
if (DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y)))
|
||||||
{
|
{
|
||||||
if (
|
if (((IsOnGround()) && (GetSpeed().SqrLength() == 0.0f)) ||
|
||||||
(IsOnGround() && (GetSpeedX() == 0.0f) && (GetSpeedY() == 0.0f)) // TODO water handling?
|
(IsSwimming()))
|
||||||
)
|
|
||||||
{
|
{
|
||||||
m_bOnGround = false;
|
m_bOnGround = false;
|
||||||
m_JumpCoolDown = 20;
|
m_JumpCoolDown = 20;
|
||||||
@ -303,18 +302,18 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
|||||||
case IDLE:
|
case IDLE:
|
||||||
{
|
{
|
||||||
// If enemy passive we ignore checks for player visibility.
|
// If enemy passive we ignore checks for player visibility.
|
||||||
InStateIdle(a_Dt);
|
InStateIdle(a_Dt, a_Chunk);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CHASING:
|
case CHASING:
|
||||||
{
|
{
|
||||||
// If we do not see a player anymore skip chasing action.
|
// If we do not see a player anymore skip chasing action.
|
||||||
InStateChasing(a_Dt);
|
InStateChasing(a_Dt, a_Chunk);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESCAPING:
|
case ESCAPING:
|
||||||
{
|
{
|
||||||
InStateEscaping(a_Dt);
|
InStateEscaping(a_Dt, a_Chunk);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ATTACKING: break;
|
case ATTACKING: break;
|
||||||
@ -377,7 +376,7 @@ void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath)
|
|||||||
double HeadRotation, HeadPitch;
|
double HeadRotation, HeadPitch;
|
||||||
HeadDistance.Normalize();
|
HeadDistance.Normalize();
|
||||||
VectorToEuler(HeadDistance.x, HeadDistance.y, HeadDistance.z, HeadRotation, HeadPitch);
|
VectorToEuler(HeadDistance.x, HeadDistance.y, HeadDistance.z, HeadRotation, HeadPitch);
|
||||||
if (std::abs(BodyRotation - HeadRotation) < 90)
|
if ((std::abs(BodyRotation - HeadRotation) < 70) && (std::abs(HeadPitch) < 60))
|
||||||
{
|
{
|
||||||
SetHeadYaw(HeadRotation);
|
SetHeadYaw(HeadRotation);
|
||||||
SetPitch(-HeadPitch);
|
SetPitch(-HeadPitch);
|
||||||
@ -385,7 +384,7 @@ void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetHeadYaw(BodyRotation);
|
SetHeadYaw(BodyRotation);
|
||||||
SetPitch(-BodyPitch);
|
SetPitch(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,7 +610,7 @@ void cMonster::EventLosePlayer(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cMonster::InStateIdle(std::chrono::milliseconds a_Dt)
|
void cMonster::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
if (m_PathfinderActivated)
|
if (m_PathfinderActivated)
|
||||||
{
|
{
|
||||||
@ -632,12 +631,27 @@ void cMonster::InStateIdle(std::chrono::milliseconds a_Dt)
|
|||||||
|
|
||||||
if ((Dist.SqrLength() > 2) && (rem >= 3))
|
if ((Dist.SqrLength() > 2) && (rem >= 3))
|
||||||
{
|
{
|
||||||
Vector3d Destination(GetPosX() + Dist.x, 0, GetPosZ() + Dist.z);
|
|
||||||
Destination.y = FindFirstNonAirBlockPosition(Destination.x, Destination.z);
|
Vector3d Destination(GetPosX() + Dist.x, GetPosition().y, GetPosZ() + Dist.z);
|
||||||
|
|
||||||
|
cChunk * Chunk = a_Chunk.GetNeighborChunk(static_cast<int>(Destination.x), static_cast<int>(Destination.z));
|
||||||
|
if ((Chunk == nullptr) || !Chunk->IsValid())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BLOCKTYPE BlockType;
|
||||||
|
NIBBLETYPE BlockMeta;
|
||||||
|
int RelX = static_cast<int>(Destination.x) - Chunk->GetPosX() * cChunkDef::Width;
|
||||||
|
int RelZ = static_cast<int>(Destination.z) - Chunk->GetPosZ() * cChunkDef::Width;
|
||||||
|
Chunk->GetBlockTypeMeta(RelX, static_cast<int>(Destination.y) - 1, RelZ, BlockType, BlockMeta);
|
||||||
|
if (BlockType != E_BLOCK_STATIONARY_WATER) // Idle mobs shouldn't enter water on purpose
|
||||||
|
{
|
||||||
MoveToPosition(Destination);
|
MoveToPosition(Destination);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -645,7 +659,7 @@ void cMonster::InStateIdle(std::chrono::milliseconds a_Dt)
|
|||||||
|
|
||||||
// What to do if in Chasing State
|
// What to do if in Chasing State
|
||||||
// This state should always be defined in each child class
|
// This state should always be defined in each child class
|
||||||
void cMonster::InStateChasing(std::chrono::milliseconds a_Dt)
|
void cMonster::InStateChasing(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
UNUSED(a_Dt);
|
UNUSED(a_Dt);
|
||||||
}
|
}
|
||||||
@ -655,7 +669,7 @@ void cMonster::InStateChasing(std::chrono::milliseconds a_Dt)
|
|||||||
|
|
||||||
|
|
||||||
// What to do if in Escaping State
|
// What to do if in Escaping State
|
||||||
void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt)
|
void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
UNUSED(a_Dt);
|
UNUSED(a_Dt);
|
||||||
|
|
||||||
|
@ -79,9 +79,9 @@ public:
|
|||||||
virtual void EventLosePlayer(void);
|
virtual void EventLosePlayer(void);
|
||||||
virtual void CheckEventLostPlayer(void);
|
virtual void CheckEventLostPlayer(void);
|
||||||
|
|
||||||
virtual void InStateIdle (std::chrono::milliseconds a_Dt);
|
virtual void InStateIdle (std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
|
||||||
virtual void InStateChasing (std::chrono::milliseconds a_Dt);
|
virtual void InStateChasing (std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
|
||||||
virtual void InStateEscaping(std::chrono::milliseconds a_Dt);
|
virtual void InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
|
||||||
|
|
||||||
int GetAttackRate() { return static_cast<int>(m_AttackRate); }
|
int GetAttackRate() { return static_cast<int>(m_AttackRate); }
|
||||||
void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; }
|
void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; }
|
||||||
@ -193,7 +193,7 @@ protected:
|
|||||||
/** Returns if a monster can reach a given height by jumping. */
|
/** Returns if a monster can reach a given height by jumping. */
|
||||||
inline bool DoesPosYRequireJump(int a_PosY)
|
inline bool DoesPosYRequireJump(int a_PosY)
|
||||||
{
|
{
|
||||||
return ((a_PosY > POSY_TOINT) && (a_PosY == POSY_TOINT + 1));
|
return ((a_PosY > POSY_TOINT));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Move in a straight line to the next waypoint in the path, will jump if needed. */
|
/** Move in a straight line to the next waypoint in the path, will jump if needed. */
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include "../Chunk.h"
|
#include "../Chunk.h"
|
||||||
|
|
||||||
#define JUMP_G_COST 20
|
#define JUMP_G_COST 20
|
||||||
|
#define NORMAL_G_COST 10
|
||||||
|
#define DIAGONAL_G_COST 14
|
||||||
|
|
||||||
#define DISTANCE_MANHATTAN 0 // 1: More speed, a bit less accuracy 0: Max accuracy, less speed.
|
#define DISTANCE_MANHATTAN 0 // 1: More speed, a bit less accuracy 0: Max accuracy, less speed.
|
||||||
#define HEURISTICS_ONLY 0 // 1: Much more speed, much less accurate.
|
#define HEURISTICS_ONLY 0 // 1: Much more speed, much less accurate.
|
||||||
@ -30,8 +32,7 @@ bool compareHeuristics::operator()(cPathCell * & a_Cell1, cPathCell * & a_Cell2)
|
|||||||
cPath::cPath(
|
cPath::cPath(
|
||||||
cChunk & a_Chunk,
|
cChunk & a_Chunk,
|
||||||
const Vector3d & a_StartingPoint, const Vector3d & a_EndingPoint, int a_MaxSteps,
|
const Vector3d & a_StartingPoint, const Vector3d & a_EndingPoint, int a_MaxSteps,
|
||||||
double a_BoundingBoxWidth, double a_BoundingBoxHeight,
|
double a_BoundingBoxWidth, double a_BoundingBoxHeight
|
||||||
int a_MaxUp, int a_MaxDown
|
|
||||||
) :
|
) :
|
||||||
m_StepsLeft(a_MaxSteps),
|
m_StepsLeft(a_MaxSteps),
|
||||||
m_IsValid(true),
|
m_IsValid(true),
|
||||||
@ -39,10 +40,8 @@ cPath::cPath(
|
|||||||
m_Chunk(&a_Chunk),
|
m_Chunk(&a_Chunk),
|
||||||
m_BadChunkFound(false)
|
m_BadChunkFound(false)
|
||||||
{
|
{
|
||||||
// TODO: if src not walkable OR dest not walkable, then abort.
|
|
||||||
// Borrow a new "isWalkable" from ProcessIfWalkable, make ProcessIfWalkable also call isWalkable
|
|
||||||
|
|
||||||
a_BoundingBoxWidth = 1; // Treat all mobs width as 1 until physics is improved. This would also require changes to stepOnce to work.
|
a_BoundingBoxWidth = 1; // Treat all mobs width as 1 until physics is improved.
|
||||||
|
|
||||||
m_BoundingBoxWidth = CeilC(a_BoundingBoxWidth);
|
m_BoundingBoxWidth = CeilC(a_BoundingBoxWidth);
|
||||||
m_BoundingBoxHeight = CeilC(a_BoundingBoxHeight);
|
m_BoundingBoxHeight = CeilC(a_BoundingBoxHeight);
|
||||||
@ -57,7 +56,7 @@ cPath::cPath(
|
|||||||
m_Destination.y = FloorC(a_EndingPoint.y);
|
m_Destination.y = FloorC(a_EndingPoint.y);
|
||||||
m_Destination.z = FloorC(a_EndingPoint.z - HalfWidthInt);
|
m_Destination.z = FloorC(a_EndingPoint.z - HalfWidthInt);
|
||||||
|
|
||||||
if (!IsWalkable(m_Source))
|
if (!IsWalkable(m_Source, m_Source))
|
||||||
{
|
{
|
||||||
m_Status = ePathFinderStatus::PATH_NOT_FOUND;
|
m_Status = ePathFinderStatus::PATH_NOT_FOUND;
|
||||||
return;
|
return;
|
||||||
@ -126,51 +125,6 @@ Vector3i cPath::AcceptNearbyPath()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool cPath::IsSolid(const Vector3i & a_Location)
|
|
||||||
{
|
|
||||||
ASSERT(m_Chunk != nullptr);
|
|
||||||
|
|
||||||
if (!cChunkDef::IsValidHeight(a_Location.y))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto Chunk = m_Chunk->GetNeighborChunk(a_Location.x, a_Location.z);
|
|
||||||
if ((Chunk == nullptr) || !Chunk->IsValid())
|
|
||||||
{
|
|
||||||
m_BadChunkFound = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
m_Chunk = Chunk;
|
|
||||||
|
|
||||||
BLOCKTYPE BlockType;
|
|
||||||
NIBBLETYPE BlockMeta;
|
|
||||||
int RelX = a_Location.x - m_Chunk->GetPosX() * cChunkDef::Width;
|
|
||||||
int RelZ = a_Location.z - m_Chunk->GetPosZ() * cChunkDef::Width;
|
|
||||||
|
|
||||||
m_Chunk->GetBlockTypeMeta(RelX, a_Location.y, RelZ, BlockType, BlockMeta);
|
|
||||||
if (
|
|
||||||
(BlockType == E_BLOCK_FENCE) ||
|
|
||||||
(BlockType == E_BLOCK_OAK_FENCE_GATE) ||
|
|
||||||
(BlockType == E_BLOCK_NETHER_BRICK_FENCE) ||
|
|
||||||
(BlockType == E_BLOCK_COBBLESTONE_WALL) ||
|
|
||||||
((BlockType >= E_BLOCK_SPRUCE_FENCE_GATE) && (BlockType <= E_BLOCK_ACACIA_FENCE))
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// TODO move this out of IsSolid to a proper place.
|
|
||||||
GetCell(a_Location + Vector3i(0, 1, 0))->m_IsSolid = true; // Mobs will always think that the fence is 2 blocks high and therefore won't jump over.
|
|
||||||
}
|
|
||||||
if (BlockType == E_BLOCK_STATIONARY_WATER)
|
|
||||||
{
|
|
||||||
GetCell(a_Location + Vector3i(0, -1, 0))->m_IsSolid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cBlockInfo::IsSolid(BlockType);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool cPath::StepOnce()
|
bool cPath::StepOnce()
|
||||||
{
|
{
|
||||||
cPathCell * CurrentCell = OpenListPop();
|
cPathCell * CurrentCell = OpenListPop();
|
||||||
@ -209,40 +163,47 @@ bool cPath::StepOnce()
|
|||||||
// Now we start checking adjacent cells.
|
// Now we start checking adjacent cells.
|
||||||
|
|
||||||
|
|
||||||
bool done_east = false,
|
// If true, no need to do more checks in that direction
|
||||||
done_west = false,
|
bool DoneEast = false,
|
||||||
done_north = false,
|
DoneWest = false,
|
||||||
done_south = false; // If true, no need to do more checks in that direction
|
DoneNorth = false,
|
||||||
|
DoneSouth = false;
|
||||||
|
|
||||||
|
// If true, we can walk in that direction without changing height
|
||||||
|
// This is used for deciding if to calculate diagonals
|
||||||
|
bool WalkableEast = false,
|
||||||
|
WalkableWest = false,
|
||||||
|
WalkableNorth = false,
|
||||||
|
WalkableSouth = false;
|
||||||
|
|
||||||
// If we can jump without hitting the ceiling
|
// If we can jump without hitting the ceiling
|
||||||
if (BodyFitsIn(CurrentCell->m_Location + Vector3i(0, 1, 0)))
|
if (BodyFitsIn(CurrentCell->m_Location + Vector3i(0, 1, 0), CurrentCell->m_Location))
|
||||||
{
|
{
|
||||||
|
// For ladder climbing
|
||||||
|
ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 1, 0), CurrentCell, JUMP_G_COST);
|
||||||
|
|
||||||
// Check east-up
|
// Check east-up
|
||||||
if (GetCell(CurrentCell->m_Location + Vector3i(1, 0, 0))->m_IsSolid)
|
if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, 1, 0), CurrentCell, JUMP_G_COST))
|
||||||
{
|
{
|
||||||
ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, 1, 0), CurrentCell, JUMP_G_COST);
|
DoneEast = true;
|
||||||
done_east = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check west-up
|
// Check west-up
|
||||||
if (GetCell(CurrentCell->m_Location + Vector3i(-1, 0, 0))->m_IsSolid)
|
if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, 1, 0), CurrentCell, JUMP_G_COST))
|
||||||
{
|
{
|
||||||
ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, 1, 0), CurrentCell, JUMP_G_COST);
|
DoneWest = true;
|
||||||
done_west = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check north-up
|
// Check north-up
|
||||||
if (GetCell(CurrentCell->m_Location + Vector3i(0, 0, -1))->m_IsSolid)
|
if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 1, -1), CurrentCell, JUMP_G_COST))
|
||||||
{
|
{
|
||||||
ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 1, -1), CurrentCell, JUMP_G_COST);
|
DoneNorth = true;
|
||||||
done_north = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check south-up
|
// Check south-up
|
||||||
if (GetCell(CurrentCell->m_Location + Vector3i(0, 0, 1))->m_IsSolid)
|
if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 1, 1), CurrentCell, JUMP_G_COST))
|
||||||
{
|
{
|
||||||
ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, 1, 1), CurrentCell, JUMP_G_COST);
|
DoneSouth = true;
|
||||||
done_south = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -251,72 +212,87 @@ bool cPath::StepOnce()
|
|||||||
// Check North, South, East, West at our own height or below. We are willing to jump up to 3 blocks down.
|
// Check North, South, East, West at our own height or below. We are willing to jump up to 3 blocks down.
|
||||||
|
|
||||||
|
|
||||||
if (!done_east)
|
if (!DoneEast)
|
||||||
{
|
{
|
||||||
for (int i = 0; i >= -3; --i)
|
for (int y = 0; y >= -3; --y)
|
||||||
{
|
{
|
||||||
if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, i, 0), CurrentCell, 10))
|
if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, y, 0), CurrentCell, NORMAL_G_COST))
|
||||||
{
|
{
|
||||||
done_east = true;
|
DoneEast = true;
|
||||||
|
if (y == 0)
|
||||||
|
{
|
||||||
|
WalkableEast = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!done_west)
|
if (!DoneWest)
|
||||||
{
|
{
|
||||||
for (int i = 0; i >= -3; --i)
|
for (int y = 0; y >= -3; --y)
|
||||||
{
|
{
|
||||||
if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, i, 0), CurrentCell, 10))
|
if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, y, 0), CurrentCell, NORMAL_G_COST))
|
||||||
{
|
{
|
||||||
done_west = true;
|
DoneWest = true;
|
||||||
|
if (y == 0)
|
||||||
|
{
|
||||||
|
WalkableWest = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!done_south)
|
if (!DoneSouth)
|
||||||
{
|
{
|
||||||
for (int i = 0; i >= -3; --i)
|
for (int y = 0; y >= -3; --y)
|
||||||
{
|
{
|
||||||
if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, i, 1), CurrentCell, 10))
|
if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, y, 1), CurrentCell, NORMAL_G_COST))
|
||||||
{
|
{
|
||||||
done_west = true;
|
DoneWest = true;
|
||||||
|
if (y == 0)
|
||||||
|
{
|
||||||
|
WalkableSouth = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!done_north)
|
if (!DoneNorth)
|
||||||
{
|
{
|
||||||
for (int i = 0; i >= -3; --i)
|
for (int y = 0; y >= -3; --y)
|
||||||
{
|
{
|
||||||
if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, i, -1), CurrentCell, 10))
|
if (ProcessIfWalkable(CurrentCell->m_Location + Vector3i(0, y, -1), CurrentCell, NORMAL_G_COST))
|
||||||
{
|
{
|
||||||
done_north = true;
|
DoneNorth = true;
|
||||||
|
if (y == 0)
|
||||||
|
{
|
||||||
|
WalkableNorth = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Check diagonals
|
// Check diagonals
|
||||||
|
|
||||||
|
if (WalkableNorth && WalkableEast)
|
||||||
for (int x = -1; x <= 1; x += 2)
|
|
||||||
{
|
{
|
||||||
for (int z = -1; z <= 1; z += 2)
|
ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, 0, -1), CurrentCell, DIAGONAL_G_COST);
|
||||||
{
|
|
||||||
// This condition prevents diagonal corner cutting.
|
|
||||||
if (!GetCell(CurrentCell->m_Location + Vector3i(x, 0, 0))->m_IsSolid && !GetCell(CurrentCell->m_Location + Vector3i(0, 0, z))->m_IsSolid)
|
|
||||||
{
|
|
||||||
// This prevents falling of "sharp turns" e.g. a 1x1x20 rectangle in the air which breaks in a right angle suddenly.
|
|
||||||
if (GetCell(CurrentCell->m_Location + Vector3i(x, -1, 0))->m_IsSolid && GetCell(CurrentCell->m_Location + Vector3i(0, -1, z))->m_IsSolid)
|
|
||||||
{
|
|
||||||
ProcessIfWalkable(CurrentCell->m_Location + Vector3i(x, 0, z), CurrentCell, 14); // 14 is a good enough approximation of sqrt(10 + 10).
|
|
||||||
}
|
}
|
||||||
|
if (WalkableNorth && WalkableWest)
|
||||||
|
{
|
||||||
|
ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, 0, -1), CurrentCell, DIAGONAL_G_COST);
|
||||||
}
|
}
|
||||||
|
if (WalkableSouth && WalkableEast)
|
||||||
|
{
|
||||||
|
ProcessIfWalkable(CurrentCell->m_Location + Vector3i(1, 0, 1), CurrentCell, DIAGONAL_G_COST);
|
||||||
}
|
}
|
||||||
|
if (WalkableSouth && WalkableWest)
|
||||||
|
{
|
||||||
|
ProcessIfWalkable(CurrentCell->m_Location + Vector3i(-1, 0, 1), CurrentCell, DIAGONAL_G_COST);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -349,6 +325,12 @@ void cPath::BuildPath()
|
|||||||
cPathCell * CurrentCell = GetCell(m_Destination);
|
cPathCell * CurrentCell = GetCell(m_Destination);
|
||||||
while (CurrentCell->m_Parent != nullptr)
|
while (CurrentCell->m_Parent != nullptr)
|
||||||
{
|
{
|
||||||
|
// Waypoints are cylinders that start at some particular x, y, z and have infinite height.
|
||||||
|
// Submerging water waypoints allows swimming mobs to be able to touch them.
|
||||||
|
if (GetCell(CurrentCell->m_Location + Vector3i(0, -1, 0))->m_BlockType == E_BLOCK_STATIONARY_WATER)
|
||||||
|
{
|
||||||
|
CurrentCell->m_Location.y -= 30;
|
||||||
|
}
|
||||||
m_PathPoints.push_back(CurrentCell->m_Location); // Populate the cPath with points. All midpoints are added. Destination is added. Source is excluded.
|
m_PathPoints.push_back(CurrentCell->m_Location); // Populate the cPath with points. All midpoints are added. Destination is added. Source is excluded.
|
||||||
CurrentCell = CurrentCell->m_Parent;
|
CurrentCell = CurrentCell->m_Parent;
|
||||||
}
|
}
|
||||||
@ -418,7 +400,7 @@ cPathCell * cPath::OpenListPop() // Popping from the open list also means addin
|
|||||||
|
|
||||||
bool cPath::ProcessIfWalkable(const Vector3i & a_Location, cPathCell * a_Parent, int a_Cost)
|
bool cPath::ProcessIfWalkable(const Vector3i & a_Location, cPathCell * a_Parent, int a_Cost)
|
||||||
{
|
{
|
||||||
if (IsWalkable(a_Location))
|
if (IsWalkable(a_Location, a_Parent->m_Location))
|
||||||
{
|
{
|
||||||
ProcessCell(GetCell(a_Location), a_Parent, a_Cost);
|
ProcessCell(GetCell(a_Location), a_Parent, a_Cost);
|
||||||
return true;
|
return true;
|
||||||
@ -486,13 +468,72 @@ void cPath::ProcessCell(cPathCell * a_Cell, cPathCell * a_Caller, int a_GDelta)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cPath::FillCellAttributes(cPathCell & a_Cell)
|
||||||
|
{
|
||||||
|
const Vector3i & Location = a_Cell.m_Location;
|
||||||
|
|
||||||
|
ASSERT(m_Chunk != nullptr);
|
||||||
|
|
||||||
|
if (!cChunkDef::IsValidHeight(Location.y))
|
||||||
|
{
|
||||||
|
// Players can't build outside the game height, so it must be air
|
||||||
|
a_Cell.m_IsSolid = false;
|
||||||
|
a_Cell.m_IsSpecial = false;
|
||||||
|
a_Cell.m_BlockType = E_BLOCK_AIR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto Chunk = m_Chunk->GetNeighborChunk(Location.x, Location.z);
|
||||||
|
if ((Chunk == nullptr) || !Chunk->IsValid())
|
||||||
|
{
|
||||||
|
m_BadChunkFound = true;
|
||||||
|
a_Cell.m_IsSolid = true;
|
||||||
|
a_Cell.m_IsSpecial = false;
|
||||||
|
a_Cell.m_BlockType = E_BLOCK_AIR; // m_BlockType is never used when m_IsSpecial is false, but it may be used if we implement dijkstra
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_Chunk = Chunk;
|
||||||
|
|
||||||
|
BLOCKTYPE BlockType;
|
||||||
|
NIBBLETYPE BlockMeta;
|
||||||
|
int RelX = Location.x - m_Chunk->GetPosX() * cChunkDef::Width;
|
||||||
|
int RelZ = Location.z - m_Chunk->GetPosZ() * cChunkDef::Width;
|
||||||
|
|
||||||
|
m_Chunk->GetBlockTypeMeta(RelX, Location.y, RelZ, BlockType, BlockMeta);
|
||||||
|
a_Cell.m_BlockType = BlockType;
|
||||||
|
a_Cell.m_BlockMeta = BlockMeta;
|
||||||
|
|
||||||
|
|
||||||
|
if (BlockTypeIsSpecial(BlockType))
|
||||||
|
{
|
||||||
|
a_Cell.m_IsSpecial = true;
|
||||||
|
a_Cell.m_IsSolid = true; // Specials are solids only from a certain direction. But their m_IsSolid is always true
|
||||||
|
}
|
||||||
|
else if ((a_Cell.m_BlockType == E_BLOCK_AIR) && BlockTypeIsFence(GetCell(Location + Vector3i(0, -1, 0))->m_BlockType))
|
||||||
|
{
|
||||||
|
// Air blocks with fences below them are consider Special Solids. That is, they sometimes behave as solids.
|
||||||
|
a_Cell.m_IsSpecial = true;
|
||||||
|
a_Cell.m_IsSolid = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
a_Cell.m_IsSpecial = false;
|
||||||
|
a_Cell.m_IsSolid = cBlockInfo::IsSolid(BlockType);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cPathCell * cPath::GetCell(const Vector3i & a_Location)
|
cPathCell * cPath::GetCell(const Vector3i & a_Location)
|
||||||
{
|
{
|
||||||
// Create the cell in the hash table if it's not already there.
|
// Create the cell in the hash table if it's not already there.
|
||||||
if (m_Map.count(a_Location) == 0) // Case 1: Cell is not on any list. We've never checked this cell before.
|
if (m_Map.count(a_Location) == 0) // Case 1: Cell is not on any list. We've never checked this cell before.
|
||||||
{
|
{
|
||||||
m_Map[a_Location].m_Location = a_Location;
|
m_Map[a_Location].m_Location = a_Location;
|
||||||
m_Map[a_Location].m_IsSolid = IsSolid(a_Location);
|
FillCellAttributes(m_Map[a_Location]);
|
||||||
m_Map[a_Location].m_Status = eCellStatus::NOLIST;
|
m_Map[a_Location].m_Status = eCellStatus::NOLIST;
|
||||||
#ifdef COMPILING_PATHFIND_DEBUGGER
|
#ifdef COMPILING_PATHFIND_DEBUGGER
|
||||||
#ifdef COMPILING_PATHFIND_DEBUGGER_MARK_UNCHECKED
|
#ifdef COMPILING_PATHFIND_DEBUGGER_MARK_UNCHECKED
|
||||||
@ -511,16 +552,16 @@ cPathCell * cPath::GetCell(const Vector3i & a_Location)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool cPath::IsWalkable(const Vector3i & a_Location)
|
bool cPath::IsWalkable(const Vector3i & a_Location, const Vector3i & a_Source)
|
||||||
{
|
{
|
||||||
return (HasSolidBelow(a_Location) && BodyFitsIn(a_Location));
|
return (HasSolidBelow(a_Location) && BodyFitsIn(a_Location, a_Source));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// We need the source because some special blocks are solid only from a certain direction e.g. doors
|
||||||
bool cPath::BodyFitsIn(const Vector3i & a_Location)
|
bool cPath::BodyFitsIn(const Vector3i & a_Location, const Vector3i & a_Source)
|
||||||
{
|
{
|
||||||
int x, y, z;
|
int x, y, z;
|
||||||
for (y = 0; y < m_BoundingBoxHeight; ++y)
|
for (y = 0; y < m_BoundingBoxHeight; ++y)
|
||||||
@ -529,13 +570,109 @@ bool cPath::BodyFitsIn(const Vector3i & a_Location)
|
|||||||
{
|
{
|
||||||
for (z = 0; z < m_BoundingBoxWidth; ++z)
|
for (z = 0; z < m_BoundingBoxWidth; ++z)
|
||||||
{
|
{
|
||||||
if (GetCell(a_Location + Vector3i(x, y, z))->m_IsSolid)
|
cPathCell * CurrentCell = GetCell(a_Location + Vector3i(x, y, z));
|
||||||
|
if (CurrentCell->m_IsSolid)
|
||||||
|
{
|
||||||
|
if (CurrentCell->m_IsSpecial)
|
||||||
|
{
|
||||||
|
if (SpecialIsSolidFromThisDirection(CurrentCell->m_BlockType, CurrentCell->m_BlockMeta, a_Location - a_Source))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool cPath::BlockTypeIsSpecial(BLOCKTYPE a_Type)
|
||||||
|
{
|
||||||
|
if (BlockTypeIsFence(a_Type))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (a_Type)
|
||||||
|
{
|
||||||
|
case E_BLOCK_OAK_DOOR:
|
||||||
|
case E_BLOCK_DARK_OAK_DOOR:
|
||||||
|
case E_BLOCK_TRAPDOOR:
|
||||||
|
case E_BLOCK_WATER:
|
||||||
|
case E_BLOCK_STATIONARY_WATER:
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPath::BlockTypeIsFence(BLOCKTYPE a_Type)
|
||||||
|
{
|
||||||
|
switch (a_Type)
|
||||||
|
{
|
||||||
|
case E_BLOCK_FENCE:
|
||||||
|
case E_BLOCK_OAK_FENCE_GATE:
|
||||||
|
case E_BLOCK_NETHER_BRICK_FENCE:
|
||||||
|
case E_BLOCK_COBBLESTONE_WALL:
|
||||||
|
case E_BLOCK_DARK_OAK_FENCE:
|
||||||
|
case E_BLOCK_SPRUCE_FENCE_GATE:
|
||||||
|
case E_BLOCK_ACACIA_FENCE:
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool cPath::SpecialIsSolidFromThisDirection(BLOCKTYPE a_Type, NIBBLETYPE a_Meta, const Vector3i & a_Direction)
|
||||||
|
{
|
||||||
|
if (a_Direction == Vector3i(0, 0, 0))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
switch (a_Type)
|
||||||
|
{
|
||||||
|
// Air is special only when above a fence
|
||||||
|
case E_BLOCK_AIR:
|
||||||
|
{
|
||||||
|
// Treat the air block as solid if the mob is going upward and trying to climb a fence
|
||||||
|
if (a_Direction.y > 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Fill this with the other specials after physics is fixed
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ enum class ePathFinderStatus;
|
|||||||
class cPath;
|
class cPath;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "../FastRandom.h"
|
#include "../FastRandom.h"
|
||||||
#ifdef COMPILING_PATHFIND_DEBUGGER
|
#ifdef COMPILING_PATHFIND_DEBUGGER
|
||||||
/* Note: the COMPILING_PATHFIND_DEBUGGER flag is used by Native / WiseOldMan95 to debug
|
/* Note: the COMPILING_PATHFIND_DEBUGGER flag is used by Native / WiseOldMan95 to debug
|
||||||
@ -19,22 +20,40 @@ class cPath;
|
|||||||
//fwd: ../Chunk.h
|
//fwd: ../Chunk.h
|
||||||
class cChunk;
|
class cChunk;
|
||||||
|
|
||||||
|
|
||||||
/* Various little structs and classes */
|
/* Various little structs and classes */
|
||||||
enum class ePathFinderStatus {CALCULATING, PATH_FOUND, PATH_NOT_FOUND, NEARBY_FOUND};
|
enum class ePathFinderStatus {CALCULATING, PATH_FOUND, PATH_NOT_FOUND, NEARBY_FOUND};
|
||||||
enum class eCellStatus {OPENLIST, CLOSEDLIST, NOLIST};
|
enum class eCellStatus {OPENLIST, CLOSEDLIST, NOLIST};
|
||||||
|
/** 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. */
|
||||||
struct cPathCell
|
struct cPathCell
|
||||||
{
|
{
|
||||||
Vector3i m_Location; // Location of the cell in the world.
|
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*.
|
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.
|
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*.
|
cPathCell * m_Parent; // Cell's parent, as defined in regular A*.
|
||||||
bool m_IsSolid; // Is the cell an air or a solid? Partial solids are currently considered solids.
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class compareHeuristics
|
class compareHeuristics
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -48,32 +67,22 @@ public:
|
|||||||
class cPath
|
class cPath
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/** Creates a pathfinder instance. A Mob will probably need a single pathfinder instance for its entire life.
|
/** 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.
|
||||||
|
|
||||||
Note that if you have a man-sized mob (1x1x2, zombies, etc), you are advised to call this function without parameters
|
|
||||||
because the declaration might change in later version of the pathFinder, and a parameter-less call always assumes a man-sized mob.
|
|
||||||
|
|
||||||
If your mob is not man-sized, you are advised to use cPath(width, height), this would be compatible with future versions,
|
|
||||||
but please be aware that as of now those parameters will be ignored and your mob will be assumed to be man sized.
|
|
||||||
|
|
||||||
@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.
|
|
||||||
@param a_MaxUp the character's max jump height in blocks. Currently the parameter is ignored and 1 is assumed.
|
|
||||||
@param a_MaxDown How far is the character willing to fall? Currently the parameter is ignored and 1 is assumed. */
|
|
||||||
/** Attempts to find a path starting from source to destination.
|
|
||||||
After calling this, you are expected to call Step() once per tick or once per several ticks until it returns true. You should then call getPath() to obtain the path.
|
|
||||||
Calling this before a path is found resets the current path and starts another search.
|
|
||||||
@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_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".
|
@param a_EndingPoint "The block where the Zombie's knees want to be".
|
||||||
@param a_MaxSteps The maximum steps before giving up. */
|
@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. */
|
||||||
cPath(
|
cPath(
|
||||||
cChunk & a_Chunk,
|
cChunk & a_Chunk,
|
||||||
const Vector3d & a_StartingPoint, const Vector3d & a_EndingPoint, int a_MaxSteps,
|
const Vector3d & a_StartingPoint, const Vector3d & a_EndingPoint, int a_MaxSteps,
|
||||||
double a_BoundingBoxWidth, double a_BoundingBoxHeight,
|
double a_BoundingBoxWidth, double a_BoundingBoxHeight
|
||||||
int a_MaxUp = 1, int a_MaxDown = 1
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Creates a dummy path which does nothing except returning false when isValid is called. */
|
/** Creates an invalid path which is not usable. You shouldn't call any method other than isValid on such a path. */
|
||||||
cPath();
|
cPath();
|
||||||
|
|
||||||
/** delete default constructors */
|
/** delete default constructors */
|
||||||
@ -84,9 +93,11 @@ public:
|
|||||||
cPath & operator=(cPath && a_other) = delete;
|
cPath & operator=(cPath && a_other) = delete;
|
||||||
|
|
||||||
/** Performs part of the path calculation and returns the appropriate status.
|
/** Performs part of the path calculation and returns the appropriate status.
|
||||||
|
If PATH_FOUND is returned, the path was found, and you can call query the instance for waypoints via GetNextWayPoint, etc.
|
||||||
If NEARBY_FOUND is returned, it means that the destination is not reachable, but a nearby destination
|
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,
|
is reachable. If the user likes the alternative destination, they can call AcceptNearbyPath to treat the path as found,
|
||||||
and to make consequent calls to step return PATH_FOUND */
|
and to make consequent calls to step return PATH_FOUND
|
||||||
|
If PATH_NOT_FOUND is returned, then no path was found. */
|
||||||
ePathFinderStatus CalculationStep(cChunk & a_Chunk);
|
ePathFinderStatus CalculationStep(cChunk & a_Chunk);
|
||||||
|
|
||||||
/** Called after the PathFinder's step returns NEARBY_FOUND.
|
/** Called after the PathFinder's step returns NEARBY_FOUND.
|
||||||
@ -142,7 +153,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
/* General */
|
/* General */
|
||||||
bool IsSolid(const Vector3i & a_Location); // Query our hosting world and ask it if there's a solid at a_location.
|
|
||||||
bool StepOnce(); // The public version just calls this version * CALCULATIONS_PER_CALL times.
|
bool StepOnce(); // The public version just calls this version * CALCULATIONS_PER_CALL times.
|
||||||
void FinishCalculation(); // Clears the memory used for calculating the path.
|
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.
|
void FinishCalculation(ePathFinderStatus a_NewStatus); // Clears the memory used for calculating the path and changes the status.
|
||||||
@ -152,7 +162,7 @@ private:
|
|||||||
/* Openlist and closedlist management */
|
/* Openlist and closedlist management */
|
||||||
void OpenListAdd(cPathCell * a_Cell);
|
void OpenListAdd(cPathCell * a_Cell);
|
||||||
cPathCell * OpenListPop();
|
cPathCell * OpenListPop();
|
||||||
bool ProcessIfWalkable(const Vector3i &a_Location, cPathCell * a_Parent, int a_Cost);
|
bool ProcessIfWalkable(const Vector3i & a_Location, cPathCell * a_Source, int a_Cost);
|
||||||
|
|
||||||
/* Map management */
|
/* Map management */
|
||||||
void ProcessCell(cPathCell * a_Cell, cPathCell * a_Caller, int a_GDelta);
|
void ProcessCell(cPathCell * a_Cell, cPathCell * a_Caller, int a_GDelta);
|
||||||
@ -179,12 +189,16 @@ private:
|
|||||||
std::vector<Vector3i> m_PathPoints;
|
std::vector<Vector3i> m_PathPoints;
|
||||||
|
|
||||||
/* Interfacing with the world */
|
/* Interfacing with the world */
|
||||||
|
void FillCellAttributes(cPathCell & a_Cell); // Query our hosting world and fill the cell with info
|
||||||
cChunk * m_Chunk; // Only valid inside Step()!
|
cChunk * m_Chunk; // Only valid inside Step()!
|
||||||
bool m_BadChunkFound;
|
bool m_BadChunkFound;
|
||||||
|
|
||||||
/* High level world queries */
|
/* High level world queries */
|
||||||
bool IsWalkable(const Vector3i & a_Location);
|
bool IsWalkable(const Vector3i & a_Location, const Vector3i & a_Source);
|
||||||
bool BodyFitsIn(const Vector3i & a_Location);
|
bool BodyFitsIn(const Vector3i & a_Location, const Vector3i & a_Source);
|
||||||
|
bool BlockTypeIsSpecial(BLOCKTYPE a_Type);
|
||||||
|
bool BlockTypeIsFence(BLOCKTYPE a_Type); // TODO Perhaps this should be moved to cBlockInfo
|
||||||
|
bool SpecialIsSolidFromThisDirection(BLOCKTYPE a_Type, NIBBLETYPE a_Meta, const Vector3i & a_Direction);
|
||||||
bool HasSolidBelow(const Vector3i & a_Location);
|
bool HasSolidBelow(const Vector3i & a_Location);
|
||||||
#ifdef COMPILING_PATHFIND_DEBUGGER
|
#ifdef COMPILING_PATHFIND_DEBUGGER
|
||||||
#include "../path_irrlicht.cpp"
|
#include "../path_irrlicht.cpp"
|
||||||
|
@ -32,12 +32,20 @@ ePathFinderStatus cPathFinder::GetNextWayPoint(cChunk & a_Chunk, const Vector3d
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tweak the destination. If something is wrong with the destination or the chunk, rest for a while.
|
// Tweak the destination. If something is wrong with the destination or the chunk, rest for a while.
|
||||||
if (!EnsureProperDestination(a_Chunk))
|
if (!(EnsureProperPoint(m_FinalDestination, a_Chunk) && EnsureProperPoint(m_Source, a_Chunk)))
|
||||||
{
|
{
|
||||||
m_NotFoundCooldown = 20;
|
m_NotFoundCooldown = 20;
|
||||||
return ePathFinderStatus::PATH_NOT_FOUND;
|
return ePathFinderStatus::PATH_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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)); */
|
||||||
|
|
||||||
// Rest is over. Prepare m_Path by calling ResetPathFinding.
|
// Rest is over. Prepare m_Path by calling ResetPathFinding.
|
||||||
if (m_NotFoundCooldown == 0)
|
if (m_NotFoundCooldown == 0)
|
||||||
{
|
{
|
||||||
@ -83,7 +91,25 @@ ePathFinderStatus cPathFinder::GetNextWayPoint(cChunk & a_Chunk, const Vector3d
|
|||||||
{
|
{
|
||||||
m_GiveUpCounter -= 1;
|
m_GiveUpCounter -= 1;
|
||||||
|
|
||||||
if ((m_GiveUpCounter == 0) || PathIsTooOld())
|
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())
|
||||||
{
|
{
|
||||||
ResetPathFinding(a_Chunk);
|
ResetPathFinding(a_Chunk);
|
||||||
return ePathFinderStatus::CALCULATING;
|
return ePathFinderStatus::CALCULATING;
|
||||||
@ -153,9 +179,9 @@ void cPathFinder::ResetPathFinding(cChunk &a_Chunk)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool cPathFinder::EnsureProperDestination(cChunk & a_Chunk)
|
bool cPathFinder::EnsureProperPoint(Vector3d & a_Vector, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x), FloorC(m_FinalDestination.z));
|
cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Vector.x), FloorC(a_Vector.z));
|
||||||
BLOCKTYPE BlockType;
|
BLOCKTYPE BlockType;
|
||||||
NIBBLETYPE BlockMeta;
|
NIBBLETYPE BlockMeta;
|
||||||
|
|
||||||
@ -164,14 +190,14 @@ bool cPathFinder::EnsureProperDestination(cChunk & a_Chunk)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RelX = FloorC(m_FinalDestination.x) - Chunk->GetPosX() * cChunkDef::Width;
|
int RelX = FloorC(a_Vector.x) - Chunk->GetPosX() * cChunkDef::Width;
|
||||||
int RelZ = FloorC(m_FinalDestination.z) - Chunk->GetPosZ() * cChunkDef::Width;
|
int RelZ = FloorC(a_Vector.z) - Chunk->GetPosZ() * cChunkDef::Width;
|
||||||
|
|
||||||
// If destination in the air, first try to go 1 block north, or east, or west.
|
// If destination in the air, first try to go 1 block north, or east, or west.
|
||||||
// This fixes the player leaning issue.
|
// This fixes the player leaning issue.
|
||||||
// If that failed, we instead go down to the lowest air block.
|
// If that failed, we instead go down to the lowest air block.
|
||||||
Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
|
Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y) - 1, RelZ, BlockType, BlockMeta);
|
||||||
if (!cBlockInfo::IsSolid(BlockType))
|
if (!(IsWaterOrSolid(BlockType)))
|
||||||
{
|
{
|
||||||
bool InTheAir = true;
|
bool InTheAir = true;
|
||||||
int x, z;
|
int x, z;
|
||||||
@ -183,18 +209,18 @@ bool cPathFinder::EnsureProperDestination(cChunk & a_Chunk)
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x+x), FloorC(m_FinalDestination.z+z));
|
Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Vector.x+x), FloorC(a_Vector.z+z));
|
||||||
if ((Chunk == nullptr) || !Chunk->IsValid())
|
if ((Chunk == nullptr) || !Chunk->IsValid())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
RelX = FloorC(m_FinalDestination.x+x) - Chunk->GetPosX() * cChunkDef::Width;
|
RelX = FloorC(a_Vector.x+x) - Chunk->GetPosX() * cChunkDef::Width;
|
||||||
RelZ = FloorC(m_FinalDestination.z+z) - Chunk->GetPosZ() * cChunkDef::Width;
|
RelZ = FloorC(a_Vector.z+z) - Chunk->GetPosZ() * cChunkDef::Width;
|
||||||
Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
|
Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y) - 1, RelZ, BlockType, BlockMeta);
|
||||||
if (cBlockInfo::IsSolid(BlockType))
|
if (IsWaterOrSolid((BlockType)))
|
||||||
{
|
{
|
||||||
m_FinalDestination.x += x;
|
a_Vector.x += x;
|
||||||
m_FinalDestination.z += z;
|
a_Vector.z += z;
|
||||||
InTheAir = false;
|
InTheAir = false;
|
||||||
goto breakBothLoops;
|
goto breakBothLoops;
|
||||||
}
|
}
|
||||||
@ -205,43 +231,28 @@ bool cPathFinder::EnsureProperDestination(cChunk & a_Chunk)
|
|||||||
// Go down to the lowest air block.
|
// Go down to the lowest air block.
|
||||||
if (InTheAir)
|
if (InTheAir)
|
||||||
{
|
{
|
||||||
while (m_FinalDestination.y > 0)
|
while (a_Vector.y > 0)
|
||||||
{
|
{
|
||||||
Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
|
Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y) - 1, RelZ, BlockType, BlockMeta);
|
||||||
if (cBlockInfo::IsSolid(BlockType))
|
if (IsWaterOrSolid(BlockType))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
m_FinalDestination.y -= 1;
|
a_Vector.y -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If destination in water, go up to the highest water block.
|
// If destination in water or solid, go up to the first air block.
|
||||||
// If destination in solid, go up to first air block.
|
while (a_Vector.y < cChunkDef::Height)
|
||||||
bool InWater = false;
|
|
||||||
while (m_FinalDestination.y < cChunkDef::Height)
|
|
||||||
{
|
{
|
||||||
Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y), RelZ, BlockType, BlockMeta);
|
Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y), RelZ, BlockType, BlockMeta);
|
||||||
if (BlockType == E_BLOCK_STATIONARY_WATER)
|
if (!IsWaterOrSolid(BlockType))
|
||||||
{
|
|
||||||
InWater = true;
|
|
||||||
}
|
|
||||||
else if (cBlockInfo::IsSolid(BlockType))
|
|
||||||
{
|
|
||||||
InWater = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
m_FinalDestination.y += 1;
|
a_Vector.y += 1;
|
||||||
}
|
}
|
||||||
if (InWater)
|
|
||||||
{
|
|
||||||
m_FinalDestination.y -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -250,6 +261,15 @@ bool cPathFinder::EnsureProperDestination(cChunk & a_Chunk)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool cPathFinder::IsWaterOrSolid(BLOCKTYPE a_BlockType)
|
||||||
|
{
|
||||||
|
return ((a_BlockType == E_BLOCK_STATIONARY_WATER) || cBlockInfo::IsSolid(a_BlockType));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool cPathFinder::PathIsTooOld() const
|
bool cPathFinder::PathIsTooOld() const
|
||||||
{
|
{
|
||||||
size_t acceptableDeviation = m_Path->WayPointsLeft() / 2;
|
size_t acceptableDeviation = m_Path->WayPointsLeft() / 2;
|
||||||
|
@ -3,16 +3,11 @@
|
|||||||
#include "Path.h"
|
#include "Path.h"
|
||||||
|
|
||||||
#define WAYPOINT_RADIUS 0.5
|
#define WAYPOINT_RADIUS 0.5
|
||||||
/*
|
|
||||||
TODO DOXY style
|
|
||||||
|
|
||||||
This class wraps cPath.
|
/** This class wraps cPath.
|
||||||
cPath is a "dumb device" - You give it point A and point B, and it returns a full path path.
|
cPath is a "dumb device" - You give it point A and point B, and it returns a full path path.
|
||||||
cPathFinder - You give it a constant stream of point A (where you are) and point B (where you want to go),
|
cPathFinder - You give it a constant stream of point A (where you are) and point B (where you want to go),
|
||||||
and it tells you where to go next. It manages path recalculation internally, and is much more efficient that calling cPath every step.
|
and it tells you where to go next. It manages path recalculation internally, and is much more efficient that calling cPath every step. */
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class cPathFinder
|
class cPathFinder
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -80,17 +75,18 @@ private:
|
|||||||
/** When a path is not found, this cooldown prevents any recalculations for several ticks. */
|
/** When a path is not found, this cooldown prevents any recalculations for several ticks. */
|
||||||
int m_NotFoundCooldown;
|
int m_NotFoundCooldown;
|
||||||
|
|
||||||
/** Ensures the destination is not buried underground or under water. Also ensures the destination is not in the air.
|
/** Ensures the location is not in the air or under water.
|
||||||
Only the Y coordinate of m_FinalDestination might be changed by this call.
|
May change the Y coordinate of the given vector.
|
||||||
1. If m_FinalDestination is the position of a water block, m_FinalDestination's Y will be modified to point to the heighest water block in the pool in the current column.
|
1. If a_Vector is the position of water, a_Vector's Y will be modified to point to the first air block above it.
|
||||||
2. If m_FinalDestination is the position of a solid, m_FinalDestination's Y will be modified to point to the first airblock above the solid in the current column.
|
2. If a_Vector is the position of air, a_Vector's Y will be modified to point to the first airblock below it which has solid or water beneath. */
|
||||||
3. If m_FinalDestination is the position of an air block, Y will keep decreasing until hitting either a solid or water.
|
bool EnsureProperPoint(Vector3d & a_Vector, cChunk & a_Chunk);
|
||||||
Now either 1 or 2 is performed. */
|
|
||||||
bool EnsureProperDestination(cChunk & a_Chunk);
|
|
||||||
|
|
||||||
/** Resets a pathfinding task, typically because m_FinalDestination has deviated too much from m_DeviationOrigin. */
|
/** Resets a pathfinding task, typically because m_FinalDestination has deviated too much from m_DeviationOrigin. */
|
||||||
void ResetPathFinding(cChunk &a_Chunk);
|
void ResetPathFinding(cChunk &a_Chunk);
|
||||||
|
|
||||||
|
/** Return true the the blocktype is either water or solid */
|
||||||
|
bool IsWaterOrSolid(BLOCKTYPE a_BlockType);
|
||||||
|
|
||||||
/** Is the path too old and should be recalculated? When this is true ResetPathFinding() is called. */
|
/** Is the path too old and should be recalculated? When this is true ResetPathFinding() is called. */
|
||||||
bool PathIsTooOld() const;
|
bool PathIsTooOld() const;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user