From b64df2ad5f119c2d8d9abb5760c2cf8fcf57ea98 Mon Sep 17 00:00:00 2001 From: Samuel Barney Date: Tue, 26 Aug 2014 11:59:09 -0600 Subject: Reworked Component and Monster classes to mimic the old code. --- src/Mobs/Components/AIAggressiveComponent.cpp | 257 +----------------- src/Mobs/Components/AIAggressiveComponent.h | 39 --- src/Mobs/Components/AIComponent.cpp | 364 +++++++++++++++++++++++++- src/Mobs/Components/AIComponent.h | 47 +++- src/Mobs/Components/EnvironmentComponent.cpp | 55 +++- src/Mobs/Components/EnvironmentComponent.h | 14 +- src/Mobs/Components/MovementComponent.cpp | 17 ++ src/Mobs/Components/MovementComponent.h | 4 + src/Mobs/Monster.cpp | 26 +- src/Mobs/Monster.h | 6 + 10 files changed, 528 insertions(+), 301 deletions(-) diff --git a/src/Mobs/Components/AIAggressiveComponent.cpp b/src/Mobs/Components/AIAggressiveComponent.cpp index a1b22af59..a89d157fe 100644 --- a/src/Mobs/Components/AIAggressiveComponent.cpp +++ b/src/Mobs/Components/AIAggressiveComponent.cpp @@ -3,19 +3,11 @@ #include "AIAggressiveComponent.h" #include -#include "../Monster.h" -#include "../../World.h" -#include "../../Entities/Player.h" -#include "../../Tracer.h" - - -cAIAggressiveComponent::cAIAggressiveComponent(cMonster * a_Monster) : cAIComponent(a_Monster), m_Target(NULL){ - m_EMPersonality = AGGRESSIVE; -} +cAIAggressiveComponent::cAIAggressiveComponent(cMonster * a_Monster) : cAIComponent(a_Monster){} @@ -23,250 +15,5 @@ cAIAggressiveComponent::cAIAggressiveComponent(cMonster * a_Monster) : cAICompon void cAIAggressiveComponent::Tick(float a_Dt, cChunk & a_Chunk) { - if (m_EMState == CHASING) - { - CheckEventLostPlayer(); - } - else - { - CheckEventSeePlayer(); - } - - if (m_Target == NULL) - return; - - cTracer LineOfSight(m_Self->GetWorld()); - Vector3d AttackDirection(m_Target->GetPosition() - m_Self->GetPosition()); - - if (ReachedFinalDestination() && !LineOfSight.Trace(m_Self->GetPosition(), AttackDirection, (int)AttackDirection.Length())) - { - // Attack if reached destination, target isn't null, and have a clear line of sight to target (so won't attack through walls) - //Attack(a_Dt / 1000); - } -} - - - - - -void cAIAggressiveComponent::Attack(float a_Dt) -{ -} - - - - - -void cAIAggressiveComponent::EventSeePlayer(cEntity * a_Entity) -{ - if (!((cPlayer *)a_Entity)->IsGameModeCreative()) - { - m_Target = a_Entity; - m_EMState = CHASING; - } -} - - - - -// What to do if in Chasing State -void cAIAggressiveComponent::InStateChasing(float a_Dt) -{ - if (m_Target != NULL) - { - if (m_Target->IsPlayer()) - { - if (((cPlayer *)m_Target)->IsGameModeCreative()) - { - m_EMState = IDLE; - return; - } - } - - if (!IsMovingToTargetPosition()) - { - MoveToPosition(m_Target->GetPosition()); - } - } -} - - - - -bool cAIAggressiveComponent::ReachedFinalDestination() -{ - if ((m_Self->GetPosition() - m_FinalDestination).Length() <= m_Self->GetAttackComponent().GetAttackRange()) - { - return true; - } - - return false; -} - - - - - -void cAIAggressiveComponent::MoveToPosition(const Vector3d & a_Position) -{ - FinishPathFinding(); - - m_FinalDestination = a_Position; - m_bMovingToDestination = true; - TickPathFinding(); -} - - - - - -void cAIAggressiveComponent::TickPathFinding() -{ - const int PosX = (int)floor(m_Self->GetPosX()); - const int PosY = (int)floor(m_Self->GetPosY()); - const int PosZ = (int)floor(m_Self->GetPosZ()); - - std::vector m_PotentialCoordinates; - m_TraversedCoordinates.push_back(Vector3i(PosX, PosY, PosZ)); - - static const struct // Define which directions to try to move to - { - int x, z; - } gCrossCoords[] = - { - { 1, 0}, - {-1, 0}, - { 0, 1}, - { 0, -1}, - } ; - - if ((PosY - 1 < 0) || (PosY + 2 > cChunkDef::Height) /* PosY + 1 will never be true if PosY + 2 is not */) - { - // Too low/high, can't really do anything - FinishPathFinding(); - return; - } - - for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) - { - if (IsCoordinateInTraversedList(Vector3i(gCrossCoords[i].x + PosX, PosY, gCrossCoords[i].z + PosZ))) - { - continue; - } - - BLOCKTYPE BlockAtY = m_Self->GetWorld()->GetBlock(gCrossCoords[i].x + PosX, PosY, gCrossCoords[i].z + PosZ); - BLOCKTYPE BlockAtYP = m_Self->GetWorld()->GetBlock(gCrossCoords[i].x + PosX, PosY + 1, gCrossCoords[i].z + PosZ); - BLOCKTYPE BlockAtYPP = m_Self->GetWorld()->GetBlock(gCrossCoords[i].x + PosX, PosY + 2, gCrossCoords[i].z + PosZ); - int LowestY = m_Self->GetMovementComponent().FindFirstNonAirBlockPosition(gCrossCoords[i].x + PosX, gCrossCoords[i].z + PosZ); - BLOCKTYPE BlockAtLowestY = m_Self->GetWorld()->GetBlock(gCrossCoords[i].x + PosX, LowestY, gCrossCoords[i].z + PosZ); - - if ( - (!cBlockInfo::IsSolid(BlockAtY)) && - (!cBlockInfo::IsSolid(BlockAtYP)) && - (!IsBlockLava(BlockAtLowestY)) && - (BlockAtLowestY != E_BLOCK_CACTUS) && - (PosY - LowestY < 4) - ) - { - m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY, gCrossCoords[i].z + PosZ)); - } - else if ( - (cBlockInfo::IsSolid(BlockAtY)) && - (BlockAtY != E_BLOCK_CACTUS) && - (!cBlockInfo::IsSolid(BlockAtYP)) && - (!cBlockInfo::IsSolid(BlockAtYPP)) && - (BlockAtY != E_BLOCK_FENCE) && - (BlockAtY != E_BLOCK_FENCE_GATE) - ) - { - m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY + 1, gCrossCoords[i].z + PosZ)); - } - } - - if (!m_PotentialCoordinates.empty()) - { - Vector3f ShortestCoords = m_PotentialCoordinates.front(); - for (std::vector::const_iterator itr = m_PotentialCoordinates.begin(); itr != m_PotentialCoordinates.end(); ++itr) - { - Vector3f Distance = m_FinalDestination - ShortestCoords; - Vector3f Distance2 = m_FinalDestination - *itr; - if (Distance.SqrLength() > Distance2.SqrLength()) - { - ShortestCoords = *itr; - } - } - - m_Destination = ShortestCoords; - m_Destination.z += 0.5f; - m_Destination.x += 0.5f; - } - else - { - FinishPathFinding(); - } -} - - - - - -bool cAIAggressiveComponent::IsMovingToTargetPosition() -{ - // Difference between destination x and target x is negligible (to 10^-12 precision) - if (fabsf((float)m_FinalDestination.x - (float)m_Target->GetPosX()) < std::numeric_limits::epsilon()) - { - return false; - } - // Difference between destination z and target z is negligible (to 10^-12 precision) - else if (fabsf((float)m_FinalDestination.z - (float)m_Target->GetPosZ()) > std::numeric_limits::epsilon()) - { - return false; - } - return true; -} - - - - - -// Checks to see if EventSeePlayer should be fired -// monster sez: Do I see the player -void cAIAggressiveComponent::CheckEventSeePlayer(void) -{ - // TODO: Rewrite this to use cWorld's DoWithPlayers() - cPlayer * Closest = m_Self->GetWorld()->FindClosestPlayer(m_Self->GetPosition(), (float)m_Self->GetEnvironmentComponent().GetSightDistance(), false); - - if (Closest != NULL) - { - EventSeePlayer(Closest); - } -} - - - - - -void cAIAggressiveComponent::CheckEventLostPlayer(void) -{ - if (m_Target != NULL) - { - if ((m_Target->GetPosition() - m_Self->GetPosition()).Length() > m_Self->GetEnvironmentComponent().GetSightDistance()) - { - EventLosePlayer(); - } - } - else - { - EventLosePlayer(); - } -} - - - - - -void cAIAggressiveComponent::EventLosePlayer(void) -{ - m_Target = NULL; - m_EMState = IDLE; + super::Tick(a_Dt, a_Chunk); } diff --git a/src/Mobs/Components/AIAggressiveComponent.h b/src/Mobs/Components/AIAggressiveComponent.h index 373861ffd..2cea452c5 100644 --- a/src/Mobs/Components/AIAggressiveComponent.h +++ b/src/Mobs/Components/AIAggressiveComponent.h @@ -5,47 +5,8 @@ class cEntity; class cAIAggressiveComponent : public cAIComponent { typedef cAIComponent super; - void EventSeePlayer(cEntity * a_Entity); - void EventLosePlayer(void); protected: - void InStateChasing(float a_Dt); - - enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState; - enum MPersonality{PASSIVE, AGGRESSIVE, COWARDLY} m_EMPersonality; - - cEntity * m_Target; - /** Coordinates of the next position that should be reached */ - Vector3d m_Destination; - /** Coordinates for the ultimate, final destination. */ - Vector3d m_FinalDestination; - /** A semi-temporary list to store the traversed coordinates during active pathfinding so we don't visit them again */ - std::vector m_TraversedCoordinates; - - /** Stores if mobile is currently moving towards the ultimate, final destination */ - bool m_bMovingToDestination; - - inline void FinishPathFinding(void) - { - m_TraversedCoordinates.clear(); - m_bMovingToDestination = false; - } - /** Finds the next place to go - This is based on the ultimate, final destination and the current position, as well as the traversed coordinates, and any environmental hazards */ - void TickPathFinding(void); - - inline bool IsCoordinateInTraversedList(Vector3i a_Coords) - { - return (std::find(m_TraversedCoordinates.begin(), m_TraversedCoordinates.end(), a_Coords) != m_TraversedCoordinates.end()); - } - bool IsMovingToTargetPosition(); - bool ReachedFinalDestination(); - void CheckEventSeePlayer(void); - void CheckEventLostPlayer(void); public: cAIAggressiveComponent(cMonster * a_Monster); - virtual void Tick(float a_Dt, cChunk & a_Chunk) /*override*/; - - virtual void Attack(float a_Dt); - void MoveToPosition(const Vector3d & a_Position); }; diff --git a/src/Mobs/Components/AIComponent.cpp b/src/Mobs/Components/AIComponent.cpp index b747bffd8..1c33dcaca 100644 --- a/src/Mobs/Components/AIComponent.cpp +++ b/src/Mobs/Components/AIComponent.cpp @@ -2,4 +2,366 @@ #include "AIComponent.h" #include "../Monster.h" -cAIComponent::cAIComponent(cMonster * a_Entity) : m_Self(a_Entity){} +#include "../../World.h" +#include "../../Entities/Player.h" +#include "../../Tracer.h" + +cAIComponent::cAIComponent(cMonster * a_Entity) : m_Self(a_Entity), m_Target(NULL), m_IdleInterval(0.0f), m_EMState(IDLE), m_bMovingToDestination(false) +{ + +} + +void cAIComponent::Tick(float a_Dt, cChunk & a_Chunk) +{ + if ((m_Target != NULL) && m_Target->IsDestroyed()) + m_Target = NULL; + + + a_Dt /= 1000; + + if (m_bMovingToDestination) + { + if (m_Self->GetEnvironmentComponent().GetOnGround() && m_Self->GetMovementComponent().DoesPosYRequireJump((int)floor(m_Destination.y))) + { + m_Self->GetEnvironmentComponent().SetOnGround(false); + + // TODO: Change to AddSpeedY once collision detection is fixed - currently, mobs will go into blocks attempting to jump without a teleport + m_Self->AddPosY(1.2); // Jump!! + } + + Vector3f Distance = m_Destination - m_Self->GetPosition(); + if (!ReachedDestination() && !ReachedFinalDestination()) // If we haven't reached any sort of destination, move + { + Distance.y = 0; + Distance.Normalize(); + + if (m_Self->GetEnvironmentComponent().GetOnGround()) + { + Distance *= 2.5f; + } + else if (m_Self->IsSwimming()) + { + Distance *= 1.3f; + } + else + { + // Don't let the mob move too much if he's falling. + Distance *= 0.25f; + } + + m_Self->AddSpeedX(Distance.x); + m_Self->AddSpeedZ(Distance.z); + + // It's too buggy! + /* + if (m_EMState == ESCAPING) + { + // Runs Faster when escaping :D otherwise they just walk away + SetSpeedX (GetSpeedX() * 2.f); + SetSpeedZ (GetSpeedZ() * 2.f); + } + */ + } + else + { + if (ReachedFinalDestination()) // If we have reached the ultimate, final destination, stop pathfinding and attack if appropriate + { + FinishPathFinding(); + } + else + { + TickPathFinding(); // We have reached the next point in our path, calculate another point + } + } + } + + SetPitchAndYawFromDestination(); + // HandleFalling(); + + switch (m_EMState) + { + case IDLE: + { + // If enemy passive we ignore checks for player visibility + InStateIdle(a_Dt); + break; + } + case CHASING: + { + // If we do not see a player anymore skip chasing action + InStateChasing(a_Dt); + break; + } + case ESCAPING: + { + InStateEscaping(a_Dt); + break; + } + + case ATTACKING: break; + } // switch (m_EMState) + + m_Self->BroadcastMovementUpdate(); +} + + + + + +void cAIComponent::SetPitchAndYawFromDestination() +{ + Vector3d FinalDestination = m_FinalDestination; + if (m_Target != NULL) + { + if (m_Target->IsPlayer()) + { + FinalDestination.y = ((cPlayer *)m_Target)->GetStance(); + } + else + { + FinalDestination.y = m_Self->GetHeight(); + } + } + + Vector3d Distance = FinalDestination - m_Self->GetPosition(); + if (Distance.SqrLength() > 0.1f) + { + { + double Rotation, Pitch; + Distance.Normalize(); + VectorToEuler(Distance.x, Distance.y, Distance.z, Rotation, Pitch); + m_Self->SetHeadYaw(Rotation); + m_Self->SetPitch(-Pitch); + } + + { + Vector3d BodyDistance = m_Destination - m_Self->GetPosition(); + double Rotation, Pitch; + Distance.Normalize(); + VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, Rotation, Pitch); + m_Self->SetYaw(Rotation); + } + } +} + + + + + +void cAIComponent::TickPathFinding() +{ + const int PosX = (int)floor(m_Self->GetPosX()); + const int PosY = (int)floor(m_Self->GetPosY()); + const int PosZ = (int)floor(m_Self->GetPosZ()); + + std::vector m_PotentialCoordinates; + m_TraversedCoordinates.push_back(Vector3i(PosX, PosY, PosZ)); + + static const struct // Define which directions to try to move to + { + int x, z; + } gCrossCoords[] = + { + { 1, 0}, + {-1, 0}, + { 0, 1}, + { 0, -1}, + } ; + + if ((PosY - 1 < 0) || (PosY + 2 > cChunkDef::Height) /* PosY + 1 will never be true if PosY + 2 is not */) + { + // Too low/high, can't really do anything + FinishPathFinding(); + return; + } + + for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + if (IsCoordinateInTraversedList(Vector3i(gCrossCoords[i].x + PosX, PosY, gCrossCoords[i].z + PosZ))) + { + continue; + } + + BLOCKTYPE BlockAtY = m_Self->GetWorld()->GetBlock(gCrossCoords[i].x + PosX, PosY, gCrossCoords[i].z + PosZ); + BLOCKTYPE BlockAtYP = m_Self->GetWorld()->GetBlock(gCrossCoords[i].x + PosX, PosY + 1, gCrossCoords[i].z + PosZ); + BLOCKTYPE BlockAtYPP = m_Self->GetWorld()->GetBlock(gCrossCoords[i].x + PosX, PosY + 2, gCrossCoords[i].z + PosZ); + int LowestY = m_Self->GetMovementComponent().FindFirstNonAirBlockPosition(gCrossCoords[i].x + PosX, gCrossCoords[i].z + PosZ); + BLOCKTYPE BlockAtLowestY = m_Self->GetWorld()->GetBlock(gCrossCoords[i].x + PosX, LowestY, gCrossCoords[i].z + PosZ); + + if ( + (!cBlockInfo::IsSolid(BlockAtY)) && + (!cBlockInfo::IsSolid(BlockAtYP)) && + (!IsBlockLava(BlockAtLowestY)) && + (BlockAtLowestY != E_BLOCK_CACTUS) && + (PosY - LowestY < 4) + ) + { + m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY, gCrossCoords[i].z + PosZ)); + } + else if ( + (cBlockInfo::IsSolid(BlockAtY)) && + (BlockAtY != E_BLOCK_CACTUS) && + (!cBlockInfo::IsSolid(BlockAtYP)) && + (!cBlockInfo::IsSolid(BlockAtYPP)) && + (BlockAtY != E_BLOCK_FENCE) && + (BlockAtY != E_BLOCK_FENCE_GATE) + ) + { + m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY + 1, gCrossCoords[i].z + PosZ)); + } + } + + if (!m_PotentialCoordinates.empty()) + { + Vector3f ShortestCoords = m_PotentialCoordinates.front(); + for (std::vector::const_iterator itr = m_PotentialCoordinates.begin(); itr != m_PotentialCoordinates.end(); ++itr) + { + Vector3f Distance = m_FinalDestination - ShortestCoords; + Vector3f Distance2 = m_FinalDestination - *itr; + if (Distance.SqrLength() > Distance2.SqrLength()) + { + ShortestCoords = *itr; + } + } + + m_Destination = ShortestCoords; + m_Destination.z += 0.5f; + m_Destination.x += 0.5f; + } + else + { + FinishPathFinding(); + } +} + + + + + +bool cAIComponent::IsMovingToTargetPosition() +{ + // Difference between destination x and target x is negligible (to 10^-12 precision) + if (fabsf((float)m_FinalDestination.x - (float)m_Target->GetPosX()) < std::numeric_limits::epsilon()) + { + return false; + } + // Difference between destination z and target z is negligible (to 10^-12 precision) + else if (fabsf((float)m_FinalDestination.z - (float)m_Target->GetPosZ()) > std::numeric_limits::epsilon()) + { + return false; + } + return true; +} + + + + + +bool cAIComponent::ReachedFinalDestination() +{ + if ((m_Self->GetPosition() - m_FinalDestination).Length() <= m_Self->GetAttackComponent().GetAttackRange()) + { + return true; + } + + return false; +} + + + + + +bool cAIComponent::ReachedDestination() +{ + if ((m_Destination - m_Self->GetPosition()).Length() < 0.5f) + { + return true; + } + + return false; +} + + + + + +void cAIComponent::MoveToPosition(const Vector3d & a_Position) +{ + FinishPathFinding(); + + m_FinalDestination = a_Position; + m_bMovingToDestination = true; + TickPathFinding(); +} + + + + + +void cAIComponent::InStateIdle(float a_Dt) +{ + if (m_bMovingToDestination) + { + return; // Still getting there + } + + m_IdleInterval += a_Dt; + + if (m_IdleInterval > 1) + { + // At this interval the results are predictable + int rem = m_Self->GetWorld()->GetTickRandomNumber(6) + 1; + m_IdleInterval -= 1; // So nothing gets dropped when the server hangs for a few seconds + + Vector3d Dist; + Dist.x = (double)m_Self->GetWorld()->GetTickRandomNumber(10) - 5; + Dist.z = (double)m_Self->GetWorld()->GetTickRandomNumber(10) - 5; + + if ((Dist.SqrLength() > 2) && (rem >= 3)) + { + Vector3d Destination(m_Self->GetPosX() + Dist.x, 0, m_Self->GetPosZ() + Dist.z); + + int NextHeight = m_Self->GetMovementComponent().FindFirstNonAirBlockPosition(Destination.x, Destination.z); + + if (m_Self->GetMovementComponent().IsNextYPosReachable(NextHeight)) + { + Destination.y = NextHeight; + MoveToPosition(Destination); + } + } + } +} + + + + + +// What to do if in Chasing State +// This state should always be defined in each child class +void cAIComponent::InStateChasing(float a_Dt) +{ + UNUSED(a_Dt); +} + + + + + +// What to do if in Escaping State +void cAIComponent::InStateEscaping(float a_Dt) +{ + UNUSED(a_Dt); + + if (m_Target != NULL) + { + int sight_distance = m_Self->GetEnvironmentComponent().GetSightDistance(); + Vector3d newloc = m_Self->GetPosition(); + newloc.x = (m_Target->GetPosition().x < newloc.x)? (newloc.x + sight_distance): (newloc.x - sight_distance); + newloc.z = (m_Target->GetPosition().z < newloc.z)? (newloc.z + sight_distance): (newloc.z - sight_distance); + MoveToPosition(newloc); + } + else + { + m_EMState = IDLE; // This shouldnt be required but just to be safe + } +} diff --git a/src/Mobs/Components/AIComponent.h b/src/Mobs/Components/AIComponent.h index d8bfe7900..c4cf15d14 100644 --- a/src/Mobs/Components/AIComponent.h +++ b/src/Mobs/Components/AIComponent.h @@ -8,9 +8,54 @@ class cAIComponent { protected: cMonster * m_Self; + cEntity * m_Target; + float m_IdleInterval; + + enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState; + + /** Coordinates of the next position that should be reached */ + Vector3d m_Destination; + /** Coordinates for the ultimate, final destination. */ + Vector3d m_FinalDestination; + /** A semi-temporary list to store the traversed coordinates during active pathfinding so we don't visit them again */ + std::vector m_TraversedCoordinates; + + /** Stores if mobile is currently moving towards the ultimate, final destination */ + bool m_bMovingToDestination; + + /********** + * Pathfinding + **********/ + inline void FinishPathFinding(void) + { + m_TraversedCoordinates.clear(); + m_bMovingToDestination = false; + } + /** Finds the next place to go + This is based on the ultimate, final destination and the current position, as well as the traversed coordinates, and any environmental hazards */ + void TickPathFinding(void); + + inline bool IsCoordinateInTraversedList(Vector3i a_Coords) + { + return (std::find(m_TraversedCoordinates.begin(), m_TraversedCoordinates.end(), a_Coords) != m_TraversedCoordinates.end()); + } + bool IsMovingToTargetPosition(); + bool ReachedFinalDestination(); + virtual bool ReachedDestination(void); + virtual void MoveToPosition(const Vector3d & a_Position); + void SetPitchAndYawFromDestination(); + + /********** + * Event Management + **********/ + // void EventLosePlayer(void); + // virtual void EventSeePlayer(cEntity * a_Entity); + virtual void InStateIdle (float a_Dt); + virtual void InStateChasing (float a_Dt); + virtual void InStateEscaping(float a_Dt); public: cAIComponent(cMonster * a_Entity); virtual ~cAIComponent(){} - virtual void Tick(float a_Dt, cChunk & a_Chunk){} + virtual void Tick(float a_Dt, cChunk & a_Chunk); }; diff --git a/src/Mobs/Components/EnvironmentComponent.cpp b/src/Mobs/Components/EnvironmentComponent.cpp index f8cfc37ee..b44542683 100644 --- a/src/Mobs/Components/EnvironmentComponent.cpp +++ b/src/Mobs/Components/EnvironmentComponent.cpp @@ -1,5 +1,58 @@ #include "Globals.h" #include "EnvironmentComponent.h" #include "../Monster.h" +#include "../../World.h" +#include "../../Chunk.h" -cEnvironmentComponent::cEnvironmentComponent(cMonster * a_Entity) : m_Self(a_Entity){} +cEnvironmentComponent::cEnvironmentComponent(cMonster * a_Entity, int a_SightDistance) : m_Self(a_Entity), m_SightDistance(a_SightDistance){} + + + + + +void cEnvironmentComponent::Tick(float a_Dt, cChunk & a_Chunk) { + + // Burning in daylight + HandleDaylightBurning(a_Chunk); +} + + + + + +void cEnvironmentComponent::HandleDaylightBurning(cChunk & a_Chunk) +{ + if (!m_BurnsInDaylight) + { + return; + } + + int RelY = (int)floor(m_Self->GetPosY()); + if ((RelY < 0) || (RelY >= cChunkDef::Height)) + { + // Outside the world + return; + } + int PosX = (int)floor(m_Self->GetPosX()); + int PosZ = (int)floor(m_Self->GetPosX()); + int RelX = PosX - m_Self->GetChunkX() * cChunkDef::Width; + int RelZ = PosZ - m_Self->GetChunkZ() * cChunkDef::Width; + + if (!a_Chunk.IsLightValid()) + { + m_Self->GetWorld()->QueueLightChunk(m_Self->GetChunkX(), m_Self->GetChunkZ()); + return; + } + + if ( + (a_Chunk.GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight + (a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand + (m_Self->GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime + !m_Self->IsOnFire() && // Not already burning + m_Self->GetWorld()->IsWeatherWetAt(PosX, PosZ) // Not raining + ) + { + // Burn for 100 ticks, then decide again + m_Self->StartBurning(100); + } +} diff --git a/src/Mobs/Components/EnvironmentComponent.h b/src/Mobs/Components/EnvironmentComponent.h index cbf9e0784..078e81501 100644 --- a/src/Mobs/Components/EnvironmentComponent.h +++ b/src/Mobs/Components/EnvironmentComponent.h @@ -9,15 +9,25 @@ class cEnvironmentComponent protected: cMonster * m_Self; int m_SightDistance; + bool m_OnGround; + + bool m_BurnsInDaylight; public: - cEnvironmentComponent(cMonster * a_Entity); + cEnvironmentComponent(cMonster * a_Entity, int a_SightDistance); virtual ~cEnvironmentComponent(){} - virtual void Tick(float a_Dt, cChunk & a_Chunk){} + virtual void Tick(float a_Dt, cChunk & a_Chunk); // Get Functions int GetSightDistance() { return m_SightDistance ; } + bool GetOnGround() { return m_OnGround; } + bool GetBurnsInDaylight() { return m_BurnsInDaylight; } // Set Functions void SetSightDistance(int a_SightDistance) { m_SightDistance = a_SightDistance; } + void SetOnGround(bool a_Bool) { m_OnGround = a_Bool; } + void SetBurnsInDaylight(bool a_Bool) { m_BurnsInDaylight = a_Bool; } + + // Handle functions + void HandleDaylightBurning(cChunk & a_Chunk); }; diff --git a/src/Mobs/Components/MovementComponent.cpp b/src/Mobs/Components/MovementComponent.cpp index f99e89103..e808e2948 100644 --- a/src/Mobs/Components/MovementComponent.cpp +++ b/src/Mobs/Components/MovementComponent.cpp @@ -31,3 +31,20 @@ int cMovementComponent::FindFirstNonAirBlockPosition(double a_PosX, double a_Pos return PosY; } } + + + + + +bool cMovementComponent::IsNextYPosReachable(int a_PosY) +{ + return ( + (a_PosY <= (int)floor(m_Self->GetPosY())) || + DoesPosYRequireJump(a_PosY) + ); +} +/** Returns if a monster can reach a given height by jumping */ +bool cMovementComponent::DoesPosYRequireJump(int a_PosY) +{ + return ((a_PosY > (int)floor(m_Self->GetPosY())) && (a_PosY == (int)floor(m_Self->GetPosY()) + 1)); +} diff --git a/src/Mobs/Components/MovementComponent.h b/src/Mobs/Components/MovementComponent.h index 9103a4842..ffb840f76 100644 --- a/src/Mobs/Components/MovementComponent.h +++ b/src/Mobs/Components/MovementComponent.h @@ -18,4 +18,8 @@ public: If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1 If current Y is solid, goes up to find first nonsolid block, and returns that */ int FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ); + /** Returns if a monster can actually reach a given height by jumping or walking */ + bool IsNextYPosReachable(int a_PosY); + /** Returns if a monster can reach a given height by jumping */ + bool DoesPosYRequireJump(int a_PosY); }; diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index c272dc93a..1af44271b 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -58,10 +58,11 @@ cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString , m_MobType(a_MobType) , m_SoundHurt(a_SoundHurt) , m_SoundDeath(a_SoundDeath) + , m_DestroyTimer(0.0f) { - m_AI = new cAIComponent(this); + m_AI = new cAIAggressiveComponent(this); m_Attack = new cAttackComponent(this); - m_Environment = new cEnvironmentComponent(this); + m_Environment = new cEnvironmentComponent(this, 16); m_Movement = new cMovementComponent(this); // Temporary placement till I figure out where to put it @@ -72,6 +73,27 @@ cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString m_DropChanceBoots = 0.0f; } +void cMonster::Tick(float a_Dt, cChunk & a_Chunk) { + super::Tick(a_Dt, a_Chunk); + + if (m_Health <= 0) + { + // The mob is dead, but we're still animating the "puff" they leave when they die + m_DestroyTimer += a_Dt / 1000; + if (m_DestroyTimer > 1) + { + Destroy(true); + } + return; + } + + LOG("Monster Tick..."); + m_AI->Tick(a_Dt, a_Chunk); + m_Attack->Tick(a_Dt, a_Chunk); + m_Environment->Tick(a_Dt, a_Chunk); + m_Movement->Tick(a_Dt, a_Chunk); +} + diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 587c503e3..73ad5c6d3 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -76,8 +76,11 @@ protected: cMovementComponent * m_Movement; public: cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + ~cMonster() { delete m_AI; delete m_Attack; delete m_Environment; delete m_Movement;} virtual void SpawnOn(cClientHandle & a_ClientHandle) /*override*/; + virtual void Tick(float a_Dt, cChunk & a_Chunk); + // Type Function virtual bool IsBaby (void) const { return false; } virtual bool IsTame (void) const { return false; } @@ -167,4 +170,7 @@ protected: AString m_OwnerName; AString m_OwnerUUID; + + + float m_DestroyTimer; }; -- cgit v1.2.3