Decoupled cMonster and path recalc logic, re-implemented recalc
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
#include "../Chunk.h"
|
||||
#include "../FastRandom.h"
|
||||
|
||||
#include "Path.h"
|
||||
#include "PathFinder.h"
|
||||
|
||||
|
||||
|
||||
@@ -75,11 +75,8 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
|
||||
, m_EMState(IDLE)
|
||||
, m_EMPersonality(AGGRESSIVE)
|
||||
, m_Target(nullptr)
|
||||
, m_Path(nullptr)
|
||||
, m_IsFollowingPath(false)
|
||||
, m_PathFinder(a_Width, a_Height)
|
||||
, m_PathfinderActivated(false)
|
||||
, m_GiveUpCounter(0)
|
||||
, m_TicksSinceLastPathReset(1000)
|
||||
, m_LastGroundHeight(POSY_TOINT)
|
||||
, m_JumpCoolDown(0)
|
||||
, m_IdleInterval(0)
|
||||
@@ -125,127 +122,20 @@ void cMonster::SpawnOn(cClientHandle & a_Client)
|
||||
|
||||
|
||||
|
||||
bool cMonster::TickPathFinding(cChunk & a_Chunk)
|
||||
{
|
||||
if (!m_PathfinderActivated)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_TicksSinceLastPathReset < 1000)
|
||||
{
|
||||
// No need to count beyond 1000. 1000 is arbitary here.
|
||||
++m_TicksSinceLastPathReset;
|
||||
}
|
||||
|
||||
if (ReachedFinalDestination())
|
||||
{
|
||||
StopMovingToPosition();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m_FinalDestination - m_PathFinderDestination).Length() > 0.25) // if the distance between where we're going and where we should go is too big.
|
||||
{
|
||||
/* If we reached the last path waypoint,
|
||||
Or if we haven't re-calculated for too long.
|
||||
Interval is proportional to distance squared, and its minimum is 10.
|
||||
(Recalculate lots when close, calculate rarely when far) */
|
||||
if (
|
||||
((GetPosition() - m_PathFinderDestination).Length() < 0.25) ||
|
||||
((m_TicksSinceLastPathReset > 10) && (m_TicksSinceLastPathReset > (0.4 * (m_FinalDestination - GetPosition()).SqrLength())))
|
||||
)
|
||||
{
|
||||
/* Re-calculating is expensive when there's no path to target, and it results in mobs freezing very often as a result of always recalculating.
|
||||
This is a workaround till we get better path recalculation. */
|
||||
if (!m_NoPathToTarget)
|
||||
{
|
||||
ResetPathFinding();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Path == nullptr)
|
||||
{
|
||||
if (!EnsureProperDestination(a_Chunk))
|
||||
{
|
||||
StopMovingToPosition(); // Invalid chunks, probably world is loading or something, cancel movement.
|
||||
return false;
|
||||
}
|
||||
m_GiveUpCounter = 40;
|
||||
m_NoPathToTarget = false;
|
||||
m_NoMoreWayPoints = false;
|
||||
m_PathFinderDestination = m_FinalDestination;
|
||||
m_Path = new cPath(a_Chunk, GetPosition(), m_PathFinderDestination, 20, GetWidth(), GetHeight());
|
||||
}
|
||||
|
||||
switch (m_Path->Step(a_Chunk))
|
||||
{
|
||||
case ePathFinderStatus::NEARBY_FOUND:
|
||||
{
|
||||
m_NoPathToTarget = true;
|
||||
m_PathFinderDestination = m_Path->AcceptNearbyPath();
|
||||
break;
|
||||
}
|
||||
|
||||
case ePathFinderStatus::PATH_NOT_FOUND:
|
||||
{
|
||||
StopMovingToPosition(); // Try to calculate a path again.
|
||||
// Note that the next time may succeed, e.g. if a player breaks a barrier.
|
||||
break;
|
||||
}
|
||||
case ePathFinderStatus::CALCULATING:
|
||||
{
|
||||
// Pathfinder needs more time
|
||||
break;
|
||||
}
|
||||
case ePathFinderStatus::PATH_FOUND:
|
||||
{
|
||||
if (m_NoMoreWayPoints || (--m_GiveUpCounter == 0))
|
||||
{
|
||||
if (m_EMState == ATTACKING)
|
||||
{
|
||||
ResetPathFinding(); // Try to calculate a path again.
|
||||
// This results in mobs hanging around an unreachable target (player).
|
||||
}
|
||||
else
|
||||
{
|
||||
StopMovingToPosition(); // Find a different place to go to.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (!m_Path->IsLastPoint()) // Have we arrived at the next cell, as denoted by m_NextWayPointPosition?
|
||||
{
|
||||
if ((m_Path->IsFirstPoint() || ReachedNextWaypoint()))
|
||||
{
|
||||
m_NextWayPointPosition = m_Path->GetNextPoint();
|
||||
m_GiveUpCounter = 40; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NoMoreWayPoints = true;
|
||||
}
|
||||
|
||||
m_IsFollowingPath = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cMonster::MoveToWayPoint(cChunk & a_Chunk)
|
||||
{
|
||||
if ((m_NextWayPointPosition - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (m_JumpCoolDown == 0)
|
||||
{
|
||||
if (DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y)))
|
||||
{
|
||||
if (
|
||||
(IsOnGround() && (GetSpeedX() == 0.0f) && (GetSpeedY() == 0.0f)) ||
|
||||
(IsSwimming() && (m_GiveUpCounter < 15))
|
||||
(IsOnGround() && (GetSpeedX() == 0.0f) && (GetSpeedY() == 0.0f)) // TODO water handling?
|
||||
)
|
||||
{
|
||||
m_bOnGround = false;
|
||||
@@ -296,98 +186,7 @@ void cMonster::MoveToWayPoint(cChunk & a_Chunk)
|
||||
|
||||
|
||||
|
||||
bool cMonster::EnsureProperDestination(cChunk & a_Chunk)
|
||||
{
|
||||
cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x), FloorC(m_FinalDestination.z));
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
|
||||
if ((Chunk == nullptr) || !Chunk->IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int RelX = FloorC(m_FinalDestination.x) - Chunk->GetPosX() * cChunkDef::Width;
|
||||
int RelZ = FloorC(m_FinalDestination.z) - Chunk->GetPosZ() * cChunkDef::Width;
|
||||
|
||||
// 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.
|
||||
Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
|
||||
if (!cBlockInfo::IsSolid(BlockType))
|
||||
{
|
||||
bool InTheAir = true;
|
||||
int x, z;
|
||||
for (z = -1; z <= 1; ++z)
|
||||
{
|
||||
for (x = -1; x <= 1; ++x)
|
||||
{
|
||||
if ((x == 0) && (z == 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x+x), FloorC(m_FinalDestination.z+z));
|
||||
if ((Chunk == nullptr) || !Chunk->IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
RelX = FloorC(m_FinalDestination.x + x) - Chunk->GetPosX() * cChunkDef::Width;
|
||||
RelZ = FloorC(m_FinalDestination.z + z) - Chunk->GetPosZ() * cChunkDef::Width;
|
||||
Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
|
||||
if (cBlockInfo::IsSolid(BlockType))
|
||||
{
|
||||
m_FinalDestination.x += x;
|
||||
m_FinalDestination.z += z;
|
||||
InTheAir = false;
|
||||
goto breakBothLoops;
|
||||
}
|
||||
}
|
||||
}
|
||||
breakBothLoops:
|
||||
|
||||
// Go down to the lowest air block.
|
||||
if (InTheAir)
|
||||
{
|
||||
while (m_FinalDestination.y > 0)
|
||||
{
|
||||
Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
|
||||
if (cBlockInfo::IsSolid(BlockType))
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_FinalDestination.y -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If destination in water, go up to the highest water block.
|
||||
// If destination in solid, go up to first air block.
|
||||
bool InWater = false;
|
||||
while (m_FinalDestination.y < cChunkDef::Height)
|
||||
{
|
||||
Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y), RelZ, BlockType, BlockMeta);
|
||||
if (BlockType == E_BLOCK_STATIONARY_WATER)
|
||||
{
|
||||
InWater = true;
|
||||
}
|
||||
else if (cBlockInfo::IsSolid(BlockType))
|
||||
{
|
||||
InWater = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_FinalDestination.y += 1;
|
||||
}
|
||||
if (InWater)
|
||||
{
|
||||
m_FinalDestination.y -= 1;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -406,22 +205,6 @@ void cMonster::MoveToPosition(const Vector3d & a_Position)
|
||||
void cMonster::StopMovingToPosition()
|
||||
{
|
||||
m_PathfinderActivated = false;
|
||||
ResetPathFinding();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cMonster::ResetPathFinding(void)
|
||||
{
|
||||
m_TicksSinceLastPathReset = 0;
|
||||
m_IsFollowingPath = false;
|
||||
if (m_Path != nullptr)
|
||||
{
|
||||
delete m_Path;
|
||||
m_Path = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -435,7 +218,7 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||
|
||||
if (m_Health <= 0)
|
||||
{
|
||||
// The mob is dead, but we're still animating the "puff" they leave when they die.
|
||||
// The mob is dead, but we're still animating the "puff" they leave when they die
|
||||
m_DestroyTimer += a_Dt;
|
||||
if (m_DestroyTimer > std::chrono::seconds(1))
|
||||
{
|
||||
@@ -453,34 +236,57 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||
m_Target = nullptr;
|
||||
}
|
||||
|
||||
if (GetPosY() >= 0)
|
||||
// Process the undead burning in daylight.
|
||||
HandleDaylightBurning(*Chunk, WouldBurnAt(GetPosition(), *Chunk));
|
||||
|
||||
bool a_IsFollowingPath = false;
|
||||
if (m_PathfinderActivated)
|
||||
{
|
||||
// Process the undead burning in daylight.
|
||||
HandleDaylightBurning(*Chunk, WouldBurnAt(GetPosition(), *Chunk));
|
||||
if (TickPathFinding(*Chunk))
|
||||
if (ReachedFinalDestination())
|
||||
{
|
||||
/* If I burn in daylight, and I won't burn where I'm standing, and I'll burn in my next position, and at least one of those is true:
|
||||
1. I am idle
|
||||
2. I was not hurt by a player recently.
|
||||
Then STOP. */
|
||||
if (
|
||||
m_BurnsInDaylight && ((m_TicksSinceLastDamaged >= 100) || (m_EMState == IDLE)) &&
|
||||
WouldBurnAt(m_NextWayPointPosition, *Chunk) &&
|
||||
!WouldBurnAt(GetPosition(), *Chunk)
|
||||
)
|
||||
StopMovingToPosition(); // Simply sets m_PathfinderActivated to false.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Note that m_NextWayPointPosition is actually returned by GetNextWayPoint)
|
||||
switch (m_PathFinder.GetNextWayPoint(*Chunk, GetPosition(), &m_FinalDestination, &m_NextWayPointPosition, m_EMState == IDLE ? true : false))
|
||||
{
|
||||
// If we burn in daylight, and we would burn at the next step, and we won't burn where we are right now, and we weren't provoked recently:
|
||||
StopMovingToPosition();
|
||||
m_GiveUpCounter = 40; // This doesn't count as giving up, keep the giveup timer as is.
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveToWayPoint(*Chunk);
|
||||
case ePathFinderStatus::PATH_FOUND:
|
||||
{
|
||||
/* If I burn in daylight, and I won't burn where I'm standing, and I'll burn in my next position, and at least one of those is true:
|
||||
1. I am idle
|
||||
2. I was not hurt by a player recently.
|
||||
Then STOP. */
|
||||
if (
|
||||
m_BurnsInDaylight && ((m_TicksSinceLastDamaged >= 100) || (m_EMState == IDLE)) &&
|
||||
WouldBurnAt(m_NextWayPointPosition, *Chunk) &&
|
||||
!WouldBurnAt(GetPosition(), *Chunk)
|
||||
)
|
||||
{
|
||||
// If we burn in daylight, and we would burn at the next step, and we won't burn where we are right now, and we weren't provoked recently:
|
||||
StopMovingToPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
a_IsFollowingPath = true; // Used for proper body / head orientation only.
|
||||
MoveToWayPoint(*Chunk);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ePathFinderStatus::PATH_NOT_FOUND:
|
||||
{
|
||||
StopMovingToPosition();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetPitchAndYawFromDestination();
|
||||
SetPitchAndYawFromDestination(a_IsFollowingPath);
|
||||
HandleFalling();
|
||||
|
||||
switch (m_EMState)
|
||||
@@ -522,24 +328,11 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||
|
||||
|
||||
|
||||
void cMonster::SetPitchAndYawFromDestination()
|
||||
void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath)
|
||||
{
|
||||
Vector3d FinalDestination = m_FinalDestination;
|
||||
if (m_Target != nullptr)
|
||||
{
|
||||
if (m_Target->IsPlayer())
|
||||
{
|
||||
FinalDestination.y = static_cast<cPlayer *>(m_Target)->GetStance() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
FinalDestination.y = m_Target->GetPosY() + GetHeight();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Todo Buggy */
|
||||
Vector3d BodyDistance;
|
||||
if (!m_IsFollowingPath && (m_Target != nullptr))
|
||||
if (!a_IsFollowingPath && (m_Target != nullptr))
|
||||
{
|
||||
BodyDistance = m_Target->GetPosition() - GetPosition();
|
||||
}
|
||||
@@ -552,22 +345,39 @@ void cMonster::SetPitchAndYawFromDestination()
|
||||
VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, BodyRotation, BodyPitch);
|
||||
SetYaw(BodyRotation);
|
||||
|
||||
Vector3d Distance = FinalDestination - GetPosition();
|
||||
Vector3d HeadDistance;
|
||||
if (m_Target != nullptr)
|
||||
{
|
||||
double HeadRotation, HeadPitch;
|
||||
Distance.Normalize();
|
||||
VectorToEuler(Distance.x, Distance.y, Distance.z, HeadRotation, HeadPitch);
|
||||
if (std::abs(BodyRotation - HeadRotation) < 90)
|
||||
if (m_Target->IsPlayer()) // Look at a player
|
||||
{
|
||||
SetHeadYaw(HeadRotation);
|
||||
SetPitch(-HeadPitch);
|
||||
HeadDistance = m_Target->GetPosition() - GetPosition();
|
||||
// HeadDistance.y = static_cast<cPlayer *>(m_Target)->GetStance() - 1;
|
||||
}
|
||||
else // We're not an owl. If it's more than 120, don't look behind and instead look at where you're walking.
|
||||
else // Look at some other entity
|
||||
{
|
||||
SetHeadYaw(BodyRotation);
|
||||
SetPitch(-BodyPitch);
|
||||
HeadDistance = m_Target->GetPosition() - GetPosition();
|
||||
// HeadDistance.y = m_Target->GetPosY() + GetHeight();
|
||||
}
|
||||
}
|
||||
else // Look straight
|
||||
{
|
||||
HeadDistance = BodyDistance;
|
||||
HeadDistance.y = 0;
|
||||
}
|
||||
|
||||
double HeadRotation, HeadPitch;
|
||||
HeadDistance.Normalize();
|
||||
VectorToEuler(HeadDistance.x, HeadDistance.y, HeadDistance.z, HeadRotation, HeadPitch);
|
||||
if (std::abs(BodyRotation - HeadRotation) < 90)
|
||||
{
|
||||
SetHeadYaw(HeadRotation);
|
||||
SetPitch(-HeadPitch);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetHeadYaw(BodyRotation);
|
||||
SetPitch(-BodyPitch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -806,7 +616,7 @@ void cMonster::EventLosePlayer(void)
|
||||
|
||||
void cMonster::InStateIdle(std::chrono::milliseconds a_Dt)
|
||||
{
|
||||
if (m_IsFollowingPath)
|
||||
if (m_PathfinderActivated)
|
||||
{
|
||||
return; // Still getting there
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user