summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Blocks/BlockFence.h1
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/ClientHandle.cpp2
-rw-r--r--src/Entities/Entity.cpp19
-rw-r--r--src/Entities/Entity.h2
-rw-r--r--src/Entities/Pawn.cpp52
-rw-r--r--src/Entities/Pawn.h16
-rw-r--r--src/Entities/Player.cpp2
-rw-r--r--src/Entities/WitherSkullEntity.cpp1
-rw-r--r--src/Mobs/AggressiveMonster.cpp109
-rw-r--r--src/Mobs/AggressiveMonster.h33
-rw-r--r--src/Mobs/Bat.cpp6
-rw-r--r--src/Mobs/Bat.h11
-rw-r--r--src/Mobs/Behaviors/Behavior.cpp102
-rw-r--r--src/Mobs/Behaviors/Behavior.h30
-rw-r--r--src/Mobs/Behaviors/BehaviorAggressive.cpp66
-rw-r--r--src/Mobs/Behaviors/BehaviorAggressive.h40
-rw-r--r--src/Mobs/Behaviors/BehaviorAttacker.cpp301
-rw-r--r--src/Mobs/Behaviors/BehaviorAttacker.h83
-rw-r--r--src/Mobs/Behaviors/BehaviorAttackerMelee.cpp27
-rw-r--r--src/Mobs/Behaviors/BehaviorAttackerMelee.h17
-rw-r--r--src/Mobs/Behaviors/BehaviorAttackerRanged.cpp43
-rw-r--r--src/Mobs/Behaviors/BehaviorAttackerRanged.h23
-rw-r--r--src/Mobs/Behaviors/BehaviorAttackerSuicideBomber.cpp125
-rw-r--r--src/Mobs/Behaviors/BehaviorAttackerSuicideBomber.h24
-rw-r--r--src/Mobs/Behaviors/BehaviorBrave.cpp27
-rw-r--r--src/Mobs/Behaviors/BehaviorBrave.h15
-rw-r--r--src/Mobs/Behaviors/BehaviorBreeder.cpp365
-rw-r--r--src/Mobs/Behaviors/BehaviorBreeder.h52
-rw-r--r--src/Mobs/Behaviors/BehaviorCoward.cpp97
-rw-r--r--src/Mobs/Behaviors/BehaviorCoward.h22
-rw-r--r--src/Mobs/Behaviors/BehaviorDayLightBurner.cpp103
-rw-r--r--src/Mobs/Behaviors/BehaviorDayLightBurner.h17
-rw-r--r--src/Mobs/Behaviors/BehaviorDoNothing.cpp24
-rw-r--r--src/Mobs/Behaviors/BehaviorDoNothing.h19
-rw-r--r--src/Mobs/Behaviors/BehaviorItemDropper.cpp50
-rw-r--r--src/Mobs/Behaviors/BehaviorItemDropper.h17
-rw-r--r--src/Mobs/Behaviors/BehaviorItemFollower.cpp64
-rw-r--r--src/Mobs/Behaviors/BehaviorItemFollower.h19
-rw-r--r--src/Mobs/Behaviors/BehaviorItemReplacer.cpp40
-rw-r--r--src/Mobs/Behaviors/BehaviorItemReplacer.h26
-rw-r--r--src/Mobs/Behaviors/BehaviorWanderer.cpp102
-rw-r--r--src/Mobs/Behaviors/BehaviorWanderer.h24
-rw-r--r--src/Mobs/Behaviors/CMakeLists.txt45
-rw-r--r--src/Mobs/Blaze.cpp49
-rw-r--r--src/Mobs/Blaze.h23
-rw-r--r--src/Mobs/CMakeLists.txt8
-rw-r--r--src/Mobs/CaveSpider.cpp39
-rw-r--r--src/Mobs/CaveSpider.h18
-rw-r--r--src/Mobs/Chicken.cpp51
-rw-r--r--src/Mobs/Chicken.h21
-rw-r--r--src/Mobs/Cow.cpp33
-rw-r--r--src/Mobs/Cow.h30
-rw-r--r--src/Mobs/Creeper.cpp96
-rw-r--r--src/Mobs/Creeper.h29
-rw-r--r--src/Mobs/EnderDragon.cpp5
-rw-r--r--src/Mobs/EnderDragon.h14
-rw-r--r--src/Mobs/Enderman.cpp67
-rw-r--r--src/Mobs/Enderman.h11
-rw-r--r--src/Mobs/Ghast.cpp11
-rw-r--r--src/Mobs/Ghast.h14
-rw-r--r--src/Mobs/Giant.cpp6
-rw-r--r--src/Mobs/Giant.h11
-rw-r--r--src/Mobs/Guardian.cpp4
-rw-r--r--src/Mobs/Guardian.h6
-rw-r--r--src/Mobs/Horse.cpp21
-rw-r--r--src/Mobs/Horse.h17
-rw-r--r--src/Mobs/IronGolem.cpp5
-rw-r--r--src/Mobs/IronGolem.h11
-rw-r--r--src/Mobs/MagmaCube.cpp5
-rw-r--r--src/Mobs/MagmaCube.h11
-rw-r--r--src/Mobs/MobPointer.cpp78
-rw-r--r--src/Mobs/MobPointer.h35
-rw-r--r--src/Mobs/Monster.cpp632
-rw-r--r--src/Mobs/Monster.h129
-rw-r--r--src/Mobs/Mooshroom.cpp10
-rw-r--r--src/Mobs/Mooshroom.h16
-rw-r--r--src/Mobs/Ocelot.cpp6
-rw-r--r--src/Mobs/Ocelot.h11
-rw-r--r--src/Mobs/PassiveAggressiveMonster.cpp48
-rw-r--r--src/Mobs/PassiveAggressiveMonster.h24
-rw-r--r--src/Mobs/PassiveMonster.h66
-rw-r--r--src/Mobs/Path.h2
-rw-r--r--src/Mobs/PathFinder.cpp46
-rw-r--r--src/Mobs/PathFinder.h27
-rw-r--r--src/Mobs/Pig.cpp18
-rw-r--r--src/Mobs/Pig.h20
-rw-r--r--src/Mobs/Rabbit.cpp15
-rw-r--r--src/Mobs/Rabbit.h17
-rw-r--r--src/Mobs/Sheep.cpp17
-rw-r--r--src/Mobs/Sheep.h29
-rw-r--r--src/Mobs/Silverfish.h15
-rw-r--r--src/Mobs/Skeleton.cpp54
-rw-r--r--src/Mobs/Skeleton.h22
-rw-r--r--src/Mobs/Slime.cpp13
-rw-r--r--src/Mobs/Slime.h9
-rw-r--r--src/Mobs/SnowGolem.cpp5
-rw-r--r--src/Mobs/SnowGolem.h11
-rw-r--r--src/Mobs/Spider.cpp60
-rw-r--r--src/Mobs/Spider.h18
-rw-r--r--src/Mobs/Squid.cpp5
-rw-r--r--src/Mobs/Squid.h10
-rw-r--r--src/Mobs/Villager.cpp5
-rw-r--r--src/Mobs/Villager.h16
-rw-r--r--src/Mobs/Witch.cpp5
-rw-r--r--src/Mobs/Witch.h12
-rw-r--r--src/Mobs/Wither.cpp5
-rw-r--r--src/Mobs/Wither.h9
-rw-r--r--src/Mobs/Wolf.cpp350
-rw-r--r--src/Mobs/Wolf.h9
-rw-r--r--src/Mobs/Zombie.cpp10
-rw-r--r--src/Mobs/Zombie.h19
-rw-r--r--src/Mobs/ZombiePigman.cpp7
-rw-r--r--src/Mobs/ZombiePigman.h17
-rw-r--r--src/MonsterConfig.cpp15
-rw-r--r--src/Protocol/Protocol_1_10.cpp3
-rw-r--r--src/Protocol/Protocol_1_8.cpp17
-rw-r--r--src/Protocol/Protocol_1_9.cpp3
118 files changed, 3210 insertions, 1643 deletions
diff --git a/src/Blocks/BlockFence.h b/src/Blocks/BlockFence.h
index 63390cd37..9c25013f0 100644
--- a/src/Blocks/BlockFence.h
+++ b/src/Blocks/BlockFence.h
@@ -6,7 +6,6 @@
#include "../EffectID.h"
#include "Entities/LeashKnot.h"
#include "BoundingBox.h"
-#include "../Mobs/PassiveMonster.h"
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 08bb2c270..661db57ae 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -8,7 +8,7 @@ include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/libevent/include
set(FOLDERS
OSSupport HTTP Items Blocks Protocol Generating mbedTLS++ Bindings
- WorldStorage Mobs Entities Simulator Simulator/IncrementalRedstoneSimulator
+ WorldStorage Mobs Mobs/Behaviors Entities Simulator Simulator/IncrementalRedstoneSimulator
BlockEntities UI Noise
)
@@ -348,7 +348,7 @@ if (NOT MSVC)
target_link_libraries(${CMAKE_PROJECT_NAME}
OSSupport HTTPServer Bindings Items Blocks Noise
Protocol Generating WorldStorage
- Mobs Entities Simulator IncrementalRedstoneSimulator
+ Mobs Behaviors Entities Simulator IncrementalRedstoneSimulator
BlockEntities UI mbedTLS++
)
endif ()
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index f42f52cb1..7aa613f4c 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -189,7 +189,7 @@ void cClientHandle::Destroy(void)
auto world = player->GetWorld();
if (world != nullptr)
{
- player->StopEveryoneFromTargetingMe();
+ // player->StopEveryoneFromTargetingMe(); // mobTodo
player->SetIsTicking(false);
if (WasAddedToWorld)
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 33f5a6135..71647200d 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -236,8 +236,9 @@ void cEntity::Destroy(bool a_ShouldBroadcast)
m_World->BroadcastDestroyEntity(*this);
}
+ // Destroy the entity after two seconds, to give time for all cMobPointers to nullify.
auto ParentChunkCoords = cChunkDef::BlockToChunk(GetPosition());
- m_World->QueueTask([this, ParentChunkCoords](cWorld & a_World)
+ m_World->ScheduleTask(40, [this, ParentChunkCoords](cWorld & a_World)
{
LOGD("Destroying entity #%i (%s) from chunk (%d, %d)",
this->GetUniqueID(), this->GetClass(),
@@ -402,6 +403,16 @@ void cEntity::SetPitchFromSpeed(void)
+const Vector3i cEntity::GetHeadPosition(void)
+{
+ int HeadHeight = CeilC(GetPosY() + GetHeight()) - 1;
+ return Vector3i(POSX_TOINT, HeadHeight, POSZ_TOINT);
+}
+
+
+
+
+
bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
{
if (m_Health <= 0)
@@ -1598,12 +1609,12 @@ bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d
// Stop all mobs from targeting this entity
// Stop this entity from targeting other mobs
- if (this->IsMob())
+ /* if (this->IsMob())
{
cMonster * Monster = static_cast<cMonster*>(this);
Monster->SetTarget(nullptr);
- Monster->StopEveryoneFromTargetingMe();
- }
+ Monster->StopEveryoneFromTargetingMe(); mobTodo MovingWorld event for all behaviors?
+ }*/
// Queue add to new world and removal from the old one
cWorld * OldWorld = GetWorld();
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index 3dc2d8f42..a6ca390fd 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -304,6 +304,8 @@ public:
/** Exported in ManualBindings */
const Vector3d & GetPosition(void) const { return m_Position; }
+ const Vector3i GetHeadPosition(void);
+
/** Exported in ManualBindings */
const Vector3d & GetSpeed(void) const { return m_Speed; }
diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp
index 233cdfa85..0fa11997b 100644
--- a/src/Entities/Pawn.cpp
+++ b/src/Entities/Pawn.cpp
@@ -29,7 +29,7 @@ cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) :
cPawn::~cPawn()
{
- ASSERT(m_TargetingMe.size() == 0);
+
}
@@ -38,7 +38,6 @@ cPawn::~cPawn()
void cPawn::Destroyed()
{
- StopEveryoneFromTargetingMe();
super::Destroyed();
}
@@ -260,38 +259,6 @@ void cPawn::ClearEntityEffects()
-void cPawn::NoLongerTargetingMe(cMonster * a_Monster)
-{
- ASSERT(IsTicking()); // Our destroy override is supposed to clear all targets before we're destroyed.
- for (auto i = m_TargetingMe.begin(); i != m_TargetingMe.end(); ++i)
- {
- cMonster * Monster = *i;
- if (Monster == a_Monster)
- {
- ASSERT(Monster->GetTarget() != this); // The monster is notifying us it is no longer targeting us, assert if that's a lie
- m_TargetingMe.erase(i);
- return;
- }
- }
- ASSERT(false); // If this happens, something is wrong. Perhaps the monster never called TargetingMe() or called NoLongerTargetingMe() twice.
-}
-
-
-
-
-
-void cPawn::TargetingMe(cMonster * a_Monster)
-{
- ASSERT(IsTicking());
- ASSERT(m_TargetingMe.size() < 10000);
- ASSERT(a_Monster->GetTarget() == this);
- m_TargetingMe.push_back(a_Monster);
-}
-
-
-
-
-
void cPawn::HandleFalling(void)
{
/* Not pretty looking, and is more suited to wherever server-sided collision detection is implemented.
@@ -462,23 +429,6 @@ void cPawn::HandleFalling(void)
-void cPawn::StopEveryoneFromTargetingMe()
-{
- std::vector<cMonster*>::iterator i = m_TargetingMe.begin();
- while (i != m_TargetingMe.end())
- {
- cMonster * Monster = *i;
- ASSERT(Monster->GetTarget() == this);
- Monster->UnsafeUnsetTarget();
- i = m_TargetingMe.erase(i);
- }
- ASSERT(m_TargetingMe.size() == 0);
-}
-
-
-
-
-
std::map<cEntityEffect::eType, cEntityEffect *> cPawn::GetEntityEffects()
{
std::map<cEntityEffect::eType, cEntityEffect *> Effects;
diff --git a/src/Entities/Pawn.h b/src/Entities/Pawn.h
index 480b523ea..935122012 100644
--- a/src/Entities/Pawn.h
+++ b/src/Entities/Pawn.h
@@ -33,11 +33,6 @@ public:
virtual void HandleAir(void) override;
virtual void HandleFalling(void);
- /** Tells all pawns which are targeting us to stop targeting us. */
- void StopEveryoneFromTargetingMe();
-
-
-
// tolua_begin
/** Applies an entity effect.
@@ -58,12 +53,6 @@ public:
// tolua_end
- /** Remove the monster from the list of monsters targeting this pawn. */
- void NoLongerTargetingMe(cMonster * a_Monster);
-
- /** Add the monster to the list of monsters targeting this pawn. (Does not check if already in list!) */
- void TargetingMe(cMonster * a_Monster);
-
/** Returns all entity effects */
std::map<cEntityEffect::eType, cEntityEffect *> GetEntityEffects();
@@ -76,11 +65,6 @@ protected:
double m_LastGroundHeight;
bool m_bTouchGround;
-
-private:
-
- /** A list of all monsters that are targeting this pawn. */
- std::vector<cMonster*> m_TargetingMe;
} ; // tolua_export
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 8d35d0bf5..7aef38b93 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -2018,7 +2018,7 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d
FreezeInternal(a_NewPosition, false);
// Stop all mobs from targeting this player
- StopEveryoneFromTargetingMe();
+ // StopEveryoneFromTargetingMe(); // mobTodo
cClientHandle * ch = this->GetClientHandle();
if (ch != nullptr)
diff --git a/src/Entities/WitherSkullEntity.cpp b/src/Entities/WitherSkullEntity.cpp
index 354a08dc6..a82ea8e7a 100644
--- a/src/Entities/WitherSkullEntity.cpp
+++ b/src/Entities/WitherSkullEntity.cpp
@@ -1,4 +1,5 @@
+
// WitherSkullEntity.cpp
// Implements the cWitherSkullEntity class representing the entity used by both blue and black wither skulls
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp
deleted file mode 100644
index fec14e6e9..000000000
--- a/src/Mobs/AggressiveMonster.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "AggressiveMonster.h"
-
-#include "../World.h"
-#include "../Entities/Player.h"
-#include "../LineBlockTracer.h"
-
-
-
-
-
-cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
- super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height)
-{
- m_EMPersonality = AGGRESSIVE;
-}
-
-
-
-
-
-// What to do if in Chasing State
-void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
-{
- super::InStateChasing(a_Dt, a_Chunk);
-
- if (GetTarget() != nullptr)
- {
- MoveToPosition(GetTarget()->GetPosition());
- }
-}
-
-
-
-
-
-
-void cAggressiveMonster::EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk)
-{
- if (!a_Player->CanMobsTarget())
- {
- return;
- }
-
- super::EventSeePlayer(a_Player, a_Chunk);
- m_EMState = CHASING;
-}
-
-
-
-
-
-void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
-{
- super::Tick(a_Dt, a_Chunk);
- if (!IsTicking())
- {
- // The base class tick destroyed us
- return;
- }
-
- if (m_EMState == CHASING)
- {
- CheckEventLostPlayer();
- }
- else
- {
- CheckEventSeePlayer(a_Chunk);
- }
-
- auto target = GetTarget();
- if (target == nullptr)
- {
- return;
- }
-
- // TODO: Currently all mobs see through lava, but only Nether-native mobs should be able to.
- Vector3d MyHeadPosition = GetPosition() + Vector3d(0, GetHeight(), 0);
- Vector3d TargetPosition = target->GetPosition() + Vector3d(0, target->GetHeight(), 0);
- if (
- TargetIsInRange() &&
- cLineBlockTracer::LineOfSightTrace(*GetWorld(), MyHeadPosition, TargetPosition, cLineBlockTracer::losAirWaterLava) &&
- (GetHealth() > 0.0)
- )
- {
- // 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);
- }
-}
-
-
-
-
-
-bool cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt)
-{
- if ((GetTarget() == nullptr) || (m_AttackCoolDownTicksLeft != 0))
- {
- return false;
- }
-
- // Setting this higher gives us more wiggle room for attackrate
- ResetAttackCooldown();
- GetTarget()->TakeDamage(dtMobAttack, this, m_AttackDamage, 0);
-
- return true;
-}
diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h
deleted file mode 100644
index 9ab8df06f..000000000
--- a/src/Mobs/AggressiveMonster.h
+++ /dev/null
@@ -1,33 +0,0 @@
-
-#pragma once
-
-#include "Monster.h"
-
-
-
-
-
-class cAggressiveMonster :
- public cMonster
-{
- typedef cMonster super;
-
-public:
-
- 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 InStateChasing(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
-
-
- virtual void EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk) override;
-
- /** Try to perform attack
- returns true if attack was deemed successful (hit player, fired projectile, creeper exploded, etc.) even if it didn't actually do damage
- return false if e.g. the mob is still in cooldown from a previous attack */
- virtual bool Attack(std::chrono::milliseconds a_Dt);
-} ;
-
-
-
-
diff --git a/src/Mobs/Bat.cpp b/src/Mobs/Bat.cpp
index e419ceb2d..317dc50dc 100644
--- a/src/Mobs/Bat.cpp
+++ b/src/Mobs/Bat.cpp
@@ -4,12 +4,14 @@
#include "Bat.h"
#include "../Chunk.h"
-
cBat::cBat(void) :
- super("Bat", mtBat, "entity.bat.hurt", "entity.bat.death", 0.5, 0.9)
+ super(mtBat, "entity.bat.hurt", "entity.bat.death", 0.5, 0.9)
{
SetGravity(-2.0f);
SetAirDrag(0.05f);
+ m_EMPersonality = PASSIVE;
+ m_BehaviorWanderer.AttachToMonster(*this);
+ GetMonsterConfig("Bat");
}
diff --git a/src/Mobs/Bat.h b/src/Mobs/Bat.h
index 2da2dc3f2..cdda2e1c7 100644
--- a/src/Mobs/Bat.h
+++ b/src/Mobs/Bat.h
@@ -1,16 +1,16 @@
#pragma once
-#include "PassiveMonster.h"
-
+#include "Monster.h"
+#include "Behaviors/BehaviorWanderer.h"
class cBat :
- public cPassiveMonster
+ public cMonster
{
- typedef cPassiveMonster super;
+ typedef cMonster super;
public:
cBat(void);
@@ -18,6 +18,9 @@ public:
CLASS_PROTODEF(cBat)
bool IsHanging(void) const {return false; }
+
+private:
+ cBehaviorWanderer m_BehaviorWanderer;
} ;
diff --git a/src/Mobs/Behaviors/Behavior.cpp b/src/Mobs/Behaviors/Behavior.cpp
new file mode 100644
index 000000000..86922427f
--- /dev/null
+++ b/src/Mobs/Behaviors/Behavior.cpp
@@ -0,0 +1,102 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Behavior.h"
+#include "../Monster.h"
+
+
+
+bool cBehavior::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ LOGD("ERROR: Probably forgot to implement cBehavior::IsControlDesired but implement cBehavior::Tick");
+ return false;
+}
+
+
+
+
+
+bool cBehavior::ControlStarting(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ return true;
+}
+
+
+
+
+
+bool cBehavior::ControlEnding(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ return true;
+}
+
+
+
+
+
+void cBehavior::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ LOGD("ERROR: Called a TICK on a behavior that doesn't have one.");
+ ASSERT(1 == 0);
+}
+
+
+
+
+
+void cBehavior::PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ LOGD("ERROR: Called a PostTick on a behavior that doesn't have one.");
+ ASSERT(1 == 0);
+}
+
+
+
+
+
+void cBehavior::PreTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ LOGD("ERROR: Called a PreTick on a behavior that doesn't have one.");
+ ASSERT(1 == 0);
+}
+
+
+
+
+
+void cBehavior::OnRightClicked(cPlayer & a_Player)
+{
+ LOGD("ERROR: Called onRightClicked on a behavior that doesn't have one.");
+ ASSERT(1 == 0);
+}
+
+
+
+
+
+void cBehavior::Destroyed()
+{
+ LOGD("ERROR: Called Destroyed on a behavior that doesn't have one.");
+ ASSERT(1 == 0);
+}
+
+
+
+
+void cBehavior::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ UNUSED(a_TDI);
+ LOGD("ERROR: Called DoTakeDamage on a behavior that doesn't have one.");
+ ASSERT(1 == 0);
+}
diff --git a/src/Mobs/Behaviors/Behavior.h b/src/Mobs/Behaviors/Behavior.h
new file mode 100644
index 000000000..27833cc9b
--- /dev/null
+++ b/src/Mobs/Behaviors/Behavior.h
@@ -0,0 +1,30 @@
+#pragma once
+
+struct TakeDamageInfo;
+class cChunk;
+class cPlayer;
+class cMonster;
+class cPawn;
+class cWorld;
+class cItems;
+class cEntity;
+struct TakeDamageInfo;
+#include <chrono>
+
+class cBehavior
+{
+public:
+ // Tick-related
+ virtual bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
+ virtual bool ControlStarting(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
+ virtual bool ControlEnding(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
+ virtual void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
+ virtual void PreTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
+
+ // Other
+ virtual void OnRightClicked(cPlayer & a_Player);
+ virtual void Destroyed();
+ virtual void DoTakeDamage(TakeDamageInfo & a_TDI);
+ virtual ~cBehavior() {}
+};
diff --git a/src/Mobs/Behaviors/BehaviorAggressive.cpp b/src/Mobs/Behaviors/BehaviorAggressive.cpp
new file mode 100644
index 000000000..8cbacf5fa
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorAggressive.cpp
@@ -0,0 +1,66 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorAggressive.h"
+#include "BehaviorAttacker.h"
+#include "../Monster.h"
+#include "../../Chunk.h"
+#include "../../Entities/Player.h"
+
+
+
+cBehaviorAggressive::cBehaviorAggressive(ShouldBeAggressiveFunction a_ShouldBeAggressiveFunction)
+ : m_ShouldBeAggressiveFunction(a_ShouldBeAggressiveFunction)
+ , m_ShouldBeAgressive(true)
+ , m_AgressionCheckCountdown(1)
+{
+
+}
+
+void cBehaviorAggressive::AttachToMonster(cMonster & a_Parent)
+{
+ m_Parent = &a_Parent;
+ m_Parent->AttachPreTickBehavior(this);
+}
+
+
+void cBehaviorAggressive::PreTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+
+ if (m_ShouldBeAggressiveFunction != nullptr)
+ {
+ if (--m_AgressionCheckCountdown == 0)
+ {
+ m_AgressionCheckCountdown = 40;
+ m_ShouldBeAgressive = m_ShouldBeAggressiveFunction(*this, *m_Parent, a_Chunk);
+ }
+ }
+
+ if (!m_ShouldBeAgressive)
+ {
+ return;
+ }
+
+ // Target something new if we have no target
+ cBehaviorAttacker * BehaviorAttacker = m_Parent->GetBehaviorAttacker();
+ if ((BehaviorAttacker != nullptr) && (BehaviorAttacker->GetTarget() == nullptr))
+ {
+ // mobTodo enhance this
+ BehaviorAttacker->SetTarget(FindNewTarget());
+ }
+}
+
+
+
+
+
+cPawn * cBehaviorAggressive::FindNewTarget()
+{
+ cPlayer * Closest = m_Parent->GetNearestPlayer();
+ if ((Closest != nullptr) && (!Closest->CanMobsTarget()))
+ {
+ return nullptr;
+ }
+ return Closest; // May be null
+}
diff --git a/src/Mobs/Behaviors/BehaviorAggressive.h b/src/Mobs/Behaviors/BehaviorAggressive.h
new file mode 100644
index 000000000..923d646e0
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorAggressive.h
@@ -0,0 +1,40 @@
+#pragma once
+
+
+class cBehaviorAggressive;
+
+#include "Behavior.h"
+#include <functional>
+
+/** The mob is agressive toward specific mobtypes, or toward the player.
+This Behavior has a dependency on BehaviorAttacker. */
+
+typedef std::function<bool(cBehaviorAggressive & a_Behavior, cMonster & a_Monster, cChunk & a_Chunk)> ShouldBeAggressiveFunction;
+
+class cBehaviorAggressive : public cBehavior
+{
+
+public:
+ cBehaviorAggressive(ShouldBeAggressiveFunction a_ShouldBeAggressiveFunction = nullptr);
+ void AttachToMonster(cMonster & a_Parent);
+
+ // cBehaviorAggressive(cMonster * a_Parent, bool a_HatesPlayer);
+ // TODO agression toward specific players, and specific mobtypes, etc
+ // Agression under specific conditions (nighttime, etc)
+
+ // Functions our host Monster should invoke:
+ void PreTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+
+private:
+ cPawn * FindNewTarget();
+
+ // Our parent
+ cMonster * m_Parent;
+
+ // The mob we want to attack
+ cPawn * m_Target;
+
+ ShouldBeAggressiveFunction m_ShouldBeAggressiveFunction;
+ bool m_ShouldBeAgressive;
+ int m_AgressionCheckCountdown;
+};
diff --git a/src/Mobs/Behaviors/BehaviorAttacker.cpp b/src/Mobs/Behaviors/BehaviorAttacker.cpp
new file mode 100644
index 000000000..dd3b48de2
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorAttacker.cpp
@@ -0,0 +1,301 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorAttacker.h"
+#include "../Monster.h"
+#include "../../Entities/Pawn.h"
+#include "../../Entities/Player.h"
+#include "../../Tracer.h"
+
+
+cBehaviorAttacker::cBehaviorAttacker() :
+ m_AttackRate(3)
+ , m_AttackDamage(1)
+ , m_AttackRange(1)
+ , m_AttackCoolDownTicksLeft(0)
+ , m_IsStriking(false)
+ , m_Target(nullptr)
+ , m_ShouldRetaliate(true)
+{
+
+}
+
+
+
+
+
+void cBehaviorAttacker::AttachToMonster(cMonster & a_Parent)
+{
+ m_Parent = &a_Parent;
+ m_Parent->AttachTickBehavior(this);
+ m_Parent->AttachDestroyBehavior(this);
+ m_Parent->AttachPostTickBehavior(this);
+ m_Parent->AttachDoTakeDamageBehavior(this);
+ m_Parent->m_BehaviorAttackerPointer = this;
+}
+
+
+
+
+
+bool cBehaviorAttacker::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ // If we have a target, we have something to do! Return true and control the mob Ticks.
+ // Otherwise return false.
+ return (m_IsStriking || (GetTarget() != nullptr));
+}
+
+
+
+
+
+void cBehaviorAttacker::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+
+ ASSERT(m_Parent->GetHealth() > 0.0);
+
+ if (m_IsStriking)
+ {
+ if (DoStrike(++m_StrikeTickCnt))
+ {
+ m_Parent->UnpinBehavior(this);
+ m_IsStriking = false;
+ ResetStrikeCooldown();
+ }
+ #ifdef _DEBUG
+ if (m_StrikeTickCnt > 100)
+ {
+ LOGD("Sanity check failed. An attack took more than 5 seconds. Hmm");
+ ASSERT(1 == 0);
+ }
+ #endif
+ return;
+ }
+
+ if ((GetTarget() != nullptr))
+ {
+ ASSERT(GetTarget()->IsTicking());
+
+ if (GetTarget()->IsPlayer())
+ {
+ if (!static_cast<cPlayer *>(GetTarget())->CanMobsTarget())
+ {
+ SetTarget(nullptr);
+ }
+ }
+ }
+
+
+ ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == m_Parent->GetWorld())));
+ if (GetTarget() != nullptr)
+ {
+ m_Parent->SetLookingAt(m_Target);
+ if (TargetTooFar())
+ {
+ SetTarget(nullptr);
+ }
+ else
+ {
+ if (TargetIsInStrikeRadius() && TargetIsInLineOfSight())
+ {
+ StrikeIfReady();
+ }
+ else
+ {
+ m_Parent->MoveToPosition(m_Target->GetPosition());
+ // todo BehaviorApproacher for creeper sneaking, etc
+ }
+ }
+ }
+}
+
+
+
+
+
+void cBehaviorAttacker::PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ if (m_AttackCoolDownTicksLeft > 0)
+ {
+ m_AttackCoolDownTicksLeft -= 1;
+ }
+}
+
+
+
+
+
+void cBehaviorAttacker::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ if (!m_ShouldRetaliate)
+ {
+ return;
+ }
+
+ if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn())
+ {
+ if (
+ (!a_TDI.Attacker->IsPlayer()) ||
+ (static_cast<cPlayer *>(a_TDI.Attacker)->CanMobsTarget())
+ )
+ {
+ SetTarget(static_cast<cPawn*>(a_TDI.Attacker));
+ }
+ }
+}
+
+
+
+
+
+void cBehaviorAttacker::Destroyed()
+{
+ SetTarget(nullptr);
+}
+
+
+
+
+
+void cBehaviorAttacker::SetAttackRate(float a_AttackRate)
+{
+ m_AttackRate = a_AttackRate;
+}
+
+
+
+
+
+void cBehaviorAttacker::SetAttackRange(int a_AttackRange)
+{
+ m_AttackRange = a_AttackRange;
+}
+
+
+
+
+
+void cBehaviorAttacker::SetAttackDamage(int a_AttackDamage)
+{
+ m_AttackDamage = a_AttackDamage;
+}
+
+
+
+
+cPawn * cBehaviorAttacker::GetTarget()
+{
+ return m_Target;
+}
+
+
+
+
+
+void cBehaviorAttacker::SetTarget(cPawn * a_Target)
+{
+ m_Target = a_Target;
+}
+
+
+
+
+
+void cBehaviorAttacker::Strike()
+{
+ if (m_IsStriking)
+ {
+ return;
+ }
+ m_IsStriking = true;
+ m_StrikeTickCnt = 0;
+ m_Parent->PinBehavior(this);
+ m_Parent->StopMovingToPosition();
+}
+
+
+
+
+
+bool cBehaviorAttacker::TargetIsInStrikeRadius(void)
+{
+ ASSERT(GetTarget() != nullptr);
+ ASSERT(m_Parent != nullptr);
+ return ((GetTarget()->GetPosition() - m_Parent->GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange));
+}
+
+
+
+
+
+bool cBehaviorAttacker::TargetIsInLineOfSight()
+{
+ ASSERT(GetTarget() != nullptr);
+ ASSERT(m_Parent != nullptr);
+
+
+ // mobtodo make this smarter
+
+ cTracer LineOfSight(m_Parent->GetWorld());
+ Vector3d MyHeadPosition = m_Parent->GetHeadPosition();
+ Vector3d TargetHeadPosition = GetTarget()->GetHeadPosition();
+ Vector3d AttackDirection1(TargetHeadPosition - MyHeadPosition);
+
+ if (LineOfSight.Trace(MyHeadPosition, AttackDirection1,
+ static_cast<int>(AttackDirection1.Length()))
+ )
+ {
+ return false;
+ }
+
+ Vector3d MyFeetPosition = m_Parent->GetPosition() + Vector3d(0, 0.5, 0);
+ Vector3d TargetFeetPosition = GetTarget()->GetPosition() + Vector3d(0, 0.5, 0);
+ Vector3d AttackDirection2(TargetFeetPosition - MyFeetPosition);
+ if (LineOfSight.Trace(MyFeetPosition, AttackDirection2,
+ static_cast<int>(AttackDirection2.Length()))
+ )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cBehaviorAttacker::TargetTooFar()
+{
+ ASSERT(m_Target != nullptr);
+ if ((GetTarget()->GetPosition() - m_Parent->GetPosition()).Length() > m_Parent->GetSightDistance())
+ {
+ return true;
+ }
+ return false;
+}
+
+
+
+
+
+void cBehaviorAttacker::ResetStrikeCooldown()
+{
+ m_AttackCoolDownTicksLeft = static_cast<int>(3 * 20 * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every 3 seconds
+}
+
+
+
+
+
+void cBehaviorAttacker::StrikeIfReady()
+{
+ if (m_AttackCoolDownTicksLeft == 0)
+ {
+ Strike();
+ }
+}
diff --git a/src/Mobs/Behaviors/BehaviorAttacker.h b/src/Mobs/Behaviors/BehaviorAttacker.h
new file mode 100644
index 000000000..428e7a340
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorAttacker.h
@@ -0,0 +1,83 @@
+#pragma once
+
+class cBehaviorAttacker;
+
+#include "Behavior.h"
+
+
+/** Grants attack capability to the mob. Note that this is not the same as agression!
+The mob may possess this trait and not attack anyone or only attack when provoked.
+Unlike most traits, this one has several forms, and therefore it is an abstract type
+You should use one of its derived classes, and you cannot use it directly. */
+class cBehaviorAttacker : public cBehavior
+{
+
+public:
+ cBehaviorAttacker();
+ virtual void AttachToMonster(cMonster & a_Parent);
+
+
+ // Our host monster will call these once it loads its config file
+ void SetAttackRate(float a_AttackRate);
+ void SetAttackRange(int a_AttackRange);
+ void SetAttackDamage(int a_AttackDamage);
+
+ // Behavior functions
+ bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ void Destroyed() override;
+ void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+
+ /** Returns the target pointer, or a nullptr if we're not targeting anyone. */
+ cPawn * GetTarget();
+
+ /** Sets a new target. Forgets the older target if present. Set this to nullptr to unset target. */
+ void SetTarget(cPawn * a_Target);
+
+ /** Makes the mob perform a strike the next tick. Ignores the strike cooldown.
+ * Ignored if already striking. Some attack behaviors do not require a target
+ to be set (e.g. creeper explosion). The behaviors that require a target
+ ignore this call if the target is null. */
+ void Strike();
+
+ /** Makes the mob strike a target the next tick only if the strike cooldown permits it.
+ * Ignored if already striking or if no target is set. */
+ void StrikeIfReady();
+protected:
+
+ /** Called when the actual attack should be made. Will be called again and again every tick until
+ it returns false. a_StrikeTickCnt tracks how many times it was called. It is 1 the first call.
+ It increments by 1 each call. This mechanism allows multi-tick attacks, like blazes shooting multiple
+ fireballs, but most attacks are single tick and return true the first call. Target is not guaranteed to be valid
+ during these calls. The behavior is pinned until true is returned, meaning no other behaviors can tick. */
+ virtual bool DoStrike(int a_StrikeTickCnt) = 0;
+
+ // Target related methods
+ bool TargetIsInStrikeRadius();
+ bool TargetIsInLineOfSight();
+ bool TargetTooFar();
+ void StrikeIfReady(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
+
+ // Cooldown stuff
+ void ResetStrikeCooldown();
+
+ // Our attacking parameters (Set by the setter methods, loaded from a config file in cMonster)
+ float m_AttackRate;
+ int m_AttackDamage;
+ int m_AttackRange;
+ int m_AttackCoolDownTicksLeft;
+
+ bool m_IsStriking;
+
+ /** Our parent */
+ cMonster * m_Parent;
+
+private:
+
+ // The mob we want to attack
+ cPawn * m_Target;
+ int m_StrikeTickCnt;
+ bool m_ShouldRetaliate; // Should we attack back whoever attacks us?
+
+};
diff --git a/src/Mobs/Behaviors/BehaviorAttackerMelee.cpp b/src/Mobs/Behaviors/BehaviorAttackerMelee.cpp
new file mode 100644
index 000000000..3d25f34b4
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorAttackerMelee.cpp
@@ -0,0 +1,27 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorAttackerMelee.h"
+#include "../Monster.h"
+#include "../../Entities/Pawn.h"
+#include "../../BlockID.h"
+
+cBehaviorAttackerMelee::cBehaviorAttackerMelee(PostAttackFunction a_PostAttackFunction)
+ : m_PostAttackFunction(a_PostAttackFunction)
+{
+
+}
+
+
+
+
+
+bool cBehaviorAttackerMelee::DoStrike(int a_StrikeTickCnt)
+{
+ UNUSED(a_StrikeTickCnt);
+ GetTarget()->TakeDamage(dtMobAttack, m_Parent, m_AttackDamage, 0);
+ if (m_PostAttackFunction != nullptr)
+ {
+ m_PostAttackFunction(*this, *m_Parent, *GetTarget());
+ }
+ return true; // Finish the strike. It only takes 1 tick.
+}
diff --git a/src/Mobs/Behaviors/BehaviorAttackerMelee.h b/src/Mobs/Behaviors/BehaviorAttackerMelee.h
new file mode 100644
index 000000000..69d626fbc
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorAttackerMelee.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "BehaviorAttacker.h"
+#include <functional>
+class cBehaviorAttackerMelee;
+
+/** Grants the mob that ability to approach a target and then melee attack it.
+Use BehaviorAttackerMelee::SetTarget to attack. */
+typedef std::function<void(cBehaviorAttackerMelee & a_Behavior, cMonster & a_Attacker, cPawn & a_Attacked)> PostAttackFunction;
+class cBehaviorAttackerMelee : public cBehaviorAttacker
+{
+public:
+ cBehaviorAttackerMelee(PostAttackFunction a_PostAttackFunction = nullptr);
+ bool DoStrike(int a_StrikeTickCnt) override;
+private:
+ PostAttackFunction m_PostAttackFunction;
+};
diff --git a/src/Mobs/Behaviors/BehaviorAttackerRanged.cpp b/src/Mobs/Behaviors/BehaviorAttackerRanged.cpp
new file mode 100644
index 000000000..888dc3497
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorAttackerRanged.cpp
@@ -0,0 +1,43 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorAttackerRanged.h"
+#include "../Monster.h"
+#include "../../Entities/Pawn.h"
+#include "../../BlockID.h"
+#include "../../Entities/ArrowEntity.h"
+
+cBehaviorAttackerRanged::cBehaviorAttackerRanged(
+ RangedShootingFunction a_RangedShootingFunction, int a_ProjectileAmount,
+ int a_ShootingIntervals) :
+ m_ShootingFunction(a_RangedShootingFunction),
+ m_ProjectileAmount(a_ProjectileAmount),
+ m_ShootingIntervals(a_ShootingIntervals)
+{
+
+}
+
+
+bool cBehaviorAttackerRanged::DoStrike(int a_StrikeTickCnt)
+{
+ UNUSED(a_StrikeTickCnt);
+
+ // stop shooting if target is lost
+ if ((GetTarget() == nullptr))
+ {
+ return true;
+ }
+
+ // Stop shooting if we've shot m_ProjectileAmount times.
+ if (a_StrikeTickCnt - 1 == m_ShootingIntervals * m_ProjectileAmount)
+ {
+ return true;
+ }
+
+ // shoot once every m_ShootingIntervals.
+ // Starting immediately at first call to DoStrike
+ if ((a_StrikeTickCnt - 1) % m_ShootingIntervals == 0)
+ {
+ m_ShootingFunction(*this, *m_Parent, *GetTarget());
+ }
+ return false;
+}
diff --git a/src/Mobs/Behaviors/BehaviorAttackerRanged.h b/src/Mobs/Behaviors/BehaviorAttackerRanged.h
new file mode 100644
index 000000000..1d9e4fcd9
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorAttackerRanged.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "BehaviorAttacker.h"
+#include <functional>
+class cBehaviorAttackerRanged;
+
+/** Grants the mob that ability to approach a target and then melee attack it.
+Use BehaviorAttackerMelee::SetTarget to attack. */
+typedef std::function<void(cBehaviorAttackerRanged & a_Behavior,
+ cMonster & a_Attacker, cPawn & a_Attacked)> RangedShootingFunction;
+
+class cBehaviorAttackerRanged : public cBehaviorAttacker
+{
+public:
+ cBehaviorAttackerRanged(RangedShootingFunction a_RangedShootingFUnction,
+ int a_ProjectileAmount = 1, int a_ShootingIntervals = 1);
+ bool DoStrike(int a_StrikeTickCnt) override;
+
+private:
+ RangedShootingFunction m_ShootingFunction;
+ int m_ProjectileAmount;
+ int m_ShootingIntervals;
+};
diff --git a/src/Mobs/Behaviors/BehaviorAttackerSuicideBomber.cpp b/src/Mobs/Behaviors/BehaviorAttackerSuicideBomber.cpp
new file mode 100644
index 000000000..843cc58ef
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorAttackerSuicideBomber.cpp
@@ -0,0 +1,125 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorAttackerSuicideBomber.h"
+#include "../Monster.h"
+#include "../../Entities/Pawn.h"
+#include "../../Entities/Player.h"
+#include "../../BlockID.h"
+
+
+
+
+cBehaviorAttackerSuicideBomber::cBehaviorAttackerSuicideBomber() :
+ m_bIsBlowing(false),
+ m_bIsCharged(false),
+ m_BurnedWithFlintAndSteel(false)
+{
+
+}
+
+
+
+
+
+void cBehaviorAttackerSuicideBomber::AttachToMonster(cMonster & a_Parent)
+{
+ cBehaviorAttacker::AttachToMonster(a_Parent);
+ m_Parent->AttachRightClickBehavior(this);
+}
+
+
+
+
+
+bool cBehaviorAttackerSuicideBomber::DoStrike(int a_StrikeTickCnt)
+{
+ UNUSED(a_StrikeTickCnt);
+
+ // phase 1: start blowing up
+ if (a_StrikeTickCnt == 1)
+ {
+ ASSERT(!m_bIsBlowing);
+
+ m_Parent->GetWorld()->BroadcastSoundEffect("entity.creeper.primed", m_Parent->GetPosX(), m_Parent->GetPosY(), m_Parent->GetPosZ(), 1.f, (0.75f + (static_cast<float>((m_Parent->GetUniqueID() * 23) % 32)) / 64));
+ m_bIsBlowing = true;
+ m_Parent->GetWorld()->BroadcastEntityMetadata(*m_Parent);
+
+ return false;
+ }
+
+ ASSERT(m_bIsBlowing);
+ if (((GetTarget() == nullptr) || (!TargetIsInStrikeRadius())) && (!m_BurnedWithFlintAndSteel))
+ {
+ m_bIsBlowing = false;
+ m_Parent->GetWorld()->BroadcastEntityMetadata(*m_Parent);
+ return true;
+ }
+
+ if (a_StrikeTickCnt == 30)
+ {
+ ASSERT(m_Parent->GetHealth() > 0.0);
+ m_Parent->GetWorld()->DoExplosionAt((m_bIsCharged ? 5 : 3), m_Parent->GetPosX(), m_Parent->GetPosY(), m_Parent->GetPosZ(), false, esMonster, this);
+ m_Parent->Destroy(); // Just in case we aren't killed by the explosion
+ return true;
+ }
+
+ return false;
+}
+
+
+
+
+
+void cBehaviorAttackerSuicideBomber::OnRightClicked(cPlayer & a_Player)
+{
+ if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_FLINT_AND_STEEL))
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.UseEquippedItem();
+ }
+ if (!m_BurnedWithFlintAndSteel)
+ {
+ m_BurnedWithFlintAndSteel = true;
+ Strike();
+ }
+ }
+}
+
+
+bool cBehaviorAttackerSuicideBomber::IsBlowing(void) const
+{
+ return m_bIsBlowing;
+}
+
+
+
+
+
+bool cBehaviorAttackerSuicideBomber::IsCharged(void) const
+{
+ return m_bIsCharged;
+}
+
+
+
+
+
+bool cBehaviorAttackerSuicideBomber::IsBurnedWithFlintAndSteel(void) const
+{
+ return m_BurnedWithFlintAndSteel;
+}
+
+
+
+
+
+void cBehaviorAttackerSuicideBomber::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ if (a_TDI.DamageType == dtLightning)
+ {
+ m_bIsCharged = true;
+ m_Parent->GetWorld()->BroadcastEntityMetadata(*m_Parent);
+ }
+ cBehaviorAttacker::DoTakeDamage(a_TDI);
+}
diff --git a/src/Mobs/Behaviors/BehaviorAttackerSuicideBomber.h b/src/Mobs/Behaviors/BehaviorAttackerSuicideBomber.h
new file mode 100644
index 000000000..b9cb155e8
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorAttackerSuicideBomber.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "BehaviorAttacker.h"
+
+/** Grants the mob that ability to approach a target and then melee attack it.
+Use BehaviorAttackerMelee::SetTarget to attack. */
+class cBehaviorAttackerSuicideBomber : public cBehaviorAttacker
+{
+public:
+ cBehaviorAttackerSuicideBomber();
+ void AttachToMonster(cMonster & a_Parent) override;
+ // cBehaviorAttacker also implements those and we need to call super on them
+ void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+
+ bool DoStrike(int a_StrikeTickCnt) override;
+ void OnRightClicked(cPlayer & a_Player) override;
+
+ bool IsBlowing(void) const;
+ bool IsCharged(void) const;
+ bool IsBurnedWithFlintAndSteel(void) const;
+
+private:
+ bool m_bIsBlowing, m_bIsCharged, m_BurnedWithFlintAndSteel;
+};
diff --git a/src/Mobs/Behaviors/BehaviorBrave.cpp b/src/Mobs/Behaviors/BehaviorBrave.cpp
new file mode 100644
index 000000000..6aba310a1
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorBrave.cpp
@@ -0,0 +1,27 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorBrave.h"
+#include "BehaviorAttacker.h"
+#include "../Monster.h"
+#include "../../Entities/Entity.h"
+
+void cBehaviorBrave::AttachToMonster(cMonster & a_Parent)
+{
+ m_Parent = &a_Parent;
+ m_Parent->AttachDoTakeDamageBehavior(this);
+}
+
+
+
+
+void cBehaviorBrave::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ cBehaviorAttacker * AttackBehavior = m_Parent->GetBehaviorAttacker();
+ if ((AttackBehavior != nullptr) && (a_TDI.Attacker != m_Parent) &&
+ (a_TDI.Attacker != nullptr) && (a_TDI.Attacker->IsPawn())
+ )
+ {
+ AttackBehavior->SetTarget(static_cast<cPawn*>(a_TDI.Attacker));
+ }
+}
+
diff --git a/src/Mobs/Behaviors/BehaviorBrave.h b/src/Mobs/Behaviors/BehaviorBrave.h
new file mode 100644
index 000000000..0a59f90b8
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorBrave.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "Behavior.h"
+
+/** Makes the mob fight back any other mob that damages it. Mob should have BehaviorAttacker to work.
+This behavior does not make sense in combination with BehaviorCoward. */
+class cBehaviorBrave : cBehavior
+{
+public:
+ void AttachToMonster(cMonster & a_Parent);
+ void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+
+private:
+ cMonster * m_Parent; // Our Parent
+};
diff --git a/src/Mobs/Behaviors/BehaviorBreeder.cpp b/src/Mobs/Behaviors/BehaviorBreeder.cpp
new file mode 100644
index 000000000..cec7e6295
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorBreeder.cpp
@@ -0,0 +1,365 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorBreeder.h"
+#include "../../World.h"
+#include "../Monster.h"
+#include "../../Entities/Player.h"
+#include "../../Item.h"
+#include "../../BoundingBox.h"
+
+cBehaviorBreeder::cBehaviorBreeder() :
+ m_LovePartner(nullptr),
+ m_LoveTimer(0),
+ m_LoveCooldown(0),
+ m_MatingTimer(0)
+{
+}
+
+
+
+
+
+void cBehaviorBreeder::AttachToMonster(cMonster & a_Parent)
+{
+ m_Parent = &a_Parent;
+ m_Parent->AttachTickBehavior(this);
+ m_Parent->AttachPostTickBehavior(this);
+ m_Parent->AttachRightClickBehavior(this);
+ m_Parent->AttachDestroyBehavior(this);
+ m_Parent->m_BehaviorBreederPointer = this;
+}
+
+
+
+
+
+void cBehaviorBreeder::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ LOGD("mobDebug - Behavior Breeder: Tick");
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ cWorld * World = m_Parent->GetWorld();
+ // if we have a partner, mate
+ if (m_LovePartner != nullptr)
+ {
+ if (m_MatingTimer > 0)
+ {
+ // If we should still mate, keep bumping into them until baby is made
+ Vector3d Pos = m_LovePartner->GetPosition();
+ m_Parent->MoveToPosition(Pos);
+ }
+ else
+ {
+ // Mating finished. Spawn baby
+ Vector3f Pos = (m_Parent->GetPosition() + m_LovePartner->GetPosition()) * 0.5;
+ UInt32 BabyID = World->SpawnMob(Pos.x, Pos.y, Pos.z, m_Parent->GetMobType(), true);
+
+ class cBabyInheritCallback :
+ public cEntityCallback
+ {
+ public:
+ cMonster * Baby;
+ cBabyInheritCallback() : Baby(nullptr) { }
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ Baby = static_cast<cMonster *>(a_Entity);
+ return true;
+ }
+ } Callback;
+
+ m_Parent->GetWorld()->DoWithEntityByID(BabyID, Callback);
+ if (Callback.Baby != nullptr)
+ {
+ Callback.Baby->InheritFromParents(m_Parent, m_LovePartner);
+ }
+
+ cFastRandom Random;
+ World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, 1 + (Random.RandInt() % 6));
+
+ m_LovePartner->GetBehaviorBreeder()->ResetLoveMode();
+ ResetLoveMode();
+ }
+ }
+}
+
+
+
+
+
+void cBehaviorBreeder::PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ if (m_MatingTimer > 0)
+ {
+ m_MatingTimer--;
+ }
+ if (m_LoveCooldown > 0)
+ {
+ m_LoveCooldown--;
+ }
+ if (m_LoveTimer > 0)
+ {
+ m_LoveTimer--;
+ }
+}
+
+
+
+
+
+bool cBehaviorBreeder::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ cWorld * World = m_Parent->GetWorld();
+
+ // if we have a love partner, we should control the mob
+ if (m_LovePartner != nullptr)
+ {
+ return true;
+ }
+
+ // If we are in love mode and we have no partner, try to find one
+ if (m_LoveTimer > 0)
+ {
+ class LookForLover : public cEntityCallback
+ {
+ public:
+ cMonster * m_Me;
+ LookForLover(cMonster * a_Me) :
+ m_Me(a_Me)
+ {
+ }
+
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ // If the entity is not a monster, don't breed with it
+ // Also, do not self-breed
+ if ((a_Entity->GetEntityType() != cEntity::eEntityType::etMonster) || (a_Entity == m_Me))
+ {
+ return false;
+ }
+
+ auto PotentialPartner = static_cast<cMonster*>(a_Entity);
+
+ // If the potential partner is not of the same species, don't breed with it
+ if (PotentialPartner->GetMobType() != m_Me->GetMobType())
+ {
+ return false;
+ }
+
+ auto PartnerBreedingBehavior = PotentialPartner->GetBehaviorBreeder();
+ auto MyBreedingBehavior = m_Me->GetBehaviorBreeder();
+
+ // If the potential partner is not in love
+ // Or they already have a mate, do not breed with them
+
+ if ((!PartnerBreedingBehavior->IsInLove()) || (PartnerBreedingBehavior->GetPartner() != nullptr))
+ {
+ return false;
+ }
+
+ // All conditions met, let's breed!
+ PartnerBreedingBehavior->EngageLoveMode(m_Me);
+ MyBreedingBehavior->EngageLoveMode(PotentialPartner);
+ return true;
+ }
+ } Callback(m_Parent);
+
+ World->ForEachEntityInBox(cBoundingBox(m_Parent->GetPosition(), 8, 8), Callback);
+ if (m_LovePartner != nullptr)
+ {
+ return true; // We found love and took control of the monster, prevent other Behaviors from doing so
+ }
+ }
+
+ return false;
+}
+
+void cBehaviorBreeder::Destroyed()
+{
+ LOGD("mobDebug - Behavior Breeder: Destroyed");
+ if (m_LovePartner != nullptr)
+ {
+ m_LovePartner->GetBehaviorBreeder()->ResetLoveMode();
+ }
+}
+
+
+
+
+
+void cBehaviorBreeder::OnRightClicked(cPlayer & a_Player)
+{
+ // If a player holding breeding items right-clicked me, go into love mode
+ if ((m_LoveCooldown == 0) && !IsInLove() && !m_Parent->IsBaby())
+ {
+ short HeldItem = a_Player.GetEquippedItem().m_ItemType;
+ cItems BreedingItems;
+ m_Parent->GetFollowedItems(BreedingItems);
+ if (BreedingItems.ContainsType(HeldItem))
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ m_LoveTimer = 20 * 30; // half a minute
+ m_Parent->GetWorld()->BroadcastEntityStatus(*m_Parent, cEntity::eEntityStatus::esMobInLove);
+ }
+ }
+}
+
+
+
+void cBehaviorBreeder::EngageLoveMode(cMonster * a_Partner)
+{
+ m_LovePartner = a_Partner;
+ m_MatingTimer = 50; // about 3 seconds of mating
+}
+
+
+
+
+
+void cBehaviorBreeder::ResetLoveMode()
+{
+ m_LovePartner = nullptr;
+ m_LoveTimer = 0;
+ m_MatingTimer = 0;
+ m_LoveCooldown = 20 * 60 * 5; // 5 minutes
+
+ // when an animal is in love mode, the client only stops sending the hearts if we let them know it's in cooldown, which is done with the "age" metadata
+ m_Parent->GetWorld()->BroadcastEntityMetadata(*m_Parent);
+}
+
+
+
+
+
+bool cBehaviorBreeder::IsInLove() const
+{
+ return m_LoveTimer > 0;
+}
+
+
+
+
+
+bool cBehaviorBreeder::IsInLoveCooldown() const
+{
+ return (m_LoveCooldown > 0);
+}
+
+
+
+
+/*** CODE TO BE USED AFTER the lambda merge
+ * bool cBehaviorBreeder::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ cWorld * World = m_Parent->GetWorld();
+
+ // if we have a love partner, we should control the mob
+ if (m_LovePartner != nullptr)
+ {
+ return true;
+ }
+
+ // If we are in love mode and we have no partner, try to find one
+ if (m_LoveTimer > 0)
+ {
+ m_Parent->GetWorld()->ForEachEntityInBox(cBoundingBox(m_Parent->GetPosition(), 8, 8), [=](cEntity & a_Entity)
+ {
+ // If the entity is not a monster, don't breed with it
+ // Also, do not self-breed
+ if ((a_Entity.GetEntityType() != cEntity::eEntityType::etMonster) || (&a_Entity == m_Parent))
+ {
+ return false;
+ }
+
+ auto PotentialPartner = static_cast<cMonster*>(&a_Entity);
+
+ // If the potential partner is not of the same species, don't breed with it
+ if (PotentialPartner->GetMobType() != m_Parent->GetMobType())
+ {
+ return false;
+ }
+
+ auto PartnerBreedingBehavior = PotentialPartner->GetBehaviorBreeder();
+ auto MyBreedingBehavior = m_Parent->GetBehaviorBreeder();
+
+ // If the potential partner is not in love
+ // Or they already have a mate, do not breed with them
+
+ if ((!PartnerBreedingBehavior->IsInLove()) || (PartnerBreedingBehavior->GetPartner() != nullptr))
+ {
+ return false;
+ }
+
+ // All conditions met, let's breed!
+ PartnerBreedingBehavior->EngageLoveMode(m_Parent);
+ MyBreedingBehavior->EngageLoveMode(PotentialPartner);
+ return true;
+ });
+
+
+ if (m_LovePartner != nullptr)
+ {
+ return true; // We found love and took control of the monster, prevent other Behaviors from doing so
+ }
+ }
+
+ return false;
+} **/
+
+
+
+
+
+
+/*
+ * CODE to be used AFTER the lambda merge
+ * void cBehaviorBreeder::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ LOGD("mobDebug - Behavior Breeder: Tick");
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ cWorld * World = m_Parent->GetWorld();
+ // if we have a partner, mate
+ if (m_LovePartner != nullptr)
+ {
+ if (m_MatingTimer > 0)
+ {
+ // If we should still mate, keep bumping into them until baby is made
+ Vector3d Pos = m_LovePartner->GetPosition();
+ m_Parent->MoveToPosition(Pos);
+ }
+ else
+ {
+ // Mating finished. Spawn baby
+ Vector3f Pos = (m_Parent->GetPosition() + m_LovePartner->GetPosition()) * 0.5;
+ UInt32 BabyID = World->SpawnMob(Pos.x, Pos.y, Pos.z, m_Parent->GetMobType(), true);
+
+ cMonster * Baby = nullptr;
+
+ m_Parent->GetWorld()->DoWithEntityByID(BabyID, [&](cEntity & a_Entity)
+ {
+ Baby = static_cast<cMonster *>(&a_Entity);
+ return true;
+ });
+
+ if (Baby != nullptr)
+ {
+ Baby->InheritFromParents(m_Parent, m_LovePartner);
+ }
+
+ cFastRandom Random;
+ World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, 1 + (Random.RandInt() % 6));
+
+ m_LovePartner->GetBehaviorBreeder()->ResetLoveMode();
+ ResetLoveMode();
+ }
+ }
+}*/
diff --git a/src/Mobs/Behaviors/BehaviorBreeder.h b/src/Mobs/Behaviors/BehaviorBreeder.h
new file mode 100644
index 000000000..a3d0b51af
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorBreeder.h
@@ -0,0 +1,52 @@
+#pragma once
+
+class cBehaviorBreeder;
+
+#include "Behavior.h"
+
+/** Grants breeding capabilities to the mob. */
+class cBehaviorBreeder : public cBehavior
+{
+
+public:
+ cBehaviorBreeder();
+ void AttachToMonster(cMonster & a_Parent);
+
+ // Functions our host Monster should invoke:
+ bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ void OnRightClicked(cPlayer & a_Player) override;
+ void Destroyed() override;
+
+ /** Returns the partner which the monster is currently mating with. */
+ cMonster * GetPartner(void) const { return m_LovePartner; }
+
+ /** Start the mating process. Causes the monster to keep bumping into the partner until m_MatingTimer reaches zero. */
+ void EngageLoveMode(cMonster * a_Partner);
+
+ /** Finish the mating process. Called after a baby is born. Resets all breeding related timers and sets m_LoveCooldown to 20 minutes. */
+ void ResetLoveMode();
+
+ /** Returns whether the monster has just been fed and is ready to mate. If this is "true" and GetPartner isn't "nullptr", then the monster is mating. */
+ bool IsInLove() const;
+
+ /** Returns whether the monster is tired of breeding and is in the cooldown state. */
+ bool IsInLoveCooldown() const;
+
+private:
+ /** Our parent */
+ cMonster * m_Parent;
+
+ /** The monster's breeding partner. */
+ cMonster * m_LovePartner;
+
+ /** If above 0, the monster is in love mode, and will breed if a nearby monster is also in love mode. Decrements by 1 per tick till reaching zero. */
+ int m_LoveTimer;
+
+ /** If above 0, the monster is in cooldown mode and will refuse to breed. Decrements by 1 per tick till reaching zero. */
+ int m_LoveCooldown;
+
+ /** The monster is engaged in mating, once this reaches zero, a baby will be born. Decrements by 1 per tick till reaching zero, then a baby is made and ResetLoveMode() is called. */
+ int m_MatingTimer;
+};
diff --git a/src/Mobs/Behaviors/BehaviorCoward.cpp b/src/Mobs/Behaviors/BehaviorCoward.cpp
new file mode 100644
index 000000000..fc13cf5b9
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorCoward.cpp
@@ -0,0 +1,97 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorCoward.h"
+#include "../Monster.h"
+#include "../../World.h"
+#include "../../Entities/Player.h"
+#include "../../Entities/Entity.h"
+
+cBehaviorCoward::cBehaviorCoward() :
+ m_Attacker(nullptr)
+{
+}
+
+
+
+
+void cBehaviorCoward::AttachToMonster(cMonster & a_Parent)
+{
+ m_Parent = &a_Parent;
+ m_Parent->AttachTickBehavior(this);
+ m_Parent->AttachDoTakeDamageBehavior(this);
+}
+
+
+
+
+
+bool cBehaviorCoward::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ return (m_Attacker != nullptr); //mobTodo probably not so safe pointer (and cChaser m_Target too)
+}
+
+
+
+
+
+bool cBehaviorCoward::ControlStarting(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ m_Parent->GetPathFinder().SetDontCare(true); // We don't care we're we are going when
+ // wandering. If a path is not found, the pathfinder just modifies our destination.
+ m_Parent->SetRelativeWalkSpeed(m_Parent->GetRelativeWalkSpeed() * 3);
+ return true;
+}
+
+
+bool cBehaviorCoward::ControlEnding(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ m_Parent->SetRelativeWalkSpeed(m_Parent->GetRelativeWalkSpeed() / 3);
+ m_Parent->GetPathFinder().SetDontCare(false);
+ return true;
+}
+
+
+
+
+
+void cBehaviorCoward::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ if (m_Attacker == nullptr)
+ {
+ return;
+ }
+
+ // TODO NOT SAFE
+ if (m_Attacker->IsDestroyed() || (m_Attacker->GetPosition() - m_Parent->GetPosition()).Length() > m_Parent->GetSightDistance())
+ {
+ // We lost the attacker
+ m_Attacker = nullptr;
+ return;
+ }
+
+ Vector3d newloc = m_Parent->GetPosition();
+ newloc.x = (m_Attacker->GetPosition().x < newloc.x)? (newloc.x + m_Parent->GetSightDistance()): (newloc.x - m_Parent->GetSightDistance());
+ newloc.z = (m_Attacker->GetPosition().z < newloc.z)? (newloc.z + m_Parent->GetSightDistance()): (newloc.z - m_Parent->GetSightDistance());
+ m_Parent->MoveToPosition(newloc);
+}
+
+
+
+
+
+void cBehaviorCoward::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ if ((a_TDI.Attacker != m_Parent) && (a_TDI.Attacker != nullptr))
+ {
+ m_Attacker = a_TDI.Attacker;
+ }
+}
+
diff --git a/src/Mobs/Behaviors/BehaviorCoward.h b/src/Mobs/Behaviors/BehaviorCoward.h
new file mode 100644
index 000000000..3232f807b
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorCoward.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "Behavior.h"
+
+/** Makes the mob run away from any other mob that damages it. */
+class cBehaviorCoward : cBehavior
+{
+public:
+ cBehaviorCoward();
+ void AttachToMonster(cMonster & a_Parent);
+
+ bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ bool ControlStarting(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ bool ControlEnding(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+
+
+private:
+ cMonster * m_Parent; // Our Parent
+ cEntity * m_Attacker; // The entity we're running away from
+};
diff --git a/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp b/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp
new file mode 100644
index 000000000..1271574fe
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp
@@ -0,0 +1,103 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorDayLightBurner.h"
+#include "../Monster.h"
+#include "../../Entities/Player.h"
+#include "../../Entities/Entity.h"
+#include "../../Chunk.h"
+
+
+
+
+
+void cBehaviorDayLightBurner::AttachToMonster(cMonster & a_Parent)
+{
+ m_Parent = &a_Parent;
+ m_Parent->AttachPostTickBehavior(this);
+ m_Parent->GetPathFinder().SetAvoidSunlight(true);
+}
+
+
+
+
+
+void cBehaviorDayLightBurner::PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ int RelY = static_cast<int>(m_Parent->GetPosY());
+ if ((RelY < 0) || (RelY >= cChunkDef::Height))
+ {
+ // Outside the world
+ return;
+ }
+ if (!a_Chunk.IsLightValid())
+ {
+ m_Parent->GetWorld()->QueueLightChunk(m_Parent->GetChunkX(), m_Parent->GetChunkZ());
+ return;
+ }
+
+ if (!m_Parent->IsOnFire() && WouldBurnAt(m_Parent->GetPosition(), a_Chunk, *m_Parent))
+ {
+ // Burn for 100 ticks, then decide again
+ m_Parent->StartBurning(100);
+ }
+}
+
+
+
+
+bool cBehaviorDayLightBurner::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk, cMonster & a_Monster)
+{
+ int RelY = FloorC(a_Location.y);
+ if (RelY <= 0)
+ {
+ // The mob is about to die, no point in burning
+ return false;
+ }
+ if (RelY >= cChunkDef::Height)
+ {
+ // Always burn above the world
+ return true;
+ }
+
+ PREPARE_REL_AND_CHUNK(a_Location, a_Chunk);
+ if (!RelSuccess)
+ {
+ return false;
+ }
+
+ if (
+ (Chunk->GetBlock(Rel.x, Rel.y, Rel.z) != E_BLOCK_SOULSAND) && // Not on soulsand
+ (a_Monster.GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime
+ a_Monster.GetWorld()->IsWeatherSunnyAt(static_cast<int>(a_Monster.GetPosX()), static_cast<int>(a_Monster.GetPosZ())) // Not raining
+ )
+ {
+ int MobHeight = CeilC(a_Location.y + a_Monster.GetHeight()) - 1; // The height of the mob head
+ if (MobHeight >= cChunkDef::Height)
+ {
+ return true;
+ }
+ // Start with the highest block and scan down to just abovethe mob's head.
+ // If a non transparent is found, return false (do not burn). Otherwise return true.
+ // Note that this loop is not a performance concern as transparent blocks are rare and the loop almost always bailes out
+ // instantly.(An exception is e.g. standing under a long column of glass).
+ int CurrentBlock = Chunk->GetHeight(Rel.x, Rel.z);
+ while (CurrentBlock > MobHeight)
+ {
+ BLOCKTYPE Block = Chunk->GetBlock(Rel.x, CurrentBlock, Rel.z);
+ if (
+ // Do not burn if a block above us meets one of the following conditions:
+ (!cBlockInfo::IsTransparent(Block)) ||
+ (Block == E_BLOCK_LEAVES) ||
+ (Block == E_BLOCK_NEW_LEAVES) ||
+ (IsBlockWater(Block))
+ )
+ {
+ return false;
+ }
+ --CurrentBlock;
+ }
+ return true;
+
+ }
+ return false;
+}
diff --git a/src/Mobs/Behaviors/BehaviorDayLightBurner.h b/src/Mobs/Behaviors/BehaviorDayLightBurner.h
new file mode 100644
index 000000000..920f9d5c7
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorDayLightBurner.h
@@ -0,0 +1,17 @@
+#pragma once
+
+// mobTodo I just need vector3d
+#include "Behavior.h"
+#include "../../World.h"
+
+class cBehaviorDayLightBurner : cBehavior
+{
+public:
+ void AttachToMonster(cMonster & a_Parent);
+ void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ static bool WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk, cMonster & a_Monster);
+
+private:
+ cMonster * m_Parent; // Our Parent
+ cEntity * m_Attacker; // The entity we're running away from
+};
diff --git a/src/Mobs/Behaviors/BehaviorDoNothing.cpp b/src/Mobs/Behaviors/BehaviorDoNothing.cpp
new file mode 100644
index 000000000..aeb4b815e
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorDoNothing.cpp
@@ -0,0 +1,24 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorDoNothing.h"
+#include "../Monster.h"
+
+void cBehaviorDoNothing::AttachToMonster(cMonster & a_Parent)
+{
+ m_Parent = &a_Parent;
+ m_Parent->AttachTickBehavior(this);
+}
+
+bool cBehaviorDoNothing::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ return true;
+}
+
+void cBehaviorDoNothing::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ return;
+}
diff --git a/src/Mobs/Behaviors/BehaviorDoNothing.h b/src/Mobs/Behaviors/BehaviorDoNothing.h
new file mode 100644
index 000000000..52c0c7c08
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorDoNothing.h
@@ -0,0 +1,19 @@
+#pragma once
+
+// Always takes control of the tick and does nothing. Used for unimplemented mobs like squids.
+
+class cBehaviorDoNothing;
+
+#include "Behavior.h"
+
+class cBehaviorDoNothing : public cBehavior
+{
+public:
+ void AttachToMonster(cMonster & a_Parent);
+ bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
+ void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
+
+private:
+ /** Our parent */
+ cMonster * m_Parent;
+};
diff --git a/src/Mobs/Behaviors/BehaviorItemDropper.cpp b/src/Mobs/Behaviors/BehaviorItemDropper.cpp
new file mode 100644
index 000000000..d39993012
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorItemDropper.cpp
@@ -0,0 +1,50 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorItemDropper.h"
+#include "../Monster.h"
+#include "../../World.h"
+
+
+void cBehaviorItemDropper::AttachToMonster(cMonster & a_Parent)
+{
+ m_Parent = &a_Parent;
+ m_EggDropTimer = 0;
+ m_Parent->AttachPostTickBehavior(this);
+}
+
+
+
+
+
+void cBehaviorItemDropper::PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ if (!m_Parent->IsTicking())
+ {
+ // The base class tick destroyed us
+ return;
+ }
+
+ if (m_Parent->IsBaby())
+ {
+ return; // Babies don't lay eggs
+ }
+
+ if ((m_EggDropTimer == 6000) && GetRandomProvider().RandBool())
+ {
+ cItems Drops;
+ m_EggDropTimer = 0;
+ Drops.push_back(cItem(E_ITEM_EGG, 1));
+ m_Parent->GetWorld()->SpawnItemPickups(Drops, m_Parent->GetPosX(), m_Parent->GetPosY(), m_Parent->GetPosZ(), 10);
+ }
+ else if (m_EggDropTimer == 12000)
+ {
+ cItems Drops;
+ m_EggDropTimer = 0;
+ Drops.push_back(cItem(E_ITEM_EGG, 1));
+ m_Parent->GetWorld()->SpawnItemPickups(Drops, m_Parent->GetPosX(), m_Parent->GetPosY(), m_Parent->GetPosZ(), 10);
+ }
+ else
+ {
+ m_EggDropTimer++;
+ }
+}
diff --git a/src/Mobs/Behaviors/BehaviorItemDropper.h b/src/Mobs/Behaviors/BehaviorItemDropper.h
new file mode 100644
index 000000000..2b9623ecf
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorItemDropper.h
@@ -0,0 +1,17 @@
+#pragma once
+
+// mobTodo a more generic, not chickenspecific dropper
+
+#include "Behavior.h"
+
+/** Makes the mob periodically lay eggs. */
+class cBehaviorItemDropper : cBehavior
+{
+public:
+ void AttachToMonster(cMonster & a_Parent);
+ void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+
+private:
+ cMonster * m_Parent; // Our Parent
+ int m_EggDropTimer;
+};
diff --git a/src/Mobs/Behaviors/BehaviorItemFollower.cpp b/src/Mobs/Behaviors/BehaviorItemFollower.cpp
new file mode 100644
index 000000000..6978b2765
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorItemFollower.cpp
@@ -0,0 +1,64 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorItemFollower.h"
+#include "../Monster.h"
+#include "../../World.h"
+#include "../../Entities/Player.h"
+
+
+void cBehaviorItemFollower::AttachToMonster(cMonster & a_Parent)
+{
+ LOGD("mobDebug - Behavior ItemFollower: Attach");
+ m_Parent = &a_Parent;
+ m_Parent->AttachTickBehavior(this);
+}
+
+
+
+
+
+bool cBehaviorItemFollower::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ cItems FollowedItems;
+ m_Parent->GetFollowedItems(FollowedItems);
+ if (FollowedItems.Size() > 0)
+ {
+ cPlayer * a_Closest_Player = m_Parent->GetNearestPlayer();
+ if (a_Closest_Player != nullptr)
+ {
+ cItem EquippedItem = a_Closest_Player->GetEquippedItem();
+ if (FollowedItems.ContainsType(EquippedItem))
+ {
+ return true; // We take control of the monster. Now it'll call our tick
+ }
+ }
+ }
+ return false;
+}
+
+
+
+
+
+void cBehaviorItemFollower::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ cItems FollowedItems;
+ m_Parent->GetFollowedItems(FollowedItems);
+ if (FollowedItems.Size() > 0)
+ {
+ cPlayer * a_Closest_Player = m_Parent->GetNearestPlayer();
+ if (a_Closest_Player != nullptr)
+ {
+ cItem EquippedItem = a_Closest_Player->GetEquippedItem();
+ if (FollowedItems.ContainsType(EquippedItem))
+ {
+ Vector3d PlayerPos = a_Closest_Player->GetPosition();
+ m_Parent->MoveToPosition(PlayerPos);
+ }
+ }
+ }
+}
diff --git a/src/Mobs/Behaviors/BehaviorItemFollower.h b/src/Mobs/Behaviors/BehaviorItemFollower.h
new file mode 100644
index 000000000..8c79ec763
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorItemFollower.h
@@ -0,0 +1,19 @@
+#pragma once
+
+
+class cBehaviorItemFollower;
+
+#include "Behavior.h"
+/** Makes the mob follow specific items when held by the player.
+Currently relies on cMonster::GetFollowedItems for the item list. */
+class cBehaviorItemFollower : public cBehavior
+{
+public:
+ void AttachToMonster(cMonster & a_Parent);
+ bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
+ void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
+
+private:
+ /** Our parent */
+ cMonster * m_Parent;
+};
diff --git a/src/Mobs/Behaviors/BehaviorItemReplacer.cpp b/src/Mobs/Behaviors/BehaviorItemReplacer.cpp
new file mode 100644
index 000000000..b53f08b33
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorItemReplacer.cpp
@@ -0,0 +1,40 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorItemReplacer.h"
+#include "../Monster.h"
+#include "../Entities/Player.h"
+
+
+cBehaviorItemReplacer::cBehaviorItemReplacer(short a_OriginalItem, short a_NewItem) :
+ m_OriginalItem(a_OriginalItem) ,
+ m_NewItem(a_NewItem)
+{
+
+}
+
+
+
+
+
+void cBehaviorItemReplacer::AttachToMonster(cMonster & a_Parent)
+{
+ m_Parent = &a_Parent;
+ m_Parent->AttachRightClickBehavior(this);
+}
+
+
+
+
+
+void cBehaviorItemReplacer::OnRightClicked(cPlayer & a_Player)
+{
+ short HeldItem = a_Player.GetEquippedItem().m_ItemType;
+ if (HeldItem == m_OriginalItem)
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ a_Player.GetInventory().AddItem(m_NewItem);
+ }
+ }
+}
diff --git a/src/Mobs/Behaviors/BehaviorItemReplacer.h b/src/Mobs/Behaviors/BehaviorItemReplacer.h
new file mode 100644
index 000000000..e35729f0e
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorItemReplacer.h
@@ -0,0 +1,26 @@
+#pragma once
+
+
+class cBehaviorItemReplacer;
+
+#include "Behavior.h"
+
+/** When right clicked while holding a_OriginalItem, a mob having this behavior replaces the original item
+with a_NewItem. This is used for milking cows.
+*/
+class cBehaviorItemReplacer : public cBehavior
+{
+
+public:
+ cBehaviorItemReplacer(short a_OriginalItem, short a_NewItem);
+ void AttachToMonster(cMonster & a_Parent);
+ void OnRightClicked(cPlayer & a_Player) override;
+private:
+ // Our parent
+ cMonster * m_Parent;
+ short m_OriginalItem; // Replace this item with NewItem
+ short m_NewItem;
+};
+
+
+
diff --git a/src/Mobs/Behaviors/BehaviorWanderer.cpp b/src/Mobs/Behaviors/BehaviorWanderer.cpp
new file mode 100644
index 000000000..56316bd9f
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorWanderer.cpp
@@ -0,0 +1,102 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+#include "BehaviorWanderer.h"
+#include "../Monster.h"
+#include "../../Chunk.h"
+#include "../../World.h"
+
+cBehaviorWanderer::cBehaviorWanderer() : m_IdleInterval(0)
+{
+
+}
+
+
+
+
+
+void cBehaviorWanderer::AttachToMonster(cMonster & a_Parent)
+{
+ LOGD("mobDebug - Behavior Wanderer: Attach");
+ m_Parent = &a_Parent;
+ m_Parent->AttachTickBehavior(this);
+}
+
+
+
+
+
+bool cBehaviorWanderer::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ // Wandering behavior always happily accepts control.
+ // It should therefore be the last one attached to a monster.
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ return true;
+}
+
+
+
+bool cBehaviorWanderer::ControlStarting(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ m_Parent->GetPathFinder().SetDontCare(true); // We don't care we're we are going when
+ // wandering. If a path is not found, the pathfinder just modifies our destination.
+ return true;
+}
+
+
+bool cBehaviorWanderer::ControlEnding(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ m_Parent->GetPathFinder().SetDontCare(false);
+ return true;
+}
+
+
+void cBehaviorWanderer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ if (m_Parent->IsPathFinderActivated())
+ {
+ return; // Still getting there
+ }
+
+ m_IdleInterval += a_Dt;
+
+ if (m_IdleInterval > std::chrono::seconds(1))
+ {
+ // At this interval the results are predictable
+ int rem = m_Parent->GetWorld()->GetTickRandomNumber(6) + 1;
+ m_IdleInterval -= std::chrono::seconds(1); // So nothing gets dropped when the server hangs for a few seconds
+
+ Vector3d Dist;
+ Dist.x = static_cast<double>(m_Parent->GetWorld()->GetTickRandomNumber(10)) - 5.0;
+ Dist.z = static_cast<double>(m_Parent->GetWorld()->GetTickRandomNumber(10)) - 5.0;
+
+ if ((Dist.SqrLength() > 2) && (rem >= 3))
+ {
+
+ Vector3d Destination(m_Parent->GetPosX() + Dist.x, m_Parent->GetPosition().y, m_Parent->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;
+ int YBelowUs = static_cast<int>(Destination.y) - 1;
+ if (YBelowUs >= 0)
+ {
+ Chunk->GetBlockTypeMeta(RelX, YBelowUs, RelZ, BlockType, BlockMeta);
+ if (BlockType != E_BLOCK_STATIONARY_WATER) // Idle mobs shouldn't enter water on purpose
+ {
+ m_Parent->MoveToPosition(Destination);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Mobs/Behaviors/BehaviorWanderer.h b/src/Mobs/Behaviors/BehaviorWanderer.h
new file mode 100644
index 000000000..24c8885bf
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorWanderer.h
@@ -0,0 +1,24 @@
+#pragma once
+
+// The mob will wander around
+#include <chrono>
+#include "Behavior.h"
+
+class cBehaviorWanderer : cBehavior
+{
+
+public:
+ cBehaviorWanderer();
+ void AttachToMonster(cMonster & a_Parent);
+
+ // Functions our host Monster should invoke:
+ bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ bool ControlStarting(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ bool ControlEnding(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+
+
+private:
+ cMonster * m_Parent; // Our Parent
+ std::chrono::milliseconds m_IdleInterval;
+};
diff --git a/src/Mobs/Behaviors/CMakeLists.txt b/src/Mobs/Behaviors/CMakeLists.txt
new file mode 100644
index 000000000..f205e9cb4
--- /dev/null
+++ b/src/Mobs/Behaviors/CMakeLists.txt
@@ -0,0 +1,45 @@
+
+cmake_minimum_required (VERSION 2.6)
+project (Cuberite)
+
+include_directories ("${PROJECT_SOURCE_DIR}/../")
+
+SET (SRCS
+ Behavior.cpp
+ BehaviorAggressive.cpp
+ BehaviorAttacker.cpp
+ BehaviorBrave.cpp
+ BehaviorBreeder.cpp
+ BehaviorDoNothing.cpp
+ BehaviorDayLightBurner.cpp
+ BehaviorCoward.cpp
+ BehaviorItemDropper.cpp
+ BehaviorItemFollower.cpp
+ BehaviorItemReplacer.cpp
+ BehaviorAttackerMelee.cpp
+ BehaviorAttackerRanged.cpp
+ BehaviorAttackerSuicideBomber.cpp
+ BehaviorWanderer.cpp
+)
+
+SET (HDRS
+ Behavior.h
+ BehaviorAggressive.h
+ BehaviorAttacker.h
+ BehaviorBrave.h
+ BehaviorBreeder.h
+ BehaviorDoNothing.h
+ BehaviorDayLightBurner.h
+ BehaviorCoward.h
+ BehaviorItemDropper.h
+ BehaviorItemFollower.h
+ BehaviorItemReplacer.h
+ BehaviorAttackerMelee.h
+ BehaviorAttackerRanged.h
+ BehaviorAttackerSuicideBomber.h
+ BehaviorWanderer.h
+)
+
+if(NOT MSVC)
+ add_library(Behaviors ${SRCS} ${HDRS})
+endif()
diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp
index a48bfa886..444c8ef39 100644
--- a/src/Mobs/Blaze.cpp
+++ b/src/Mobs/Blaze.cpp
@@ -6,13 +6,33 @@
#include "../Entities/FireChargeEntity.h"
+void FireballShootingFunction(cBehaviorAttackerRanged & a_Behavior,
+ cMonster & a_Attacker, cPawn & a_Attacked)
+{
+ UNUSED(a_Behavior);
+
+ // Setting this higher gives us more wiggle room for attackrate
+ Vector3d Speed = a_Attacker.GetLookVector() * 20;
+ Speed.y = Speed.y + 3;
+
+ auto FireCharge = cpp14::make_unique<cFireChargeEntity>(&a_Attacker,
+ a_Attacker.GetPosX(), a_Attacker.GetPosY() + 1, a_Attacker.GetPosZ(), Speed);
+ auto FireChargePtr = FireCharge.get();
+ FireChargePtr->Initialize(std::move(FireCharge), *(a_Attacker.GetWorld()));
+}
cBlaze::cBlaze(void) :
- super("Blaze", mtBlaze, "entity.blaze.hurt", "entity.blaze.death", 0.6, 1.8)
+ super(mtBlaze, "entity.blaze.hurt", "entity.blaze.death", 0.6, 1.8),
+ m_BehaviorAttackerRanged(FireballShootingFunction, 3, 15)
{
+ m_EMPersonality = AGGRESSIVE;
+ m_BehaviorAttackerRanged.AttachToMonster(*this);
+ m_BehaviorDoNothing.AttachToMonster(*this);
+ m_BehaviorAggressive.AttachToMonster(*this);
SetGravity(-8.0f);
SetAirDrag(0.05f);
+ GetMonsterConfig("Blaze");
}
@@ -27,30 +47,3 @@ void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer)
AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_BLAZE_ROD);
}
}
-
-
-
-
-
-bool cBlaze::Attack(std::chrono::milliseconds a_Dt)
-{
- if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0))
- {
- // Setting this higher gives us more wiggle room for attackrate
- Vector3d Speed = GetLookVector() * 20;
- Speed.y = Speed.y + 1;
-
- auto FireCharge = cpp14::make_unique<cFireChargeEntity>(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed);
- auto FireChargePtr = FireCharge.get();
- if (!FireChargePtr->Initialize(std::move(FireCharge), *m_World))
- {
- return false;
- }
-
- ResetAttackCooldown();
- // ToDo: Shoot 3 fireballs instead of 1.
-
- return true;
- }
- return false;
-}
diff --git a/src/Mobs/Blaze.h b/src/Mobs/Blaze.h
index ca755b626..aad6b4af6 100644
--- a/src/Mobs/Blaze.h
+++ b/src/Mobs/Blaze.h
@@ -1,16 +1,14 @@
-
#pragma once
-#include "AggressiveMonster.h"
-
-
-
-
+#include "Monster.h"
+#include "Behaviors/BehaviorAttackerRanged.h"
+#include "Behaviors/BehaviorDoNothing.h"
+#include "Behaviors/BehaviorAggressive.h"
class cBlaze :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
cBlaze(void);
@@ -18,5 +16,12 @@ public:
CLASS_PROTODEF(cBlaze)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual bool Attack(std::chrono::milliseconds a_Dt) override;
+
+private:
+ // tick behaviors
+ cBehaviorAttackerRanged m_BehaviorAttackerRanged;
+ cBehaviorDoNothing m_BehaviorDoNothing;
+
+ // other behaviors
+ cBehaviorAggressive m_BehaviorAggressive;
} ;
diff --git a/src/Mobs/CMakeLists.txt b/src/Mobs/CMakeLists.txt
index 55ae36e1e..51d9cd630 100644
--- a/src/Mobs/CMakeLists.txt
+++ b/src/Mobs/CMakeLists.txt
@@ -3,7 +3,6 @@ project (Cuberite)
include_directories ("${PROJECT_SOURCE_DIR}/../")
SET (SRCS
- AggressiveMonster.cpp
Bat.cpp
Blaze.cpp
CaveSpider.cpp
@@ -21,12 +20,11 @@ SET (SRCS
Monster.cpp
Mooshroom.cpp
Ocelot.cpp
- PassiveAggressiveMonster.cpp
- PassiveMonster.cpp
Path.cpp
PathFinder.cpp
Pig.cpp
Rabbit.cpp
+ MobPointer.cpp
Sheep.cpp
Skeleton.cpp
Slime.cpp
@@ -41,7 +39,6 @@ SET (SRCS
ZombiePigman.cpp)
SET (HDRS
- AggressiveMonster.h
Bat.h
Blaze.h
CaveSpider.h
@@ -61,12 +58,11 @@ SET (HDRS
MonsterTypes.h
Mooshroom.h
Ocelot.h
- PassiveAggressiveMonster.h
- PassiveMonster.h
Path.h
PathFinder.h
Pig.h
Rabbit.h
+ MobPointer.h
Sheep.h
Silverfish.h
Skeleton.h
diff --git a/src/Mobs/CaveSpider.cpp b/src/Mobs/CaveSpider.cpp
index c6aa8af61..505e6a9df 100644
--- a/src/Mobs/CaveSpider.cpp
+++ b/src/Mobs/CaveSpider.cpp
@@ -2,14 +2,30 @@
#include "CaveSpider.h"
#include "../World.h"
+#include "Mobs/Behaviors/BehaviorAttackerMelee.h"
+
+
+void CaveSpiderPostAttack(cBehaviorAttackerMelee & a_Behavior, cMonster & a_Attacker, cPawn & a_Attacked)
+{
+ UNUSED(a_Behavior);
+ UNUSED(a_Attacker);
+ // TODO: Easy = no poison, Medium = 7 seconds, Hard = 15 seconds
+ a_Attacked.AddEntityEffect(cEntityEffect::effPoison, 7 * 20, 0);
+}
-cCaveSpider::cCaveSpider(void) :
- super("CaveSpider", mtCaveSpider, "entity.spider.hurt", "entity.spider.death", 0.7, 0.5)
+cCaveSpider::cCaveSpider(void)
+ : super(mtCaveSpider, "entity.spider.hurt", "entity.spider.death", 0.7, 0.5)
+ , m_BehaviorAttackerMelee(CaveSpiderPostAttack)
{
+ m_EMPersonality = AGGRESSIVE;
+ m_BehaviorAttackerMelee.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+ m_BehaviorAggressive.AttachToMonster(*this);
+ GetMonsterConfig("CaveSpider");
}
@@ -32,25 +48,6 @@ void cCaveSpider::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
-bool cCaveSpider::Attack(std::chrono::milliseconds a_Dt)
-{
- if (!super::Attack(a_Dt))
- {
- return false;
- }
-
- if (GetTarget()->IsPawn())
- {
- // TODO: Easy = no poison, Medium = 7 seconds, Hard = 15 seconds
- static_cast<cPawn *>(GetTarget())->AddEntityEffect(cEntityEffect::effPoison, 7 * 20, 0);
- }
- return true;
-}
-
-
-
-
-
void cCaveSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
unsigned int LootingLevel = 0;
diff --git a/src/Mobs/CaveSpider.h b/src/Mobs/CaveSpider.h
index cf4b8e17c..c7a660c75 100644
--- a/src/Mobs/CaveSpider.h
+++ b/src/Mobs/CaveSpider.h
@@ -1,15 +1,17 @@
#pragma once
-#include "AggressiveMonster.h"
-
+#include "Monster.h"
+#include "Behaviors/BehaviorAttackerMelee.h"
+#include "Behaviors/BehaviorWanderer.h"
+#include "Behaviors/BehaviorAggressive.h"
class cCaveSpider :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
cCaveSpider(void);
@@ -17,8 +19,14 @@ public:
CLASS_PROTODEF(cCaveSpider)
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
- virtual bool Attack(std::chrono::milliseconds a_Dt) override;
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
+
+ // tick behaviors
+ cBehaviorAttackerMelee m_BehaviorAttackerMelee;
+ cBehaviorWanderer m_BehaviorWanderer;
+
+ // other behaviors
+ cBehaviorAggressive m_BehaviorAggressive;
} ;
diff --git a/src/Mobs/Chicken.cpp b/src/Mobs/Chicken.cpp
index 1068295e6..556f02319 100644
--- a/src/Mobs/Chicken.cpp
+++ b/src/Mobs/Chicken.cpp
@@ -5,53 +5,18 @@
-
-
-
-
cChicken::cChicken(void) :
- super("Chicken", mtChicken, "entity.chicken.hurt", "entity.chicken.death", 0.3, 0.4),
- m_EggDropTimer(0)
+ super(mtChicken, "entity.chicken.hurt", "entity.chicken.death", 0.3, 0.4)
{
SetGravity(-2.0f);
SetAirDrag(0.0f);
-}
-
-
-
-
-void cChicken::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
-{
- super::Tick(a_Dt, a_Chunk);
- if (!IsTicking())
- {
- // The base class tick destroyed us
- return;
- }
-
- if (IsBaby())
- {
- return; // Babies don't lay eggs
- }
-
- if ((m_EggDropTimer == 6000) && GetRandomProvider().RandBool())
- {
- cItems Drops;
- m_EggDropTimer = 0;
- Drops.push_back(cItem(E_ITEM_EGG, 1));
- m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10);
- }
- else if (m_EggDropTimer == 12000)
- {
- cItems Drops;
- m_EggDropTimer = 0;
- Drops.push_back(cItem(E_ITEM_EGG, 1));
- m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10);
- }
- else
- {
- m_EggDropTimer++;
- }
+ m_EMPersonality = PASSIVE;
+ m_BehaviorBreeder.AttachToMonster(*this);
+ m_BehaviorCoward.AttachToMonster(*this);
+ m_BehaviorItemFollower.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+ m_BehaviorItemDropper.AttachToMonster(*this);
+ GetMonsterConfig("Chicken");
}
diff --git a/src/Mobs/Chicken.h b/src/Mobs/Chicken.h
index 3be338b15..f60f3672f 100644
--- a/src/Mobs/Chicken.h
+++ b/src/Mobs/Chicken.h
@@ -1,15 +1,18 @@
#pragma once
-#include "PassiveMonster.h"
-
-
+#include "Behaviors/BehaviorBreeder.h"
+#include "Behaviors/BehaviorItemFollower.h"
+#include "Behaviors/BehaviorCoward.h"
+#include "Behaviors/BehaviorWanderer.h"
+#include "Behaviors/BehaviorItemDropper.h"
+#include "Monster.h"
class cChicken :
- public cPassiveMonster
+ public cMonster
{
- typedef cPassiveMonster super;
+ typedef cMonster super;
public:
cChicken(void);
@@ -17,7 +20,6 @@ public:
CLASS_PROTODEF(cChicken)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void GetFollowedItems(cItems & a_Items) override
{
@@ -27,8 +29,13 @@ public:
virtual void HandleFalling(void) override;
private:
+ // Tick controlling behaviors
+ cBehaviorBreeder m_BehaviorBreeder;
+ cBehaviorItemFollower m_BehaviorItemFollower;
+ cBehaviorCoward m_BehaviorCoward;
+ cBehaviorWanderer m_BehaviorWanderer;
- int m_EggDropTimer;
+ cBehaviorItemDropper m_BehaviorItemDropper;
} ;
diff --git a/src/Mobs/Cow.cpp b/src/Mobs/Cow.cpp
index 9736fe440..6c142b04b 100644
--- a/src/Mobs/Cow.cpp
+++ b/src/Mobs/Cow.cpp
@@ -6,13 +6,17 @@
-
-
-
-
cCow::cCow(void) :
- super("Cow", mtCow, "entity.cow.hurt", "entity.cow.death", 0.9, 1.3)
+ super(mtCow, "entity.cow.hurt", "entity.cow.death", 0.9, 1.3),
+ m_BehaviorItemReplacer(E_ITEM_BUCKET, E_ITEM_MILK)
{
+ m_EMPersonality = PASSIVE;
+ m_BehaviorBreeder.AttachToMonster(*this);
+ m_BehaviorCoward.AttachToMonster(*this);
+ m_BehaviorItemFollower.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+ m_BehaviorItemReplacer.AttachToMonster(*this);
+ GetMonsterConfig("Cow");
}
@@ -29,22 +33,3 @@ void cCow::GetDrops(cItems & a_Drops, cEntity * a_Killer)
AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER);
AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF);
}
-
-
-
-
-
-void cCow::OnRightClicked(cPlayer & a_Player)
-{
- super::OnRightClicked(a_Player);
-
- short HeldItem = a_Player.GetEquippedItem().m_ItemType;
- if (HeldItem == E_ITEM_BUCKET)
- {
- if (!a_Player.IsGameModeCreative())
- {
- a_Player.GetInventory().RemoveOneEquippedItem();
- a_Player.GetInventory().AddItem(E_ITEM_MILK);
- }
- }
-}
diff --git a/src/Mobs/Cow.h b/src/Mobs/Cow.h
index 569c6e619..58380065a 100644
--- a/src/Mobs/Cow.h
+++ b/src/Mobs/Cow.h
@@ -1,30 +1,36 @@
-
#pragma once
-#include "PassiveMonster.h"
-
-
-
+#include "Behaviors/BehaviorBreeder.h"
+#include "Behaviors/BehaviorItemFollower.h"
+#include "Behaviors/BehaviorCoward.h"
+#include "Behaviors/BehaviorWanderer.h"
+#include "Behaviors/BehaviorItemReplacer.h"
+#include "Monster.h"
-
-class cCow :
- public cPassiveMonster
+class cCow : public cMonster
{
- typedef cPassiveMonster super;
-
public:
cCow();
+ ~cCow() {}
+ typedef cMonster super;
CLASS_PROTODEF(cCow)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual void OnRightClicked(cPlayer & a_Player) override;
virtual void GetFollowedItems(cItems & a_Items) override
{
a_Items.Add(E_ITEM_WHEAT);
}
-
+private:
+ // Tick controlling behaviors
+ cBehaviorBreeder m_BehaviorBreeder;
+ cBehaviorItemFollower m_BehaviorItemFollower;
+ cBehaviorCoward m_BehaviorCoward;
+ cBehaviorWanderer m_BehaviorWanderer;
+
+ // Non tick controlling behaviors
+ cBehaviorItemReplacer m_BehaviorItemReplacer;
} ;
diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp
index 84b68cf7c..8fb46a3c3 100644
--- a/src/Mobs/Creeper.cpp
+++ b/src/Mobs/Creeper.cpp
@@ -11,49 +11,13 @@
cCreeper::cCreeper(void) :
- super("Creeper", mtCreeper, "entity.creeper.hurt", "entity.creeper.death", 0.6, 1.8),
- m_bIsBlowing(false),
- m_bIsCharged(false),
- m_BurnedWithFlintAndSteel(false),
- m_ExplodingTimer(0)
+ super(mtCreeper, "entity.creeper.hurt", "entity.creeper.death", 0.6, 1.8)
{
-}
-
-
-
-
-
-void cCreeper::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
-{
- super::Tick(a_Dt, a_Chunk);
- if (!IsTicking())
- {
- // The base class tick destroyed us
- return;
- }
-
- if (((GetTarget() == nullptr) || !TargetIsInRange()) && !m_BurnedWithFlintAndSteel)
- {
- if (m_bIsBlowing)
- {
- m_ExplodingTimer = 0;
- m_bIsBlowing = false;
- m_World->BroadcastEntityMetadata(*this);
- }
- }
- else
- {
- if (m_bIsBlowing)
- {
- m_ExplodingTimer += 1;
- }
-
- if ((m_ExplodingTimer == 30) && (GetHealth() > 0.0)) // only explode when not already dead
- {
- m_World->DoExplosionAt((m_bIsCharged ? 5 : 3), GetPosX(), GetPosY(), GetPosZ(), false, esMonster, this);
- Destroy(); // Just in case we aren't killed by the explosion
- }
- }
+ m_EMPersonality = AGGRESSIVE;
+ m_BehaviorAttackerSuicideBomber.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+ m_BehaviorAggressive.AttachToMonster(*this);
+ GetMonsterConfig("Creeper");
}
@@ -62,7 +26,7 @@ void cCreeper::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- if (m_ExplodingTimer == 30)
+ if (IsBlowing())
{
// Exploded creepers drop naught but charred flesh, which Minecraft doesn't have
return;
@@ -106,59 +70,25 @@ void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-bool cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI)
+bool cCreeper::IsBlowing(void) const
{
- if (!super::DoTakeDamage(a_TDI))
- {
- return false;
- }
-
- if (a_TDI.DamageType == dtLightning)
- {
- m_bIsCharged = true;
- }
-
- m_World->BroadcastEntityMetadata(*this);
- return true;
+ return m_BehaviorAttackerSuicideBomber.IsBlowing();
}
-bool cCreeper::Attack(std::chrono::milliseconds a_Dt)
+bool cCreeper::IsCharged(void) const
{
- UNUSED(a_Dt);
-
- if (!m_bIsBlowing)
- {
- m_World->BroadcastSoundEffect("entity.creeper.primed", GetPosX(), GetPosY(), GetPosZ(), 1.f, (0.75f + (static_cast<float>((GetUniqueID() * 23) % 32)) / 64));
- m_bIsBlowing = true;
- m_World->BroadcastEntityMetadata(*this);
-
- return true;
- }
- return false;
+ return m_BehaviorAttackerSuicideBomber.IsCharged();
}
-void cCreeper::OnRightClicked(cPlayer & a_Player)
+bool cCreeper::IsBurnedWithFlintAndSteel(void) const
{
- super::OnRightClicked(a_Player);
-
- if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_FLINT_AND_STEEL))
- {
- if (!a_Player.IsGameModeCreative())
- {
- a_Player.UseEquippedItem();
- }
- m_World->BroadcastSoundEffect("entity.creeper.primed", GetPosX(), GetPosY(), GetPosZ(), 1.f, (0.75f + (static_cast<float>((GetUniqueID() * 23) % 32)) / 64));
- m_bIsBlowing = true;
- m_World->BroadcastEntityMetadata(*this);
- m_BurnedWithFlintAndSteel = true;
- }
+ return m_BehaviorAttackerSuicideBomber.IsBurnedWithFlintAndSteel();
}
-
diff --git a/src/Mobs/Creeper.h b/src/Mobs/Creeper.h
index aea36def3..85b94c397 100644
--- a/src/Mobs/Creeper.h
+++ b/src/Mobs/Creeper.h
@@ -1,16 +1,17 @@
#pragma once
-#include "AggressiveMonster.h"
-
-
+#include "Monster.h"
+#include "Behaviors/BehaviorAttackerSuicideBomber.h"
+#include "Behaviors/BehaviorWanderer.h"
+#include "Behaviors/BehaviorAggressive.h"
class cCreeper :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
cCreeper(void);
@@ -18,20 +19,18 @@ public:
CLASS_PROTODEF(cCreeper)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
- virtual bool Attack(std::chrono::milliseconds a_Dt) override;
- virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
- virtual void OnRightClicked(cPlayer & a_Player) override;
- bool IsBlowing(void) const {return m_bIsBlowing; }
- bool IsCharged(void) const {return m_bIsCharged; }
- bool IsBurnedWithFlintAndSteel(void) const {return m_BurnedWithFlintAndSteel; }
+ bool IsBlowing(void) const;
+ bool IsCharged(void) const;
+ bool IsBurnedWithFlintAndSteel(void) const;
private:
+ // tick behaviors
+ cBehaviorAttackerSuicideBomber m_BehaviorAttackerSuicideBomber;
+ cBehaviorWanderer m_BehaviorWanderer;
- bool m_bIsBlowing, m_bIsCharged, m_BurnedWithFlintAndSteel;
- int m_ExplodingTimer;
-
+ // other behaviors
+ cBehaviorAggressive m_BehaviorAggressive;
} ;
diff --git a/src/Mobs/EnderDragon.cpp b/src/Mobs/EnderDragon.cpp
index 360fe581b..f33a9d888 100644
--- a/src/Mobs/EnderDragon.cpp
+++ b/src/Mobs/EnderDragon.cpp
@@ -9,8 +9,11 @@
cEnderDragon::cEnderDragon(void) :
// TODO: Vanilla source says this, but is it right? Dragons fly, they don't stand
- super("EnderDragon", mtEnderDragon, "entity.enderdragon.hurt", "entity.enderdragon.death", 16.0, 8.0)
+ super(mtEnderDragon, "entity.enderdragon.hurt", "entity.enderdragon.death", 16.0, 8.0)
{
+ m_EMPersonality = AGGRESSIVE;
+ m_BehaviorDoNothing.AttachToMonster(*this);
+ GetMonsterConfig("EnderDragon");
}
diff --git a/src/Mobs/EnderDragon.h b/src/Mobs/EnderDragon.h
index 43acdcd54..b756aab17 100644
--- a/src/Mobs/EnderDragon.h
+++ b/src/Mobs/EnderDragon.h
@@ -1,16 +1,13 @@
#pragma once
-#include "AggressiveMonster.h"
-
-
-
-
+#include "Monster.h"
+#include "Behaviors/BehaviorDoNothing.h"
class cEnderDragon :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
cEnderDragon(void);
@@ -18,6 +15,9 @@ public:
CLASS_PROTODEF(cEnderDragon)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
+
+private:
+ cBehaviorDoNothing m_BehaviorDoNothing;
} ;
diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp
index 5cfe0d4cd..3d874e4d6 100644
--- a/src/Mobs/Enderman.cpp
+++ b/src/Mobs/Enderman.cpp
@@ -74,11 +74,13 @@ protected:
cEnderman::cEnderman(void) :
- super("Enderman", mtEnderman, "entity.endermen.hurt", "entity.endermen.death", 0.5, 2.9),
+ super(mtEnderman, "entity.endermen.hurt", "entity.endermen.death", 0.5, 2.9),
m_bIsScreaming(false),
CarriedBlock(E_BLOCK_AIR),
CarriedMeta(0)
{
+ m_EMPersonality = PASSIVE;
+ GetMonsterConfig("Enderman");
}
@@ -98,67 +100,6 @@ void cEnderman::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-void cEnderman::CheckEventSeePlayer(cChunk & a_Chunk)
-{
- if (GetTarget() != nullptr)
- {
- return;
- }
-
- cPlayerLookCheck Callback(GetPosition(), m_SightDistance);
- if (m_World->ForEachPlayer(Callback))
- {
- return;
- }
-
- ASSERT(Callback.GetPlayer() != nullptr);
-
- if (!CheckLight())
- {
- // Insufficient light for enderman to become aggravated
- // TODO: Teleport to a suitable location
- return;
- }
-
- if (!Callback.GetPlayer()->CanMobsTarget())
- {
- return;
- }
-
- // Target the player
- cMonster::EventSeePlayer(Callback.GetPlayer(), a_Chunk);
- m_EMState = CHASING;
- m_bIsScreaming = true;
- GetWorld()->BroadcastEntityMetadata(*this);
-}
-
-
-
-
-
-void cEnderman::CheckEventLostPlayer(void)
-{
- super::CheckEventLostPlayer();
- if (!CheckLight())
- {
- EventLosePlayer();
- }
-}
-
-
-
-
-
-void cEnderman::EventLosePlayer()
-{
- super::EventLosePlayer();
- m_bIsScreaming = false;
- GetWorld()->BroadcastEntityMetadata(*this);
-}
-
-
-
-
bool cEnderman::CheckLight()
{
@@ -197,7 +138,7 @@ void cEnderman::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
// Take damage when touching water, drowning damage seems to be most appropriate
if (CheckRain() || IsSwimming())
{
- EventLosePlayer();
+ // EventLosePlayer(); //mobTodo
TakeDamage(dtDrowning, nullptr, 1, 0);
// TODO teleport to a safe location
}
diff --git a/src/Mobs/Enderman.h b/src/Mobs/Enderman.h
index c9ffbeaba..c3568e1db 100644
--- a/src/Mobs/Enderman.h
+++ b/src/Mobs/Enderman.h
@@ -1,16 +1,14 @@
-
#pragma once
-
-#include "PassiveAggressiveMonster.h"
+#include "Monster.h"
class cEnderman :
- public cPassiveAggressiveMonster
+ public cMonster
{
- typedef cPassiveAggressiveMonster super;
+ typedef cMonster super;
public:
cEnderman(void);
@@ -18,9 +16,6 @@ public:
CLASS_PROTODEF(cEnderman)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual void CheckEventSeePlayer(cChunk & a_Chunk) override;
- virtual void CheckEventLostPlayer(void) override;
- virtual void EventLosePlayer(void) override;
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
bool IsScreaming(void) const {return m_bIsScreaming; }
diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp
index 2488e63b1..913d88c7f 100644
--- a/src/Mobs/Ghast.cpp
+++ b/src/Mobs/Ghast.cpp
@@ -9,8 +9,11 @@
cGhast::cGhast(void) :
- super("Ghast", mtGhast, "entity.ghast.hurt", "entity.ghast.death", 4, 4)
+ super(mtGhast, "entity.ghast.hurt", "entity.ghast.death", 4, 4)
{
+ m_EMPersonality = AGGRESSIVE;
+ m_BehaviorDoNothing.AttachToMonster(*this);
+ GetMonsterConfig("Ghast");
}
@@ -31,8 +34,8 @@ void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-
-bool cGhast::Attack(std::chrono::milliseconds a_Dt)
+// mobTODO
+/*bool cGhast::Attack(std::chrono::milliseconds a_Dt)
{
if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0))
{
@@ -51,7 +54,7 @@ bool cGhast::Attack(std::chrono::milliseconds a_Dt)
return true;
}
return false;
-}
+}*/
diff --git a/src/Mobs/Ghast.h b/src/Mobs/Ghast.h
index a41a72ddc..014e9c0dc 100644
--- a/src/Mobs/Ghast.h
+++ b/src/Mobs/Ghast.h
@@ -1,16 +1,14 @@
-
#pragma once
-
-#include "AggressiveMonster.h"
-
+#include "Monster.h"
+#include "Behaviors/BehaviorDoNothing.h"
class cGhast :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
cGhast(void);
@@ -18,9 +16,11 @@ public:
CLASS_PROTODEF(cGhast)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual bool Attack(std::chrono::milliseconds a_Dt) override;
bool IsCharging(void) const {return false; }
+
+private:
+ cBehaviorDoNothing m_BehaviorDoNothing;
} ;
diff --git a/src/Mobs/Giant.cpp b/src/Mobs/Giant.cpp
index 0f235e10f..3dc621865 100644
--- a/src/Mobs/Giant.cpp
+++ b/src/Mobs/Giant.cpp
@@ -8,9 +8,11 @@
cGiant::cGiant(void) :
- super("Giant", mtGiant, "entity.zombie.hurt", "entity.zombie.death", 3.6, 10.8)
+ super(mtGiant, "entity.zombie.hurt", "entity.zombie.death", 3.6, 10.8)
{
-
+ m_EMPersonality = AGGRESSIVE;
+ m_BehaviorDoNothing.AttachToMonster(*this);
+ GetMonsterConfig("Giant");
}
diff --git a/src/Mobs/Giant.h b/src/Mobs/Giant.h
index 70e93894c..9cd7c2865 100644
--- a/src/Mobs/Giant.h
+++ b/src/Mobs/Giant.h
@@ -1,16 +1,16 @@
#pragma once
-#include "AggressiveMonster.h"
-
+#include "Monster.h"
+#include "Behaviors/BehaviorDoNothing.h"
class cGiant :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
cGiant(void);
@@ -18,6 +18,9 @@ public:
CLASS_PROTODEF(cGiant)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
+
+private:
+ cBehaviorDoNothing m_BehaviorDoNothing;
} ;
diff --git a/src/Mobs/Guardian.cpp b/src/Mobs/Guardian.cpp
index f36f98ea8..c5fc0405d 100644
--- a/src/Mobs/Guardian.cpp
+++ b/src/Mobs/Guardian.cpp
@@ -9,8 +9,10 @@
cGuardian::cGuardian(void) :
- super("Guardian", mtGuardian, "entity.guardian.hurt", "entity.guardian.death", 0.875, 0.8)
+ super(mtGuardian, "entity.guardian.hurt", "entity.guardian.death", 0.875, 0.8)
{
+ m_EMPersonality = AGGRESSIVE;
+ GetMonsterConfig("Guardian");
}
diff --git a/src/Mobs/Guardian.h b/src/Mobs/Guardian.h
index 289654f57..988f36832 100644
--- a/src/Mobs/Guardian.h
+++ b/src/Mobs/Guardian.h
@@ -1,16 +1,16 @@
#pragma once
-#include "AggressiveMonster.h"
+#include "Monster.h"
class cGuardian :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
cGuardian();
diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp
index 13630b0e3..c3d805b69 100644
--- a/src/Mobs/Horse.cpp
+++ b/src/Mobs/Horse.cpp
@@ -11,7 +11,7 @@
cHorse::cHorse(int Type, int Color, int Style, int TameTimes) :
- super("Horse", mtHorse, "entity.horse.hurt", "entity.horse.death", 1.4, 1.6),
+ super(mtHorse, "entity.horse.hurt", "entity.horse.death", 1.4, 1.6),
m_bHasChest(false),
m_bIsEating(false),
m_bIsRearing(false),
@@ -27,6 +27,12 @@ cHorse::cHorse(int Type, int Color, int Style, int TameTimes) :
m_RearTickCount(0),
m_MaxSpeed(14.0)
{
+ m_EMPersonality = PASSIVE;
+ m_BehaviorBreeder.AttachToMonster(*this);
+ m_BehaviorCoward.AttachToMonster(*this);
+ m_BehaviorItemFollower.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+ GetMonsterConfig("Horse");
}
@@ -185,19 +191,6 @@ void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-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 ((!m_bIsTame) || (m_Attachee == nullptr))
- {
- super::InStateIdle(a_Dt, a_Chunk);
- }
-}
-
-
-
-
-
void cHorse::HandleSpeedFromAttachee(float a_Forward, float a_Sideways)
{
if ((m_bIsTame) && (m_bIsSaddled))
diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h
index 82026a0ee..e96a1f8a9 100644
--- a/src/Mobs/Horse.h
+++ b/src/Mobs/Horse.h
@@ -1,16 +1,20 @@
#pragma once
-#include "PassiveMonster.h"
+#include "Behaviors/BehaviorBreeder.h"
+#include "Behaviors/BehaviorItemFollower.h"
+#include "Behaviors/BehaviorCoward.h"
+#include "Behaviors/BehaviorWanderer.h"
+#include "Monster.h"
class cHorse :
- public cPassiveMonster
+ public cMonster
{
- typedef cPassiveMonster super;
+ typedef cMonster super;
public:
cHorse(int Type, int Color, int Style, int TameTimes);
@@ -18,7 +22,6 @@ public:
CLASS_PROTODEF(cHorse)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) 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 Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
@@ -39,8 +42,12 @@ public:
a_Items.Add(E_ITEM_GOLDEN_CARROT);
a_Items.Add(E_ITEM_GOLDEN_APPLE);
}
-
private:
+ // Tick controlling behaviors
+ cBehaviorBreeder m_BehaviorBreeder;
+ cBehaviorItemFollower m_BehaviorItemFollower;
+ cBehaviorCoward m_BehaviorCoward;
+ cBehaviorWanderer m_BehaviorWanderer;
bool m_bHasChest, m_bIsEating, m_bIsRearing, m_bIsMouthOpen, m_bIsTame, m_bIsSaddled;
int m_Type, m_Color, m_Style, m_Armour, m_TimesToTame, m_TameAttemptTimes, m_RearTickCount;
diff --git a/src/Mobs/IronGolem.cpp b/src/Mobs/IronGolem.cpp
index 448ed8dc0..a6fd5e8c4 100644
--- a/src/Mobs/IronGolem.cpp
+++ b/src/Mobs/IronGolem.cpp
@@ -8,8 +8,11 @@
cIronGolem::cIronGolem(void) :
- super("IronGolem", mtIronGolem, "entity.irongolem.hurt", "entity.irongolem.death", 1.4, 2.9)
+ super(mtIronGolem, "entity.irongolem.hurt", "entity.irongolem.death", 1.4, 2.9)
{
+ m_EMPersonality = PASSIVE;
+ m_BehaviorWanderer.AttachToMonster(*this);
+ GetMonsterConfig("IronGolem");
}
diff --git a/src/Mobs/IronGolem.h b/src/Mobs/IronGolem.h
index 7d35686e7..ea1cc759d 100644
--- a/src/Mobs/IronGolem.h
+++ b/src/Mobs/IronGolem.h
@@ -1,16 +1,16 @@
#pragma once
-#include "PassiveAggressiveMonster.h"
-
+#include "Monster.h"
+#include "Behaviors/BehaviorWanderer.h"
class cIronGolem :
- public cPassiveAggressiveMonster
+ public cMonster
{
- typedef cPassiveAggressiveMonster super;
+ typedef cMonster super;
public:
cIronGolem(void);
@@ -22,6 +22,9 @@ public:
// Iron golems do not drown nor float
virtual void HandleAir(void) override {}
virtual void SetSwimState(cChunk & a_Chunk) override {}
+
+private:
+ cBehaviorWanderer m_BehaviorWanderer;
} ;
diff --git a/src/Mobs/MagmaCube.cpp b/src/Mobs/MagmaCube.cpp
index 4d70a0291..2891605c8 100644
--- a/src/Mobs/MagmaCube.cpp
+++ b/src/Mobs/MagmaCube.cpp
@@ -7,9 +7,12 @@
cMagmaCube::cMagmaCube(int a_Size) :
- super("MagmaCube", mtMagmaCube, Printf("entity.%smagmacube.hurt", GetSizeName(a_Size).c_str()), Printf("entity.%smagmacube.death", GetSizeName(a_Size).c_str()), 0.6 * a_Size, 0.6 * a_Size),
+ super(mtMagmaCube, Printf("entity.%smagmacube.hurt", GetSizeName(a_Size).c_str()), Printf("entity.%smagmacube.death", GetSizeName(a_Size).c_str()), 0.6 * a_Size, 0.6 * a_Size),
m_Size(a_Size)
{
+ m_EMPersonality = AGGRESSIVE;
+ m_BehaviorDoNothing.AttachToMonster(*this);
+ GetMonsterConfig("MagmaCube");
}
diff --git a/src/Mobs/MagmaCube.h b/src/Mobs/MagmaCube.h
index 5fc8105ba..4cfc1c27d 100644
--- a/src/Mobs/MagmaCube.h
+++ b/src/Mobs/MagmaCube.h
@@ -1,15 +1,15 @@
#pragma once
-#include "AggressiveMonster.h"
-
+#include "Monster.h"
+#include "Behaviors/BehaviorDoNothing.h"
class cMagmaCube :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
/** Creates a MagmaCube of the specified size; with 1 being the smallest */
@@ -28,6 +28,9 @@ protected:
/** Size of the MagmaCube, with 1 being the smallest */
int m_Size;
+
+private:
+ cBehaviorDoNothing m_BehaviorDoNothing;
} ;
diff --git a/src/Mobs/MobPointer.cpp b/src/Mobs/MobPointer.cpp
new file mode 100644
index 000000000..8fb64867d
--- /dev/null
+++ b/src/Mobs/MobPointer.cpp
@@ -0,0 +1,78 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "../Entities/Pawn.h"
+#include "MobPointer.h"
+#include "../World.h"
+
+cMobPointer::cMobPointer(cPawn * a_Pointer) : m_Pointer(a_Pointer)
+{
+ // Constructor
+}
+
+
+
+
+
+cMobPointer::cMobPointer(const cMobPointer & a_MobPointer) : m_Pointer(a_MobPointer.m_Pointer)
+{
+ // Copy constructor
+}
+
+
+
+
+
+cMobPointer::cMobPointer(cMobPointer && a_MobPointer)
+{
+ // move Constructor
+ m_Pointer = a_MobPointer.m_Pointer;
+ a_MobPointer.m_Pointer = nullptr;
+}
+
+
+
+
+
+cMobPointer& cMobPointer::operator=(const cMobPointer& a_MobPointer)
+{
+ // Copy assignment operator
+ m_Pointer = a_MobPointer.m_Pointer;
+ return *this;
+}
+
+
+
+
+
+cMobPointer& cMobPointer::operator=(cMobPointer&& a_MobPointer)
+{
+ // Move assignment operator
+ m_Pointer = a_MobPointer.m_Pointer;
+ a_MobPointer.m_Pointer = nullptr;
+ return *this;
+}
+
+
+
+
+
+void cMobPointer::SetPointer(cPawn * a_Pointer)
+{
+ m_Pointer = a_Pointer;
+}
+
+
+
+
+
+cPawn * cMobPointer::GetPointer(cWorld * a_CurrentWorld)
+{
+ if (m_Pointer != nullptr)
+ {
+ if (!m_Pointer->IsTicking() || (m_Pointer->GetWorld() != a_CurrentWorld))
+ {
+ m_Pointer = nullptr;
+ }
+ }
+ return m_Pointer;
+}
diff --git a/src/Mobs/MobPointer.h b/src/Mobs/MobPointer.h
new file mode 100644
index 000000000..3534d760d
--- /dev/null
+++ b/src/Mobs/MobPointer.h
@@ -0,0 +1,35 @@
+#pragma once
+
+/** This class allows mobs to hold pointers to other mobs/players in a safe manner.
+When calling GetPointer(), it first checks if the Monster/Player being pointed to is still valid.
+If not, it returns a nullptr. The returned raw pointer must be used locally and then discarded.
+it MUST NOT be preserved across ticks.
+*/
+
+class cPawn;
+class cWorld;
+class cMobPointer
+{
+public:
+ cMobPointer(cPawn * a_Pointer); // Constructor
+ cMobPointer(const cMobPointer & a_MobPointer); // Copy constructor
+ cMobPointer(cMobPointer && a_MobPointer); // Move constructor
+ cMobPointer& operator=(const cMobPointer& a_MobPointer); // Copy assignment operator
+ cMobPointer& operator=(cMobPointer&& a_MobPointer); // Move assignment operator
+
+ void SetPointer(cPawn * a_Pointer); // set Pointer
+
+ /** Returns the raw pointer. The returned raw pointer must
+ be used locally and then discarded. it MUST NOT be preserved across ticks.
+ Returns null if raw pointer is null. Returns null if mob is destroyed or moved worlds.
+ Must be called at least once per tick, even if the raw pointer is not going to be used that tick. */
+ cPawn * GetPointer(cWorld * a_CurrentWorld);
+
+
+private:
+ cPawn * m_Pointer;
+} ;
+
+
+
+
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index 944e8aa94..bf6eda50c 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -19,6 +19,9 @@
#include "PathFinder.h"
#include "../Entities/LeashKnot.h"
+// Temporary pathfinder hack
+#include "Behaviors/BehaviorDayLightBurner.h"
+
@@ -80,10 +83,12 @@ static const struct
////////////////////////////////////////////////////////////////////////////////
// cMonster:
-cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height)
+cMonster::cMonster(eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height)
: super(etMonster, a_Width, a_Height)
- , m_EMState(IDLE)
, m_EMPersonality(AGGRESSIVE)
+ , m_BehaviorBreederPointer(nullptr)
+ , m_BehaviorAttackerPointer(nullptr)
+ , m_NearestPlayerIsStale(true)
, m_PathFinder(a_Width, a_Height)
, m_PathfinderActivated(false)
, m_JumpCoolDown(0)
@@ -94,10 +99,6 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_CustomNameAlwaysVisible(false)
, m_SoundHurt(a_SoundHurt)
, m_SoundDeath(a_SoundDeath)
- , m_AttackRate(3)
- , m_AttackDamage(1)
- , m_AttackRange(1)
- , m_AttackCoolDownTicksLeft(0)
, m_SightDistance(25)
, m_DropChanceWeapon(0.085f)
, m_DropChanceHelmet(0.085f)
@@ -105,8 +106,6 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_DropChanceLeggings(0.085f)
, m_DropChanceBoots(0.085f)
, m_CanPickUpLoot(true)
- , m_TicksSinceLastDamaged(100)
- , m_BurnsInDaylight(false)
, m_RelativeWalkSpeed(1)
, m_Age(1)
, m_AgingTimer(20 * 60 * 20) // about 20 minutes
@@ -115,12 +114,14 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_LeashToPos(nullptr)
, m_IsLeashActionJustDone(false)
, m_CanBeLeashed(GetMobFamily() == eFamily::mfPassive)
- , m_Target(nullptr)
+ , m_LookingAt(nullptr)
+ , m_CurrentTickControllingBehavior(nullptr)
+ , m_NewTickControllingBehavior(nullptr)
+ , m_PinnedBehavior(nullptr)
+ , m_TickControllingBehaviorState(Normal)
+ , m_TicksSinceLastDamaged(1000)
{
- if (!a_ConfigName.empty())
- {
- GetMonsterConfig(a_ConfigName);
- }
+
}
@@ -129,7 +130,7 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
cMonster::~cMonster()
{
- ASSERT(GetTarget() == nullptr);
+
}
@@ -138,6 +139,7 @@ cMonster::~cMonster()
void cMonster::Destroy(bool a_ShouldBroadcast)
{
+ // mobTodo behavior for leash
if (IsLeashed())
{
cEntity * LeashedTo = GetLeashedTo();
@@ -159,7 +161,11 @@ void cMonster::Destroy(bool a_ShouldBroadcast)
void cMonster::Destroyed()
{
- SetTarget(nullptr); // Tell them we're no longer targeting them.
+ for (cBehavior * Behavior : m_AttachedDestroyBehaviors)
+ {
+ Behavior->Destroyed();
+ }
+
super::Destroyed();
}
@@ -183,6 +189,7 @@ void cMonster::SpawnOn(cClientHandle & a_Client)
void cMonster::MoveToWayPoint(cChunk & a_Chunk)
{
+ UNUSED(a_Chunk);
if ((m_NextWayPointPosition - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS)
{
return;
@@ -282,6 +289,8 @@ void cMonster::StopMovingToPosition()
void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
+ // LOGD("mobDebug - Monster tick begins");
+ m_NearestPlayerIsStale = true;
super::Tick(a_Dt, a_Chunk);
if (!IsTicking())
{
@@ -290,12 +299,6 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
GET_AND_VERIFY_CURRENT_CHUNK(Chunk, POSX_TOINT, POSZ_TOINT);
- ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == GetWorld())));
- if (m_AttackCoolDownTicksLeft > 0)
- {
- m_AttackCoolDownTicksLeft -= 1;
- }
-
if (m_Health <= 0)
{
// The mob is dead, but we're still animating the "puff" they leave when they die
@@ -307,26 +310,121 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
return;
}
- if (m_TicksSinceLastDamaged < 100)
+ // All behaviors can execute PostTick and PreTick.
+ // These are for bookkeeping or passive actions like laying eggs.
+ // They MUST NOT control mob movement or interefere with the main Tick.
+ for (cBehavior * Behavior : m_AttachedPreTickBehaviors)
{
- ++m_TicksSinceLastDamaged;
+ // LOGD("mobDebug - preTick");
+ ASSERT(Behavior != nullptr);
+ Behavior->PreTick(a_Dt, a_Chunk);
}
- if ((GetTarget() != nullptr))
- {
- ASSERT(GetTarget()->IsTicking());
- if (GetTarget()->IsPlayer())
+ // Note 1: Each monster tick, at most one Behavior executes its Tick method.
+ // Note 2: Each monster tick, exactly one of these is executed:
+ // ControlStarting, Tick, ControlEnding
+
+ // If we're in a regular tick cycle
+ if (m_TickControllingBehaviorState == Normal)
+ {
+ if (IsLeashed())
+ {
+ // do not tick behaviors
+ // mobTodo temporary leash special case. Needs a behavior eventually.
+ }
+ else
{
- if (!static_cast<cPlayer *>(GetTarget())->CanMobsTarget())
+ // STEP 1: Decide who'll control us this tick
+
+ if (m_PinnedBehavior != nullptr)
+ {
+ // A behavior is pinned. We give it control automatically.
+ m_NewTickControllingBehavior = m_PinnedBehavior;
+ }
+ else
+ {
+ // ask the behaviors sequentially if they are interested in controlling this mob
+ // Stop at the first one that says yes.
+ m_NewTickControllingBehavior = nullptr;
+ for (cBehavior * Behavior : m_AttachedTickBehaviors)
+ {
+ if (Behavior->IsControlDesired(a_Dt, a_Chunk))
+ {
+ m_NewTickControllingBehavior = Behavior;
+ break;
+ }
+ }
+ }
+ ASSERT(m_NewTickControllingBehavior != nullptr); // it's not OK if no one asks for control
+
+ // STEP 2: decide whether to tick or do behavior swapping
+
+ if (m_CurrentTickControllingBehavior == m_NewTickControllingBehavior)
{
- SetTarget(nullptr);
- m_EMState = IDLE;
+ // The Behavior asking for control is the same as the behavior from last tick.
+ // Nothing special, just tick it.
+ m_CurrentTickControllingBehavior->Tick(a_Dt, a_Chunk);
}
+ else if (m_CurrentTickControllingBehavior == nullptr)
+ {
+ // first behavior to ever control
+ m_TickControllingBehaviorState = NewControlStarting;
+ }
+ else
+ {
+ // The behavior asking for control is not the same as the behavior from last tick.
+ // Begin the control swapping process.
+ m_TickControllingBehaviorState = OldControlEnding;
+ }
+ }
+
+ }
+
+ // STEP 3: Behavior swapping if needed
+
+ // Make the current controlling behavior clean up
+ if (m_TickControllingBehaviorState == OldControlEnding)
+ {
+ ASSERT(m_CurrentTickControllingBehavior != nullptr);
+ if (m_CurrentTickControllingBehavior->ControlEnding(a_Dt, a_Chunk))
+ {
+ // The current behavior told us it is ready for letting go of control
+ m_TickControllingBehaviorState = NewControlStarting;
+ }
+ else
+ {
+ // The current behavior is not ready for releasing control. We'll execute ControlEnding
+ // next tick too.
+ m_TickControllingBehaviorState = OldControlEnding;
+ }
+ }
+ // Make the new controlling behavior set up
+ else if (m_TickControllingBehaviorState == NewControlStarting)
+ {
+ ASSERT(m_NewTickControllingBehavior != nullptr);
+ if (m_NewTickControllingBehavior->ControlStarting(a_Dt, a_Chunk))
+ {
+ // The new behavior told us it is ready for taking control
+ // The new behavior is now the current behavior. Next tick it will execute its Tick.
+ m_TickControllingBehaviorState = Normal;
+ m_CurrentTickControllingBehavior = m_NewTickControllingBehavior;
+ }
+ else
+ {
+ // The new behavior is not ready for taking control.
+ // We'll execute ControlStarting next tick too.
+ m_TickControllingBehaviorState = NewControlStarting;
}
}
- // Process the undead burning in daylight.
- HandleDaylightBurning(*Chunk, WouldBurnAt(GetPosition(), *Chunk));
+ // All behaviors can execute PostTick and PreTick.
+ // These are for bookkeeping or passive actions like laying eggs.
+ // They MUST NOT control mob movement or interefere with the main Tick.
+ for (cBehavior * Behavior : m_AttachedPostTickBehaviors)
+ {
+ // LOGD("mobDebug - PostTick");
+ Behavior->PostTick(a_Dt, a_Chunk);
+ }
bool a_IsFollowingPath = false;
if (m_PathfinderActivated)
@@ -338,21 +436,23 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
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))
+ switch (m_PathFinder.GetNextWayPoint(*Chunk, GetPosition(), &m_FinalDestination, &m_NextWayPointPosition))
{
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. */
+ // mobTodo move this logic to cPathfinder or to something
+ // more generic in cPath.
if (
- m_BurnsInDaylight && ((m_TicksSinceLastDamaged >= 100) || (m_EMState == IDLE)) &&
- WouldBurnAt(m_NextWayPointPosition, *Chunk) &&
- !WouldBurnAt(GetPosition(), *Chunk)
+ // I am supposed to avoid daylight
+ (m_PathFinder.GetAvoidSunlight()) &&
+ // I was not hurt recently
+ (m_TicksSinceLastDamaged >= 100) &&
+ // I won't burn where I stand now
+ cBehaviorDayLightBurner::WouldBurnAt(m_NextWayPointPosition, *Chunk, *this) &&
+ // I will burn where I'm going to
+ !(cBehaviorDayLightBurner::WouldBurnAt(GetPosition(), *Chunk, *this))
)
{
- // 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
@@ -369,7 +469,8 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
default:
{
-
+ // NEARBY_FOUND is handled internally by cPathFinder.
+ // Do nothing if CALCULATING.
}
}
}
@@ -377,30 +478,10 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
SetPitchAndYawFromDestination(a_IsFollowingPath);
- switch (m_EMState)
- {
- case IDLE:
- {
- // If enemy passive we ignore checks for player visibility.
- InStateIdle(a_Dt, a_Chunk);
- break;
- }
- case CHASING:
- {
- // If we do not see a player anymore skip chasing action.
- InStateChasing(a_Dt, a_Chunk);
- break;
- }
- case ESCAPING:
- {
- InStateEscaping(a_Dt, a_Chunk);
- break;
- }
- case ATTACKING: break;
- } // switch (m_EMState)
-
// Leash calculations
- if ((m_TicksAlive % LEASH_ACTIONS_TICK_STEP) == 0)
+ if ((m_TickControllingBehaviorState == Normal) &&
+ ((m_TicksAlive % LEASH_ACTIONS_TICK_STEP) == 0)
+ )
{
CalcLeashActions();
}
@@ -416,6 +497,11 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_World->BroadcastEntityMetadata(*this);
}
}
+
+ if (m_TicksSinceLastDamaged < 1000)
+ {
+ ++m_TicksSinceLastDamaged;
+ }
}
@@ -458,9 +544,10 @@ void cMonster::CalcLeashActions()
void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath)
{
Vector3d BodyDistance;
- if (!a_IsFollowingPath && (GetTarget() != nullptr))
+ cPawn * LookingAt = m_LookingAt.GetPointer(GetWorld());
+ if (!a_IsFollowingPath && (LookingAt != nullptr))
{
- BodyDistance = GetTarget()->GetPosition() - GetPosition();
+ BodyDistance = LookingAt->GetPosition() - GetPosition();
}
else
{
@@ -472,15 +559,15 @@ void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath)
SetYaw(BodyRotation);
Vector3d HeadDistance;
- if (GetTarget() != nullptr)
+ if (LookingAt != nullptr)
{
- if (GetTarget()->IsPlayer()) // Look at a player
+ if (LookingAt->IsPlayer()) // Look at a player
{
- HeadDistance = GetTarget()->GetPosition() - GetPosition();
+ HeadDistance = LookingAt->GetPosition() - GetPosition();
}
else // Look at some other entity
{
- HeadDistance = GetTarget()->GetPosition() - GetPosition();
+ HeadDistance = LookingAt->GetPosition() - GetPosition();
// HeadDistance.y = GetTarget()->GetPosY() + GetHeight();
}
}
@@ -555,22 +642,20 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
return false;
}
+
if (!m_SoundHurt.empty() && (m_Health > 0))
{
m_World->BroadcastSoundEffect(m_SoundHurt, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f);
}
- if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn())
+ for (cBehavior * Behavior : m_AttachedDoTakeDamageBehaviors)
{
- if (
- (!a_TDI.Attacker->IsPlayer()) ||
- (static_cast<cPlayer *>(a_TDI.Attacker)->CanMobsTarget())
- )
- {
- SetTarget(static_cast<cPawn*>(a_TDI.Attacker));
- }
- m_TicksSinceLastDamaged = 0;
+ ASSERT(Behavior != nullptr);
+ Behavior->DoTakeDamage(a_TDI);
}
+
+ m_TicksSinceLastDamaged = 0;
+
return true;
}
@@ -661,6 +746,7 @@ void cMonster::OnRightClicked(cPlayer & a_Player)
{
super::OnRightClicked(a_Player);
+ // mobTodo put this in a behavior?
const cItem & EquippedItem = a_Player.GetEquippedItem();
if ((EquippedItem.m_ItemType == E_ITEM_NAME_TAG) && !EquippedItem.m_CustomName.empty())
{
@@ -671,6 +757,12 @@ void cMonster::OnRightClicked(cPlayer & a_Player)
}
}
+ for (cBehavior * Behavior : m_AttachedOnRightClickBehaviors)
+ {
+ Behavior->OnRightClicked(a_Player);
+ }
+
+ // mobTodo put this in a behavior?
// Using leashes
m_IsLeashActionJustDone = false;
if (IsLeashed() && (GetLeashedTo() == &a_Player)) // a player can only unleash a mob leashed to him
@@ -696,159 +788,6 @@ void cMonster::OnRightClicked(cPlayer & a_Player)
-// Checks to see if EventSeePlayer should be fired
-// monster sez: Do I see the player
-void cMonster::CheckEventSeePlayer(cChunk & a_Chunk)
-{
- // TODO: Rewrite this to use cWorld's DoWithPlayers()
- cPlayer * Closest = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance), false);
-
- if (Closest != nullptr)
- {
- EventSeePlayer(Closest, a_Chunk);
- }
-}
-
-
-
-
-
-void cMonster::CheckEventLostPlayer(void)
-{
- if (GetTarget() != nullptr)
- {
- if ((GetTarget()->GetPosition() - GetPosition()).Length() > m_SightDistance)
- {
- EventLosePlayer();
- }
- }
- else
- {
- EventLosePlayer();
- }
-}
-
-
-
-
-
-// What to do if player is seen
-// default to change state to chasing
-void cMonster::EventSeePlayer(cPlayer * a_SeenPlayer, cChunk & a_Chunk)
-{
- UNUSED(a_Chunk);
- SetTarget(a_SeenPlayer);
-}
-
-
-
-
-
-void cMonster::EventLosePlayer(void)
-{
- SetTarget(nullptr);
- m_EMState = IDLE;
-}
-
-
-
-
-
-void cMonster::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
-{
- if (m_PathfinderActivated)
- {
- return; // Still getting there
- }
-
- m_IdleInterval += a_Dt;
-
- if (m_IdleInterval > std::chrono::seconds(1))
- {
- auto & Random = GetRandomProvider();
-
- // At this interval the results are predictable
- int rem = Random.RandInt(1, 7);
- m_IdleInterval -= std::chrono::seconds(1); // So nothing gets dropped when the server hangs for a few seconds
-
- Vector3d Dist;
- Dist.x = static_cast<double>(Random.RandInt(-5, 5));
- Dist.z = static_cast<double>(Random.RandInt(-5, 5));
-
- if ((Dist.SqrLength() > 2) && (rem >= 3))
- {
-
- 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;
- int YBelowUs = static_cast<int>(Destination.y) - 1;
- if (YBelowUs >= 0)
- {
- Chunk->GetBlockTypeMeta(RelX, YBelowUs, RelZ, BlockType, BlockMeta);
- if (BlockType != E_BLOCK_STATIONARY_WATER) // Idle mobs shouldn't enter water on purpose
- {
- MoveToPosition(Destination);
- }
- }
- }
- }
-}
-
-
-
-
-
-// What to do if in Chasing State
-// This state should always be defined in each child class
-void cMonster::InStateChasing(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
-{
- UNUSED(a_Dt);
-}
-
-
-
-
-
-// What to do if in Escaping State
-void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
-{
- UNUSED(a_Dt);
-
- if (GetTarget() != nullptr)
- {
- Vector3d newloc = GetPosition();
- newloc.x = (GetTarget()->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance);
- newloc.z = (GetTarget()->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance);
- MoveToPosition(newloc);
- }
- else
- {
- m_EMState = IDLE; // This shouldnt be required but just to be safe
- }
-}
-
-
-
-
-
-void cMonster::ResetAttackCooldown()
-{
- m_AttackCoolDownTicksLeft = static_cast<int>(3 * 20 * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every 3 seconds
-}
-
-
-
-
-
void cMonster::SetCustomName(const AString & a_CustomName)
{
m_CustomName = a_CustomName;
@@ -1064,16 +1003,19 @@ int cMonster::GetSpawnDelay(cMonster::eFamily a_MobFamily)
-/** Sets the target. */
-void cMonster::SetTarget (cPawn * a_NewTarget)
+
+void cMonster::SetLookingAt(cPawn * a_NewTarget)
{
+ m_LookingAt.SetPointer(a_NewTarget);
+
+ /*
ASSERT((a_NewTarget == nullptr) || (IsTicking()));
- if (m_Target == a_NewTarget)
+ if (m_LookingAt == a_NewTarget)
{
return;
}
- cPawn * OldTarget = m_Target;
- m_Target = a_NewTarget;
+ cPawn * OldTarget = m_LookingAt;
+ m_LookingAt = a_NewTarget;
if (OldTarget != nullptr)
{
@@ -1087,26 +1029,91 @@ void cMonster::SetTarget (cPawn * a_NewTarget)
// Notify the new target that we are now targeting it.
m_Target->TargetingMe(this);
m_WasLastTargetAPlayer = m_Target->IsPlayer();
- }
+ }*/
}
+bool cMonster::IsPathFinderActivated() const
+{
+ return m_PathfinderActivated;
+}
+
+
+
-void cMonster::UnsafeUnsetTarget()
+
+cBehaviorBreeder * cMonster::GetBehaviorBreeder()
{
- m_Target = nullptr;
+ return m_BehaviorBreederPointer;
}
-cPawn * cMonster::GetTarget()
+const cBehaviorBreeder * cMonster::GetBehaviorBreeder() const
{
- return m_Target;
+ return static_cast<const cBehaviorBreeder *>(m_BehaviorBreederPointer);
+}
+
+
+
+
+
+cBehaviorAttacker * cMonster::GetBehaviorAttacker()
+{
+ return m_BehaviorAttackerPointer;
+}
+
+
+
+
+
+void cMonster::InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2)
+{
+ UNUSED(a_Parent1);
+ UNUSED(a_Parent2);
+ return;
+}
+
+
+
+
+
+void cMonster::GetFollowedItems(cItems & a_Items)
+{
+ return;
+}
+
+
+
+
+
+void cMonster::GetBreedingItems(cItems & a_Items)
+{
+ return GetFollowedItems(a_Items);
+}
+
+
+
+
+
+cPlayer * cMonster::GetNearestPlayer()
+{
+ if (m_NearestPlayerIsStale)
+ {
+ // TODO: Rewrite this to use cWorld's DoWithPlayers()
+ m_NearestPlayer = GetWorld()->FindClosestPlayer(GetPosition(), static_cast<float>(GetSightDistance()));
+ m_NearestPlayerIsStale = false;
+ }
+ if ((m_NearestPlayer != nullptr) && (!m_NearestPlayer->IsTicking()))
+ {
+ m_NearestPlayer = nullptr;
+ }
+ return m_NearestPlayer;
}
@@ -1300,93 +1307,86 @@ void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingL
+void cMonster::AttachPreTickBehavior(cBehavior * a_Behavior)
+{
+ ASSERT(a_Behavior != nullptr);
+ m_AttachedPreTickBehaviors.push_back(a_Behavior);
+}
+
+
+
-void cMonster::HandleDaylightBurning(cChunk & a_Chunk, bool WouldBurn)
+void cMonster::AttachPostTickBehavior(cBehavior * a_Behavior)
{
- if (!m_BurnsInDaylight)
- {
- return;
- }
+ ASSERT(a_Behavior != nullptr);
+ m_AttachedPostTickBehaviors.push_back(a_Behavior);
+}
- int RelY = POSY_TOINT;
- if ((RelY < 0) || (RelY >= cChunkDef::Height))
- {
- // Outside the world
- return;
- }
- if (!a_Chunk.IsLightValid())
- {
- m_World->QueueLightChunk(GetChunkX(), GetChunkZ());
- return;
- }
- if (!IsOnFire() && WouldBurn)
- {
- // Burn for 100 ticks, then decide again
- StartBurning(100);
- }
+
+
+
+void cMonster::AttachTickBehavior(cBehavior * a_Behavior)
+{
+ ASSERT(a_Behavior != nullptr);
+ m_AttachedTickBehaviors.push_back(a_Behavior);
}
-bool cMonster::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk)
+
+void cMonster::AttachDestroyBehavior(cBehavior * a_Behavior)
{
- // If the Y coord is out of range, return the most logical result without considering anything else:
- int RelY = FloorC(a_Location.y);
- if (RelY >= cChunkDef::Height)
- {
- // Always burn above the world
- return true;
- }
- if (RelY <= 0)
- {
- // The mob is about to die, no point in burning
- return false;
- }
+ ASSERT(a_Behavior != nullptr);
+ m_AttachedDestroyBehaviors.push_back(a_Behavior);
+}
- PREPARE_REL_AND_CHUNK(a_Location, a_Chunk);
- if (!RelSuccess)
- {
- return false;
- }
- if (
- (Chunk->GetBlock(Rel.x, Rel.y, Rel.z) != E_BLOCK_SOULSAND) && // Not on soulsand
- (GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime
- GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) // Not raining
- )
- {
- int MobHeight = CeilC(a_Location.y + GetHeight()) - 1; // The block Y coord of the mob's head
- if (MobHeight >= cChunkDef::Height)
- {
- return true;
- }
- // Start with the highest block and scan down to just above the mob's head.
- // If a non transparent is found, return false (do not burn). Otherwise return true.
- // Note that this loop is not a performance concern as transparent blocks are rare and the loop almost always bailes out
- // instantly.(An exception is e.g. standing under a long column of glass).
- int CurrentBlock = Chunk->GetHeight(Rel.x, Rel.z);
- while (CurrentBlock > MobHeight)
- {
- BLOCKTYPE Block = Chunk->GetBlock(Rel.x, CurrentBlock, Rel.z);
- if (
- // Do not burn if a block above us meets one of the following conditions:
- (!cBlockInfo::IsTransparent(Block)) ||
- (Block == E_BLOCK_LEAVES) ||
- (Block == E_BLOCK_NEW_LEAVES) ||
- (IsBlockWater(Block))
- )
- {
- return false;
- }
- --CurrentBlock;
- }
- return true;
- }
- return false;
+
+
+void cMonster::AttachRightClickBehavior(cBehavior * a_Behavior)
+{
+ ASSERT(a_Behavior != nullptr);
+ m_AttachedOnRightClickBehaviors.push_back(a_Behavior);
+}
+
+
+
+
+
+void cMonster::AttachDoTakeDamageBehavior(cBehavior * a_Behavior)
+{
+ m_AttachedDoTakeDamageBehaviors.push_back(a_Behavior);
+}
+
+
+
+
+void cMonster::PinBehavior(cBehavior * a_Behavior)
+{
+ m_PinnedBehavior = a_Behavior;
+}
+
+
+
+
+
+void cMonster::UnpinBehavior(cBehavior * a_Behavior)
+{
+ ASSERT(m_PinnedBehavior == a_Behavior);
+ m_PinnedBehavior = nullptr;
+}
+
+
+
+
+
+cPathFinder & cMonster::GetPathFinder()
+{
+ return m_PathFinder;
}
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index 1f6bd6011..f8cac4e7a 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -4,12 +4,18 @@
#include "../Entities/Pawn.h"
#include "MonsterTypes.h"
#include "PathFinder.h"
-
+#include "Behaviors/BehaviorWanderer.h"
+#include "MobPointer.h"
+#include <vector>
class cItem;
class cClientHandle;
+//Behavior fwds
+class cBehaviorBreeder;
+class cBehaviorAttacker;
+class cBehavior;
// tolua_begin
class cMonster :
@@ -31,15 +37,13 @@ public:
// tolua_end
- enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState;
enum MPersonality{PASSIVE, AGGRESSIVE, COWARDLY} m_EMPersonality;
/** Creates the mob object.
- If a_ConfigName is not empty, the configuration is loaded using GetMonsterConfig()
a_MobType is the type of the mob (also used in the protocol ( http://wiki.vg/Entities#Mobs 2012_12_22))
a_SoundHurt and a_SoundDeath are assigned into m_SoundHurt and m_SoundDeath, respectively
*/
- cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
+ cMonster(eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
virtual ~cMonster() override;
@@ -64,14 +68,17 @@ public:
/** Engage pathfinder and tell it to calculate a path to a given position, and move the mob accordingly. */
virtual void MoveToPosition(const Vector3d & a_Position); // tolua_export
+ // mobTodo - MoveToPosition export is probably broken. The AI will keep overriding the
+ // destination.
+
+ /** Stops pathfinding. Calls ResetPathFinding and sets m_IsFollowingPath to false */
+ void StopMovingToPosition();
+
// tolua_begin
eMonsterType GetMobType(void) const { return m_MobType; }
eFamily GetMobFamily(void) const;
// tolua_end
- virtual void CheckEventSeePlayer(cChunk & a_Chunk);
- virtual void EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk);
-
// tolua_begin
/** Returns whether the mob can be leashed. */
@@ -109,18 +116,8 @@ public:
/** Returns whether this mob is undead (skeleton, zombie, etc.) */
virtual bool IsUndead(void);
- virtual void EventLosePlayer(void);
- virtual void CheckEventLostPlayer(void);
-
- virtual void InStateIdle (std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
- virtual void InStateChasing (std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
- virtual void InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk);
-
- int GetAttackRate() { return static_cast<int>(m_AttackRate); }
- 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 SetSightDistance(int a_SightDistance) { m_SightDistance = a_SightDistance; }
+ int GetSightDistance() { return m_SightDistance; }
float GetDropChanceWeapon() { return m_DropChanceWeapon; }
float GetDropChanceHelmet() { return m_DropChanceHelmet; }
@@ -134,10 +131,6 @@ public:
void SetDropChanceLeggings(float a_DropChanceLeggings) { m_DropChanceLeggings = a_DropChanceLeggings; }
void SetDropChanceBoots(float a_DropChanceBoots) { m_DropChanceBoots = a_DropChanceBoots; }
void SetCanPickUpLoot(bool a_CanPickUpLoot) { m_CanPickUpLoot = a_CanPickUpLoot; }
- void ResetAttackCooldown();
-
- /** Sets whether the mob burns in daylight. Only evaluated at next burn-decision tick */
- void SetBurnsInDaylight(bool a_BurnsInDaylight) { m_BurnsInDaylight = a_BurnsInDaylight; }
double GetRelativeWalkSpeed(void) const { return m_RelativeWalkSpeed; } // tolua_export
void SetRelativeWalkSpeed(double a_WalkSpeed) { m_RelativeWalkSpeed = a_WalkSpeed; } // tolua_export
@@ -193,14 +186,7 @@ public:
static AString MobTypeToVanillaNBT(eMonsterType a_MobType);
/** Sets the target that this mob will chase. Pass a nullptr to unset. */
- void SetTarget (cPawn * a_NewTarget);
-
- /** Unset the target without notifying the target entity. Do not use this, use SetTarget(nullptr) instead.
- This is only used by cPawn internally. */
- void UnsafeUnsetTarget();
-
- /** Returns the current target. */
- cPawn * GetTarget();
+ void SetLookingAt(cPawn * a_NewTarget);
/** Creates a new object of the specified mob.
a_MobType is the type of the mob to be created
@@ -211,8 +197,52 @@ public:
/** Returns if this mob last target was a player to avoid destruction on player quit */
bool WasLastTargetAPlayer() const { return m_WasLastTargetAPlayer; }
+ bool IsPathFinderActivated() const;
+
+ // Behavior getters for the rare occasion where we need "polymorphism"
+ // Currently only for attacking and breeding.
+ cBehaviorBreeder * GetBehaviorBreeder();
+ const cBehaviorBreeder * GetBehaviorBreeder() const;
+ cBehaviorAttacker * GetBehaviorAttacker();\
+ cBehaviorBreeder * m_BehaviorBreederPointer;
+ cBehaviorAttacker * m_BehaviorAttackerPointer;
+
+ // Polymorphic monster-specific functions that behaviors may use
+ virtual void InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2);
+ virtual void GetFollowedItems(cItems & a_Items);
+ virtual void GetBreedingItems(cItems & a_Items);
+
+ cPlayer * GetNearestPlayer();
+
+ // These should only be called from cBehavior::attachToMonster
+ void AttachPreTickBehavior(cBehavior * a_Behavior);
+ void AttachPostTickBehavior(cBehavior * a_Behavior);
+ void AttachTickBehavior(cBehavior * a_Behavior);
+ void AttachDestroyBehavior(cBehavior * a_Behavior);
+ void AttachRightClickBehavior(cBehavior * a_Behavior);
+ void AttachDoTakeDamageBehavior(cBehavior * a_Behavior);
+
+ /** If a behavior calls this within its tick, it will be the only behavior
+ that gets to tick until UnpinBehavior is called. ControlDesired will no
+ longer be called for any tick until unpinning is done. MUST be called
+ from the tick function of a behavior. */
+ void PinBehavior(cBehavior * a_Behavior);
+
+ /** Unpins the behavior and ticking goes back to normal. MUST be called
+ on the same behavior pointer that called PinBehavior. */
+ void UnpinBehavior(cBehavior * a_Behavior);
+
+ /** Returns a reference to this mob's pathfinder. Typically used by behaviors
+ to tweak pathfinding behavior as they gain or release control of the mob. */
+ cPathFinder & GetPathFinder();
protected:
+ /** Whether or not m_NearestPlayer is stale. Always true at the beginning of a tick.
+ When true, GetNearestPlayer() actually searches for a player, updates m_NearestPlayer, and sets it to false.
+ otherwise it returns m_NearestPlayer. This means we only perform 1 search per tick. */
+ bool m_NearestPlayerIsStale;
+ cPlayer * m_NearestPlayer;
+
/** The pathfinder instance handles pathfinding for this monster. */
cPathFinder m_PathFinder;
@@ -234,13 +264,6 @@ protected:
/** Returns if the ultimate, final destination has been reached. */
bool ReachedFinalDestination(void) { return ((m_FinalDestination - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS); }
- /** Returns whether or not the target is close enough for attack. */
- bool TargetIsInRange(void)
- {
- ASSERT(GetTarget() != nullptr);
- return ((GetTarget()->GetPosition() - GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange));
- }
-
/** Returns whether the monster needs to jump to reach a given height. */
inline bool DoesPosYRequireJump(double a_PosY)
{
@@ -250,9 +273,6 @@ protected:
/** Move in a straight line to the next waypoint in the path, will jump if needed. */
void MoveToWayPoint(cChunk & a_Chunk);
- /** Stops pathfinding. Calls ResetPathFinding and sets m_IsFollowingPath to false */
- void StopMovingToPosition();
-
/** Sets the body yaw and head yaw */
void SetPitchAndYawFromDestination(bool a_IsFollowingPath);
@@ -268,11 +288,7 @@ protected:
AString m_SoundHurt;
AString m_SoundDeath;
- float m_AttackRate;
- int m_AttackDamage;
- int m_AttackRange;
- int m_AttackCoolDownTicksLeft;
- int m_SightDistance;
+ int m_SightDistance; // mobTodo what to do with this?
float m_DropChanceWeapon;
float m_DropChanceHelmet;
@@ -280,11 +296,7 @@ protected:
float m_DropChanceLeggings;
float m_DropChanceBoots;
bool m_CanPickUpLoot;
- int m_TicksSinceLastDamaged; // How many ticks ago we were last damaged by a player?
- void HandleDaylightBurning(cChunk & a_Chunk, bool WouldBurn);
- bool WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk);
- bool m_BurnsInDaylight;
double m_RelativeWalkSpeed;
int m_Age;
@@ -320,12 +332,23 @@ protected:
void AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingLevel);
private:
- /** A pointer to the entity this mobile is aiming to reach.
- The validity of this pointer SHALL be guaranteed by the pointee;
- it MUST be reset when the pointee changes worlds or is destroyed. */
- cPawn * m_Target;
+ /** A pointer to the entity this mobile is lookingAt */
+ cMobPointer m_LookingAt;
/** Leash calculations inside Tick function */
void CalcLeashActions();
+ std::vector<cBehavior*> m_AttachedPreTickBehaviors;
+ std::vector<cBehavior*> m_AttachedTickBehaviors;
+ std::vector<cBehavior*> m_AttachedPostTickBehaviors;
+ std::vector<cBehavior*> m_AttachedDestroyBehaviors;
+ std::vector<cBehavior*> m_AttachedOnRightClickBehaviors;
+ std::vector<cBehavior*> m_AttachedDoTakeDamageBehaviors;
+
+ cBehavior * m_CurrentTickControllingBehavior;
+ cBehavior * m_NewTickControllingBehavior;
+ cBehavior * m_PinnedBehavior;
+ enum TickState{NewControlStarting, OldControlEnding, Normal} m_TickControllingBehaviorState;
+
+ int m_TicksSinceLastDamaged; // How many ticks ago were we last damaged by a player?
} ; // tolua_export
diff --git a/src/Mobs/Mooshroom.cpp b/src/Mobs/Mooshroom.cpp
index b6feca76e..aa5209ecf 100644
--- a/src/Mobs/Mooshroom.cpp
+++ b/src/Mobs/Mooshroom.cpp
@@ -14,8 +14,14 @@
cMooshroom::cMooshroom(void) :
- super("Mooshroom", mtMooshroom, "entity.cow.hurt", "entity.cow.death", 0.9, 1.3)
+ super(mtMooshroom, "entity.cow.hurt", "entity.cow.death", 0.9, 1.3)
{
+ m_EMPersonality = PASSIVE;
+ m_BehaviorBreeder.AttachToMonster(*this);
+ m_BehaviorCoward.AttachToMonster(*this);
+ m_BehaviorItemFollower.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+ GetMonsterConfig("Mooshroom");
}
@@ -39,6 +45,7 @@ void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer)
void cMooshroom::OnRightClicked(cPlayer & a_Player)
{
+ // mobTodo Behaviors
switch (a_Player.GetEquippedItem().m_ItemType)
{
case E_ITEM_BUCKET:
@@ -72,4 +79,3 @@ void cMooshroom::OnRightClicked(cPlayer & a_Player)
} break;
}
}
-
diff --git a/src/Mobs/Mooshroom.h b/src/Mobs/Mooshroom.h
index 625963190..06e5cf8ce 100644
--- a/src/Mobs/Mooshroom.h
+++ b/src/Mobs/Mooshroom.h
@@ -1,16 +1,20 @@
#pragma once
-#include "PassiveMonster.h"
+#include "Behaviors/BehaviorBreeder.h"
+#include "Behaviors/BehaviorItemFollower.h"
+#include "Behaviors/BehaviorCoward.h"
+#include "Behaviors/BehaviorWanderer.h"
+#include "Monster.h"
class cMooshroom :
- public cPassiveMonster
+ public cMonster
{
- typedef cPassiveMonster super;
+ typedef cMonster super;
public:
cMooshroom(void);
@@ -24,6 +28,12 @@ public:
{
a_Items.Add(E_ITEM_WHEAT);
}
+private:
+ // Tick controlling behaviors
+ cBehaviorBreeder m_BehaviorBreeder;
+ cBehaviorItemFollower m_BehaviorItemFollower;
+ cBehaviorCoward m_BehaviorCoward;
+ cBehaviorWanderer m_BehaviorWanderer;
} ;
diff --git a/src/Mobs/Ocelot.cpp b/src/Mobs/Ocelot.cpp
index e5004a1d1..cf4356f4f 100644
--- a/src/Mobs/Ocelot.cpp
+++ b/src/Mobs/Ocelot.cpp
@@ -13,13 +13,16 @@
cOcelot::cOcelot(void) :
- super("Ocelot", mtOcelot, "entity.cat.hurt", "entity.cat.death", 0.6, 0.8),
+ super(mtOcelot, "entity.cat.hurt", "entity.cat.death", 0.6, 0.8),
m_IsSitting(false),
m_IsTame(false),
m_IsBegging(false),
m_CatType(ctWildOcelot),
m_OwnerName("")
{
+ m_EMPersonality = PASSIVE;
+ m_BehaviorDoNothing.AttachToMonster(*this);
+ GetMonsterConfig("Ocelot");
}
@@ -29,6 +32,7 @@ cOcelot::cOcelot(void) :
void cOcelot::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
+
if (!IsTicking())
{
// The base class tick destroyed us
diff --git a/src/Mobs/Ocelot.h b/src/Mobs/Ocelot.h
index 75758a973..1c1b383cb 100644
--- a/src/Mobs/Ocelot.h
+++ b/src/Mobs/Ocelot.h
@@ -1,17 +1,17 @@
#pragma once
-#include "PassiveMonster.h"
+#include "Monster.h"
#include "../UUID.h"
-
+#include "Behaviors/BehaviorDoNothing.h"
class cOcelot :
- public cPassiveMonster
+ public cMonster
{
- typedef cPassiveMonster super;
+ typedef cMonster super;
public:
@@ -68,6 +68,9 @@ protected:
int m_CheckPlayerTickCount;
AString m_OwnerName;
cUUID m_OwnerUUID;
+
+private:
+ cBehaviorDoNothing m_BehaviorDoNothing;
} ;
diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp
deleted file mode 100644
index 8715ba9c2..000000000
--- a/src/Mobs/PassiveAggressiveMonster.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "PassiveAggressiveMonster.h"
-
-#include "../Entities/Player.h"
-
-
-
-
-
-cPassiveAggressiveMonster::cPassiveAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
- super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height)
-{
- m_EMPersonality = PASSIVE;
-}
-
-
-
-
-
-bool cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
-{
- if (!super::DoTakeDamage(a_TDI))
- {
- return false;
- }
-
- if ((GetTarget() != nullptr) && (GetTarget()->IsPlayer()))
- {
- if (static_cast<cPlayer *>(GetTarget())->CanMobsTarget())
- {
- m_EMState = CHASING;
- }
- }
- return true;
-}
-
-
-
-
-
-void cPassiveAggressiveMonster::EventSeePlayer(cPlayer *, cChunk & a_Chunk)
-{
- // don't do anything, neutral mobs don't react to just seeing the player
-}
-
-
diff --git a/src/Mobs/PassiveAggressiveMonster.h b/src/Mobs/PassiveAggressiveMonster.h
deleted file mode 100644
index 764e27779..000000000
--- a/src/Mobs/PassiveAggressiveMonster.h
+++ /dev/null
@@ -1,24 +0,0 @@
-
-#pragma once
-
-#include "AggressiveMonster.h"
-
-
-
-
-
-class cPassiveAggressiveMonster :
- public cAggressiveMonster
-{
- typedef cAggressiveMonster super;
-
-public:
- cPassiveAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
-
- virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
- virtual void EventSeePlayer(cPlayer *, cChunk & a_Chunk) override;
-} ;
-
-
-
-
diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h
deleted file mode 100644
index 9a2627417..000000000
--- a/src/Mobs/PassiveMonster.h
+++ /dev/null
@@ -1,66 +0,0 @@
-
-#pragma once
-
-#include "Monster.h"
-
-
-
-
-
-class cPassiveMonster :
- public cMonster
-{
- typedef cMonster super;
-
-public:
- cPassiveMonster(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 OnRightClicked(cPlayer & a_Player) override;
-
- /** When hit by someone, run away */
- virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
-
- /** Returns the items that the animal of this class follows when a player holds it in hand. */
- virtual void GetFollowedItems(cItems & a_Items) { }
-
- /** Returns the items that make the animal breed - this is usually the same as the ones that make the animal follow, but not necessarily. */
- virtual void GetBreedingItems(cItems & a_Items) { GetFollowedItems(a_Items); }
-
- /** Called after the baby is born, allows the baby to inherit the parents' properties (color, etc.) */
- virtual void InheritFromParents(cPassiveMonster * a_Parent1, cPassiveMonster * a_Parent2) { }
-
- /** Returns the partner which the monster is currently mating with. */
- cPassiveMonster * GetPartner(void) const { return m_LovePartner; }
-
- /** Start the mating process. Causes the monster to keep bumping into the partner until m_MatingTimer reaches zero. */
- void EngageLoveMode(cPassiveMonster * a_Partner);
-
- /** Finish the mating process. Called after a baby is born. Resets all breeding related timers and sets m_LoveCooldown to 20 minutes. */
- void ResetLoveMode();
-
- /** Returns whether the monster has just been fed and is ready to mate. If this is "true" and GetPartner isn't "nullptr", then the monster is mating. */
- bool IsInLove() const { return (m_LoveTimer > 0); }
-
- /** Returns whether the monster is tired of breeding and is in the cooldown state. */
- bool IsInLoveCooldown() const { return (m_LoveCooldown > 0); }
-
- virtual void Destroyed(void) override;
-
-protected:
- /** The monster's breeding partner. */
- cPassiveMonster * m_LovePartner;
-
- /** If above 0, the monster is in love mode, and will breed if a nearby monster is also in love mode. Decrements by 1 per tick till reaching zero. */
- int m_LoveTimer;
-
- /** If above 0, the monster is in cooldown mode and will refuse to breed. Decrements by 1 per tick till reaching zero. */
- int m_LoveCooldown;
-
- /** The monster is engaged in mating, once this reaches zero, a baby will be born. Decrements by 1 per tick till reaching zero, then a baby is made and ResetLoveMode() is called. */
- int m_MatingTimer;
-};
-
-
-
-
diff --git a/src/Mobs/Path.h b/src/Mobs/Path.h
index 977c5be1c..40e1de6f0 100644
--- a/src/Mobs/Path.h
+++ b/src/Mobs/Path.h
@@ -10,7 +10,7 @@ class cPath;
#include "../FastRandom.h"
#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 LogicParrot to debug
this class outside of Cuberite. This preprocessor flag is never set when compiling Cuberite. */
#include "PathFinderIrrlicht_Head.h"
#endif
diff --git a/src/Mobs/PathFinder.cpp b/src/Mobs/PathFinder.cpp
index 93664b596..8fdcb1ae6 100644
--- a/src/Mobs/PathFinder.cpp
+++ b/src/Mobs/PathFinder.cpp
@@ -9,7 +9,9 @@
cPathFinder::cPathFinder(double a_MobWidth, double a_MobHeight) :
m_Path(),
m_GiveUpCounter(0),
- m_NotFoundCooldown(0)
+ m_NotFoundCooldown(0),
+ m_DontCare(false),
+ m_AvoidSunlight(false)
{
m_Width = a_MobWidth;
m_Height = a_MobHeight;
@@ -19,7 +21,7 @@ cPathFinder::cPathFinder(double a_MobWidth, double a_MobHeight) :
-ePathFinderStatus cPathFinder::GetNextWayPoint(cChunk & a_Chunk, const Vector3d & a_Source, Vector3d * a_Destination, Vector3d * a_OutputWaypoint, bool a_DontCare)
+ePathFinderStatus cPathFinder::GetNextWayPoint(cChunk & a_Chunk, const Vector3d & a_Source, Vector3d * a_Destination, Vector3d * a_OutputWaypoint)
{
m_FinalDestination = *a_Destination;
m_Source = a_Source;
@@ -65,7 +67,7 @@ ePathFinderStatus cPathFinder::GetNextWayPoint(cChunk & a_Chunk, const Vector3d
{
m_NoPathToTarget = true;
m_PathDestination = m_Path->AcceptNearbyPath();
- if (a_DontCare)
+ if (m_DontCare)
{
m_FinalDestination = m_PathDestination;
*a_Destination = m_FinalDestination; // Modify the mob's final destination because it doesn't care about reaching an exact spot
@@ -93,7 +95,7 @@ ePathFinderStatus cPathFinder::GetNextWayPoint(cChunk & a_Chunk, const Vector3d
if (m_GiveUpCounter == 0)
{
- if (a_DontCare)
+ if (m_DontCare)
{
// We're having trouble reaching the next waypoint but the mob
// Doesn't care where to go, just tell him we got there ;)
@@ -166,6 +168,42 @@ ePathFinderStatus cPathFinder::GetNextWayPoint(cChunk & a_Chunk, const Vector3d
+void cPathFinder::SetDontCare(bool a_DontCare)
+{
+ m_DontCare = a_DontCare;
+}
+
+
+
+
+
+bool cPathFinder::GetDontCare()
+{
+ return m_DontCare;
+}
+
+
+
+
+
+void cPathFinder::SetAvoidSunlight(bool a_AvoidSunlight)
+{
+ m_AvoidSunlight = a_AvoidSunlight;
+}
+
+
+
+
+
+bool cPathFinder::GetAvoidSunlight()
+{
+ return m_AvoidSunlight;
+}
+
+
+
+
+
void cPathFinder::ResetPathFinding(cChunk &a_Chunk)
{
m_GiveUpCounter = 40;
diff --git a/src/Mobs/PathFinder.h b/src/Mobs/PathFinder.h
index 213530b11..252ac29ff 100644
--- a/src/Mobs/PathFinder.h
+++ b/src/Mobs/PathFinder.h
@@ -25,8 +25,9 @@ public:
@param a_Source The mob's position. a_Source's coordinates are expected to be within the chunk given in a_Chunk.
@param a_Destination The position the mob would like to reach. If a_ExactPath is true, the PathFinder may modify this.
@param a_OutputWaypoint An output parameter: The next waypoint to go to.
- @param a_DontCare If true, the mob doesn't care where to go, and the Pathfinder may modify a_Destination.
- This should usually be false. An exception is a wandering idle mob which doesn't care about its final destination.
+
+ If m_DontCare is true, the mob doesn't care where to go, and the Pathfinder may modify a_Destination.
+ This should usually be false. One exception is a wandering idle mob which doesn't care about its final destination.
In the future, idle mobs shouldn't use A* at all.
Returns an ePathFinderStatus.
@@ -36,8 +37,19 @@ public:
ePathFinderStatus:PATH_NOT_FOUND - The PathFinder did not find a destination to the target. Nothing was written to a_OutputWaypoint. The mob should probably not move.
Note: Once NEARBY_FOUND is returned once, subsequent calls return PATH_FOUND. */
- ePathFinderStatus GetNextWayPoint(cChunk & a_Chunk, const Vector3d & a_Source, Vector3d * a_Destination, Vector3d * a_OutputWaypoint, bool a_DontCare = false);
+ ePathFinderStatus GetNextWayPoint(cChunk & a_Chunk, const Vector3d & a_Source, Vector3d * a_Destination, Vector3d * a_OutputWaypoint);
+
+ /** Sets the dontCare value. See the GetNextWayPoint documentation for details. */
+ void SetDontCare(bool a_DontCare);
+
+ /** Returns the current dontCare value. */
+ bool GetDontCare();
+ /** If true, the mob will try avoiding sunlight. */
+ void SetAvoidSunlight(bool a_AvoidSunlight);
+
+ /** Returns the current AvoidSunlight value. */
+ bool GetAvoidSunlight();
private:
/** The width of the Mob which owns this PathFinder. */
@@ -55,7 +67,7 @@ private:
/** Coordinates of the next position that should be reached. */
Vector3d m_WayPoint;
- /** Coordinates for where we should go. This is out ultimate, final destination. */
+ /** Coordinates for where we should go. This is our ultimate, final destination. */
Vector3d m_FinalDestination;
/** Coordinates for where we are practically going. */
@@ -75,6 +87,13 @@ private:
/** When a path is not found, this cooldown prevents any recalculations for several ticks. */
int m_NotFoundCooldown;
+ /** If true, the mob doesn't care where to go, and the Pathfinder may modify a_Destination
+ in an GetNextWayPoint call. */
+ bool m_DontCare;
+
+ /** If true, */
+ bool m_AvoidSunlight;
+
/** Ensures the location is not in the air or under water.
May change the Y coordinate of the given vector.
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.
diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp
index 82901b061..e6cc197bb 100644
--- a/src/Mobs/Pig.cpp
+++ b/src/Mobs/Pig.cpp
@@ -10,9 +10,15 @@
cPig::cPig(void) :
- super("Pig", mtPig, "entity.pig.hurt", "entity.pig.death", 0.9, 0.9),
+ super(mtPig, "entity.pig.hurt", "entity.pig.death", 0.9, 0.9),
m_bIsSaddled(false)
{
+ m_EMPersonality = PASSIVE;
+ m_BehaviorBreeder.AttachToMonster(*this);
+ m_BehaviorCoward.AttachToMonster(*this);
+ m_BehaviorItemFollower.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+ GetMonsterConfig("Pig");
}
@@ -41,6 +47,9 @@ void cPig::OnRightClicked(cPlayer & a_Player)
{
super::OnRightClicked(a_Player);
+ // Behavior note: saddling is pig-specific. It is not transferrable to other mobs.
+ // Therefore saddling is not a standalone behavior and is hardcoded into the pig.
+
if (m_bIsSaddled)
{
if (m_Attachee != nullptr)
@@ -91,6 +100,9 @@ void cPig::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
return;
}
+ // Behavior note: saddling is pig-specific. It is not transferrable to other mobs.
+ // Therefore saddling is not a standalone behavior and is hardcoded into the pig.
+
// If the attachee player is holding a carrot-on-stick, let them drive this pig:
if (m_bIsSaddled && (m_Attachee != nullptr))
{
@@ -120,7 +132,3 @@ bool cPig::DoTakeDamage(TakeDamageInfo & a_TDI)
}
return true;
}
-
-
-
-
diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h
index ed0685e5f..9a138f83e 100644
--- a/src/Mobs/Pig.h
+++ b/src/Mobs/Pig.h
@@ -1,16 +1,16 @@
-
#pragma once
-#include "PassiveMonster.h"
-
-
-
+#include "Behaviors/BehaviorBreeder.h"
+#include "Behaviors/BehaviorItemFollower.h"
+#include "Behaviors/BehaviorCoward.h"
+#include "Behaviors/BehaviorWanderer.h"
+#include "Monster.h"
class cPig :
- public cPassiveMonster
+ public cMonster
{
- typedef cPassiveMonster super;
+ typedef cMonster super;
public:
cPig(void);
@@ -30,8 +30,12 @@ public:
}
bool IsSaddled(void) const { return m_bIsSaddled; }
-
private:
+ // Tick controlling behaviors
+ cBehaviorBreeder m_BehaviorBreeder;
+ cBehaviorItemFollower m_BehaviorItemFollower;
+ cBehaviorCoward m_BehaviorCoward;
+ cBehaviorWanderer m_BehaviorWanderer;
bool m_bIsSaddled;
diff --git a/src/Mobs/Rabbit.cpp b/src/Mobs/Rabbit.cpp
index f4de0ba0c..a4a71a44d 100644
--- a/src/Mobs/Rabbit.cpp
+++ b/src/Mobs/Rabbit.cpp
@@ -14,6 +14,12 @@ cRabbit::cRabbit(void) :
static_cast<UInt8>(eRabbitType::SaltAndPepper) // Max possible Rabbit-Type
)), 0)
{
+ m_EMPersonality = PASSIVE;
+ m_BehaviorBreeder.AttachToMonster(*this);
+ m_BehaviorCoward.AttachToMonster(*this);
+ m_BehaviorItemFollower.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+ GetMonsterConfig("Rabbit");
}
@@ -21,10 +27,16 @@ cRabbit::cRabbit(void) :
cRabbit::cRabbit(eRabbitType Type, int MoreCarrotTicks) :
- super("Rabbit", mtRabbit, "entity.rabbit.hurt", "entity.rabbit.death", 0.82, 0.68),
+ super(mtRabbit, "entity.rabbit.hurt", "entity.rabbit.death", 0.82, 0.68),
m_Type(Type),
m_MoreCarrotTicks(MoreCarrotTicks)
{
+ m_EMPersonality = PASSIVE;
+ m_BehaviorBreeder.AttachToMonster(*this);
+ m_BehaviorCoward.AttachToMonster(*this);
+ m_BehaviorItemFollower.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+ GetMonsterConfig("Rabbit");
}
@@ -44,4 +56,3 @@ void cRabbit::GetDrops(cItems & a_Drops, cEntity * a_Killer)
RareDrops.Add(cItem(E_ITEM_RABBITS_FOOT));
AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel);
}
-
diff --git a/src/Mobs/Rabbit.h b/src/Mobs/Rabbit.h
index 289ff0282..119ba280b 100644
--- a/src/Mobs/Rabbit.h
+++ b/src/Mobs/Rabbit.h
@@ -1,8 +1,11 @@
#pragma once
-#include "PassiveMonster.h"
-
+#include "Behaviors/BehaviorBreeder.h"
+#include "Behaviors/BehaviorItemFollower.h"
+#include "Behaviors/BehaviorCoward.h"
+#include "Behaviors/BehaviorWanderer.h"
+#include "Monster.h"
@@ -23,9 +26,9 @@ enum class eRabbitType : UInt8
class cRabbit :
- public cPassiveMonster
+ public cMonster
{
- typedef cPassiveMonster super;
+ typedef cMonster super;
public:
cRabbit();
@@ -43,8 +46,12 @@ public:
eRabbitType GetRabbitType() const { return m_Type; }
int GetMoreCarrotTicks() const { return m_MoreCarrotTicks; }
-
private:
+ // Tick controlling behaviors
+ cBehaviorBreeder m_BehaviorBreeder;
+ cBehaviorItemFollower m_BehaviorItemFollower;
+ cBehaviorCoward m_BehaviorCoward;
+ cBehaviorWanderer m_BehaviorWanderer;
eRabbitType m_Type;
int m_MoreCarrotTicks; // Ticks until the Rabbit eat planted Carrots
diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp
index fef1adac6..001b80294 100644
--- a/src/Mobs/Sheep.cpp
+++ b/src/Mobs/Sheep.cpp
@@ -12,11 +12,17 @@
cSheep::cSheep(int a_Color) :
- super("Sheep", mtSheep, "entity.sheep.hurt", "entity.sheep.death", 0.6, 1.3),
+ super(mtSheep, "entity.sheep.hurt", "entity.sheep.death", 0.6, 1.3),
+ m_TimeToStopEating(-1),
m_IsSheared(false),
- m_WoolColor(a_Color),
- m_TimeToStopEating(-1)
+ m_WoolColor(a_Color)
{
+ m_EMPersonality = PASSIVE;
+ m_BehaviorBreeder.AttachToMonster(*this);
+ m_BehaviorCoward.AttachToMonster(*this);
+ m_BehaviorItemFollower.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+
// Generate random wool color.
if (m_WoolColor == -1)
{
@@ -27,6 +33,8 @@ cSheep::cSheep(int a_Color) :
{
m_WoolColor = 0;
}
+
+ GetMonsterConfig("Sheep");
}
@@ -135,7 +143,7 @@ void cSheep::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
-void cSheep::InheritFromParents(cPassiveMonster * a_Parent1, cPassiveMonster * a_Parent2)
+void cSheep::InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2)
{
static const struct
{
@@ -203,4 +211,3 @@ NIBBLETYPE cSheep::GenerateNaturalRandomColor(void)
return E_META_WOOL_PINK;
}
}
-
diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h
index c8af067f3..89fa41edf 100644
--- a/src/Mobs/Sheep.h
+++ b/src/Mobs/Sheep.h
@@ -1,16 +1,18 @@
#pragma once
-#include "PassiveMonster.h"
-
-
+#include "Behaviors/BehaviorBreeder.h"
+#include "Behaviors/BehaviorItemFollower.h"
+#include "Behaviors/BehaviorCoward.h"
+#include "Behaviors/BehaviorWanderer.h"
+#include "Monster.h"
class cSheep :
- public cPassiveMonster
+ public cMonster
{
- typedef cPassiveMonster super;
+ typedef cMonster super;
public:
@@ -25,7 +27,7 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
- virtual void InheritFromParents(cPassiveMonster * a_Parent1, cPassiveMonster * a_Parent2) override;
+ virtual void InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2) override;
virtual void GetFollowedItems(cItems & a_Items) override
{
@@ -41,12 +43,21 @@ public:
int GetFurColor(void) const { return m_WoolColor; }
void SetFurColor(int a_WoolColor) { m_WoolColor = a_WoolColor; }
-
private:
- bool m_IsSheared;
- int m_WoolColor;
+
+ // Tick controlling behaviors
+ cBehaviorBreeder m_BehaviorBreeder;
+ cBehaviorItemFollower m_BehaviorItemFollower;
+ cBehaviorCoward m_BehaviorCoward;
+ cBehaviorWanderer m_BehaviorWanderer;
+
+ // mobTodo transfer this to a behavior
int m_TimeToStopEating;
+ // Behavior note: These are ship-specific things not transferrable to other mobs.
+ // Therefore they do not need a Behavior and can stay hardcoded in the sheep.
+ bool m_IsSheared;
+ int m_WoolColor;
} ;
diff --git a/src/Mobs/Silverfish.h b/src/Mobs/Silverfish.h
index 90ef5ea5d..72b772a2c 100644
--- a/src/Mobs/Silverfish.h
+++ b/src/Mobs/Silverfish.h
@@ -1,24 +1,29 @@
#pragma once
-#include "AggressiveMonster.h"
-
+#include "Monster.h"
+#include "Behaviors/BehaviorDoNothing.h"
class cSilverfish :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
cSilverfish(void) :
- super("Silverfish", mtSilverfish, "entity.silverfish.hurt", "entity.silverfish.death", 0.3, 0.7)
+ super(mtSilverfish, "entity.silverfish.hurt", "entity.silverfish.death", 0.3, 0.7)
{
+ m_BehaviorDoNothing.AttachToMonster(*this);
+ GetMonsterConfig("Silverfish");
}
CLASS_PROTODEF(cSilverfish)
+
+private:
+ cBehaviorDoNothing m_BehaviorDoNothing;
} ;
diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp
index e48991a06..3e74ebeeb 100644
--- a/src/Mobs/Skeleton.cpp
+++ b/src/Mobs/Skeleton.cpp
@@ -6,14 +6,33 @@
#include "../Entities/ArrowEntity.h"
#include "ClientHandle.h"
-
+void ArrowShootingFunction(cBehaviorAttackerRanged & a_Behavior,
+ cMonster & a_Attacker, cPawn & a_Attacked)
+{
+ UNUSED(a_Behavior);
+ auto & Random = GetRandomProvider();
+ Vector3d Inaccuracy = Vector3d(Random.RandReal<double>(-0.25, 0.25), Random.RandReal<double>(-0.25, 0.25), Random.RandReal<double>(-0.25, 0.25));
+ Vector3d Speed = (a_Attacked.GetPosition() + Inaccuracy - a_Attacker.GetPosition()) * 5;
+ Speed.y += Random.RandInt(-1, 1);
+
+ auto Arrow = cpp14::make_unique<cArrowEntity>(&a_Attacker,
+ a_Attacker.GetPosX(), a_Attacker.GetPosY() + 1, a_Attacker.GetPosZ(), Speed);
+ auto ArrowPtr = Arrow.get();
+ ArrowPtr->Initialize(std::move(Arrow), *(a_Attacker.GetWorld()));
+}
cSkeleton::cSkeleton(bool IsWither) :
- super("Skeleton", mtSkeleton, "entity.skeleton.hurt", "entity.skeleton.death", 0.6, 1.8),
- m_bIsWither(IsWither)
+ super(mtSkeleton, "entity.skeleton.hurt", "entity.skeleton.death", 0.6, 1.8),
+ m_bIsWither(IsWither),
+ m_BehaviorAttackerRanged(ArrowShootingFunction)
{
- SetBurnsInDaylight(true);
+ m_EMPersonality = AGGRESSIVE;
+ m_BehaviorAttackerRanged.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+ m_BehaviorAggressive.AttachToMonster(*this);
+ m_BehaviourDayLightBurner.AttachToMonster(*this);
+ GetMonsterConfig("Skeleton");
}
@@ -48,33 +67,6 @@ void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-bool cSkeleton::Attack(std::chrono::milliseconds a_Dt)
-{
- StopMovingToPosition(); // Todo handle this in a better way, the skeleton does some uneeded recalcs due to inStateChasing
- auto & Random = GetRandomProvider();
- if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0))
- {
- Vector3d Inaccuracy = Vector3d(Random.RandReal<double>(-0.25, 0.25), Random.RandReal<double>(-0.25, 0.25), Random.RandReal<double>(-0.25, 0.25));
- Vector3d Speed = (GetTarget()->GetPosition() + Inaccuracy - GetPosition()) * 5;
- Speed.y += Random.RandInt(-1, 1);
-
- auto Arrow = cpp14::make_unique<cArrowEntity>(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed);
- auto ArrowPtr = Arrow.get();
- if (!ArrowPtr->Initialize(std::move(Arrow), *m_World))
- {
- return false;
- }
-
- ResetAttackCooldown();
- return true;
- }
- return false;
-}
-
-
-
-
-
void cSkeleton::SpawnOn(cClientHandle & a_ClientHandle)
{
super::SpawnOn(a_ClientHandle);
diff --git a/src/Mobs/Skeleton.h b/src/Mobs/Skeleton.h
index 0316fb9b5..59277729b 100644
--- a/src/Mobs/Skeleton.h
+++ b/src/Mobs/Skeleton.h
@@ -1,16 +1,17 @@
#pragma once
-#include "AggressiveMonster.h"
-
-
-
+#include "Monster.h"
+#include "Behaviors/BehaviorAttackerRanged.h"
+#include "Behaviors/BehaviorWanderer.h"
+#include "Behaviors/BehaviorAggressive.h"
+#include "Behaviors/BehaviorDayLightBurner.h"
class cSkeleton :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
cSkeleton(bool IsWither);
@@ -18,7 +19,7 @@ public:
CLASS_PROTODEF(cSkeleton)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual bool Attack(std::chrono::milliseconds a_Dt) override;
+ /*virtual bool Attack(std::chrono::milliseconds a_Dt) override;*/
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual bool IsUndead(void) override { return true; }
@@ -29,6 +30,13 @@ private:
bool m_bIsWither;
+ // tick behaviors
+ cBehaviorAttackerRanged m_BehaviorAttackerRanged;
+ cBehaviorWanderer m_BehaviorWanderer;
+
+ // other behaviors
+ cBehaviorAggressive m_BehaviorAggressive;
+ cBehaviorDayLightBurner m_BehaviourDayLightBurner;
} ;
diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp
index 291a3a57f..10898fa4c 100644
--- a/src/Mobs/Slime.cpp
+++ b/src/Mobs/Slime.cpp
@@ -10,8 +10,7 @@
cSlime::cSlime(int a_Size) :
- super("Slime",
- mtSlime,
+ super(mtSlime,
Printf("entity.%sslime.hurt", GetSizeName(a_Size).c_str()),
Printf("entity.%sslime.death", GetSizeName(a_Size).c_str()),
0.6 * a_Size,
@@ -19,8 +18,10 @@ cSlime::cSlime(int a_Size) :
),
m_Size(a_Size)
{
+ m_EMPersonality = AGGRESSIVE;
SetMaxHealth(a_Size * a_Size);
- SetAttackDamage(a_Size);
+ // SetAttackDamage(a_Size); //mobTodo myBehavior.setaTTACKDamage
+ GetMonsterConfig("Slime");
}
@@ -45,8 +46,8 @@ void cSlime::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-
-bool cSlime::Attack(std::chrono::milliseconds a_Dt)
+//mobTodo
+/*bool cSlime::Attack(std::chrono::milliseconds a_Dt)
{
if (m_Size > 1)
{
@@ -55,7 +56,7 @@ bool cSlime::Attack(std::chrono::milliseconds a_Dt)
}
return false;
-}
+}*/
diff --git a/src/Mobs/Slime.h b/src/Mobs/Slime.h
index c78461a02..f4f1aabd4 100644
--- a/src/Mobs/Slime.h
+++ b/src/Mobs/Slime.h
@@ -1,16 +1,15 @@
-
#pragma once
-#include "AggressiveMonster.h"
+#include "Monster.h"
class cSlime :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
/** Creates a slime of the specified size; size can be 1, 2 or 4, with 1 is the smallest and 4 is the tallest. */
@@ -20,7 +19,7 @@ public:
// cAggressiveMonster overrides:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual bool Attack(std::chrono::milliseconds a_Dt) override;
+ // virtual bool Attack(std::chrono::milliseconds a_Dt) override;
virtual void KilledBy(TakeDamageInfo & a_TDI) override;
int GetSize(void) const { return m_Size; }
diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp
index c86577a1e..9e0295785 100644
--- a/src/Mobs/SnowGolem.cpp
+++ b/src/Mobs/SnowGolem.cpp
@@ -9,8 +9,11 @@
cSnowGolem::cSnowGolem(void) :
- super("SnowGolem", mtSnowGolem, "entity.snowman.hurt", "entity.snowman.death", 0.4, 1.8)
+ super(mtSnowGolem, "entity.snowman.hurt", "entity.snowman.death", 0.4, 1.8)
{
+ m_EMPersonality = PASSIVE;
+ m_BehaviorWanderer.AttachToMonster(*this);
+ GetMonsterConfig("SnowGolem");
}
diff --git a/src/Mobs/SnowGolem.h b/src/Mobs/SnowGolem.h
index 708ddbef9..0170a57a9 100644
--- a/src/Mobs/SnowGolem.h
+++ b/src/Mobs/SnowGolem.h
@@ -1,16 +1,16 @@
#pragma once
-#include "PassiveAggressiveMonster.h"
-
+#include "Monster.h"
+#include "Behaviors/BehaviorWanderer.h"
class cSnowGolem :
- public cPassiveAggressiveMonster
+ public cMonster
{
- typedef cPassiveAggressiveMonster super;
+ typedef cMonster super;
public:
cSnowGolem(void);
@@ -19,6 +19,9 @@ public:
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
+
+private:
+ cBehaviorWanderer m_BehaviorWanderer;
} ;
diff --git a/src/Mobs/Spider.cpp b/src/Mobs/Spider.cpp
index 971ff22f6..6db7905ec 100644
--- a/src/Mobs/Spider.cpp
+++ b/src/Mobs/Spider.cpp
@@ -7,10 +7,39 @@
#include "../Entities/Player.h"
#include "../Chunk.h"
+bool AggressiveAtNightFunction(cBehaviorAggressive & a_Behavior, cMonster & a_Monster, cChunk & a_Chunk)
+{
+ UNUSED(a_Behavior);
+ if (!a_Monster.GetWorld()->IsChunkLighted(a_Monster.GetChunkX(), a_Monster.GetChunkZ()))
+ {
+ return false;
+ }
+
+ PREPARE_REL_AND_CHUNK(a_Monster.GetPosition(), a_Chunk);
+ if (!RelSuccess)
+ {
+ return false;
+ }
+
+ if (
+ !((Chunk->GetSkyLightAltered(Rel.x, Rel.y, Rel.z) > 11) || (Chunk->GetBlockLight(Rel.x, Rel.y, Rel.z) > 11))
+ )
+ {
+ return true;
+ }
+
+ return false;
+}
cSpider::cSpider(void) :
- super("Spider", mtSpider, "entity.spider.hurt", "entity.spider.death", 1.4, 0.9)
+ super(mtSpider, "entity.spider.hurt", "entity.spider.death", 1.4, 0.9) ,
+ m_BehaviorAggressive(AggressiveAtNightFunction)
{
+ m_EMPersonality = AGGRESSIVE;
+ m_BehaviorAttackerMelee.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+ m_BehaviorAggressive.AttachToMonster(*this);
+ GetMonsterConfig("Spider");
}
@@ -35,32 +64,6 @@ void cSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-void cSpider::EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk)
-{
- if (!GetWorld()->IsChunkLighted(GetChunkX(), GetChunkZ()))
- {
- return;
- }
-
- PREPARE_REL_AND_CHUNK(GetPosition(), a_Chunk);
- if (!RelSuccess)
- {
- return;
- }
-
- if (
- a_Player->CanMobsTarget() &&
- !((Chunk->GetSkyLightAltered(Rel.x, Rel.y, Rel.z) > 11) || (Chunk->GetBlockLight(Rel.x, Rel.y, Rel.z) > 11))
- )
- {
- super::EventSeePlayer(a_Player, a_Chunk);
- }
-}
-
-
-
-
-
bool cSpider::DoTakeDamage(TakeDamageInfo & a_TDI)
{
if (!super::DoTakeDamage(a_TDI))
@@ -68,6 +71,7 @@ bool cSpider::DoTakeDamage(TakeDamageInfo & a_TDI)
return false;
}
+ /* mobTodo
// If the source of the damage is not from an pawn entity, switch to idle
if ((a_TDI.Attacker == nullptr) || !a_TDI.Attacker->IsPawn())
{
@@ -77,7 +81,7 @@ bool cSpider::DoTakeDamage(TakeDamageInfo & a_TDI)
{
// If the source of the damage is from a pawn entity, chase that entity
m_EMState = CHASING;
- }
+ }*/
return true;
}
diff --git a/src/Mobs/Spider.h b/src/Mobs/Spider.h
index af2753012..7113b9a3e 100644
--- a/src/Mobs/Spider.h
+++ b/src/Mobs/Spider.h
@@ -1,16 +1,20 @@
#pragma once
-#include "AggressiveMonster.h"
+#include "Monster.h"
+#include "Behaviors/BehaviorAttackerMelee.h"
+#include "Behaviors/BehaviorWanderer.h"
+#include "Behaviors/BehaviorAggressive.h"
+#include "Behaviors/BehaviorDayLightBurner.h"
class cSpider :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
cSpider(void);
@@ -18,8 +22,14 @@ public:
CLASS_PROTODEF(cSpider)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual void EventSeePlayer(cPlayer *, cChunk & a_Chunk) override;
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
+private:
+ // tick behaviors
+ cBehaviorAttackerMelee m_BehaviorAttackerMelee;
+ cBehaviorWanderer m_BehaviorWanderer;
+
+ // other behaviors
+ cBehaviorAggressive m_BehaviorAggressive;
} ;
diff --git a/src/Mobs/Squid.cpp b/src/Mobs/Squid.cpp
index 8ae688a1b..0f35fe634 100644
--- a/src/Mobs/Squid.cpp
+++ b/src/Mobs/Squid.cpp
@@ -9,8 +9,11 @@
cSquid::cSquid(void) :
- super("Squid", mtSquid, "entity.squid.hurt", "entity.squid.death", 0.95, 0.95)
+ super(mtSquid, "entity.squid.hurt", "entity.squid.death", 0.95, 0.95)
{
+ m_EMPersonality = PASSIVE;
+ m_BehaviorDoNothing.AttachToMonster(*this);
+ GetMonsterConfig("Squid");
}
diff --git a/src/Mobs/Squid.h b/src/Mobs/Squid.h
index aeeec77df..8039089e3 100644
--- a/src/Mobs/Squid.h
+++ b/src/Mobs/Squid.h
@@ -1,16 +1,17 @@
#pragma once
-#include "PassiveMonster.h"
+#include "Monster.h"
+#include "Behaviors/BehaviorDoNothing.h"
class cSquid :
- public cPassiveMonster
+ public cMonster
{
- typedef cPassiveMonster super;
+ typedef cMonster super;
public:
cSquid();
@@ -23,6 +24,9 @@ public:
// Squids do not drown (or float)
virtual void HandleAir(void) override {}
+
+private:
+ cBehaviorDoNothing m_BehaviorDoNothing;
} ;
diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp
index 26462ba31..ed3b3305f 100644
--- a/src/Mobs/Villager.cpp
+++ b/src/Mobs/Villager.cpp
@@ -12,11 +12,14 @@
cVillager::cVillager(eVillagerType VillagerType) :
- super("Villager", mtVillager, "entity.villager.hurt", "entity.villager.death", 0.6, 1.8),
+ super(mtVillager, "entity.villager.hurt", "entity.villager.death", 0.6, 1.8),
m_ActionCountDown(-1),
m_Type(VillagerType),
m_VillagerAction(false)
{
+ m_EMPersonality = PASSIVE;
+ m_BehaviorDoNothing.AttachToMonster(*this);
+ GetMonsterConfig("Villager");
}
diff --git a/src/Mobs/Villager.h b/src/Mobs/Villager.h
index 6f3e7b4e8..72c7c7301 100644
--- a/src/Mobs/Villager.h
+++ b/src/Mobs/Villager.h
@@ -1,16 +1,21 @@
#pragma once
-#include "PassiveMonster.h"
+#include "Monster.h"
#include "Blocks/ChunkInterface.h"
+#include "Behaviors/BehaviorBreeder.h"
+#include "Behaviors/BehaviorItemFollower.h"
+#include "Behaviors/BehaviorCoward.h"
+#include "Behaviors/BehaviorWanderer.h"
+#include "Monster.h"
-
+#include "Behaviors/BehaviorDoNothing.h"
class cVillager :
- public cPassiveMonster
+ public cMonster
{
- typedef cPassiveMonster super;
+ typedef cMonster super;
public:
@@ -53,12 +58,13 @@ public:
bool DoesHaveActionActivated(void) const { return m_VillagerAction; }
private:
+ // Tick controlling behaviors
+ cBehaviorDoNothing m_BehaviorDoNothing;
int m_ActionCountDown;
int m_Type;
bool m_VillagerAction;
Vector3i m_CropsPos;
-
} ;
diff --git a/src/Mobs/Witch.cpp b/src/Mobs/Witch.cpp
index 3f56108ae..43edf4b43 100644
--- a/src/Mobs/Witch.cpp
+++ b/src/Mobs/Witch.cpp
@@ -9,8 +9,11 @@
cWitch::cWitch(void) :
- super("Witch", mtWitch, "entity.witch.hurt", "entity.witch.death", 0.6, 1.8)
+ super(mtWitch, "entity.witch.hurt", "entity.witch.death", 0.6, 1.8)
{
+ m_EMPersonality = AGGRESSIVE;
+ m_BehaviorDoNothing.AttachToMonster(*this);
+ GetMonsterConfig("Witch");
}
diff --git a/src/Mobs/Witch.h b/src/Mobs/Witch.h
index 706fcd9b3..a57016a2a 100644
--- a/src/Mobs/Witch.h
+++ b/src/Mobs/Witch.h
@@ -1,16 +1,16 @@
#pragma once
-#include "AggressiveMonster.h"
-
+#include "Monster.h"
+#include "Behaviors/BehaviorDoNothing.h"
class cWitch :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
cWitch();
@@ -18,8 +18,10 @@ public:
CLASS_PROTODEF(cWitch)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
+ bool IsAngry() const { return false; }
- bool IsAngry(void) const {return ((m_EMState == ATTACKING) || (m_EMState == CHASING)); }
+private:
+ cBehaviorDoNothing m_BehaviorDoNothing;
} ;
diff --git a/src/Mobs/Wither.cpp b/src/Mobs/Wither.cpp
index dd85d7d2b..a76ac80a1 100644
--- a/src/Mobs/Wither.cpp
+++ b/src/Mobs/Wither.cpp
@@ -11,11 +11,14 @@
cWither::cWither(void) :
- super("Wither", mtWither, "entity.wither.hurt", "entity.wither.death", 0.9, 4.0),
+ super(mtWither, "entity.wither.hurt", "entity.wither.death", 0.9, 4.0),
m_WitherInvulnerableTicks(220)
{
SetMaxHealth(300);
SetHealth(GetMaxHealth() / 3);
+ m_EMPersonality = AGGRESSIVE;
+ m_BehaviorDoNothing.AttachToMonster(*this);
+ GetMonsterConfig("Wither");
}
diff --git a/src/Mobs/Wither.h b/src/Mobs/Wither.h
index 5f6ec607c..39a2d5020 100644
--- a/src/Mobs/Wither.h
+++ b/src/Mobs/Wither.h
@@ -1,16 +1,16 @@
#pragma once
-#include "AggressiveMonster.h"
-
+#include "Monster.h"
+#include "Behaviors/BehaviorDoNothing.h"
class cWither :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
cWither(void);
@@ -33,6 +33,7 @@ public:
virtual bool IsUndead(void) override { return true; }
private:
+ cBehaviorDoNothing m_BehaviorDoNothing;
/** The number of ticks of invulnerability left after being initially created. Zero once invulnerability has expired. */
unsigned int m_WitherInvulnerableTicks;
diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp
index f3b859c76..bfc7b10e8 100644
--- a/src/Mobs/Wolf.cpp
+++ b/src/Mobs/Wolf.cpp
@@ -12,7 +12,7 @@
cWolf::cWolf(void) :
- super("Wolf", mtWolf, "entity.wolf.hurt", "entity.wolf.death", 0.6, 0.8),
+ super(mtWolf, "entity.wolf.hurt", "entity.wolf.death", 0.6, 0.8),
m_IsSitting(false),
m_IsTame(false),
m_IsBegging(false),
@@ -22,51 +22,16 @@ cWolf::cWolf(void) :
m_NotificationCooldown(0)
{
m_RelativeWalkSpeed = 2;
+ m_EMPersonality = PASSIVE;
+ GetMonsterConfig("Wolf");
}
-
bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI)
{
- cPawn * PreviousTarget = GetTarget();
- if (!super::DoTakeDamage(a_TDI))
- {
- return false;
- }
-
- if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn())
- {
- auto currTarget = GetTarget();
- if ((currTarget != nullptr) && currTarget->IsPlayer())
- {
- if (m_IsTame)
- {
- if ((static_cast<cPlayer*>(currTarget)->GetUUID() == m_OwnerUUID))
- {
- SetTarget(PreviousTarget); // Do not attack owner
- }
- else
- {
- SetIsSitting(false);
- NotifyAlliesOfFight(static_cast<cPawn*>(a_TDI.Attacker));
- }
- }
- else
- {
- m_IsAngry = true;
- }
- }
- else if (m_IsTame)
- {
- SetIsSitting(false);
- NotifyAlliesOfFight(static_cast<cPawn*>(a_TDI.Attacker));
- }
- }
-
- m_World->BroadcastEntityMetadata(*this); // Broadcast health and possibly angry face
- return true;
+ /*TODO bring from master and adapt*/
}
@@ -75,42 +40,7 @@ bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI)
void cWolf::NotifyAlliesOfFight(cPawn * a_Opponent)
{
- if (GetOwnerName() == "")
- {
- return;
- }
- m_NotificationCooldown = 15;
- class cCallback : public cPlayerListCallback
- {
- virtual bool Item(cPlayer * a_Player) override
- {
- a_Player->NotifyNearbyWolves(m_Opponent, false);
- return false;
- }
- public:
- cPawn * m_Opponent;
- } Callback;
-
- Callback.m_Opponent = a_Opponent;
- m_World->DoWithPlayerByUUID(m_OwnerUUID, Callback);
-}
-
-bool cWolf::Attack(std::chrono::milliseconds a_Dt)
-{
- UNUSED(a_Dt);
-
- if ((GetTarget() != nullptr) && (GetTarget()->IsPlayer()))
- {
- if (static_cast<cPlayer *>(GetTarget())->GetUUID() == m_OwnerUUID)
- {
- SetTarget(nullptr);
- return false;
- }
- }
-
- NotifyAlliesOfFight(static_cast<cPawn*>(GetTarget()));
- return super::Attack(a_Dt);
-
+ /*TODO bring from master and adapt*/
}
@@ -119,47 +49,8 @@ bool cWolf::Attack(std::chrono::milliseconds a_Dt)
void cWolf::ReceiveNearbyFightInfo(const cUUID & a_PlayerID, cPawn * a_Opponent, bool a_IsPlayerInvolved)
{
- if (
- (a_Opponent == nullptr) || IsSitting() || (!IsTame()) ||
- (!a_Opponent->IsPawn()) || (a_PlayerID != m_OwnerUUID)
- )
- {
- return;
- }
-
- // If we already have a target
- if (GetTarget() != nullptr)
- {
- // If a wolf is asking for help and we already have a target, do nothing
- if (!a_IsPlayerInvolved)
- {
- return;
- }
- // If a player is asking for help and we already have a target,
- // there's a 50% chance of helping and a 50% chance of doing nothing
- // This helps spread a wolf pack's targets over several mobs
- else if (GetRandomProvider().RandBool())
- {
- return;
- }
- }
-
- if (a_Opponent->IsPlayer() && static_cast<cPlayer *>(a_Opponent)->GetUUID() == m_OwnerUUID)
- {
- return; // Our owner has hurt himself, avoid attacking them.
- }
-
- if (a_Opponent->IsMob() && static_cast<cMonster *>(a_Opponent)->GetMobType() == mtWolf)
- {
- cWolf * Wolf = static_cast<cWolf *>(a_Opponent);
- if (Wolf->GetOwnerUUID() == GetOwnerUUID())
- {
- return; // Our owner attacked one of their wolves. Abort attacking wolf.
- }
- }
-
- SetTarget(a_Opponent);
-
+ /*TODO bring from master and adapt
+ */
}
@@ -169,82 +60,8 @@ void cWolf::ReceiveNearbyFightInfo(const cUUID & a_PlayerID, cPawn * a_Opponent,
void cWolf::OnRightClicked(cPlayer & a_Player)
{
- const cItem & EquippedItem = a_Player.GetEquippedItem();
- const int EquippedItemType = EquippedItem.m_ItemType;
-
- if (!IsTame() && !IsAngry())
- {
- // If the player is holding a bone, try to tame the wolf:
- if (EquippedItemType == E_ITEM_BONE)
- {
- if (!a_Player.IsGameModeCreative())
- {
- a_Player.GetInventory().RemoveOneEquippedItem();
- }
-
- if (GetRandomProvider().RandBool(0.125))
- {
- // Taming succeeded
- SetMaxHealth(20);
- SetIsTame(true);
- SetOwner(a_Player.GetName(), a_Player.GetUUID());
- m_World->BroadcastEntityStatus(*this, esWolfTamed);
- m_World->GetBroadcaster().BroadcastParticleEffect("heart", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5);
- }
- else
- {
- // Taming failed
- m_World->BroadcastEntityStatus(*this, esWolfTaming);
- m_World->GetBroadcaster().BroadcastParticleEffect("smoke", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5);
- }
- }
- }
- else if (IsTame())
- {
- // Feed the wolf, restoring its health, or dye its collar:
- switch (EquippedItemType)
- {
- case E_ITEM_RAW_BEEF:
- case E_ITEM_STEAK:
- case E_ITEM_RAW_PORKCHOP:
- case E_ITEM_COOKED_PORKCHOP:
- case E_ITEM_RAW_CHICKEN:
- case E_ITEM_COOKED_CHICKEN:
- case E_ITEM_ROTTEN_FLESH:
- {
- if (m_Health < m_MaxHealth)
- {
- Heal(ItemHandler(EquippedItemType)->GetFoodInfo(&EquippedItem).FoodLevel);
- if (!a_Player.IsGameModeCreative())
- {
- a_Player.GetInventory().RemoveOneEquippedItem();
- }
- }
- break;
- }
- case E_ITEM_DYE:
- {
- if (a_Player.GetUUID() == m_OwnerUUID) // Is the player the owner of the dog?
- {
- SetCollarColor(EquippedItem.m_ItemDamage);
- if (!a_Player.IsGameModeCreative())
- {
- a_Player.GetInventory().RemoveOneEquippedItem();
- }
- }
- break;
- }
- default:
- {
- if (a_Player.GetUUID() == m_OwnerUUID) // Is the player the owner of the dog?
- {
- SetIsSitting(!IsSitting());
- }
- }
- }
- }
-
- m_World->BroadcastEntityMetadata(*this);
+ /*TODO bring from master and adapt
+ */
}
@@ -253,92 +70,11 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
- if (!IsAngry())
- {
- cMonster::Tick(a_Dt, a_Chunk);
- if (m_NotificationCooldown > 0)
- {
- m_NotificationCooldown -= 1;
- }
- }
- else
- {
- super::Tick(a_Dt, a_Chunk);
- }
-
- if (!IsTicking())
- {
- // The base class tick destroyed us
- return;
- }
-
- if (GetTarget() == nullptr)
- {
- cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance));
- if (a_Closest_Player != nullptr)
- {
- switch (a_Closest_Player->GetEquippedItem().m_ItemType)
- {
- case E_ITEM_BONE:
- case E_ITEM_RAW_BEEF:
- case E_ITEM_STEAK:
- case E_ITEM_RAW_CHICKEN:
- case E_ITEM_COOKED_CHICKEN:
- case E_ITEM_ROTTEN_FLESH:
- case E_ITEM_RAW_PORKCHOP:
- case E_ITEM_COOKED_PORKCHOP:
- {
- if (!IsBegging())
- {
- SetIsBegging(true);
- m_World->BroadcastEntityMetadata(*this);
- }
-
- m_FinalDestination = a_Closest_Player->GetPosition(); // So that we will look at a player holding food
-
- // Don't move to the player if the wolf is sitting.
- if (!IsSitting())
- {
- MoveToPosition(a_Closest_Player->GetPosition());
- }
-
- break;
- }
- default:
- {
- if (IsBegging())
- {
- SetIsBegging(false);
- m_World->BroadcastEntityMetadata(*this);
- }
- }
- }
- }
- }
- else
- {
- if (IsSitting())
- {
- SetTarget(nullptr);
- }
- else
- {
- MoveToPosition(GetTarget()->GetPosition());
- if (TargetIsInRange())
- {
- Attack(a_Dt);
- }
- }
- }
-
- if (IsTame() && !IsSitting())
- {
- TickFollowPlayer();
- }
- else if (IsSitting())
- {
- StopMovingToPosition();
- }
+ //mobTodo behaviors!
+
+ /*
+ TODO bring from master and adapt
+ */
}
@@ -347,61 +83,9 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
void cWolf::TickFollowPlayer()
{
- class cCallback :
- public cPlayerListCallback
- {
- virtual bool Item(cPlayer * a_Player) override
- {
- OwnerPos = a_Player->GetPosition();
- OwnerFlying = a_Player->IsFlying();
- return true;
- }
- public:
- Vector3d OwnerPos;
- bool OwnerFlying;
- } Callback;
-
- if (m_World->DoWithPlayerByUUID(m_OwnerUUID, Callback))
- {
- // The player is present in the world, follow him:
- double Distance = (Callback.OwnerPos - GetPosition()).Length();
- if (Distance > 20)
- {
- if (!Callback.OwnerFlying)
- {
- Callback.OwnerPos.y = FindFirstNonAirBlockPosition(Callback.OwnerPos.x, Callback.OwnerPos.z);
- TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z);
- SetTarget(nullptr);
- }
- }
- if (Distance < 2)
- {
- if (GetTarget() == nullptr)
- {
- StopMovingToPosition();
- }
- }
- else
- {
- if (GetTarget() == nullptr)
- {
- if (!Callback.OwnerFlying)
- {
- MoveToPosition(Callback.OwnerPos);
- }
- }
- }
- }
-}
-
-
-
-void cWolf::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
-{
- if (!IsTame())
- {
- cMonster::InStateIdle(a_Dt, a_Chunk);
- }
+ /*
+ TODO bring from master and adapt
+ */
}
diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h
index 861419ba8..515cd33e8 100644
--- a/src/Mobs/Wolf.h
+++ b/src/Mobs/Wolf.h
@@ -1,7 +1,7 @@
#pragma once
-#include "PassiveAggressiveMonster.h"
+#include "Monster.h"
#include "../UUID.h"
@@ -10,9 +10,9 @@ class cEntity;
class cWolf :
- public cPassiveAggressiveMonster
+ public cMonster
{
- typedef cPassiveAggressiveMonster super;
+ typedef cMonster super;
public:
cWolf(void);
@@ -24,7 +24,6 @@ public:
virtual void OnRightClicked(cPlayer & a_Player) override;
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void TickFollowPlayer();
- virtual bool Attack(std::chrono::milliseconds a_Dt) override;
// Get functions
bool IsSitting (void) const override { return m_IsSitting; }
@@ -56,8 +55,6 @@ public:
@param a_IsPlayerInvolved Whether the fighter a player or a wolf. */
void ReceiveNearbyFightInfo(const cUUID & a_PlayerUUID, cPawn * a_Opponent, bool a_IsPlayerInvolved);
- virtual void InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
-
protected:
bool m_IsSitting;
diff --git a/src/Mobs/Zombie.cpp b/src/Mobs/Zombie.cpp
index 882e98bf1..085d90b7f 100644
--- a/src/Mobs/Zombie.cpp
+++ b/src/Mobs/Zombie.cpp
@@ -10,11 +10,17 @@
cZombie::cZombie(bool a_IsVillagerZombie) :
- super("Zombie", mtZombie, "entity.zombie.hurt", "entity.zombie.death", 0.6, 1.8),
+ super(mtZombie, "entity.zombie.hurt", "entity.zombie.death", 0.6, 1.8),
m_IsVillagerZombie(a_IsVillagerZombie),
m_IsConverting(false)
{
- SetBurnsInDaylight(true);
+ m_EMPersonality = AGGRESSIVE;
+ m_BehaviorAttackerMelee.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+ m_BehaviorAggressive.AttachToMonster(*this);
+ m_BehaviourDayLightBurner.AttachToMonster(*this);
+ GetMonsterConfig("Zombie");
+ // mobTodo I need the config to load after attaching the Behaviors but this is not clean.
}
diff --git a/src/Mobs/Zombie.h b/src/Mobs/Zombie.h
index 47a9f1904..5c2c225e4 100644
--- a/src/Mobs/Zombie.h
+++ b/src/Mobs/Zombie.h
@@ -1,15 +1,18 @@
#pragma once
-#include "AggressiveMonster.h"
-
+#include "Monster.h"
+#include "Behaviors/BehaviorAttackerMelee.h"
+#include "Behaviors/BehaviorWanderer.h"
+#include "Behaviors/BehaviorAggressive.h"
+#include "Behaviors/BehaviorDayLightBurner.h"
class cZombie :
- public cAggressiveMonster
+ public cMonster
{
- typedef cAggressiveMonster super;
+ typedef cMonster super;
public:
cZombie(bool a_IsVillagerZombie);
@@ -21,12 +24,18 @@ public:
bool IsVillagerZombie(void) const { return m_IsVillagerZombie; }
bool IsConverting (void) const { return m_IsConverting; }
-
private:
bool m_IsVillagerZombie;
bool m_IsConverting;
+ // tick behaviors
+ cBehaviorAttackerMelee m_BehaviorAttackerMelee;
+ cBehaviorWanderer m_BehaviorWanderer;
+
+ // other behaviors
+ cBehaviorAggressive m_BehaviorAggressive;
+ cBehaviorDayLightBurner m_BehaviourDayLightBurner;
} ;
diff --git a/src/Mobs/ZombiePigman.cpp b/src/Mobs/ZombiePigman.cpp
index 2581d3751..d817902f8 100644
--- a/src/Mobs/ZombiePigman.cpp
+++ b/src/Mobs/ZombiePigman.cpp
@@ -9,8 +9,13 @@
cZombiePigman::cZombiePigman(void) :
- super("ZombiePigman", mtZombiePigman, "entity.zombie_pig.hurt", "entity.zombie_pig.death", 0.6, 1.8)
+ super(mtZombiePigman, "entity.zombie_pig.hurt", "entity.zombie_pig.death", 0.6, 1.8)
{
+ m_EMPersonality = PASSIVE;
+ m_BehaviorAttackerMelee.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+ m_BehaviorAggressive.AttachToMonster(*this);
+ GetMonsterConfig("ZombiePigman");
}
diff --git a/src/Mobs/ZombiePigman.h b/src/Mobs/ZombiePigman.h
index 23a7be8da..e3bab0539 100644
--- a/src/Mobs/ZombiePigman.h
+++ b/src/Mobs/ZombiePigman.h
@@ -1,15 +1,17 @@
#pragma once
-#include "PassiveAggressiveMonster.h"
-
+#include "Monster.h"
+#include "Behaviors/BehaviorAttackerMelee.h"
+#include "Behaviors/BehaviorWanderer.h"
+#include "Behaviors/BehaviorAggressive.h"
class cZombiePigman :
- public cPassiveAggressiveMonster
+ public cMonster
{
- typedef cPassiveAggressiveMonster super;
+ typedef cMonster super;
public:
cZombiePigman(void);
@@ -21,6 +23,13 @@ public:
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual bool IsUndead(void) override { return true; }
+
+ // tick behaviors
+ cBehaviorAttackerMelee m_BehaviorAttackerMelee;
+ cBehaviorWanderer m_BehaviorWanderer;
+
+ // other behaviors
+ cBehaviorAggressive m_BehaviorAggressive;
} ;
diff --git a/src/MonsterConfig.cpp b/src/MonsterConfig.cpp
index 28132607e..fdd15e01e 100644
--- a/src/MonsterConfig.cpp
+++ b/src/MonsterConfig.cpp
@@ -3,6 +3,7 @@
#include "MonsterConfig.h"
#include "Mobs/Monster.h"
+#include "Mobs/Behaviors/BehaviorAttacker.h"
#include "IniFile.h"
@@ -90,11 +91,17 @@ void cMonsterConfig::AssignAttributes(cMonster * a_Monster, const AString & a_Na
{
if (itr->m_Name.compare(a_Name) == 0)
{
- a_Monster->SetAttackDamage (itr->m_AttackDamage);
- a_Monster->SetAttackRange (itr->m_AttackRange);
- a_Monster->SetSightDistance(itr->m_SightDistance);
- a_Monster->SetAttackRate (static_cast<float>(itr->m_AttackRate));
+ cBehaviorAttacker * Attacker = a_Monster->GetBehaviorAttacker();
+
+ if (Attacker != nullptr)
+ {
+ Attacker->SetAttackDamage (itr->m_AttackDamage);
+ Attacker->SetAttackRange (itr->m_AttackRange);
+ Attacker->SetAttackRate (static_cast<float>(itr->m_AttackRate));
+ }
+
a_Monster->SetMaxHealth (itr->m_MaxHealth);
+ a_Monster->SetSightDistance(itr->m_SightDistance);
a_Monster->SetIsFireproof (itr->m_IsFireproof);
return;
}
diff --git a/src/Protocol/Protocol_1_10.cpp b/src/Protocol/Protocol_1_10.cpp
index bb29c58ce..706558810 100644
--- a/src/Protocol/Protocol_1_10.cpp
+++ b/src/Protocol/Protocol_1_10.cpp
@@ -925,7 +925,8 @@ void cProtocol_1_10_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_
auto & Witch = reinterpret_cast<const cWitch &>(a_Mob);
a_Pkt.WriteBEUInt8(WITCH_AGGRESIVE);
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
- a_Pkt.WriteBool(Witch.IsAngry());
+ // a_Pkt.WriteBool(Witch.IsAngry()); //mobTodo
+ a_Pkt.WriteBool(0);
break;
} // case mtWitch
diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp
index b6e5b5a38..053aa75e5 100644
--- a/src/Protocol/Protocol_1_8.cpp
+++ b/src/Protocol/Protocol_1_8.cpp
@@ -3484,7 +3484,7 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M
a_Pkt.WriteBEUInt8(0x56); // Int at index 22
a_Pkt.WriteBEInt32(Horse.GetHorseArmour());
a_Pkt.WriteBEUInt8(0x0c);
- a_Pkt.WriteBEInt8(Horse.IsBaby() ? -1 : (Horse.IsInLoveCooldown() ? 1 : 0));
+ a_Pkt.WriteBEInt8(Horse.IsBaby() ? -1 : (Horse.GetBehaviorBreeder()->IsInLoveCooldown() ? 1 : 0));
break;
} // case mtHorse
@@ -3500,7 +3500,7 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M
{
auto & Ocelot = reinterpret_cast<const cOcelot &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
- a_Pkt.WriteBEInt8(Ocelot.IsBaby() ? -1 : (Ocelot.IsInLoveCooldown() ? 1 : 0));
+ a_Pkt.WriteBEInt8(Ocelot.IsBaby() ? -1 : (Ocelot.GetBehaviorBreeder()->IsInLoveCooldown() ? 1 : 0));
break;
} // case mtOcelot
@@ -3508,7 +3508,7 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M
{
auto & Cow = reinterpret_cast<const cCow &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
- a_Pkt.WriteBEInt8(Cow.IsBaby() ? -1 : (Cow.IsInLoveCooldown() ? 1 : 0));
+ a_Pkt.WriteBEInt8(Cow.IsBaby() ? -1 : (Cow.GetBehaviorBreeder()->IsInLoveCooldown() ? 1 : 0));
break;
} // case mtCow
@@ -3516,7 +3516,7 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M
{
auto & Chicken = reinterpret_cast<const cChicken &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
- a_Pkt.WriteBEInt8(Chicken.IsBaby() ? -1 : (Chicken.IsInLoveCooldown() ? 1 : 0));
+ a_Pkt.WriteBEInt8(Chicken.IsBaby() ? -1 : (Chicken.GetBehaviorBreeder()->IsInLoveCooldown() ? 1 : 0));
break;
} // case mtChicken
@@ -3524,7 +3524,7 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M
{
auto & Pig = reinterpret_cast<const cPig &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
- a_Pkt.WriteBEInt8(Pig.IsBaby() ? -1 : (Pig.IsInLoveCooldown() ? 1 : 0));
+ a_Pkt.WriteBEInt8(Pig.IsBaby() ? -1 : (Pig.GetBehaviorBreeder()->IsInLoveCooldown() ? 1 : 0));
a_Pkt.WriteBEUInt8(0x10);
a_Pkt.WriteBEUInt8(Pig.IsSaddled() ? 1 : 0);
break;
@@ -3534,7 +3534,7 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M
{
auto & Sheep = reinterpret_cast<const cSheep &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
- a_Pkt.WriteBEInt8(Sheep.IsBaby() ? -1 : (Sheep.IsInLoveCooldown() ? 1 : 0));
+ a_Pkt.WriteBEInt8(Sheep.IsBaby() ? -1 : (Sheep.GetBehaviorBreeder()->IsInLoveCooldown() ? 1 : 0));
a_Pkt.WriteBEUInt8(0x10);
Byte SheepMetadata = 0;
@@ -3553,7 +3553,7 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M
a_Pkt.WriteBEUInt8(0x12);
a_Pkt.WriteBEUInt8(static_cast<UInt8>(Rabbit.GetRabbitType()));
a_Pkt.WriteBEUInt8(0x0c);
- a_Pkt.WriteBEInt8(Rabbit.IsBaby() ? -1 : (Rabbit.IsInLoveCooldown() ? 1 : 0));
+ a_Pkt.WriteBEInt8(Rabbit.IsBaby() ? -1 : (Rabbit.GetBehaviorBreeder()->IsInLoveCooldown() ? 1 : 0));
break;
} // case mtRabbit
@@ -3587,7 +3587,8 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M
{
auto & Witch = reinterpret_cast<const cWitch &>(a_Mob);
a_Pkt.WriteBEUInt8(0x15);
- a_Pkt.WriteBEUInt8(Witch.IsAngry() ? 1 : 0);
+ // a_Pkt.WriteBEUInt8(Witch.IsAngry() ? 1 : 0); // mobTodo
+ a_Pkt.WriteBEUInt8(0);
break;
} // case mtWitch
diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp
index 90eedf91e..9f30e559d 100644
--- a/src/Protocol/Protocol_1_9.cpp
+++ b/src/Protocol/Protocol_1_9.cpp
@@ -4053,7 +4053,8 @@ void cProtocol_1_9_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M
auto & Witch = reinterpret_cast<const cWitch &>(a_Mob);
a_Pkt.WriteBEUInt8(11); // Index 11: Is angry
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
- a_Pkt.WriteBool(Witch.IsAngry());
+ // a_Pkt.WriteBool(Witch.IsAngry()); // mobTodo
+ a_Pkt.WriteBool(0);
break;
} // case mtWitch