summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/Mobs/AggressiveMonster.cpp98
-rw-r--r--src/Mobs/AggressiveMonster.h27
-rw-r--r--src/Mobs/Bat.cpp9
-rw-r--r--src/Mobs/Behaviors/Behavior.cpp102
-rw-r--r--src/Mobs/Behaviors/Behavior.h30
-rw-r--r--src/Mobs/Behaviors/BehaviorAggressive.cpp35
-rw-r--r--src/Mobs/Behaviors/BehaviorAggressive.h33
-rw-r--r--src/Mobs/Behaviors/BehaviorBreeder.cpp252
-rw-r--r--src/Mobs/Behaviors/BehaviorBreeder.h52
-rw-r--r--src/Mobs/Behaviors/BehaviorChaser.cpp256
-rw-r--r--src/Mobs/Behaviors/BehaviorChaser.h64
-rw-r--r--src/Mobs/Behaviors/BehaviorCoward.cpp96
-rw-r--r--src/Mobs/Behaviors/BehaviorCoward.h25
-rw-r--r--src/Mobs/Behaviors/BehaviorDayLightBurner.cpp106
-rw-r--r--src/Mobs/Behaviors/BehaviorDayLightBurner.h21
-rw-r--r--src/Mobs/Behaviors/BehaviorItemDropper.cpp1
-rw-r--r--src/Mobs/Behaviors/BehaviorItemDropper.h1
-rw-r--r--src/Mobs/Behaviors/BehaviorItemFollower.cpp64
-rw-r--r--src/Mobs/Behaviors/BehaviorItemFollower.h18
-rw-r--r--src/Mobs/Behaviors/BehaviorItemReplacer.cpp40
-rw-r--r--src/Mobs/Behaviors/BehaviorItemReplacer.h26
-rw-r--r--src/Mobs/Behaviors/BehaviorStriker.cpp7
-rw-r--r--src/Mobs/Behaviors/BehaviorStriker.h13
-rw-r--r--src/Mobs/Behaviors/BehaviorWanderer.cpp85
-rw-r--r--src/Mobs/Behaviors/BehaviorWanderer.h23
-rw-r--r--src/Mobs/Behaviors/CMakeLists.txt37
-rw-r--r--src/Mobs/Blaze.cpp59
-rw-r--r--src/Mobs/Blaze.h11
-rw-r--r--src/Mobs/CaveSpider.cpp64
-rw-r--r--src/Mobs/CaveSpider.h13
-rw-r--r--src/Mobs/Chicken.cpp88
-rw-r--r--src/Mobs/Chicken.h24
-rw-r--r--src/Mobs/Cow.cpp39
-rw-r--r--src/Mobs/Cow.h31
-rw-r--r--src/Mobs/Creeper.cpp10
-rw-r--r--src/Mobs/Creeper.h27
-rw-r--r--src/Mobs/Enderman.cpp63
-rw-r--r--src/Mobs/Enderman.h35
-rw-r--r--src/Mobs/Ghast.cpp56
-rw-r--r--src/Mobs/Ghast.h13
-rw-r--r--src/Mobs/Horse.cpp307
-rw-r--r--src/Mobs/Horse.h61
-rw-r--r--src/Mobs/Monster.cpp544
-rw-r--r--src/Mobs/Monster.h90
-rw-r--r--src/Mobs/PassiveAggressiveMonster.cpp14
-rw-r--r--src/Mobs/PassiveAggressiveMonster.h9
-rw-r--r--src/Mobs/PassiveMonster.cpp222
-rw-r--r--src/Mobs/PassiveMonster.h63
-rw-r--r--src/Mobs/Sheep.cpp300
-rw-r--r--src/Mobs/Sheep.h2
-rw-r--r--src/Mobs/Silverfish.h14
-rw-r--r--src/Mobs/Skeleton.cpp94
-rw-r--r--src/Mobs/Skeleton.h20
-rw-r--r--src/Mobs/Slime.cpp8
-rw-r--r--src/Mobs/Slime.h32
-rw-r--r--src/Mobs/Spider.cpp29
-rw-r--r--src/Mobs/Spider.h13
-rw-r--r--src/Mobs/Witch.h3
-rw-r--r--src/Mobs/Wolf.cpp20
-rw-r--r--src/Mobs/Wolf.h7
-rw-r--r--src/Mobs/Zombie.cpp34
-rw-r--r--src/MonsterConfig.cpp16
-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
66 files changed, 2488 insertions, 1495 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d7eb4e903..080cca119 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 PolarSSL++ 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 PolarSSL++
)
endif ()
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp
index fec14e6e9..0e6911305 100644
--- a/src/Mobs/AggressiveMonster.cpp
+++ b/src/Mobs/AggressiveMonster.cpp
@@ -5,9 +5,10 @@
#include "../World.h"
#include "../Entities/Player.h"
-#include "../LineBlockTracer.h"
-
-
+#include "../Tracer.h"
+#include "Behaviors/BehaviorAggressive.h"
+#include "Behaviors/BehaviorChaser.h"
+#include "Behaviors/BehaviorWanderer.h"
@@ -15,95 +16,36 @@ cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eMonsterTyp
super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height)
{
m_EMPersonality = AGGRESSIVE;
+ ASSERT(GetBehaviorChaser() != nullptr);
}
-// 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;
- }
+ /* cBehaviorChaser * BehaviorChaser = GetBehaviorChaser();
+ cBehaviorWanderer * BehaviorWanderer = GetBehaviorWanderer();
- // 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)
- )
+ for (;;)
{
- // 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);
- }
-}
-
-
+ m_BehaviorAggressive.Tick();
+ if (BehaviorChaser->Tick())
+ {
+ break;
+ }
+ if ((BehaviorWanderer != nullptr) && BehaviorWanderer->ActiveTick(a_Dt, a_Chunk))
+ {
+ break;
+ }
-
-
-bool cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt)
-{
- if ((GetTarget() == nullptr) || (m_AttackCoolDownTicksLeft != 0))
- {
- return false;
+ ASSERT(!"Not a single Behavior took control, this is not normal. ");
+ break;
}
- // Setting this higher gives us more wiggle room for attackrate
- ResetAttackCooldown();
- GetTarget()->TakeDamage(dtMobAttack, this, m_AttackDamage, 0);
-
- return true;
+ BehaviorChaser->Tick();*/
}
diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h
index 9ab8df06f..27ad88834 100644
--- a/src/Mobs/AggressiveMonster.h
+++ b/src/Mobs/AggressiveMonster.h
@@ -1,33 +1,22 @@
-
#pragma once
#include "Monster.h"
+#include "Behaviors/BehaviorAggressive.h"
-
-
+typedef std::string AString;
class cAggressiveMonster :
- public cMonster
+ public cMonster
{
- typedef cMonster super;
+ 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;
-
+ cAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
- virtual void EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk) override;
+ virtual void Tick (std::chrono::milliseconds a_Dt, 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);
+private:
+ cBehaviorAggressive m_BehaviorAggressive;
} ;
-
-
-
-
diff --git a/src/Mobs/Bat.cpp b/src/Mobs/Bat.cpp
index e419ceb2d..906b99320 100644
--- a/src/Mobs/Bat.cpp
+++ b/src/Mobs/Bat.cpp
@@ -4,12 +4,13 @@
#include "Bat.h"
#include "../Chunk.h"
-
cBat::cBat(void) :
- super("Bat", mtBat, "entity.bat.hurt", "entity.bat.death", 0.5, 0.9)
+ super("Bat", mtBat, "entity.bat.hurt", "entity.bat.death", 0.5, 0.9)
{
- SetGravity(-2.0f);
- SetAirDrag(0.05f);
+ SetGravity(-2.0f);
+ SetAirDrag(0.05f);
+
+
}
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..65618e123
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorAggressive.cpp
@@ -0,0 +1,35 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorAggressive.h"
+#include "BehaviorChaser.h"
+#include "../Monster.h"
+#include "../../Chunk.h"
+#include "../../Entities/Player.h"
+
+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);
+ // Target something new if we have no target
+ if (m_ParentChaser->GetTarget() == nullptr)
+ {
+ m_ParentChaser->SetTarget(FindNewTarget());
+ }
+}
+
+
+
+
+
+cPawn * cBehaviorAggressive::FindNewTarget()
+{
+ cPlayer * Closest = m_Parent->GetNearestPlayer();
+ 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..88c226b27
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorAggressive.h
@@ -0,0 +1,33 @@
+#pragma once
+
+
+class cBehaviorAggressive;
+
+#include "Behavior.h"
+class cBehaviorChaser;
+
+/** The mob is agressive toward specific mobtypes, or toward the player.
+This Behavior has a dependency on BehaviorChaser. */
+class cBehaviorAggressive : public cBehavior
+{
+
+public:
+ 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;
+ cBehaviorChaser * m_ParentChaser;
+
+ // The mob we want to attack
+ cPawn * m_Target;
+};
diff --git a/src/Mobs/Behaviors/BehaviorBreeder.cpp b/src/Mobs/Behaviors/BehaviorBreeder.cpp
new file mode 100644
index 000000000..d1d167b8b
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorBreeder.cpp
@@ -0,0 +1,252 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorBreeder.h"
+#include "../PassiveMonster.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);
+}
+
+
+
+
+
+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);
+}
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/BehaviorChaser.cpp b/src/Mobs/Behaviors/BehaviorChaser.cpp
new file mode 100644
index 000000000..dd9f193d6
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorChaser.cpp
@@ -0,0 +1,256 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "BehaviorChaser.h"
+#include "BehaviorStriker.h"
+#include "../Monster.h"
+#include "../../Entities/Pawn.h"
+#include "../../Entities/Player.h"
+
+
+
+cBehaviorChaser::cBehaviorChaser() :
+ m_AttackRate(3)
+ , m_AttackDamage(1)
+ , m_AttackRange(1)
+ , m_AttackCoolDownTicksLeft(0)
+ , m_TicksSinceLastDamaged(100)
+{
+
+}
+
+
+
+
+
+void cBehaviorChaser::AttachToMonster(cMonster & a_Parent, cBehaviorStriker & a_ParentStriker)
+{
+ m_Parent = &a_Parent;
+ m_ParentStriker = &a_ParentStriker;
+ m_Parent->AttachTickBehavior(this);
+ m_Parent->AttachDestroyBehavior(this);
+ m_Parent->AttachPostTickBehavior(this);
+ m_Parent->AttachDoTakeDamageBehavior(this);
+}
+
+
+
+
+
+bool cBehaviorChaser::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 (GetTarget() != nullptr);
+}
+
+
+
+
+
+void cBehaviorChaser::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ UNUSED(a_Dt);
+ UNUSED(a_Chunk);
+ /*
+ * if ((GetTarget() != nullptr))
+ {
+ ASSERT(GetTarget()->IsTicking());
+
+ if (GetTarget()->IsPlayer())
+ {
+ if (!static_cast<cPlayer *>(GetTarget())->CanMobsTarget())
+ {
+ SetTarget(nullptr);
+ }
+ }
+ } //mobTodo copied from monster.cpp
+ * */
+
+ ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == m_Parent->GetWorld())));
+ // Stop targeting out of range targets
+ if (GetTarget() != nullptr)
+ {
+ if (TargetOutOfSight())
+ {
+ SetTarget(nullptr);
+ }
+ else
+ {
+ if (TargetIsInStrikeRange())
+ {
+ StrikeTarget();
+ }
+ else
+ {
+ ApproachTarget(); // potential mobTodo: decoupling approaching from attacking
+ // Not important now, but important for future extensibility, e.g.
+ // cow chases wheat but using the netherman approacher to teleport around.
+ }
+ }
+ }
+}
+
+void cBehaviorChaser::ApproachTarget()
+{
+ // potential mobTodo inheritence for creaper approachers, etc
+ m_Parent->MoveToPosition(m_Target->GetPosition());
+}
+
+
+
+void cBehaviorChaser::PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ if (m_TicksSinceLastDamaged < 100)
+ {
+ ++m_TicksSinceLastDamaged;
+ }
+
+ if (m_AttackCoolDownTicksLeft > 0)
+ {
+ m_AttackCoolDownTicksLeft -= 1;
+ }
+}
+
+
+
+
+
+void cBehaviorChaser::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ 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));
+ }
+ m_TicksSinceLastDamaged = 0;
+ }
+}
+
+
+
+
+
+void cBehaviorChaser::Destroyed()
+{
+ SetTarget(nullptr);
+}
+
+
+
+
+
+void cBehaviorChaser::SetAttackRate(float a_AttackRate)
+{
+ m_AttackRate = a_AttackRate;
+}
+
+
+
+
+
+void cBehaviorChaser::SetAttackRange(int a_AttackRange)
+{
+ m_AttackRange = a_AttackRange;
+}
+
+
+
+
+
+void cBehaviorChaser::SetAttackDamage(int a_AttackDamage)
+{
+ m_AttackDamage = a_AttackDamage;
+}
+
+
+
+
+cPawn * cBehaviorChaser::GetTarget()
+{
+ return m_Target;
+}
+
+
+
+
+
+void cBehaviorChaser::SetTarget(cPawn * a_Target)
+{
+ m_Target = a_Target;
+}
+
+
+
+
+
+bool cBehaviorChaser::TargetIsInStrikeRange()
+{
+ ASSERT(m_Target != nullptr);
+ ASSERT(m_Parent != nullptr);
+ /*
+ #include "../../Tracer.h"
+ cTracer LineOfSight(m_Parent->GetWorld());
+ Vector3d MyHeadPosition = m_Parent->GetPosition() + Vector3d(0, m_Parent->GetHeight(), 0);
+ Vector3d AttackDirection(m_ParentChaser->GetTarget()->GetPosition() + Vector3d(0, GetTarget()->GetHeight(), 0) - MyHeadPosition);
+
+
+ if (GetTarget() != nullptr)
+ {
+ MoveToPosition(GetTarget()->GetPosition());
+ }
+ if (TargetIsInRange() && !LineOfSight.Trace(MyHeadPosition, AttackDirection, static_cast<int>(AttackDirection.Length())) && (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);
+ }
+ */
+
+ return ((m_Target->GetPosition() - m_Parent->GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange));
+}
+
+
+
+
+
+bool cBehaviorChaser::TargetOutOfSight()
+{
+ ASSERT(m_Target != nullptr);
+ if ((GetTarget()->GetPosition() - m_Parent->GetPosition()).Length() > m_Parent->GetSightDistance())
+ {
+ return true;
+ }
+ return false;
+}
+
+
+
+
+
+void cBehaviorChaser::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 cBehaviorChaser::StrikeTarget()
+{
+ if (m_AttackCoolDownTicksLeft != 0)
+ {
+ cBehaviorStriker * Striker = m_ParentStriker;
+ if (Striker != nullptr)
+ {
+ // Striker->Strike(m_Target); //mobTodo
+ }
+ ResetStrikeCooldown();
+ }
+}
diff --git a/src/Mobs/Behaviors/BehaviorChaser.h b/src/Mobs/Behaviors/BehaviorChaser.h
new file mode 100644
index 000000000..20b60a921
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorChaser.h
@@ -0,0 +1,64 @@
+#pragma once
+
+class cBehaviorChaser;
+
+#include "Behavior.h"
+class cBehaviorStriker;
+
+
+/** Grants chase 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 cBehaviorChaser : public cBehavior
+{
+
+public:
+ cBehaviorChaser();
+ void AttachToMonster(cMonster & a_Parent, cBehaviorStriker & a_ParentStriker);
+
+ // 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 Destroyed() override;
+ void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+
+ // 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);
+
+ /** Returns the target pointer, or a nullptr if we're not targeting anyone. */
+ cPawn * GetTarget();
+
+ /** Sets the target. */
+ void SetTarget(cPawn * a_Target);
+protected:
+ void ApproachTarget();
+ // virtual void ApproachTarget() = 0; //mobTodo
+private:
+
+ /** Our parent */
+ cMonster * m_Parent;
+ cBehaviorStriker * m_ParentStriker;
+
+ // The mob we want to attack
+ cPawn * m_Target;
+
+ // Target stuff
+ bool TargetIsInStrikeRange();
+ bool TargetOutOfSight();
+ void StrikeTarget();
+
+ // 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;
+
+ int m_TicksSinceLastDamaged; // How many ticks ago were we last damaged by a player?
+};
diff --git a/src/Mobs/Behaviors/BehaviorCoward.cpp b/src/Mobs/Behaviors/BehaviorCoward.cpp
new file mode 100644
index 000000000..c2c59a633
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorCoward.cpp
@@ -0,0 +1,96 @@
+#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)
+{
+ LOGD("mobDebug - Behavior Coward: Attach");
+ 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->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);
+ 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)
+{
+ LOGD("mobDebug - Behavior Coward: DoTakeDamage");
+ 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..9058419e3
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorCoward.h
@@ -0,0 +1,25 @@
+#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);
+
+ // 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;
+ 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..ab234e56f
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp
@@ -0,0 +1,106 @@
+#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);
+}
+
+
+
+
+
+void cBehaviorDayLightBurner::PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ // mobTodo WouldBurn
+ bool WouldBurn = false; // TEMP
+
+ 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() && WouldBurn)
+ {
+ // Burn for 100 ticks, then decide again
+ m_Parent->StartBurning(100);
+ }
+}
+
+
+
+
+bool cBehaviorDayLightBurner::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk)
+{
+ 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
+ (m_Parent->GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime
+ m_Parent->GetWorld()->IsWeatherSunnyAt(static_cast<int>(m_Parent->GetPosX()), static_cast<int>(m_Parent->GetPosZ())) // Not raining
+ )
+ {
+ int MobHeight = CeilC(a_Location.y + m_Parent->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..780827910
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorDayLightBurner.h
@@ -0,0 +1,21 @@
+#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;
+ bool WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk);
+
+ // Functions our host Monster should invoke:
+ void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+
+private:
+ cMonster * m_Parent; // Our Parent
+ cEntity * m_Attacker; // The entity we're running away from
+};
diff --git a/src/Mobs/Behaviors/BehaviorItemDropper.cpp b/src/Mobs/Behaviors/BehaviorItemDropper.cpp
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorItemDropper.cpp
@@ -0,0 +1 @@
+
diff --git a/src/Mobs/Behaviors/BehaviorItemDropper.h b/src/Mobs/Behaviors/BehaviorItemDropper.h
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorItemDropper.h
@@ -0,0 +1 @@
+
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..e0a8a85e4
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorItemFollower.h
@@ -0,0 +1,18 @@
+#pragma once
+
+// Makes the mob follow specific held items
+class cBehaviorItemFollower;
+
+#include "Behavior.h"
+
+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/BehaviorStriker.cpp b/src/Mobs/Behaviors/BehaviorStriker.cpp
new file mode 100644
index 000000000..e09bb45f9
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorStriker.cpp
@@ -0,0 +1,7 @@
+/*
+bool cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt)
+{
+ GetTarget()->TakeDamage(dtMobAttack, this, m_AttackDamage, 0);
+
+ return true;
+} */
diff --git a/src/Mobs/Behaviors/BehaviorStriker.h b/src/Mobs/Behaviors/BehaviorStriker.h
new file mode 100644
index 000000000..6fc094476
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorStriker.h
@@ -0,0 +1,13 @@
+#pragma once
+class cBehaviorStriker
+{
+
+};
+
+/*
+bool cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt)
+{
+ GetTarget()->TakeDamage(dtMobAttack, this, m_AttackDamage, 0);
+
+ return true;
+} */
diff --git a/src/Mobs/Behaviors/BehaviorWanderer.cpp b/src/Mobs/Behaviors/BehaviorWanderer.cpp
new file mode 100644
index 000000000..b57d5f033
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorWanderer.cpp
@@ -0,0 +1,85 @@
+#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;
+}
+
+
+
+
+
+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..c694eb331
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorWanderer.h
@@ -0,0 +1,23 @@
+#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;
+ 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..68b4bf731
--- /dev/null
+++ b/src/Mobs/Behaviors/CMakeLists.txt
@@ -0,0 +1,37 @@
+
+cmake_minimum_required (VERSION 2.6)
+project (Cuberite)
+
+include_directories ("${PROJECT_SOURCE_DIR}/../")
+
+SET (SRCS
+ Behavior.cpp
+ BehaviorAggressive.cpp
+ BehaviorChaser.cpp
+ BehaviorBreeder.cpp
+ BehaviorDayLightBurner.cpp
+ BehaviorCoward.cpp
+ BehaviorItemDropper.cpp
+ BehaviorItemFollower.cpp
+ BehaviorItemReplacer.cpp
+ BehaviorStriker.cpp
+ BehaviorWanderer.cpp
+)
+
+SET (HDRS
+ Behavior.h
+ BehaviorAggressive.h
+ BehaviorChaser.h
+ BehaviorBreeder.h
+ BehaviorDayLightBurner.h
+ BehaviorCoward.h
+ BehaviorItemDropper.h
+ BehaviorItemFollower.h
+ BehaviorItemReplacer.h
+ BehaviorStriker.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..7ada21369 100644
--- a/src/Mobs/Blaze.cpp
+++ b/src/Mobs/Blaze.cpp
@@ -9,10 +9,10 @@
cBlaze::cBlaze(void) :
- super("Blaze", mtBlaze, "entity.blaze.hurt", "entity.blaze.death", 0.6, 1.8)
+ super("Blaze", mtBlaze, "entity.blaze.hurt", "entity.blaze.death", 0.6, 1.8)
{
- SetGravity(-8.0f);
- SetAirDrag(0.05f);
+ SetGravity(-8.0f);
+ SetAirDrag(0.05f);
}
@@ -21,36 +21,37 @@ cBlaze::cBlaze(void) :
void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- if ((a_Killer != nullptr) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf")))
- {
- unsigned int LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
- AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_BLAZE_ROD);
- }
+ if ((a_Killer != nullptr) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf")))
+ {
+ unsigned int LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_BLAZE_ROD);
+ }
}
-
+// mobTODO
+/*
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;
-}
+ 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..7e389fa6c 100644
--- a/src/Mobs/Blaze.h
+++ b/src/Mobs/Blaze.h
@@ -8,15 +8,14 @@
class cBlaze :
- public cAggressiveMonster
+ public cAggressiveMonster
{
- typedef cAggressiveMonster super;
+ typedef cAggressiveMonster super;
public:
- cBlaze(void);
+ cBlaze(void);
- CLASS_PROTODEF(cBlaze)
+ CLASS_PROTODEF(cBlaze)
- virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual bool Attack(std::chrono::milliseconds a_Dt) override;
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
} ;
diff --git a/src/Mobs/CaveSpider.cpp b/src/Mobs/CaveSpider.cpp
index c6aa8af61..8d8e15d85 100644
--- a/src/Mobs/CaveSpider.cpp
+++ b/src/Mobs/CaveSpider.cpp
@@ -8,7 +8,7 @@
cCaveSpider::cCaveSpider(void) :
- super("CaveSpider", mtCaveSpider, "entity.spider.hurt", "entity.spider.death", 0.7, 0.5)
+ super("CaveSpider", mtCaveSpider, "entity.spider.hurt", "entity.spider.death", 0.7, 0.5)
{
}
@@ -18,34 +18,34 @@ cCaveSpider::cCaveSpider(void) :
void cCaveSpider::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
- super::Tick(a_Dt, a_Chunk);
- if (!IsTicking())
- {
- // The base class tick destroyed us
- return;
- }
-
- m_EMPersonality = (GetWorld()->GetTimeOfDay() < (12000 + 1000)) ? PASSIVE : AGGRESSIVE;
+ super::Tick(a_Dt, a_Chunk);
+ if (!IsTicking())
+ {
+ // The base class tick destroyed us
+ return;
+ }
+
+ m_EMPersonality = (GetWorld()->GetTimeOfDay() < (12000 + 1000)) ? PASSIVE : AGGRESSIVE;
}
-
+/*
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;
-}
+ 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;
+}*/
@@ -53,16 +53,16 @@ bool cCaveSpider::Attack(std::chrono::milliseconds a_Dt)
void cCaveSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- unsigned int LootingLevel = 0;
- if (a_Killer != nullptr)
- {
- LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
- }
- AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_STRING);
- if ((a_Killer != nullptr) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf")))
- {
- AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_SPIDER_EYE);
- }
+ unsigned int LootingLevel = 0;
+ if (a_Killer != nullptr)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_STRING);
+ if ((a_Killer != nullptr) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf")))
+ {
+ AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_SPIDER_EYE);
+ }
}
diff --git a/src/Mobs/CaveSpider.h b/src/Mobs/CaveSpider.h
index cf4b8e17c..71ee0b244 100644
--- a/src/Mobs/CaveSpider.h
+++ b/src/Mobs/CaveSpider.h
@@ -7,18 +7,17 @@
class cCaveSpider :
- public cAggressiveMonster
+ public cAggressiveMonster
{
- typedef cAggressiveMonster super;
+ typedef cAggressiveMonster super;
public:
- cCaveSpider(void);
+ cCaveSpider(void);
- CLASS_PROTODEF(cCaveSpider)
+ 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;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
} ;
diff --git a/src/Mobs/Chicken.cpp b/src/Mobs/Chicken.cpp
index 1068295e6..80cff7fb8 100644
--- a/src/Mobs/Chicken.cpp
+++ b/src/Mobs/Chicken.cpp
@@ -5,16 +5,12 @@
-
-
-
-
cChicken::cChicken(void) :
- super("Chicken", mtChicken, "entity.chicken.hurt", "entity.chicken.death", 0.3, 0.4),
- m_EggDropTimer(0)
+ super("Chicken", mtChicken, "entity.chicken.hurt", "entity.chicken.death", 0.3, 0.4),
+ m_EggDropTimer(0)
{
- SetGravity(-2.0f);
- SetAirDrag(0.0f);
+ SetGravity(-2.0f);
+ SetAirDrag(0.0f);
}
@@ -22,36 +18,36 @@ cChicken::cChicken(void) :
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++;
- }
+ 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++;
+ }
}
@@ -60,13 +56,13 @@ void cChicken::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
void cChicken::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- unsigned int LootingLevel = 0;
- if (a_Killer != nullptr)
- {
- LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
- }
- AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_FEATHER);
- AddRandomDropItem(a_Drops, 1, 1, IsOnFire() ? E_ITEM_COOKED_CHICKEN : E_ITEM_RAW_CHICKEN);
+ unsigned int LootingLevel = 0;
+ if (a_Killer != nullptr)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_FEATHER);
+ AddRandomDropItem(a_Drops, 1, 1, IsOnFire() ? E_ITEM_COOKED_CHICKEN : E_ITEM_RAW_CHICKEN);
}
@@ -75,7 +71,7 @@ void cChicken::GetDrops(cItems & a_Drops, cEntity * a_Killer)
void cChicken::HandleFalling(void)
{
- // empty - chickens don't take fall damage
+ // empty - chickens don't take fall damage
}
diff --git a/src/Mobs/Chicken.h b/src/Mobs/Chicken.h
index 3be338b15..b954c19ec 100644
--- a/src/Mobs/Chicken.h
+++ b/src/Mobs/Chicken.h
@@ -7,28 +7,28 @@
class cChicken :
- public cPassiveMonster
+ public cPassiveMonster
{
- typedef cPassiveMonster super;
+ typedef cPassiveMonster super;
public:
- cChicken(void);
+ cChicken(void);
- CLASS_PROTODEF(cChicken)
+ 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 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
- {
- a_Items.Add(E_ITEM_SEEDS);
- }
+ virtual void GetFollowedItems(cItems & a_Items) override
+ {
+ a_Items.Add(E_ITEM_SEEDS);
+ }
- virtual void HandleFalling(void) override;
+ virtual void HandleFalling(void) override;
private:
- int m_EggDropTimer;
+ int m_EggDropTimer;
} ;
diff --git a/src/Mobs/Cow.cpp b/src/Mobs/Cow.cpp
index 9736fe440..f3c5c821b 100644
--- a/src/Mobs/Cow.cpp
+++ b/src/Mobs/Cow.cpp
@@ -6,13 +6,15 @@
-
-
-
-
cCow::cCow(void) :
- super("Cow", mtCow, "entity.cow.hurt", "entity.cow.death", 0.9, 1.3)
+ super("Cow", mtCow, "entity.cow.hurt", "entity.cow.death", 0.9, 1.3),
+ m_BehaviorItemReplacer(E_ITEM_BUCKET, E_ITEM_MILK)
{
+ m_BehaviorBreeder.AttachToMonster(*this);
+ m_BehaviorCoward.AttachToMonster(*this);
+ m_BehaviorItemFollower.AttachToMonster(*this);
+ m_BehaviorWanderer.AttachToMonster(*this);
+ m_BehaviorItemReplacer.AttachToMonster(*this);
}
@@ -34,17 +36,22 @@ void cCow::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-void cCow::OnRightClicked(cPlayer & a_Player)
+
+
+
+
+
+
+cBehaviorBreeder * cCow::GetBehaviorBreeder()
{
- super::OnRightClicked(a_Player);
+ return &m_BehaviorBreeder;
+}
- 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);
- }
- }
+
+
+
+
+const cBehaviorBreeder * cCow::GetBehaviorBreeder() const
+{
+ return static_cast<const cBehaviorBreeder *>(&m_BehaviorBreeder);
}
diff --git a/src/Mobs/Cow.h b/src/Mobs/Cow.h
index 569c6e619..337d0674d 100644
--- a/src/Mobs/Cow.h
+++ b/src/Mobs/Cow.h
@@ -1,30 +1,39 @@
-
#pragma once
-#include "PassiveMonster.h"
-
-
+#include "Behaviors/BehaviorBreeder.h"
+#include "Behaviors/BehaviorItemFollower.h"
+#include "Behaviors/BehaviorItemReplacer.h"
+#include "Behaviors/BehaviorCoward.h"
+#include "Behaviors/BehaviorWanderer.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);
}
+ virtual cBehaviorBreeder * GetBehaviorBreeder() override;
+ virtual const cBehaviorBreeder * GetBehaviorBreeder() const override;
+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..65576ff25 100644
--- a/src/Mobs/Creeper.cpp
+++ b/src/Mobs/Creeper.cpp
@@ -26,6 +26,8 @@ cCreeper::cCreeper(void) :
void cCreeper::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
+ /* mobTodo
+
if (!IsTicking())
{
// The base class tick destroyed us
@@ -53,7 +55,8 @@ void cCreeper::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_World->DoExplosionAt((m_bIsCharged ? 5 : 3), GetPosX(), GetPosY(), GetPosZ(), false, esMonster, this);
Destroy(); // Just in case we aren't killed by the explosion
}
- }
+ } */
+
}
@@ -125,7 +128,8 @@ bool cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI)
-
+// mobTODO
+/*
bool cCreeper::Attack(std::chrono::milliseconds a_Dt)
{
UNUSED(a_Dt);
@@ -139,7 +143,7 @@ bool cCreeper::Attack(std::chrono::milliseconds a_Dt)
return true;
}
return false;
-}
+}*/
diff --git a/src/Mobs/Creeper.h b/src/Mobs/Creeper.h
index aea36def3..2ef72650b 100644
--- a/src/Mobs/Creeper.h
+++ b/src/Mobs/Creeper.h
@@ -8,29 +8,28 @@
class cCreeper :
- public cAggressiveMonster
+ public cAggressiveMonster
{
- typedef cAggressiveMonster super;
+ typedef cAggressiveMonster super;
public:
- cCreeper(void);
+ cCreeper(void);
- CLASS_PROTODEF(cCreeper)
+ 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;
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
+ virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) 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 {return m_bIsBlowing; }
+ bool IsCharged(void) const {return m_bIsCharged; }
+ bool IsBurnedWithFlintAndSteel(void) const {return m_BurnedWithFlintAndSteel; }
private:
- bool m_bIsBlowing, m_bIsCharged, m_BurnedWithFlintAndSteel;
- int m_ExplodingTimer;
+ bool m_bIsBlowing, m_bIsCharged, m_BurnedWithFlintAndSteel;
+ int m_ExplodingTimer;
} ;
diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp
index 5cfe0d4cd..6aa72369f 100644
--- a/src/Mobs/Enderman.cpp
+++ b/src/Mobs/Enderman.cpp
@@ -98,67 +98,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 +136,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..0dc648468 100644
--- a/src/Mobs/Enderman.h
+++ b/src/Mobs/Enderman.h
@@ -8,35 +8,32 @@
class cEnderman :
- public cPassiveAggressiveMonster
+ public cPassiveAggressiveMonster
{
- typedef cPassiveAggressiveMonster super;
+ typedef cPassiveAggressiveMonster super;
public:
- cEnderman(void);
+ cEnderman(void);
- CLASS_PROTODEF(cEnderman)
+ 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;
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
- bool IsScreaming(void) const {return m_bIsScreaming; }
- BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; }
- NIBBLETYPE GetCarriedMeta(void) const {return CarriedMeta; }
+ bool IsScreaming(void) const {return m_bIsScreaming; }
+ BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; }
+ NIBBLETYPE GetCarriedMeta(void) const {return CarriedMeta; }
- /** Returns if the current sky light level is sufficient for the enderman to become aggravated */
- bool CheckLight(void);
- /** Returns if the enderman gets hit by the rain */
- bool CheckRain(void);
+ /** Returns if the current sky light level is sufficient for the enderman to become aggravated */
+ bool CheckLight(void);
+ /** Returns if the enderman gets hit by the rain */
+ bool CheckRain(void);
private:
- bool m_bIsScreaming;
- BLOCKTYPE CarriedBlock;
- NIBBLETYPE CarriedMeta;
+ bool m_bIsScreaming;
+ BLOCKTYPE CarriedBlock;
+ NIBBLETYPE CarriedMeta;
} ;
diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp
index 2488e63b1..97a27be5a 100644
--- a/src/Mobs/Ghast.cpp
+++ b/src/Mobs/Ghast.cpp
@@ -9,7 +9,7 @@
cGhast::cGhast(void) :
- super("Ghast", mtGhast, "entity.ghast.hurt", "entity.ghast.death", 4, 4)
+ super("Ghast", mtGhast, "entity.ghast.hurt", "entity.ghast.death", 4, 4)
{
}
@@ -19,39 +19,39 @@ cGhast::cGhast(void) :
void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- unsigned int LootingLevel = 0;
- if (a_Killer != nullptr)
- {
- LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
- }
- AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GUNPOWDER);
- AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_GHAST_TEAR);
+ unsigned int LootingLevel = 0;
+ if (a_Killer != nullptr)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GUNPOWDER);
+ AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_GHAST_TEAR);
}
-
-bool cGhast::Attack(std::chrono::milliseconds a_Dt)
+// mobTODO
+/*bool cGhast::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 GhastBall = cpp14::make_unique<cGhastFireballEntity>(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed);
- auto GhastBallPtr = GhastBall.get();
- if (!GhastBallPtr->Initialize(std::move(GhastBall), *m_World))
- {
- return false;
- }
-
- ResetAttackCooldown();
- return true;
- }
- return false;
-}
+ 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 GhastBall = cpp14::make_unique<cGhastFireballEntity>(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed);
+ auto GhastBallPtr = GhastBall.get();
+ if (!GhastBallPtr->Initialize(std::move(GhastBall), *m_World))
+ {
+ return false;
+ }
+
+ ResetAttackCooldown();
+ return true;
+ }
+ return false;
+}*/
diff --git a/src/Mobs/Ghast.h b/src/Mobs/Ghast.h
index a41a72ddc..c3423590a 100644
--- a/src/Mobs/Ghast.h
+++ b/src/Mobs/Ghast.h
@@ -8,19 +8,18 @@
class cGhast :
- public cAggressiveMonster
+ public cAggressiveMonster
{
- typedef cAggressiveMonster super;
+ typedef cAggressiveMonster super;
public:
- cGhast(void);
+ cGhast(void);
- CLASS_PROTODEF(cGhast)
+ CLASS_PROTODEF(cGhast)
- virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual bool Attack(std::chrono::milliseconds a_Dt) override;
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- bool IsCharging(void) const {return false; }
+ bool IsCharging(void) const {return false; }
} ;
diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp
index 13630b0e3..21a58489d 100644
--- a/src/Mobs/Horse.cpp
+++ b/src/Mobs/Horse.cpp
@@ -11,21 +11,21 @@
cHorse::cHorse(int Type, int Color, int Style, int TameTimes) :
- super("Horse", mtHorse, "entity.horse.hurt", "entity.horse.death", 1.4, 1.6),
- m_bHasChest(false),
- m_bIsEating(false),
- m_bIsRearing(false),
- m_bIsMouthOpen(false),
- m_bIsTame(false),
- m_bIsSaddled(false),
- m_Type(Type),
- m_Color(Color),
- m_Style(Style),
- m_Armour(0),
- m_TimesToTame(TameTimes),
- m_TameAttemptTimes(0),
- m_RearTickCount(0),
- m_MaxSpeed(14.0)
+ super("Horse", mtHorse, "entity.horse.hurt", "entity.horse.death", 1.4, 1.6),
+ m_bHasChest(false),
+ m_bIsEating(false),
+ m_bIsRearing(false),
+ m_bIsMouthOpen(false),
+ m_bIsTame(false),
+ m_bIsSaddled(false),
+ m_Type(Type),
+ m_Color(Color),
+ m_Style(Style),
+ m_Armour(0),
+ m_TimesToTame(TameTimes),
+ m_TameAttemptTimes(0),
+ m_RearTickCount(0),
+ m_MaxSpeed(14.0)
{
}
@@ -35,67 +35,67 @@ cHorse::cHorse(int Type, int Color, int Style, int TameTimes) :
void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
- super::Tick(a_Dt, a_Chunk);
- if (!IsTicking())
- {
- // The base class tick destroyed us
- return;
- }
-
- auto & Random = GetRandomProvider();
-
- if (!m_bIsMouthOpen)
- {
- if (Random.RandBool(0.02))
- {
- m_bIsMouthOpen = true;
- }
- }
- else
- {
- if (Random.RandBool(0.10))
- {
- m_bIsMouthOpen = false;
- }
- }
-
- if ((m_Attachee != nullptr) && (!m_bIsTame))
- {
- if (m_TameAttemptTimes < m_TimesToTame)
- {
- if (Random.RandBool(0.02))
- {
- m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_EAST));
- m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_WEST));
- m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_EAST));
- m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_WEST));
-
- m_World->BroadcastSoundEffect("entity.horse.angry", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 1.0f);
- m_Attachee->Detach();
- m_bIsRearing = true;
- }
- }
- else
- {
- m_World->GetBroadcaster().BroadcastParticleEffect("heart", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5);
- m_bIsTame = true;
- }
- }
-
- if (m_bIsRearing)
- {
- if (m_RearTickCount == 20)
- {
- m_bIsRearing = false;
- m_RearTickCount = 0;
- }
- else
- {
- m_RearTickCount++;
- }
- }
-
- m_World->BroadcastEntityMetadata(*this);
+ super::Tick(a_Dt, a_Chunk);
+ if (!IsTicking())
+ {
+ // The base class tick destroyed us
+ return;
+ }
+
+ auto & Random = GetRandomProvider();
+
+ if (!m_bIsMouthOpen)
+ {
+ if (Random.RandBool(0.02))
+ {
+ m_bIsMouthOpen = true;
+ }
+ }
+ else
+ {
+ if (Random.RandBool(0.10))
+ {
+ m_bIsMouthOpen = false;
+ }
+ }
+
+ if ((m_Attachee != nullptr) && (!m_bIsTame))
+ {
+ if (m_TameAttemptTimes < m_TimesToTame)
+ {
+ if (Random.RandBool(0.02))
+ {
+ m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_EAST));
+ m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_WEST));
+ m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_EAST));
+ m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_WEST));
+
+ m_World->BroadcastSoundEffect("entity.horse.angry", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 1.0f);
+ m_Attachee->Detach();
+ m_bIsRearing = true;
+ }
+ }
+ else
+ {
+ m_World->GetBroadcaster().BroadcastParticleEffect("heart", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5);
+ m_bIsTame = true;
+ }
+ }
+
+ if (m_bIsRearing)
+ {
+ if (m_RearTickCount == 20)
+ {
+ m_bIsRearing = false;
+ m_RearTickCount = 0;
+ }
+ else
+ {
+ m_RearTickCount++;
+ }
+ }
+
+ m_World->BroadcastEntityMetadata(*this);
}
@@ -104,63 +104,63 @@ void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
void cHorse::OnRightClicked(cPlayer & a_Player)
{
- super::OnRightClicked(a_Player);
-
- if (m_bIsTame)
- {
- if (!m_bIsSaddled)
- {
- if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE)
- {
- // Saddle the horse:
- if (!a_Player.IsGameModeCreative())
- {
- a_Player.GetInventory().RemoveOneEquippedItem();
- }
- m_bIsSaddled = true;
- m_World->BroadcastEntityMetadata(*this);
- }
- else
- {
- a_Player.AttachTo(this);
- }
- }
- else
- {
- a_Player.AttachTo(this);
- }
- }
- else if (a_Player.GetEquippedItem().IsEmpty())
- {
- // Check if leashed / unleashed to player before try to ride
- if (!m_IsLeashActionJustDone)
- {
- if (m_Attachee != nullptr)
- {
- if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
- {
- a_Player.Detach();
- return;
- }
-
- if (m_Attachee->IsPlayer())
- {
- return;
- }
-
- m_Attachee->Detach();
- }
-
- m_TameAttemptTimes++;
- a_Player.AttachTo(this);
- }
- }
- else
- {
- m_bIsRearing = true;
- m_RearTickCount = 0;
- m_World->BroadcastSoundEffect("entity.horse.angry", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f);
- }
+ super::OnRightClicked(a_Player);
+
+ if (m_bIsTame)
+ {
+ if (!m_bIsSaddled)
+ {
+ if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE)
+ {
+ // Saddle the horse:
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ m_bIsSaddled = true;
+ m_World->BroadcastEntityMetadata(*this);
+ }
+ else
+ {
+ a_Player.AttachTo(this);
+ }
+ }
+ else
+ {
+ a_Player.AttachTo(this);
+ }
+ }
+ else if (a_Player.GetEquippedItem().IsEmpty())
+ {
+ // Check if leashed / unleashed to player before try to ride
+ if (!m_IsLeashActionJustDone)
+ {
+ if (m_Attachee != nullptr)
+ {
+ if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
+ {
+ a_Player.Detach();
+ return;
+ }
+
+ if (m_Attachee->IsPlayer())
+ {
+ return;
+ }
+
+ m_Attachee->Detach();
+ }
+
+ m_TameAttemptTimes++;
+ a_Player.AttachTo(this);
+ }
+ }
+ else
+ {
+ m_bIsRearing = true;
+ m_RearTickCount = 0;
+ m_World->BroadcastSoundEffect("entity.horse.angry", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f);
+ }
}
@@ -169,29 +169,16 @@ void cHorse::OnRightClicked(cPlayer & a_Player)
void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- unsigned int LootingLevel = 0;
- if (a_Killer != nullptr)
- {
- LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
- }
- AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER);
- if (m_bIsSaddled)
- {
- a_Drops.push_back(cItem(E_ITEM_SADDLE, 1));
- }
-}
-
-
-
-
-
-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);
- }
+ unsigned int LootingLevel = 0;
+ if (a_Killer != nullptr)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER);
+ if (m_bIsSaddled)
+ {
+ a_Drops.push_back(cItem(E_ITEM_SADDLE, 1));
+ }
}
@@ -200,8 +187,8 @@ void cHorse::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
void cHorse::HandleSpeedFromAttachee(float a_Forward, float a_Sideways)
{
- if ((m_bIsTame) && (m_bIsSaddled))
- {
- super::HandleSpeedFromAttachee(a_Forward * m_MaxSpeed, a_Sideways * m_MaxSpeed);
- }
+ if ((m_bIsTame) && (m_bIsSaddled))
+ {
+ super::HandleSpeedFromAttachee(a_Forward * m_MaxSpeed, a_Sideways * m_MaxSpeed);
+ }
}
diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h
index 82026a0ee..e84243360 100644
--- a/src/Mobs/Horse.h
+++ b/src/Mobs/Horse.h
@@ -8,43 +8,42 @@
class cHorse :
- public cPassiveMonster
+ public cPassiveMonster
{
- typedef cPassiveMonster super;
+ typedef cPassiveMonster super;
public:
- cHorse(int Type, int Color, int Style, int TameTimes);
-
- 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;
-
- bool IsSaddled (void) const {return m_bIsSaddled; }
- bool IsChested (void) const {return m_bHasChest; }
- bool IsEating (void) const {return m_bIsEating; }
- bool IsRearing (void) const {return m_bIsRearing; }
- bool IsMthOpen (void) const {return m_bIsMouthOpen; }
- bool IsTame (void) const override {return m_bIsTame; }
- int GetHorseType (void) const {return m_Type; }
- int GetHorseColor (void) const {return m_Color; }
- int GetHorseStyle (void) const {return m_Style; }
- int GetHorseArmour (void) const {return m_Armour;}
-
- virtual void GetBreedingItems(cItems & a_Items) override
- {
- a_Items.Add(E_ITEM_GOLDEN_CARROT);
- a_Items.Add(E_ITEM_GOLDEN_APPLE);
- }
+ cHorse(int Type, int Color, int Style, int TameTimes);
+
+ CLASS_PROTODEF(cHorse)
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) 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;
+
+ bool IsSaddled (void) const {return m_bIsSaddled; }
+ bool IsChested (void) const {return m_bHasChest; }
+ bool IsEating (void) const {return m_bIsEating; }
+ bool IsRearing (void) const {return m_bIsRearing; }
+ bool IsMthOpen (void) const {return m_bIsMouthOpen; }
+ bool IsTame (void) const override {return m_bIsTame; }
+ int GetHorseType (void) const {return m_Type; }
+ int GetHorseColor (void) const {return m_Color; }
+ int GetHorseStyle (void) const {return m_Style; }
+ int GetHorseArmour (void) const {return m_Armour;}
+
+ virtual void GetBreedingItems(cItems & a_Items) override
+ {
+ a_Items.Add(E_ITEM_GOLDEN_CARROT);
+ a_Items.Add(E_ITEM_GOLDEN_APPLE);
+ }
private:
- 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;
- float m_MaxSpeed;
+ 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;
+ float m_MaxSpeed;
} ;
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index 0d433d861..be240d55c 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"
+
@@ -82,8 +85,8 @@ static const struct
cMonster::cMonster(const AString & a_ConfigName, 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_NearestPlayerIsStale(true)
, m_PathFinder(a_Width, a_Height)
, m_PathfinderActivated(false)
, m_JumpCoolDown(0)
@@ -94,10 +97,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 +104,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
@@ -116,6 +113,9 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_IsLeashActionJustDone(false)
, m_CanBeLeashed(GetMobFamily() == eFamily::mfPassive)
, m_Target(nullptr)
+ , m_CurrentTickControllingBehavior(nullptr)
+ , m_NewTickControllingBehavior(nullptr)
+ , m_TickControllingBehaviorState(Normal)
{
if (!a_ConfigName.empty())
{
@@ -138,6 +138,9 @@ cMonster::~cMonster()
void cMonster::Destroy(bool a_ShouldBroadcast)
{
+ //mobtodo Destroy vs Destroyed
+
+ // mobTodo behavior for leash
if (IsLeashed())
{
cEntity * LeashedTo = GetLeashedTo();
@@ -159,6 +162,11 @@ void cMonster::Destroy(bool a_ShouldBroadcast)
void cMonster::Destroyed()
{
+ for (cBehavior * Behavior : m_AttachedDestroyBehaviors)
+ {
+ Behavior->Destroyed();
+ }
+
SetTarget(nullptr); // Tell them we're no longer targeting them.
super::Destroyed();
}
@@ -183,6 +191,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 +291,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 +301,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 +312,99 @@ 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)
+ {
+ // 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 (!static_cast<cPlayer *>(GetTarget())->CanMobsTarget())
+ if (Behavior->IsControlDesired(a_Dt, a_Chunk))
{
- SetTarget(nullptr);
- m_EMState = IDLE;
+ m_NewTickControllingBehavior = Behavior;
+ break;
}
}
+ ASSERT(m_NewTickControllingBehavior != nullptr); // it's not OK if no one asks for control
+ if (m_CurrentTickControllingBehavior == m_NewTickControllingBehavior)
+ {
+ // The Behavior asking for control is the same as the behavior from last tick.
+ // Nothing special, just tick it.
+ // LOGD("mobDebug - Tick");
+ 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;
+ }
+
}
- // Process the undead burning in daylight.
- HandleDaylightBurning(*Chunk, WouldBurnAt(GetPosition(), *Chunk));
+ // 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;
+ }
+ }
+
+ // 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)
@@ -337,8 +415,9 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
else
{
+ // mobToDo fix dont care
// 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, true))
{
case ePathFinderStatus::PATH_FOUND:
{
@@ -347,9 +426,13 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
2. I was not hurt by a player recently.
Then STOP. */
if (
- m_BurnsInDaylight && ((m_TicksSinceLastDamaged >= 100) || (m_EMState == IDLE)) &&
- WouldBurnAt(m_NextWayPointPosition, *Chunk) &&
- !WouldBurnAt(GetPosition(), *Chunk)
+ //mobTodo emstate
+ /* (GetBehaviorDayLightBurner() != nullptr) && (m_TicksSinceLastDamaged >= 100) &&
+ GetBehaviorDayLightBurner()->WouldBurnAt(m_NextWayPointPosition, *Chunk) &&
+ !(GetBehaviorDayLightBurner()->WouldBurnAt(GetPosition(), *Chunk)) */
+ 1 == 0
+
+ // This logic should probably be in chaser
)
{
// 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:
@@ -377,28 +460,6 @@ 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)
{
@@ -555,22 +616,18 @@ 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);
}
+
return true;
}
@@ -661,6 +718,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 +729,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 +760,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;
@@ -1113,6 +1024,99 @@ cPawn * cMonster::GetTarget()
+bool cMonster::IsPathFinderActivated() const
+{
+ return m_PathfinderActivated;
+}
+
+
+
+
+
+cBehaviorBreeder * cMonster::GetBehaviorBreeder()
+{
+ return nullptr;
+}
+
+
+
+
+
+const cBehaviorBreeder * cMonster::GetBehaviorBreeder() const
+{
+ return nullptr;
+}
+
+
+
+
+
+cBehaviorChaser * cMonster::GetBehaviorChaser()
+{
+ return nullptr;
+}
+
+
+
+
+
+cBehaviorDayLightBurner * cMonster::GetBehaviorDayLightBurner()
+{
+ return nullptr;
+}
+
+
+
+
+
+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;
+}
+
+
+
+
+
std::unique_ptr<cMonster> cMonster::NewMonsterFromType(eMonsterType a_MobType)
{
auto & Random = GetRandomProvider();
@@ -1300,104 +1304,64 @@ 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 < 0)
- {
- // Never burn under the world
- return false;
- }
- 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);
+}
+
+
+
+
cMonster::eFamily cMonster::GetMobFamily(void) const
{
return FamilyFromType(m_MobType);
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index 1f6bd6011..a2e16080b 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -4,12 +4,21 @@
#include "../Entities/Pawn.h"
#include "MonsterTypes.h"
#include "PathFinder.h"
-
+#include <vector>
class cItem;
class cClientHandle;
+//Behavior fwds
+class cPassiveMonster;
+class cBehaviorAggressive;
+class cBehaviorBreeder;
+class cBehaviorChaser;
+class cBehaviorStriker;
+class cBehaviorWanderer;
+class cBehaviorDayLightBurner;
+class cBehavior;
// tolua_begin
class cMonster :
@@ -31,7 +40,6 @@ public:
// tolua_end
- enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState;
enum MPersonality{PASSIVE, AGGRESSIVE, COWARDLY} m_EMPersonality;
/** Creates the mob object.
@@ -69,9 +77,6 @@ public:
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 +114,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 +129,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
@@ -211,7 +202,36 @@ public:
/** Returns if this mob last target was a player to avoid destruction on player quit */
bool WasLastTargetAPlayer() const { return m_WasLastTargetAPlayer; }
-protected:
+ bool IsPathFinderActivated() const;
+
+ // Behavior getters (most are probably not used. mobTodo - cleanup most of them)
+ virtual cBehaviorBreeder * GetBehaviorBreeder();
+ virtual const cBehaviorBreeder * GetBehaviorBreeder() const;
+ virtual cBehaviorChaser * GetBehaviorChaser();
+ virtual cBehaviorDayLightBurner * GetBehaviorDayLightBurner(); // mobTodo this is probably temporary
+
+ // Polymorphic behavior functions
+ 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);
+
+ 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 +254,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)
{
@@ -268,11 +281,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 +289,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;
@@ -328,4 +333,15 @@ private:
/** 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;
+ enum TickState{NewControlStarting, OldControlEnding, Normal} m_TickControllingBehaviorState;
+
} ; // tolua_export
diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp
index 8715ba9c2..addde27a7 100644
--- a/src/Mobs/PassiveAggressiveMonster.cpp
+++ b/src/Mobs/PassiveAggressiveMonster.cpp
@@ -26,23 +26,13 @@ bool cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
return false;
}
+ // mobtodo remove this class altogether
if ((GetTarget() != nullptr) && (GetTarget()->IsPlayer()))
{
if (static_cast<cPlayer *>(GetTarget())->CanMobsTarget())
{
- m_EMState = CHASING;
+ // 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
index 764e27779..726db09c5 100644
--- a/src/Mobs/PassiveAggressiveMonster.h
+++ b/src/Mobs/PassiveAggressiveMonster.h
@@ -8,15 +8,14 @@
class cPassiveAggressiveMonster :
- public cAggressiveMonster
+ public cAggressiveMonster
{
- typedef cAggressiveMonster super;
+ 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);
+ 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;
+ virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
} ;
diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp
index a2089e13f..0b1349111 100644
--- a/src/Mobs/PassiveMonster.cpp
+++ b/src/Mobs/PassiveMonster.cpp
@@ -5,17 +5,12 @@
#include "../World.h"
#include "../Entities/Player.h"
#include "BoundingBox.h"
-#include "../Items/ItemSpawnEgg.h"
cPassiveMonster::cPassiveMonster(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_LovePartner(nullptr),
- m_LoveTimer(0),
- m_LoveCooldown(0),
- m_MatingTimer(0)
+ super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height)
{
m_EMPersonality = PASSIVE;
}
@@ -24,42 +19,23 @@ cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eMonsterType a_Mo
-bool cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
+cPassiveMonster::~cPassiveMonster()
{
- if (!super::DoTakeDamage(a_TDI))
- {
- return false;
- }
- if ((a_TDI.Attacker != this) && (a_TDI.Attacker != nullptr))
- {
- m_EMState = ESCAPING;
- }
- return true;
-}
-
-
-
-
-void cPassiveMonster::EngageLoveMode(cPassiveMonster * a_Partner)
-{
- m_LovePartner = a_Partner;
- m_MatingTimer = 50; // about 3 seconds of mating
}
-void cPassiveMonster::ResetLoveMode()
+bool cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
{
- 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_World->BroadcastEntityMetadata(*this);
+ if (!super::DoTakeDamage(a_TDI))
+ {
+ return false;
+ }
+ m_BehaviorCoward.DoTakeDamage(a_TDI);
+ return true;
}
@@ -68,152 +44,40 @@ void cPassiveMonster::ResetLoveMode()
void cPassiveMonster::Destroyed()
{
- if (m_LovePartner != nullptr)
- {
- m_LovePartner->ResetLoveMode();
- }
+ m_BehaviorBreeder.Destroyed();
super::Destroyed();
}
-
-
void cPassiveMonster::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 == ESCAPING)
+ for (;;)
{
- CheckEventLostPlayer();
- }
-
- // if we have a partner, mate
- if (m_LovePartner != nullptr)
- {
-
- if (m_MatingTimer > 0)
+ /*if (m_BehaviorCoward.Tick())
{
- // If we should still mate, keep bumping into them until baby is made
- Vector3d Pos = m_LovePartner->GetPosition();
- MoveToPosition(Pos);
+ break;
}
- else
+ if (m_BehaviorBreeder.Tick())
{
- // Mating finished. Spawn baby
- Vector3f Pos = (GetPosition() + m_LovePartner->GetPosition()) * 0.5;
- UInt32 BabyID = m_World->SpawnMob(Pos.x, Pos.y, Pos.z, GetMobType(), true);
-
- class cBabyInheritCallback :
- public cEntityCallback
- {
- public:
- cPassiveMonster * Baby;
- cBabyInheritCallback() : Baby(nullptr) { }
- virtual bool Item(cEntity * a_Entity) override
- {
- Baby = static_cast<cPassiveMonster *>(a_Entity);
- return true;
- }
- } Callback;
-
- m_World->DoWithEntityByID(BabyID, Callback);
- if (Callback.Baby != nullptr)
- {
- Callback.Baby->InheritFromParents(this, m_LovePartner);
- }
-
- m_World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, GetRandomProvider().RandInt(1, 6));
-
- m_LovePartner->ResetLoveMode();
- ResetLoveMode();
+ break;
}
- }
- else
- {
- // We have no partner, so we just chase the player if they have our breeding item
- cItems FollowedItems;
- GetFollowedItems(FollowedItems);
- if (FollowedItems.Size() > 0)
+ if (m_BehaviorItemFollower.Tick())
{
- cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance));
- if (a_Closest_Player != nullptr)
- {
- cItem EquippedItem = a_Closest_Player->GetEquippedItem();
- if (FollowedItems.ContainsType(EquippedItem))
- {
- Vector3d PlayerPos = a_Closest_Player->GetPosition();
- MoveToPosition(PlayerPos);
- }
- }
+ break;
}
- }
-
- // If we are in love mode but we have no partner, search for a partner neabry
- if (m_LoveTimer > 0)
- {
- if (m_LovePartner == nullptr)
+ if (m_BehaviorWanderer.ActiveTick(a_Dt, a_Chunk))
{
- class LookForLover : public cEntityCallback
- {
- public:
- cEntity * m_Me;
-
- LookForLover(cEntity * 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() != etMonster) || (a_Entity == m_Me))
- {
- return false;
- }
-
- cPassiveMonster * Me = static_cast<cPassiveMonster*>(m_Me);
- cPassiveMonster * PotentialPartner = static_cast<cPassiveMonster*>(a_Entity);
-
- // If the potential partner is not of the same species, don't breed with it
- if (PotentialPartner->GetMobType() != Me->GetMobType())
- {
- return false;
- }
-
- // If the potential partner is not in love
- // Or they already have a mate, do not breed with them
- if ((!PotentialPartner->IsInLove()) || (PotentialPartner->GetPartner() != nullptr))
- {
- return false;
- }
-
- // All conditions met, let's breed!
- PotentialPartner->EngageLoveMode(Me);
- Me->EngageLoveMode(PotentialPartner);
- return true;
- }
- } Callback(this);
-
- m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), 8, 8, -4), Callback);
- }
+ break;
+ }*/
- m_LoveTimer--;
- }
- if (m_MatingTimer > 0)
- {
- m_MatingTimer--;
- }
- if (m_LoveCooldown > 0)
- {
- m_LoveCooldown--;
+ ASSERT(!"Not a single Behavior took control, this is not normal. ");
+ break;
}
+
+ m_BehaviorBreeder.PostTick(a_Dt, a_Chunk);
}
@@ -223,41 +87,5 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
void cPassiveMonster::OnRightClicked(cPlayer & a_Player)
{
super::OnRightClicked(a_Player);
-
- const cItem & EquippedItem = a_Player.GetEquippedItem();
-
- // If a player holding breeding items right-clicked me, go into love mode
- if ((m_LoveCooldown == 0) && !IsInLove() && !IsBaby())
- {
- cItems Items;
- GetBreedingItems(Items);
- if (Items.ContainsType(EquippedItem.m_ItemType))
- {
- if (!a_Player.IsGameModeCreative())
- {
- a_Player.GetInventory().RemoveOneEquippedItem();
- }
- m_LoveTimer = 20 * 30; // half a minute
- m_World->BroadcastEntityStatus(*this, esMobInLove);
- }
- }
- // If a player holding my spawn egg right-clicked me, spawn a new baby
- if (EquippedItem.m_ItemType == E_ITEM_SPAWN_EGG)
- {
- eMonsterType MonsterType = cItemSpawnEggHandler::ItemDamageToMonsterType(EquippedItem.m_ItemDamage);
- if (
- (MonsterType == m_MobType) &&
- (m_World->SpawnMob(GetPosX(), GetPosY(), GetPosZ(), m_MobType, true) != cEntity::INVALID_ID) // Spawning succeeded
- )
- {
- if (!a_Player.IsGameModeCreative())
- {
- // The mob was spawned, "use" the item:
- a_Player.GetInventory().RemoveOneEquippedItem();
- }
- }
- }
+ m_BehaviorBreeder.OnRightClicked(a_Player);
}
-
-
-
diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h
index 9a2627417..0c1d00a3e 100644
--- a/src/Mobs/PassiveMonster.h
+++ b/src/Mobs/PassiveMonster.h
@@ -2,65 +2,32 @@
#pragma once
#include "Monster.h"
+#include "Behaviors/BehaviorBreeder.h"
+#include "Behaviors/BehaviorItemFollower.h"
+#include "Behaviors/BehaviorCoward.h"
+#include "Behaviors/BehaviorWanderer.h"
-
-
-
-class cPassiveMonster :
- public cMonster
+typedef std::string AString;
+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);
-
+ cPassiveMonster(const AString & a_ConfigName, eMonsterType a_MobType,
+ const AString & a_SoundHurt, const AString & a_SoundDeath,
+ double a_Width, double a_Height);
+ virtual ~cPassiveMonster();
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;
+private:
+ cBehaviorBreeder m_BehaviorBreeder;
+ cBehaviorItemFollower m_BehaviorItemFollower;
+ cBehaviorCoward m_BehaviorCoward;
+ cBehaviorWanderer m_BehaviorWanderer;
};
-
-
-
-
diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp
index fef1adac6..312bf74e2 100644
--- a/src/Mobs/Sheep.cpp
+++ b/src/Mobs/Sheep.cpp
@@ -12,21 +12,21 @@
cSheep::cSheep(int a_Color) :
- super("Sheep", mtSheep, "entity.sheep.hurt", "entity.sheep.death", 0.6, 1.3),
- m_IsSheared(false),
- m_WoolColor(a_Color),
- m_TimeToStopEating(-1)
+ super("Sheep", mtSheep, "entity.sheep.hurt", "entity.sheep.death", 0.6, 1.3),
+ m_IsSheared(false),
+ m_WoolColor(a_Color),
+ m_TimeToStopEating(-1)
{
- // Generate random wool color.
- if (m_WoolColor == -1)
- {
- m_WoolColor = GenerateNaturalRandomColor();
- }
-
- if ((m_WoolColor < 0) || (m_WoolColor > 15))
- {
- m_WoolColor = 0;
- }
+ // Generate random wool color.
+ if (m_WoolColor == -1)
+ {
+ m_WoolColor = GenerateNaturalRandomColor();
+ }
+
+ if ((m_WoolColor < 0) || (m_WoolColor > 15))
+ {
+ m_WoolColor = 0;
+ }
}
@@ -35,17 +35,17 @@ cSheep::cSheep(int a_Color) :
void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- if (!m_IsSheared)
- {
- a_Drops.push_back(cItem(E_BLOCK_WOOL, 1, static_cast<short>(m_WoolColor)));
- }
-
- unsigned int LootingLevel = 0;
- if (a_Killer != nullptr)
- {
- LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
- }
- AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_COOKED_MUTTON : E_ITEM_RAW_MUTTON);
+ if (!m_IsSheared)
+ {
+ a_Drops.push_back(cItem(E_BLOCK_WOOL, 1, static_cast<short>(m_WoolColor)));
+ }
+
+ unsigned int LootingLevel = 0;
+ if (a_Killer != nullptr)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_COOKED_MUTTON : E_ITEM_RAW_MUTTON);
}
@@ -54,30 +54,30 @@ void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer)
void cSheep::OnRightClicked(cPlayer & a_Player)
{
- super::OnRightClicked(a_Player);
-
- const cItem & EquippedItem = a_Player.GetEquippedItem();
- if ((EquippedItem.m_ItemType == E_ITEM_SHEARS) && !IsSheared() && !IsBaby())
- {
- m_IsSheared = true;
- m_World->BroadcastEntityMetadata(*this);
- a_Player.UseEquippedItem();
-
- cItems Drops;
- char NumDrops = GetRandomProvider().RandInt<char>(1, 3);
- Drops.emplace_back(E_BLOCK_WOOL, NumDrops, static_cast<short>(m_WoolColor));
- m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10);
- m_World->BroadcastSoundEffect("entity.sheep.shear", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 1.0f);
- }
- else if ((EquippedItem.m_ItemType == E_ITEM_DYE) && (m_WoolColor != 15 - EquippedItem.m_ItemDamage))
- {
- m_WoolColor = 15 - EquippedItem.m_ItemDamage;
- if (!a_Player.IsGameModeCreative())
- {
- a_Player.GetInventory().RemoveOneEquippedItem();
- }
- m_World->BroadcastEntityMetadata(*this);
- }
+ super::OnRightClicked(a_Player);
+
+ const cItem & EquippedItem = a_Player.GetEquippedItem();
+ if ((EquippedItem.m_ItemType == E_ITEM_SHEARS) && !IsSheared() && !IsBaby())
+ {
+ m_IsSheared = true;
+ m_World->BroadcastEntityMetadata(*this);
+ a_Player.UseEquippedItem();
+
+ cItems Drops;
+ char NumDrops = GetRandomProvider().RandInt<char>(1, 3);
+ Drops.emplace_back(E_BLOCK_WOOL, NumDrops, static_cast<short>(m_WoolColor));
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10);
+ m_World->BroadcastSoundEffect("entity.sheep.shear", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 1.0f);
+ }
+ else if ((EquippedItem.m_ItemType == E_ITEM_DYE) && (m_WoolColor != 15 - EquippedItem.m_ItemDamage))
+ {
+ m_WoolColor = 15 - EquippedItem.m_ItemDamage;
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ m_World->BroadcastEntityMetadata(*this);
+ }
}
@@ -86,88 +86,88 @@ void cSheep::OnRightClicked(cPlayer & a_Player)
void cSheep::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
- super::Tick(a_Dt, a_Chunk);
- if (!IsTicking())
- {
- // The base class tick destroyed us
- return;
- }
- int PosX = POSX_TOINT;
- int PosY = POSY_TOINT - 1;
- int PosZ = POSZ_TOINT;
-
- if ((PosY <= 0) || (PosY >= cChunkDef::Height))
- {
- return;
- }
-
- if (m_TimeToStopEating > 0)
- {
- StopMovingToPosition();
- m_TimeToStopEating--;
-
- if (m_TimeToStopEating == 0)
- {
- if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS) // Make sure grass hasn't been destroyed in the meantime
- {
- // The sheep ate the grass so we change it to dirt
- m_World->SetBlock(PosX, PosY, PosZ, E_BLOCK_DIRT, 0);
- GetWorld()->BroadcastSoundParticleEffect(EffectID::PARTICLE_BLOCK_BREAK, PosX, PosY, PosX, E_BLOCK_GRASS);
- m_IsSheared = false;
- m_World->BroadcastEntityMetadata(*this);
- }
- }
- }
- else
- {
- if (GetRandomProvider().RandBool(1.0 / 600.0))
- {
- if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS)
- {
- m_World->BroadcastEntityStatus(*this, esSheepEating);
- m_TimeToStopEating = 40;
- }
- }
- }
+ super::Tick(a_Dt, a_Chunk);
+ if (!IsTicking())
+ {
+ // The base class tick destroyed us
+ return;
+ }
+ int PosX = POSX_TOINT;
+ int PosY = POSY_TOINT - 1;
+ int PosZ = POSZ_TOINT;
+
+ if ((PosY <= 0) || (PosY >= cChunkDef::Height))
+ {
+ return;
+ }
+
+ if (m_TimeToStopEating > 0)
+ {
+ StopMovingToPosition();
+ m_TimeToStopEating--;
+
+ if (m_TimeToStopEating == 0)
+ {
+ if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS) // Make sure grass hasn't been destroyed in the meantime
+ {
+ // The sheep ate the grass so we change it to dirt
+ m_World->SetBlock(PosX, PosY, PosZ, E_BLOCK_DIRT, 0);
+ GetWorld()->BroadcastSoundParticleEffect(EffectID::PARTICLE_BLOCK_BREAK, PosX, PosY, PosX, E_BLOCK_GRASS);
+ m_IsSheared = false;
+ m_World->BroadcastEntityMetadata(*this);
+ }
+ }
+ }
+ else
+ {
+ if (GetRandomProvider().RandBool(1.0 / 600.0))
+ {
+ if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS)
+ {
+ m_World->BroadcastEntityStatus(*this, esSheepEating);
+ m_TimeToStopEating = 40;
+ }
+ }
+ }
}
-void cSheep::InheritFromParents(cPassiveMonster * a_Parent1, cPassiveMonster * a_Parent2)
+void cSheep::InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2)
{
- static const struct
- {
- short Parent1, Parent2, Child;
- } ColorInheritance[] =
- {
- { E_META_WOOL_BLUE, E_META_WOOL_RED, E_META_WOOL_PURPLE },
- { E_META_WOOL_BLUE, E_META_WOOL_GREEN, E_META_WOOL_CYAN },
- { E_META_WOOL_YELLOW, E_META_WOOL_RED, E_META_WOOL_ORANGE },
- { E_META_WOOL_GREEN, E_META_WOOL_WHITE, E_META_WOOL_LIGHTGREEN },
- { E_META_WOOL_RED, E_META_WOOL_WHITE, E_META_WOOL_PINK },
- { E_META_WOOL_WHITE, E_META_WOOL_BLACK, E_META_WOOL_GRAY },
- { E_META_WOOL_PURPLE, E_META_WOOL_PINK, E_META_WOOL_MAGENTA },
- { E_META_WOOL_WHITE, E_META_WOOL_GRAY, E_META_WOOL_LIGHTGRAY },
- { E_META_WOOL_BLUE, E_META_WOOL_WHITE, E_META_WOOL_LIGHTBLUE },
- };
- cSheep * Parent1 = static_cast<cSheep *>(a_Parent1);
- cSheep * Parent2 = static_cast<cSheep *>(a_Parent2);
- for (size_t i = 0; i < ARRAYCOUNT(ColorInheritance); i++)
- {
- if (
- ((Parent1->GetFurColor() == ColorInheritance[i].Parent1) && (Parent2->GetFurColor() == ColorInheritance[i].Parent2)) ||
- ((Parent1->GetFurColor() == ColorInheritance[i].Parent2) && (Parent2->GetFurColor() == ColorInheritance[i].Parent1))
- )
- {
- SetFurColor(ColorInheritance[i].Child);
- m_World->BroadcastEntityMetadata(*this);
- return;
- }
- }
- SetFurColor(GetRandomProvider().RandBool() ? Parent1->GetFurColor() : Parent2->GetFurColor());
- m_World->BroadcastEntityMetadata(*this);
+ static const struct
+ {
+ short Parent1, Parent2, Child;
+ } ColorInheritance[] =
+ {
+ { E_META_WOOL_BLUE, E_META_WOOL_RED, E_META_WOOL_PURPLE },
+ { E_META_WOOL_BLUE, E_META_WOOL_GREEN, E_META_WOOL_CYAN },
+ { E_META_WOOL_YELLOW, E_META_WOOL_RED, E_META_WOOL_ORANGE },
+ { E_META_WOOL_GREEN, E_META_WOOL_WHITE, E_META_WOOL_LIGHTGREEN },
+ { E_META_WOOL_RED, E_META_WOOL_WHITE, E_META_WOOL_PINK },
+ { E_META_WOOL_WHITE, E_META_WOOL_BLACK, E_META_WOOL_GRAY },
+ { E_META_WOOL_PURPLE, E_META_WOOL_PINK, E_META_WOOL_MAGENTA },
+ { E_META_WOOL_WHITE, E_META_WOOL_GRAY, E_META_WOOL_LIGHTGRAY },
+ { E_META_WOOL_BLUE, E_META_WOOL_WHITE, E_META_WOOL_LIGHTBLUE },
+ };
+ cSheep * Parent1 = static_cast<cSheep *>(a_Parent1);
+ cSheep * Parent2 = static_cast<cSheep *>(a_Parent2);
+ for (size_t i = 0; i < ARRAYCOUNT(ColorInheritance); i++)
+ {
+ if (
+ ((Parent1->GetFurColor() == ColorInheritance[i].Parent1) && (Parent2->GetFurColor() == ColorInheritance[i].Parent2)) ||
+ ((Parent1->GetFurColor() == ColorInheritance[i].Parent2) && (Parent2->GetFurColor() == ColorInheritance[i].Parent1))
+ )
+ {
+ SetFurColor(ColorInheritance[i].Child);
+ m_World->BroadcastEntityMetadata(*this);
+ return;
+ }
+ }
+ SetFurColor(GetRandomProvider().RandBool() ? Parent1->GetFurColor() : Parent2->GetFurColor());
+ m_World->BroadcastEntityMetadata(*this);
}
@@ -176,31 +176,31 @@ void cSheep::InheritFromParents(cPassiveMonster * a_Parent1, cPassiveMonster * a
NIBBLETYPE cSheep::GenerateNaturalRandomColor(void)
{
- int Chance = GetRandomProvider().RandInt(100);
-
- if (Chance <= 81)
- {
- return E_META_WOOL_WHITE;
- }
- else if (Chance <= 86)
- {
- return E_META_WOOL_BLACK;
- }
- else if (Chance <= 91)
- {
- return E_META_WOOL_GRAY;
- }
- else if (Chance <= 96)
- {
- return E_META_WOOL_LIGHTGRAY;
- }
- else if (Chance <= 99)
- {
- return E_META_WOOL_BROWN;
- }
- else
- {
- return E_META_WOOL_PINK;
- }
+ int Chance = GetRandomProvider().RandInt(100);
+
+ if (Chance <= 81)
+ {
+ return E_META_WOOL_WHITE;
+ }
+ else if (Chance <= 86)
+ {
+ return E_META_WOOL_BLACK;
+ }
+ else if (Chance <= 91)
+ {
+ return E_META_WOOL_GRAY;
+ }
+ else if (Chance <= 96)
+ {
+ return E_META_WOOL_LIGHTGRAY;
+ }
+ else if (Chance <= 99)
+ {
+ return E_META_WOOL_BROWN;
+ }
+ else
+ {
+ return E_META_WOOL_PINK;
+ }
}
diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h
index c8af067f3..01f90c968 100644
--- a/src/Mobs/Sheep.h
+++ b/src/Mobs/Sheep.h
@@ -25,7 +25,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
{
diff --git a/src/Mobs/Silverfish.h b/src/Mobs/Silverfish.h
index 90ef5ea5d..9988ca015 100644
--- a/src/Mobs/Silverfish.h
+++ b/src/Mobs/Silverfish.h
@@ -8,17 +8,17 @@
class cSilverfish :
- public cAggressiveMonster
+ public cAggressiveMonster
{
- typedef cAggressiveMonster super;
+ typedef cAggressiveMonster super;
public:
- cSilverfish(void) :
- super("Silverfish", mtSilverfish, "entity.silverfish.hurt", "entity.silverfish.death", 0.3, 0.7)
- {
- }
+ cSilverfish(void) :
+ super("Silverfish", mtSilverfish, "entity.silverfish.hurt", "entity.silverfish.death", 0.3, 0.7)
+ {
+ }
- CLASS_PROTODEF(cSilverfish)
+ CLASS_PROTODEF(cSilverfish)
} ;
diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp
index e48991a06..55616cc28 100644
--- a/src/Mobs/Skeleton.cpp
+++ b/src/Mobs/Skeleton.cpp
@@ -10,10 +10,10 @@
cSkeleton::cSkeleton(bool IsWither) :
- super("Skeleton", mtSkeleton, "entity.skeleton.hurt", "entity.skeleton.death", 0.6, 1.8),
- m_bIsWither(IsWither)
+ super("Skeleton", mtSkeleton, "entity.skeleton.hurt", "entity.skeleton.death", 0.6, 1.8),
+ m_bIsWither(IsWither)
{
- SetBurnsInDaylight(true);
+
}
@@ -22,54 +22,54 @@ cSkeleton::cSkeleton(bool IsWither) :
void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- unsigned int LootingLevel = 0;
- if (a_Killer != nullptr)
- {
- LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
- }
- if (IsWither())
- {
- AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_COAL);
- cItems RareDrops;
- RareDrops.Add(cItem(E_ITEM_HEAD, 1, 1));
- AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel);
- }
- else
- {
- AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ARROW);
-
- }
- AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_BONE);
- AddRandomArmorDropItem(a_Drops, LootingLevel);
- AddRandomWeaponDropItem(a_Drops, LootingLevel);
+ unsigned int LootingLevel = 0;
+ if (a_Killer != nullptr)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ if (IsWither())
+ {
+ AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_COAL);
+ cItems RareDrops;
+ RareDrops.Add(cItem(E_ITEM_HEAD, 1, 1));
+ AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel);
+ }
+ else
+ {
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ARROW);
+
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_BONE);
+ AddRandomArmorDropItem(a_Drops, LootingLevel);
+ AddRandomWeaponDropItem(a_Drops, LootingLevel);
}
-
-bool cSkeleton::Attack(std::chrono::milliseconds a_Dt)
+//mobTodo
+/*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;
-}
+ 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;
+}*/
@@ -77,8 +77,8 @@ bool cSkeleton::Attack(std::chrono::milliseconds a_Dt)
void cSkeleton::SpawnOn(cClientHandle & a_ClientHandle)
{
- super::SpawnOn(a_ClientHandle);
- a_ClientHandle.SendEntityEquipment(*this, 0, cItem(E_ITEM_BOW));
+ super::SpawnOn(a_ClientHandle);
+ a_ClientHandle.SendEntityEquipment(*this, 0, cItem(E_ITEM_BOW));
}
diff --git a/src/Mobs/Skeleton.h b/src/Mobs/Skeleton.h
index 0316fb9b5..91e094c4c 100644
--- a/src/Mobs/Skeleton.h
+++ b/src/Mobs/Skeleton.h
@@ -8,26 +8,26 @@
class cSkeleton :
- public cAggressiveMonster
+ public cAggressiveMonster
{
- typedef cAggressiveMonster super;
+ typedef cAggressiveMonster super;
public:
- cSkeleton(bool IsWither);
+ cSkeleton(bool IsWither);
- CLASS_PROTODEF(cSkeleton)
+ CLASS_PROTODEF(cSkeleton)
- virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual bool Attack(std::chrono::milliseconds a_Dt) override;
- virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
+ /*virtual bool Attack(std::chrono::milliseconds a_Dt) override;*/
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
- virtual bool IsUndead(void) override { return true; }
+ virtual bool IsUndead(void) override { return true; }
- bool IsWither(void) const { return m_bIsWither; }
+ bool IsWither(void) const { return m_bIsWither; }
private:
- bool m_bIsWither;
+ bool m_bIsWither;
} ;
diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp
index 291a3a57f..92c181798 100644
--- a/src/Mobs/Slime.cpp
+++ b/src/Mobs/Slime.cpp
@@ -20,7 +20,7 @@ cSlime::cSlime(int a_Size) :
m_Size(a_Size)
{
SetMaxHealth(a_Size * a_Size);
- SetAttackDamage(a_Size);
+ // SetAttackDamage(a_Size); //mobTodo myBehavior.setaTTACKDamage
}
@@ -45,8 +45,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 +55,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..26bc6716d 100644
--- a/src/Mobs/Slime.h
+++ b/src/Mobs/Slime.h
@@ -8,32 +8,32 @@
class cSlime :
- public cAggressiveMonster
+ public cAggressiveMonster
{
- typedef cAggressiveMonster super;
+ typedef cAggressiveMonster 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. */
- cSlime(int a_Size);
+ /** Creates a slime of the specified size; size can be 1, 2 or 4, with 1 is the smallest and 4 is the tallest. */
+ cSlime(int a_Size);
- CLASS_PROTODEF(cSlime)
+ CLASS_PROTODEF(cSlime)
- // cAggressiveMonster overrides:
- virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual bool Attack(std::chrono::milliseconds a_Dt) override;
- virtual void KilledBy(TakeDamageInfo & a_TDI) override;
+ // cAggressiveMonster overrides:
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
+ // virtual bool Attack(std::chrono::milliseconds a_Dt) override;
+ virtual void KilledBy(TakeDamageInfo & a_TDI) override;
- int GetSize(void) const { return m_Size; }
+ int GetSize(void) const { return m_Size; }
- /** Returns the text describing the slime's size, as used by the client's resource subsystem for sounds.
- Returns either "big" or "small". */
- static AString GetSizeName(int a_Size);
+ /** Returns the text describing the slime's size, as used by the client's resource subsystem for sounds.
+ Returns either "big" or "small". */
+ static AString GetSizeName(int a_Size);
protected:
- /** Size of the slime, with 1 being the smallest.
- Vanilla uses sizes 1, 2 and 4 only. */
- int m_Size;
+ /** Size of the slime, with 1 being the smallest.
+ Vanilla uses sizes 1, 2 and 4 only. */
+ int m_Size;
} ;
diff --git a/src/Mobs/Spider.cpp b/src/Mobs/Spider.cpp
index 971ff22f6..608bcd853 100644
--- a/src/Mobs/Spider.cpp
+++ b/src/Mobs/Spider.cpp
@@ -35,32 +35,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 +42,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 +52,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..296c9f261 100644
--- a/src/Mobs/Spider.h
+++ b/src/Mobs/Spider.h
@@ -8,18 +8,17 @@
class cSpider :
- public cAggressiveMonster
+ public cAggressiveMonster
{
- typedef cAggressiveMonster super;
+ typedef cAggressiveMonster super;
public:
- cSpider(void);
+ cSpider(void);
- CLASS_PROTODEF(cSpider)
+ 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;
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
+ virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
} ;
diff --git a/src/Mobs/Witch.h b/src/Mobs/Witch.h
index 706fcd9b3..79cc993bc 100644
--- a/src/Mobs/Witch.h
+++ b/src/Mobs/Witch.h
@@ -18,8 +18,7 @@ public:
CLASS_PROTODEF(cWitch)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
-
- bool IsAngry(void) const {return ((m_EMState == ATTACKING) || (m_EMState == CHASING)); }
+ bool IsAngry() const { return false; }
} ;
diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp
index f3b859c76..243507855 100644
--- a/src/Mobs/Wolf.cpp
+++ b/src/Mobs/Wolf.cpp
@@ -95,7 +95,7 @@ void cWolf::NotifyAlliesOfFight(cPawn * a_Opponent)
m_World->DoWithPlayerByUUID(m_OwnerUUID, Callback);
}
-bool cWolf::Attack(std::chrono::milliseconds a_Dt)
+/*bool cWolf::Attack(std::chrono::milliseconds a_Dt)
{
UNUSED(a_Dt);
@@ -111,7 +111,7 @@ bool cWolf::Attack(std::chrono::milliseconds a_Dt)
NotifyAlliesOfFight(static_cast<cPawn*>(GetTarget()));
return super::Attack(a_Dt);
-}
+}*/
@@ -253,6 +253,9 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
+ //mobTodo behaviors!
+
+ /*
if (!IsAngry())
{
cMonster::Tick(a_Dt, a_Chunk);
@@ -326,7 +329,7 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
MoveToPosition(GetTarget()->GetPosition());
if (TargetIsInRange())
{
- Attack(a_Dt);
+ // Attack(a_Dt); mobTodo
}
}
}
@@ -339,6 +342,7 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
StopMovingToPosition();
}
+ */
}
@@ -395,13 +399,3 @@ void cWolf::TickFollowPlayer()
}
-
-void cWolf::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
-{
- if (!IsTame())
- {
- cMonster::InStateIdle(a_Dt, a_Chunk);
- }
-}
-
-
diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h
index 861419ba8..851cf888b 100644
--- a/src/Mobs/Wolf.h
+++ b/src/Mobs/Wolf.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..6f889e182 100644
--- a/src/Mobs/Zombie.cpp
+++ b/src/Mobs/Zombie.cpp
@@ -10,11 +10,11 @@
cZombie::cZombie(bool a_IsVillagerZombie) :
- super("Zombie", mtZombie, "entity.zombie.hurt", "entity.zombie.death", 0.6, 1.8),
- m_IsVillagerZombie(a_IsVillagerZombie),
- m_IsConverting(false)
+ super("Zombie", mtZombie, "entity.zombie.hurt", "entity.zombie.death", 0.6, 1.8),
+ m_IsVillagerZombie(a_IsVillagerZombie),
+ m_IsConverting(false)
{
- SetBurnsInDaylight(true);
+
}
@@ -23,17 +23,17 @@ cZombie::cZombie(bool a_IsVillagerZombie) :
void cZombie::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
- unsigned int LootingLevel = 0;
- if (a_Killer != nullptr)
- {
- LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
- }
- AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ROTTEN_FLESH);
- cItems RareDrops;
- RareDrops.Add(cItem(E_ITEM_IRON));
- RareDrops.Add(cItem(E_ITEM_CARROT));
- RareDrops.Add(cItem(E_ITEM_POTATO));
- AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel);
- AddRandomArmorDropItem(a_Drops, LootingLevel);
- AddRandomWeaponDropItem(a_Drops, LootingLevel);
+ unsigned int LootingLevel = 0;
+ if (a_Killer != nullptr)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ROTTEN_FLESH);
+ cItems RareDrops;
+ RareDrops.Add(cItem(E_ITEM_IRON));
+ RareDrops.Add(cItem(E_ITEM_CARROT));
+ RareDrops.Add(cItem(E_ITEM_POTATO));
+ AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel);
+ AddRandomArmorDropItem(a_Drops, LootingLevel);
+ AddRandomWeaponDropItem(a_Drops, LootingLevel);
}
diff --git a/src/MonsterConfig.cpp b/src/MonsterConfig.cpp
index 28132607e..97336166c 100644
--- a/src/MonsterConfig.cpp
+++ b/src/MonsterConfig.cpp
@@ -3,6 +3,7 @@
#include "MonsterConfig.h"
#include "Mobs/Monster.h"
+#include "Mobs/Behaviors/BehaviorChaser.h"
#include "IniFile.h"
@@ -90,11 +91,18 @@ 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));
+ cBehaviorChaser * Chaser = a_Monster->GetBehaviorChaser();
+
+ // mobTodo chaser is kind of "attacker", not really chaser?
+ if (Chaser != nullptr)
+ {
+ Chaser->SetAttackDamage (itr->m_AttackDamage);
+ Chaser->SetAttackRange (itr->m_AttackRange);
+ Chaser->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 7f86d4bdc..63b80dace 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 c77af1029..a0afe8c15 100644
--- a/src/Protocol/Protocol_1_8.cpp
+++ b/src/Protocol/Protocol_1_8.cpp
@@ -3480,7 +3480,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
@@ -3496,7 +3496,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
@@ -3504,7 +3504,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
@@ -3512,7 +3512,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
@@ -3520,7 +3520,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;
@@ -3530,7 +3530,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;
@@ -3549,7 +3549,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
@@ -3583,7 +3583,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 c6e007984..7eeba06b4 100644
--- a/src/Protocol/Protocol_1_9.cpp
+++ b/src/Protocol/Protocol_1_9.cpp
@@ -4049,7 +4049,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