summaryrefslogtreecommitdiffstats
path: root/src/Mobs/Components
diff options
context:
space:
mode:
Diffstat (limited to 'src/Mobs/Components')
-rw-r--r--src/Mobs/Components/AIAggressiveComponent.cpp17
-rw-r--r--src/Mobs/Components/AIAggressiveComponent.h12
-rw-r--r--src/Mobs/Components/AIComponent.cpp367
-rw-r--r--src/Mobs/Components/AIComponent.h61
-rw-r--r--src/Mobs/Components/AIPassiveAgressiveComponent.cpp17
-rw-r--r--src/Mobs/Components/AIPassiveAgressiveComponent.h12
-rw-r--r--src/Mobs/Components/AIPassiveComponent.cpp17
-rw-r--r--src/Mobs/Components/AIPassiveComponent.h12
-rw-r--r--src/Mobs/Components/AllComponents.h8
-rw-r--r--src/Mobs/Components/AttackComponent.cpp5
-rw-r--r--src/Mobs/Components/AttackComponent.h32
-rw-r--r--src/Mobs/Components/CMakeLists.txt22
-rw-r--r--src/Mobs/Components/EnvironmentComponent.cpp58
-rw-r--r--src/Mobs/Components/EnvironmentComponent.h33
-rw-r--r--src/Mobs/Components/MovementComponent.cpp50
-rw-r--r--src/Mobs/Components/MovementComponent.h25
16 files changed, 748 insertions, 0 deletions
diff --git a/src/Mobs/Components/AIAggressiveComponent.cpp b/src/Mobs/Components/AIAggressiveComponent.cpp
new file mode 100644
index 000000000..2963da7f0
--- /dev/null
+++ b/src/Mobs/Components/AIAggressiveComponent.cpp
@@ -0,0 +1,17 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+#include "AIAggressiveComponent.h"
+
+
+
+
+
+cAIAggressiveComponent::cAIAggressiveComponent(cMonster * a_Monster) : cAIComponent(a_Monster){}
+
+
+
+
+
+void cAIAggressiveComponent::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+}
diff --git a/src/Mobs/Components/AIAggressiveComponent.h b/src/Mobs/Components/AIAggressiveComponent.h
new file mode 100644
index 000000000..2cea452c5
--- /dev/null
+++ b/src/Mobs/Components/AIAggressiveComponent.h
@@ -0,0 +1,12 @@
+#pragma once
+#include "AIComponent.h"
+
+class cEntity;
+
+class cAIAggressiveComponent : public cAIComponent {
+ typedef cAIComponent super;
+protected:
+public:
+ cAIAggressiveComponent(cMonster * a_Monster);
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) /*override*/;
+};
diff --git a/src/Mobs/Components/AIComponent.cpp b/src/Mobs/Components/AIComponent.cpp
new file mode 100644
index 000000000..12a89c201
--- /dev/null
+++ b/src/Mobs/Components/AIComponent.cpp
@@ -0,0 +1,367 @@
+#include "Globals.h"
+#include "AIComponent.h"
+#include "../Monster.h"
+
+#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<Vector3d> 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<Vector3d>::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<float>::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<float>::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
new file mode 100644
index 000000000..c4cf15d14
--- /dev/null
+++ b/src/Mobs/Components/AIComponent.h
@@ -0,0 +1,61 @@
+#pragma once
+
+class cMonster;
+class cEntity;
+class cChunk;
+
+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<Vector3i> 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);
+};
diff --git a/src/Mobs/Components/AIPassiveAgressiveComponent.cpp b/src/Mobs/Components/AIPassiveAgressiveComponent.cpp
new file mode 100644
index 000000000..efa88a8fa
--- /dev/null
+++ b/src/Mobs/Components/AIPassiveAgressiveComponent.cpp
@@ -0,0 +1,17 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+#include "cAIPassiveAggressiveComponent.h"
+
+
+
+
+
+cAIPassiveAggressiveComponent::cAIPassiveAggressiveComponent(cMonster * a_Monster) : cAIComponent(a_Monster){}
+
+
+
+
+
+void cAIPassiveAggressiveComponent::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+}
diff --git a/src/Mobs/Components/AIPassiveAgressiveComponent.h b/src/Mobs/Components/AIPassiveAgressiveComponent.h
new file mode 100644
index 000000000..bd35251d0
--- /dev/null
+++ b/src/Mobs/Components/AIPassiveAgressiveComponent.h
@@ -0,0 +1,12 @@
+#pragma once
+#include "AIComponent.h"
+
+class cEntity;
+
+class cAIPassiveAggressiveComponent : public cAIComponent {
+ typedef cAIComponent super;
+protected:
+public:
+ cAIPassiveAggressiveComponent(cMonster * a_Monster);
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) /*override*/;
+};
diff --git a/src/Mobs/Components/AIPassiveComponent.cpp b/src/Mobs/Components/AIPassiveComponent.cpp
new file mode 100644
index 000000000..ba3fe7ec7
--- /dev/null
+++ b/src/Mobs/Components/AIPassiveComponent.cpp
@@ -0,0 +1,17 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+#include "AIPassiveComponent.h"
+
+
+
+
+
+cAIPassiveComponent::cAIPassiveComponent(cMonster * a_Monster) : cAIComponent(a_Monster){}
+
+
+
+
+
+void cAIPassiveComponent::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+} \ No newline at end of file
diff --git a/src/Mobs/Components/AIPassiveComponent.h b/src/Mobs/Components/AIPassiveComponent.h
new file mode 100644
index 000000000..ab2e5afdb
--- /dev/null
+++ b/src/Mobs/Components/AIPassiveComponent.h
@@ -0,0 +1,12 @@
+#pragma once
+#include "AIComponent.h"
+
+class cEntity;
+
+class cAIPassiveComponent : public cAIComponent {
+ typedef cAIComponent super;
+protected:
+public:
+ cAIPassiveComponent(cMonster * a_Monster);
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) /*override*/;
+};
diff --git a/src/Mobs/Components/AllComponents.h b/src/Mobs/Components/AllComponents.h
new file mode 100644
index 000000000..e99d08cc4
--- /dev/null
+++ b/src/Mobs/Components/AllComponents.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "AIComponent.h"
+#include "AttackComponent.h"
+#include "EnvironmentComponent.h"
+#include "MovementComponent.h"
+
+#include "AIAggressiveComponent.h"
diff --git a/src/Mobs/Components/AttackComponent.cpp b/src/Mobs/Components/AttackComponent.cpp
new file mode 100644
index 000000000..ae721f00b
--- /dev/null
+++ b/src/Mobs/Components/AttackComponent.cpp
@@ -0,0 +1,5 @@
+#include "Globals.h"
+#include "AttackComponent.h"
+#include "../Monster.h"
+
+cAttackComponent::cAttackComponent(cMonster * a_Entity) : m_Self(a_Entity){}
diff --git a/src/Mobs/Components/AttackComponent.h b/src/Mobs/Components/AttackComponent.h
new file mode 100644
index 000000000..6a6932bd3
--- /dev/null
+++ b/src/Mobs/Components/AttackComponent.h
@@ -0,0 +1,32 @@
+#pragma once
+
+class cMonster;
+class cEntity;
+class cChunk;
+
+class cAttackComponent
+{
+protected:
+ cMonster * m_Self;
+ float m_AttackRate;
+ int m_AttackDamage;
+ int m_AttackRange;
+ float m_AttackInterval;
+public:
+ cAttackComponent(cMonster * a_Entity);
+ virtual ~cAttackComponent(){}
+
+ virtual void Tick(float a_Dt, cChunk & a_Chunk){}
+
+ // Get Functions
+ int GetAttackRate() { return (int)m_AttackRate; }
+ int GetAttackRange() { return m_AttackRange; }
+ int GetAttackDamage() { return m_AttackDamage; }
+ float GetAttackInterval() { return m_AttackInterval; }
+
+ // Set Functions
+ void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; }
+ void SetAttackRange(int a_AttackRange) { m_AttackRange = a_AttackRange; }
+ void SetAttackDamage(int a_AttackDamage) { m_AttackDamage = a_AttackDamage; }
+ void SetAttackInterval(float a_AttackInterval) { m_AttackInterval = a_AttackInterval; }
+};
diff --git a/src/Mobs/Components/CMakeLists.txt b/src/Mobs/Components/CMakeLists.txt
new file mode 100644
index 000000000..a933dc9e0
--- /dev/null
+++ b/src/Mobs/Components/CMakeLists.txt
@@ -0,0 +1,22 @@
+cmake_minimum_required (VERSION 2.6)
+project (MCServer)
+
+include_directories ("${PROJECT_SOURCE_DIR}/../")
+
+SET (SRCS
+ AIComponent.cpp
+ AIAggressiveComponent.cpp
+ AttackComponent.cpp
+ EnvironmentComponent.cpp
+ MovementComponent.cpp)
+
+SET (HDRS
+ AIComponent.h
+ AIAggressiveComponent.h
+ AttackComponent.h
+ EnvironmentComponent.h
+ MovementComponent.h)
+
+if(NOT MSVC)
+ add_library(Components ${SRCS} ${HDRS})
+endif() \ No newline at end of file
diff --git a/src/Mobs/Components/EnvironmentComponent.cpp b/src/Mobs/Components/EnvironmentComponent.cpp
new file mode 100644
index 000000000..b44542683
--- /dev/null
+++ b/src/Mobs/Components/EnvironmentComponent.cpp
@@ -0,0 +1,58 @@
+#include "Globals.h"
+#include "EnvironmentComponent.h"
+#include "../Monster.h"
+#include "../../World.h"
+#include "../../Chunk.h"
+
+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
new file mode 100644
index 000000000..078e81501
--- /dev/null
+++ b/src/Mobs/Components/EnvironmentComponent.h
@@ -0,0 +1,33 @@
+#pragma once
+
+class cMonster;
+class cEntity;
+class cChunk;
+
+class cEnvironmentComponent
+{
+protected:
+ cMonster * m_Self;
+ int m_SightDistance;
+ bool m_OnGround;
+
+ bool m_BurnsInDaylight;
+public:
+ cEnvironmentComponent(cMonster * a_Entity, int a_SightDistance);
+ virtual ~cEnvironmentComponent(){}
+
+ 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
new file mode 100644
index 000000000..e808e2948
--- /dev/null
+++ b/src/Mobs/Components/MovementComponent.cpp
@@ -0,0 +1,50 @@
+#include "Globals.h"
+#include "MovementComponent.h"
+#include "../Monster.h"
+
+#include "../../World.h"
+
+cMovementComponent::cMovementComponent(cMonster * a_Entity) : m_Self(a_Entity){}
+
+
+int cMovementComponent::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ)
+{
+ int PosY = (int)floor(m_Self->GetPosY());
+ PosY = Clamp(PosY, 0, cChunkDef::Height);
+
+ if (!cBlockInfo::IsSolid(m_Self->GetWorld()->GetBlock((int)floor(a_PosX), PosY, (int)floor(a_PosZ))))
+ {
+ while (!cBlockInfo::IsSolid(m_Self->GetWorld()->GetBlock((int)floor(a_PosX), PosY, (int)floor(a_PosZ))) && (PosY > 0))
+ {
+ PosY--;
+ }
+
+ return PosY + 1;
+ }
+ else
+ {
+ while (cBlockInfo::IsSolid(m_Self->GetWorld()->GetBlock((int)floor(a_PosX), PosY, (int)floor(a_PosZ))) && (PosY < cChunkDef::Height))
+ {
+ PosY++;
+ }
+
+ 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
new file mode 100644
index 000000000..ffb840f76
--- /dev/null
+++ b/src/Mobs/Components/MovementComponent.h
@@ -0,0 +1,25 @@
+#pragma once
+
+class cMonster;
+class cEntity;
+class cChunk;
+
+class cMovementComponent
+{
+protected:
+ cMonster * m_Self;
+public:
+ cMovementComponent(cMonster * a_Entity);
+ virtual ~cMovementComponent(){}
+
+ virtual void Tick(float a_Dt, cChunk & a_Chunk){}
+
+ /** Finds the first non-air block position (not the highest, as cWorld::GetHeight does)
+ 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);
+};