diff options
Diffstat (limited to 'src/Mobs/Monster.cpp')
-rw-r--r-- | src/Mobs/Monster.cpp | 354 |
1 files changed, 82 insertions, 272 deletions
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index d1173c41c..3f7153fb3 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -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) +void cMonster::MoveToWayPoint(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()) + if ((m_NextWayPointPosition - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS) { - 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; } - return false; -} - - - - -void cMonster::MoveToWayPoint(cChunk & a_Chunk) -{ 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) - ) - { - // 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 + 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)) { - 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 } |