summaryrefslogtreecommitdiffstats
path: root/src/Mobs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Mobs')
-rw-r--r--src/Mobs/AggressiveMonster.cpp97
-rw-r--r--src/Mobs/AggressiveMonster.h30
-rw-r--r--src/Mobs/Bat.cpp15
-rw-r--r--src/Mobs/Bat.h25
-rw-r--r--src/Mobs/Blaze.cpp52
-rw-r--r--src/Mobs/Blaze.h26
-rw-r--r--src/Mobs/Cavespider.cpp40
-rw-r--r--src/Mobs/Cavespider.h26
-rw-r--r--src/Mobs/Chicken.cpp62
-rw-r--r--src/Mobs/Chicken.h29
-rw-r--r--src/Mobs/Cow.cpp45
-rw-r--r--src/Mobs/Cow.h26
-rw-r--r--src/Mobs/Creeper.cpp47
-rw-r--r--src/Mobs/Creeper.h34
-rw-r--r--src/Mobs/EnderDragon.cpp27
-rw-r--r--src/Mobs/EnderDragon.h25
-rw-r--r--src/Mobs/Enderman.cpp29
-rw-r--r--src/Mobs/Enderman.h36
-rw-r--r--src/Mobs/Ghast.cpp54
-rw-r--r--src/Mobs/Ghast.h28
-rw-r--r--src/Mobs/Giant.cpp27
-rw-r--r--src/Mobs/Giant.h25
-rw-r--r--src/Mobs/Horse.cpp152
-rw-r--r--src/Mobs/Horse.h44
-rw-r--r--src/Mobs/IncludeAllMonsters.h29
-rw-r--r--src/Mobs/IronGolem.cpp26
-rw-r--r--src/Mobs/IronGolem.h25
-rw-r--r--src/Mobs/Magmacube.cpp27
-rw-r--r--src/Mobs/Magmacube.h32
-rw-r--r--src/Mobs/Monster.cpp758
-rw-r--r--src/Mobs/Monster.h195
-rw-r--r--src/Mobs/Mooshroom.cpp33
-rw-r--r--src/Mobs/Mooshroom.h25
-rw-r--r--src/Mobs/Ocelot.h26
-rw-r--r--src/Mobs/PassiveAggressiveMonster.cpp38
-rw-r--r--src/Mobs/PassiveAggressiveMonster.h23
-rw-r--r--src/Mobs/PassiveMonster.cpp59
-rw-r--r--src/Mobs/PassiveMonster.h27
-rw-r--r--src/Mobs/Pig.cpp77
-rw-r--r--src/Mobs/Pig.h32
-rw-r--r--src/Mobs/Sheep.cpp62
-rw-r--r--src/Mobs/Sheep.h34
-rw-r--r--src/Mobs/Silverfish.h26
-rw-r--r--src/Mobs/Skeleton.cpp70
-rw-r--r--src/Mobs/Skeleton.h33
-rw-r--r--src/Mobs/Slime.cpp29
-rw-r--r--src/Mobs/Slime.h32
-rw-r--r--src/Mobs/SnowGolem.cpp26
-rw-r--r--src/Mobs/SnowGolem.h25
-rw-r--r--src/Mobs/Spider.cpp27
-rw-r--r--src/Mobs/Spider.h25
-rw-r--r--src/Mobs/Squid.cpp56
-rw-r--r--src/Mobs/Squid.h28
-rw-r--r--src/Mobs/Villager.cpp35
-rw-r--r--src/Mobs/Villager.h43
-rw-r--r--src/Mobs/Witch.cpp32
-rw-r--r--src/Mobs/Witch.h27
-rw-r--r--src/Mobs/Wither.cpp26
-rw-r--r--src/Mobs/Wither.h25
-rw-r--r--src/Mobs/Wolf.cpp189
-rw-r--r--src/Mobs/Wolf.h54
-rw-r--r--src/Mobs/Zombie.cpp47
-rw-r--r--src/Mobs/Zombie.h33
-rw-r--r--src/Mobs/Zombiepigman.cpp45
-rw-r--r--src/Mobs/Zombiepigman.h26
65 files changed, 3488 insertions, 0 deletions
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp
new file mode 100644
index 000000000..cc7e7da2b
--- /dev/null
+++ b/src/Mobs/AggressiveMonster.cpp
@@ -0,0 +1,97 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "AggressiveMonster.h"
+
+#include "../World.h"
+#include "../Vector3f.h"
+#include "../Entities/Player.h"
+#include "../MersenneTwister.h"
+
+
+
+
+
+cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eType 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_ChaseTime(999999)
+{
+ m_EMPersonality = AGGRESSIVE;
+}
+
+
+
+
+
+// What to do if in Chasing State
+void cAggressiveMonster::InStateChasing(float a_Dt)
+{
+ super::InStateChasing(a_Dt);
+ m_ChaseTime += a_Dt;
+ if (m_Target != NULL)
+ {
+ if (m_Target->IsPlayer())
+ {
+ cPlayer * Player = (cPlayer *) m_Target;
+ if (Player->IsGameModeCreative())
+ {
+ m_EMState = IDLE;
+ return;
+ }
+ }
+
+ Vector3f Pos = Vector3f( GetPosition() );
+ Vector3f Their = Vector3f( m_Target->GetPosition() );
+ if ((Their - Pos).Length() <= m_AttackRange)
+ {
+ Attack(a_Dt);
+ }
+ MoveToPosition(Their + Vector3f(0, 0.65f, 0));
+ }
+ else if (m_ChaseTime > 5.f)
+ {
+ m_ChaseTime = 0;
+ m_EMState = IDLE;
+ }
+}
+
+
+
+
+
+void cAggressiveMonster::EventSeePlayer(cEntity * a_Entity)
+{
+ super::EventSeePlayer(a_Entity);
+ m_EMState = CHASING;
+}
+
+
+
+
+
+void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ m_SeePlayerInterval += a_Dt;
+
+ if (m_SeePlayerInterval > 1)
+ {
+ int rem = m_World->GetTickRandomNumber(3) + 1; // Check most of the time but miss occasionally
+
+ m_SeePlayerInterval = 0.0;
+ if (rem >= 2)
+ {
+ if (m_EMState == CHASING)
+ {
+ CheckEventLostPlayer();
+ }
+ else
+ {
+ CheckEventSeePlayer();
+ }
+ }
+ }
+}
+
+
diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h
new file mode 100644
index 000000000..5a0d93f3d
--- /dev/null
+++ b/src/Mobs/AggressiveMonster.h
@@ -0,0 +1,30 @@
+
+#pragma once
+
+#include "Monster.h"
+
+
+
+
+
+class cAggressiveMonster :
+ public cMonster
+{
+ typedef cMonster super;
+
+public:
+ cAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
+
+ virtual void Tick (float a_Dt, cChunk & a_Chunk) override;
+ virtual void InStateChasing(float a_Dt) override;
+
+ virtual void EventSeePlayer(cEntity *) override;
+
+
+protected:
+ float m_ChaseTime;
+} ;
+
+
+
+
diff --git a/src/Mobs/Bat.cpp b/src/Mobs/Bat.cpp
new file mode 100644
index 000000000..b9c82996b
--- /dev/null
+++ b/src/Mobs/Bat.cpp
@@ -0,0 +1,15 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Bat.h"
+#include "../Vector3d.h"
+#include "../Chunk.h"
+
+
+cBat::cBat(void) :
+ // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
+ super("Bat", mtBat, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7)
+{
+}
+
+
diff --git a/src/Mobs/Bat.h b/src/Mobs/Bat.h
new file mode 100644
index 000000000..e878d0ee8
--- /dev/null
+++ b/src/Mobs/Bat.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cBat :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cBat(void);
+
+ CLASS_PROTODEF(cBat);
+
+ bool IsHanging(void) const {return false; }
+} ;
+
+
+
+
diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp
new file mode 100644
index 000000000..f9c05b17a
--- /dev/null
+++ b/src/Mobs/Blaze.cpp
@@ -0,0 +1,52 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Blaze.h"
+#include "../World.h"
+
+
+
+
+cBlaze::cBlaze(void) :
+ // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
+ super("Blaze", mtBlaze, "mob.blaze.hit", "mob.blaze.death", 0.7, 1.8)
+{
+}
+
+
+
+
+
+void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_BLAZE_ROD);
+}
+
+
+
+
+
+void cBlaze::Attack(float a_Dt)
+{
+ m_AttackInterval += a_Dt * m_AttackRate;
+
+ if (m_Target != NULL && m_AttackInterval > 3.0)
+ {
+ // Setting this higher gives us more wiggle room for attackrate
+ Vector3d Speed = GetLookVector() * 20;
+ Speed.y = Speed.y + 1;
+ cFireChargeEntity * FireCharge = new cFireChargeEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed);
+ if (FireCharge == NULL)
+ {
+ return;
+ }
+ if (!FireCharge->Initialize(m_World))
+ {
+ delete FireCharge;
+ return;
+ }
+ m_World->BroadcastSpawnEntity(*FireCharge);
+ m_AttackInterval = 0.0;
+ // ToDo: Shoot 3 fireballs instead of 1.
+ }
+} \ No newline at end of file
diff --git a/src/Mobs/Blaze.h b/src/Mobs/Blaze.h
new file mode 100644
index 000000000..cdb3a1306
--- /dev/null
+++ b/src/Mobs/Blaze.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cBlaze :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cBlaze(void);
+
+ CLASS_PROTODEF(cBlaze);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void Attack(float a_Dt) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Cavespider.cpp b/src/Mobs/Cavespider.cpp
new file mode 100644
index 000000000..aba1ff9f5
--- /dev/null
+++ b/src/Mobs/Cavespider.cpp
@@ -0,0 +1,40 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Cavespider.h"
+#include "../World.h"
+
+
+
+
+
+cCavespider::cCavespider(void) :
+ super("Cavespider", mtCaveSpider, "mob.spider.say", "mob.spider.death", 0.7, 0.5)
+{
+}
+
+
+
+
+
+void cCavespider::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ // TODO: Check vanilla if cavespiders really get passive during the day / in daylight
+ m_EMPersonality = (GetWorld()->GetTimeOfDay() < (12000 + 1000)) ? PASSIVE : AGGRESSIVE;
+}
+
+
+
+
+
+void cCavespider::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_STRING);
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_SPIDER_EYE);
+}
+
+
+
+
diff --git a/src/Mobs/Cavespider.h b/src/Mobs/Cavespider.h
new file mode 100644
index 000000000..10ea03f7b
--- /dev/null
+++ b/src/Mobs/Cavespider.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cCavespider :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cCavespider(void);
+
+ CLASS_PROTODEF(cCaveSpider);
+
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Chicken.cpp b/src/Mobs/Chicken.cpp
new file mode 100644
index 000000000..087fd088a
--- /dev/null
+++ b/src/Mobs/Chicken.cpp
@@ -0,0 +1,62 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Chicken.h"
+#include "../World.h"
+
+
+
+
+
+
+
+
+cChicken::cChicken(void) :
+ super("Chicken", mtChicken, "mob.chicken.hurt", "mob.chicken.hurt", 0.3, 0.4),
+ m_EggDropTimer(0)
+{
+}
+
+
+
+
+void cChicken::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ if ((m_EggDropTimer == 6000) && (m_World->GetTickRandomNumber(1) == 0))
+ {
+ 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++;
+ }
+}
+
+
+
+
+
+void cChicken::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_FEATHER);
+ a_Drops.push_back(cItem(IsOnFire() ? E_ITEM_COOKED_CHICKEN : E_ITEM_RAW_CHICKEN, 1));
+}
+
+
+
+
+
+
+
+
diff --git a/src/Mobs/Chicken.h b/src/Mobs/Chicken.h
new file mode 100644
index 000000000..979c4d8a0
--- /dev/null
+++ b/src/Mobs/Chicken.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cChicken :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cChicken(void);
+
+ CLASS_PROTODEF(cChicken);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+private:
+
+
+ int m_EggDropTimer;
+} ;
+
+
+
diff --git a/src/Mobs/Cow.cpp b/src/Mobs/Cow.cpp
new file mode 100644
index 000000000..9eb74dac2
--- /dev/null
+++ b/src/Mobs/Cow.cpp
@@ -0,0 +1,45 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Cow.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+
+
+cCow::cCow(void) :
+ super("Cow", mtCow, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3)
+{
+}
+
+
+
+
+
+void cCow::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER);
+ AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF);
+}
+
+
+
+
+
+void cCow::OnRightClicked(cPlayer & a_Player)
+{
+ if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_BUCKET))
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ a_Player.GetInventory().AddItem(E_ITEM_MILK);
+ }
+ }
+}
+
+
+
diff --git a/src/Mobs/Cow.h b/src/Mobs/Cow.h
new file mode 100644
index 000000000..0391d4a31
--- /dev/null
+++ b/src/Mobs/Cow.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cCow :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cCow();
+
+ CLASS_PROTODEF(cCow);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp
new file mode 100644
index 000000000..4e11ae13e
--- /dev/null
+++ b/src/Mobs/Creeper.cpp
@@ -0,0 +1,47 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Creeper.h"
+#include "../World.h"
+
+
+
+
+
+cCreeper::cCreeper(void) :
+ super("Creeper", mtCreeper, "mob.creeper.say", "mob.creeper.say", 0.6, 1.8),
+ m_bIsBlowing(false),
+ m_bIsCharged(false)
+{
+}
+
+
+
+
+
+void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER);
+
+ // TODO Check if killed by a skeleton, then drop random music disk
+}
+
+
+
+
+
+void cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ super::DoTakeDamage(a_TDI);
+
+ if (a_TDI.DamageType == dtLightning)
+ {
+ m_bIsCharged = true;
+ }
+
+ m_World->BroadcastEntityMetadata(*this);
+}
+
+
+
+
diff --git a/src/Mobs/Creeper.h b/src/Mobs/Creeper.h
new file mode 100644
index 000000000..c3d4edeae
--- /dev/null
+++ b/src/Mobs/Creeper.h
@@ -0,0 +1,34 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cCreeper :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cCreeper(void);
+
+ CLASS_PROTODEF(cCreeper);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+
+ bool IsBlowing(void) const {return m_bIsBlowing; }
+ bool IsCharged(void) const {return m_bIsCharged; }
+
+private:
+
+ bool m_bIsBlowing, m_bIsCharged;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/EnderDragon.cpp b/src/Mobs/EnderDragon.cpp
new file mode 100644
index 000000000..acd81cde1
--- /dev/null
+++ b/src/Mobs/EnderDragon.cpp
@@ -0,0 +1,27 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "EnderDragon.h"
+
+
+
+
+
+cEnderDragon::cEnderDragon(void) :
+ // TODO: Vanilla source says this, but is it right? Dragons fly, they don't stand
+ super("EnderDragon", mtEnderDragon, "mob.enderdragon.hit", "mob.enderdragon.end", 16.0, 8.0)
+{
+}
+
+
+
+
+
+void cEnderDragon::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ return;
+}
+
+
+
+
diff --git a/src/Mobs/EnderDragon.h b/src/Mobs/EnderDragon.h
new file mode 100644
index 000000000..77177edfe
--- /dev/null
+++ b/src/Mobs/EnderDragon.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cEnderDragon :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cEnderDragon(void);
+
+ CLASS_PROTODEF(cEnderDragon);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp
new file mode 100644
index 000000000..a784131e4
--- /dev/null
+++ b/src/Mobs/Enderman.cpp
@@ -0,0 +1,29 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Enderman.h"
+
+
+
+
+
+cEnderman::cEnderman(void) :
+ super("Enderman", mtEnderman, "mob.endermen.hit", "mob.endermen.death", 0.5, 2.9),
+ m_bIsScreaming(false),
+ CarriedBlock(E_BLOCK_AIR),
+ CarriedMeta(0)
+{
+}
+
+
+
+
+
+void cEnderman::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ENDER_PEARL);
+}
+
+
+
+
diff --git a/src/Mobs/Enderman.h b/src/Mobs/Enderman.h
new file mode 100644
index 000000000..32e40e70b
--- /dev/null
+++ b/src/Mobs/Enderman.h
@@ -0,0 +1,36 @@
+
+#pragma once
+
+#include "PassiveAggressiveMonster.h"
+
+
+
+
+
+class cEnderman :
+ public cPassiveAggressiveMonster
+{
+ typedef cPassiveAggressiveMonster super;
+
+public:
+ cEnderman(void);
+
+ CLASS_PROTODEF(cEnderman);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+
+ bool IsScreaming(void) const {return m_bIsScreaming; }
+ BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; }
+ NIBBLETYPE GetCarriedMeta(void) const {return CarriedMeta; }
+
+private:
+
+ bool m_bIsScreaming;
+ BLOCKTYPE CarriedBlock;
+ NIBBLETYPE CarriedMeta;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp
new file mode 100644
index 000000000..96a29b2d8
--- /dev/null
+++ b/src/Mobs/Ghast.cpp
@@ -0,0 +1,54 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Ghast.h"
+#include "../World.h"
+
+
+
+
+cGhast::cGhast(void) :
+ super("Ghast", mtGhast, "mob.ghast.scream", "mob.ghast.death", 4, 4)
+{
+}
+
+
+
+
+
+void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER);
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GHAST_TEAR);
+}
+
+
+
+
+
+void cGhast::Attack(float a_Dt)
+{
+ m_AttackInterval += a_Dt * m_AttackRate;
+
+ if (m_Target != NULL && m_AttackInterval > 3.0)
+ {
+ // Setting this higher gives us more wiggle room for attackrate
+ Vector3d Speed = GetLookVector() * 20;
+ Speed.y = Speed.y + 1;
+ cGhastFireballEntity * GhastBall = new cGhastFireballEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed);
+ if (GhastBall == NULL)
+ {
+ return;
+ }
+ if (!GhastBall->Initialize(m_World))
+ {
+ delete GhastBall;
+ return;
+ }
+ m_World->BroadcastSpawnEntity(*GhastBall);
+ m_AttackInterval = 0.0;
+ }
+}
+
+
+
diff --git a/src/Mobs/Ghast.h b/src/Mobs/Ghast.h
new file mode 100644
index 000000000..43e8bedb6
--- /dev/null
+++ b/src/Mobs/Ghast.h
@@ -0,0 +1,28 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cGhast :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cGhast(void);
+
+ CLASS_PROTODEF(cGhast);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void Attack(float a_Dt) override;
+
+ bool IsCharging(void) const {return false; }
+} ;
+
+
+
+
diff --git a/src/Mobs/Giant.cpp b/src/Mobs/Giant.cpp
new file mode 100644
index 000000000..f41977535
--- /dev/null
+++ b/src/Mobs/Giant.cpp
@@ -0,0 +1,27 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Giant.h"
+
+
+
+
+
+cGiant::cGiant(void) :
+ // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here
+ super("Giant", mtGiant, "mob.zombie.hurt", "mob.zombie.death", 2.0, 13.5)
+{
+}
+
+
+
+
+
+void cGiant::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 10, 50, E_ITEM_ROTTEN_FLESH);
+}
+
+
+
+
diff --git a/src/Mobs/Giant.h b/src/Mobs/Giant.h
new file mode 100644
index 000000000..356dd4352
--- /dev/null
+++ b/src/Mobs/Giant.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cGiant :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cGiant(void);
+
+ CLASS_PROTODEF(cGiant);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp
new file mode 100644
index 000000000..bb9a4e3f6
--- /dev/null
+++ b/src/Mobs/Horse.cpp
@@ -0,0 +1,152 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Horse.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+cHorse::cHorse(int Type, int Color, int Style, int TameTimes) :
+ super("Horse", mtHorse, "mob.horse.hit", "mob.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)
+{
+}
+
+
+
+
+
+void cHorse::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ if (!m_bIsMouthOpen)
+ {
+ if (m_World->GetTickRandomNumber(50) == 25)
+ {
+ m_bIsMouthOpen = true;
+ }
+ }
+ else
+ {
+ if (m_World->GetTickRandomNumber(10) == 5)
+ {
+ m_bIsMouthOpen = false;
+ }
+ }
+
+ if ((m_Attachee != NULL) && (!m_bIsTame))
+ {
+ if (m_TameAttemptTimes < m_TimesToTame)
+ {
+ if (m_World->GetTickRandomNumber(50) == 25)
+ {
+ m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 0);
+ m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 2);
+ m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 6);
+ m_World->BroadcastSoundParticleEffect(2000, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ(), 8);
+
+ m_Attachee->Detach();
+ m_bIsRearing = true;
+ }
+ }
+ else
+ {
+ m_bIsTame = true;
+ }
+ }
+
+ if (m_bIsRearing)
+ {
+ if (m_RearTickCount == 20)
+ {
+ m_bIsRearing = false;
+ m_RearTickCount = 0;
+ }
+ else
+ {
+ m_RearTickCount++;
+ }
+ }
+
+ m_World->BroadcastEntityMetadata(*this);
+}
+
+
+
+
+
+void cHorse::OnRightClicked(cPlayer & a_Player)
+{
+ if (!m_bIsSaddled && m_bIsTame)
+ {
+ 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 if (!a_Player.GetEquippedItem().IsEmpty())
+ {
+ // The horse doesn't like being hit, make it rear:
+ m_bIsRearing = true;
+ m_RearTickCount = 0;
+ }
+ }
+ else
+ {
+ if (m_Attachee != NULL)
+ {
+ 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);
+ }
+}
+
+
+
+
+
+void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER);
+ if (m_bIsSaddled)
+ {
+ a_Drops.push_back(cItem(E_ITEM_SADDLE, 1));
+ }
+}
+
+
+
+
diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h
new file mode 100644
index 000000000..be0c23f9b
--- /dev/null
+++ b/src/Mobs/Horse.h
@@ -0,0 +1,44 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cHorse :
+ public cPassiveMonster
+{
+ 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 = NULL) override;
+ virtual void Tick(float 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 {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;}
+
+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;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/IncludeAllMonsters.h b/src/Mobs/IncludeAllMonsters.h
new file mode 100644
index 000000000..1b436a11f
--- /dev/null
+++ b/src/Mobs/IncludeAllMonsters.h
@@ -0,0 +1,29 @@
+#include "Bat.h"
+#include "Blaze.h"
+#include "Cavespider.h"
+#include "Chicken.h"
+#include "Cow.h"
+#include "Creeper.h"
+#include "Enderman.h"
+#include "EnderDragon.h"
+#include "Ghast.h"
+#include "Giant.h"
+#include "Horse.h"
+#include "IronGolem.h"
+#include "Magmacube.h"
+#include "Mooshroom.h"
+#include "Ocelot.h"
+#include "Pig.h"
+#include "Sheep.h"
+#include "Silverfish.h"
+#include "Skeleton.h"
+#include "Slime.h"
+#include "SnowGolem.h"
+#include "Spider.h"
+#include "Squid.h"
+#include "Villager.h"
+#include "Witch.h"
+#include "Wither.h"
+#include "Wolf.h"
+#include "Zombie.h"
+#include "Zombiepigman.h"
diff --git a/src/Mobs/IronGolem.cpp b/src/Mobs/IronGolem.cpp
new file mode 100644
index 000000000..47c961098
--- /dev/null
+++ b/src/Mobs/IronGolem.cpp
@@ -0,0 +1,26 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "IronGolem.h"
+
+
+
+
+
+cIronGolem::cIronGolem(void) :
+ super("IronGolem", mtIronGolem, "mob.IronGolem.hit", "mob.IronGolem.death", 1.4, 2.9)
+{
+}
+
+
+
+
+
+void cIronGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 5, E_ITEM_IRON);
+}
+
+
+
+
diff --git a/src/Mobs/IronGolem.h b/src/Mobs/IronGolem.h
new file mode 100644
index 000000000..d49ff4cab
--- /dev/null
+++ b/src/Mobs/IronGolem.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "PassiveAggressiveMonster.h"
+
+
+
+
+
+class cIronGolem :
+ public cPassiveAggressiveMonster
+{
+ typedef cPassiveAggressiveMonster super;
+
+public:
+ cIronGolem(void);
+
+ CLASS_PROTODEF(cIronGolem);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Magmacube.cpp b/src/Mobs/Magmacube.cpp
new file mode 100644
index 000000000..86447ff6b
--- /dev/null
+++ b/src/Mobs/Magmacube.cpp
@@ -0,0 +1,27 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Magmacube.h"
+
+
+
+
+
+cMagmaCube::cMagmaCube(int a_Size) :
+ super("MagmaCube", mtMagmaCube, "mob.MagmaCube.big", "mob.MagmaCube.big", 0.6 * a_Size, 0.6 * a_Size),
+ m_Size(a_Size)
+{
+}
+
+
+
+
+
+void cMagmaCube::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_MAGMA_CREAM);
+}
+
+
+
+
diff --git a/src/Mobs/Magmacube.h b/src/Mobs/Magmacube.h
new file mode 100644
index 000000000..130952970
--- /dev/null
+++ b/src/Mobs/Magmacube.h
@@ -0,0 +1,32 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cMagmaCube :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ /// Creates a MagmaCube of the specified size; size is 1 .. 3, with 1 being the smallest
+ cMagmaCube(int a_Size);
+
+ CLASS_PROTODEF(cMagmaCube);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ int GetSize(void) const { return m_Size; }
+
+protected:
+
+ /// Size of the MagmaCube, 1 .. 3, with 1 being the smallest
+ int m_Size;
+} ;
+
+
+
+
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
new file mode 100644
index 000000000..8a5717e27
--- /dev/null
+++ b/src/Mobs/Monster.cpp
@@ -0,0 +1,758 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "IncludeAllMonsters.h"
+#include "../Root.h"
+#include "../Server.h"
+#include "../ClientHandle.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+#include "../Defines.h"
+#include "../MonsterConfig.h"
+#include "../MersenneTwister.h"
+
+#include "../Vector3f.h"
+#include "../Vector3i.h"
+#include "../Vector3d.h"
+#include "../Tracer.h"
+#include "../Chunk.h"
+#include "../FastRandom.h"
+
+
+
+
+
+/** Map for eType <-> string
+Needs to be alpha-sorted by the strings, because binary search is used in StringToMobType()
+The strings need to be lowercase (for more efficient comparisons in StringToMobType())
+*/
+static const struct
+{
+ cMonster::eType m_Type;
+ const char * m_lcName;
+} g_MobTypeNames[] =
+{
+ {cMonster::mtBat, "bat"},
+ {cMonster::mtBlaze, "blaze"},
+ {cMonster::mtCaveSpider, "cavespider"},
+ {cMonster::mtChicken, "chicken"},
+ {cMonster::mtCow, "cow"},
+ {cMonster::mtCreeper, "creeper"},
+ {cMonster::mtEnderman, "enderman"},
+ {cMonster::mtGhast, "ghast"},
+ {cMonster::mtHorse, "horse"},
+ {cMonster::mtMagmaCube, "magmacube"},
+ {cMonster::mtMooshroom, "mooshroom"},
+ {cMonster::mtOcelot, "ocelot"},
+ {cMonster::mtPig, "pig"},
+ {cMonster::mtSheep, "sheep"},
+ {cMonster::mtSilverfish, "silverfish"},
+ {cMonster::mtSkeleton, "skeleton"},
+ {cMonster::mtSlime, "slime"},
+ {cMonster::mtSpider, "spider"},
+ {cMonster::mtSquid, "squid"},
+ {cMonster::mtVillager, "villager"},
+ {cMonster::mtWitch, "witch"},
+ {cMonster::mtWolf, "wolf"},
+ {cMonster::mtZombie, "zombie"},
+ {cMonster::mtZombiePigman, "zombiepigman"},
+} ;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMonster:
+
+cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height)
+ : super(etMonster, a_Width, a_Height)
+ , m_Target(NULL)
+ , m_AttackRate(3)
+ , idle_interval(0)
+ , m_bMovingToDestination(false)
+ , m_DestinationTime( 0 )
+ , m_DestroyTimer( 0 )
+ , m_Jump(0)
+ , m_MobType(a_MobType)
+ , m_SoundHurt(a_SoundHurt)
+ , m_SoundDeath(a_SoundDeath)
+ , m_EMState(IDLE)
+ , m_SightDistance(25)
+ , m_SeePlayerInterval (0)
+ , m_EMPersonality(AGGRESSIVE)
+ , m_AttackDamage(1.0f)
+ , m_AttackRange(2.0f)
+ , m_AttackInterval(0)
+ , m_BurnsInDaylight(false)
+{
+ if (!a_ConfigName.empty())
+ {
+ GetMonsterConfig(a_ConfigName);
+ }
+}
+
+
+
+
+
+void cMonster::SpawnOn(cClientHandle & a_Client)
+{
+ a_Client.SendSpawnMob(*this);
+}
+
+
+
+
+
+void cMonster::MoveToPosition( const Vector3f & a_Position )
+{
+ m_bMovingToDestination = true;
+
+ m_Destination = a_Position;
+}
+
+
+
+
+
+bool cMonster::ReachedDestination()
+{
+ Vector3f Distance = (m_Destination) - GetPosition();
+ if( Distance.SqrLength() < 2.f )
+ return true;
+
+ return false;
+}
+
+
+
+
+
+void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ if (m_Health <= 0)
+ {
+ // The mob is dead, but we're still animating the "puff" they leave when they die
+ m_DestroyTimer += a_Dt / 1000;
+ if (m_DestroyTimer > 1)
+ {
+ Destroy(true);
+ }
+ return;
+ }
+
+ // Burning in daylight
+ HandleDaylightBurning(a_Chunk);
+
+ HandlePhysics(a_Dt,a_Chunk);
+ BroadcastMovementUpdate();
+
+ a_Dt /= 1000;
+
+ if (m_bMovingToDestination)
+ {
+ Vector3f Pos( GetPosition() );
+ Vector3f Distance = m_Destination - Pos;
+ if( !ReachedDestination() )
+ {
+ Distance.y = 0;
+ Distance.Normalize();
+ Distance *= 3;
+ SetSpeedX( Distance.x );
+ SetSpeedZ( Distance.z );
+
+ if (m_EMState == ESCAPING)
+ { //Runs Faster when escaping :D otherwise they just walk away
+ SetSpeedX (GetSpeedX() * 2.f);
+ SetSpeedZ (GetSpeedZ() * 2.f);
+ }
+ }
+ else
+ {
+ m_bMovingToDestination = false;
+ }
+
+ if( GetSpeed().SqrLength() > 0.f )
+ {
+ if( m_bOnGround )
+ {
+ Vector3f NormSpeed = Vector3f(GetSpeed()).NormalizeCopy();
+ Vector3f NextBlock = Vector3f( GetPosition() ) + NormSpeed;
+ int NextHeight;
+ if (!m_World->TryGetHeight((int)NextBlock.x, (int)NextBlock.z, NextHeight))
+ {
+ // The chunk at NextBlock is not loaded
+ return;
+ }
+ if( NextHeight > (GetPosY() - 1.0) && (NextHeight - GetPosY()) < 2.5 )
+ {
+ m_bOnGround = false;
+ SetSpeedY(5.f); // Jump!!
+ }
+ }
+ }
+ }
+
+ Vector3d Distance = m_Destination - GetPosition();
+ if (Distance.SqrLength() > 0.1f)
+ {
+ double Rotation, Pitch;
+ Distance.Normalize();
+ VectorToEuler( Distance.x, Distance.y, Distance.z, Rotation, Pitch );
+ SetHeadYaw (Rotation);
+ SetRotation( Rotation );
+ SetPitch( -Pitch );
+ }
+
+ switch (m_EMState)
+ {
+ case IDLE:
+ {
+ // If enemy passive we ignore checks for player visibility
+ InStateIdle(a_Dt);
+ break;
+ }
+
+ case CHASING:
+ {
+ // If we do not see a player anymore skip chasing action
+ InStateChasing(a_Dt);
+ break;
+ }
+
+ case ESCAPING:
+ {
+ InStateEscaping(a_Dt);
+ break;
+ }
+ } // switch (m_EMState)
+}
+
+
+
+
+
+
+void cMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ super::DoTakeDamage(a_TDI);
+ if((m_SoundHurt != "") && (m_Health > 0)) m_World->BroadcastSoundEffect(m_SoundHurt, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f);
+ if (a_TDI.Attacker != NULL)
+ {
+ m_Target = a_TDI.Attacker;
+ AddReference(m_Target);
+ }
+}
+
+
+
+
+
+void cMonster::KilledBy(cEntity * a_Killer)
+{
+ super::KilledBy(a_Killer);
+ if (m_SoundHurt != "")
+ {
+ m_World->BroadcastSoundEffect(m_SoundDeath, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f);
+ }
+ m_DestroyTimer = 0;
+}
+
+
+
+
+
+//----State Logic
+
+const char *cMonster::GetState()
+{
+ switch(m_EMState)
+ {
+ case IDLE: return "Idle";
+ case ATTACKING: return "Attacking";
+ case CHASING: return "Chasing";
+ default: return "Unknown";
+ }
+}
+
+
+
+
+
+// for debugging
+void cMonster::SetState(const AString & a_State)
+{
+ if (a_State.compare("Idle") == 0)
+ {
+ m_EMState = IDLE;
+ }
+ else if (a_State.compare("Attacking") == 0)
+ {
+ m_EMState = ATTACKING;
+ }
+ else if (a_State.compare("Chasing") == 0)
+ {
+ m_EMState = CHASING;
+ }
+ else
+ {
+ LOGD("cMonster::SetState(): Invalid state");
+ ASSERT(!"Invalid state");
+ }
+}
+
+
+
+
+
+//Checks to see if EventSeePlayer should be fired
+//monster sez: Do I see the player
+void cMonster::CheckEventSeePlayer(void)
+{
+ // TODO: Rewrite this to use cWorld's DoWithPlayers()
+ cPlayer * Closest = FindClosestPlayer();
+
+ if (Closest != NULL)
+ {
+ EventSeePlayer(Closest);
+ }
+}
+
+
+
+
+
+void cMonster::CheckEventLostPlayer(void)
+{
+ Vector3f pos;
+ cTracer LineOfSight(GetWorld());
+
+ if (m_Target != NULL)
+ {
+ pos = m_Target->GetPosition();
+ if ((pos - GetPosition()).Length() > m_SightDistance || LineOfSight.Trace(GetPosition(),(pos - GetPosition()), (int)(pos - GetPosition()).Length()))
+ {
+ EventLosePlayer();
+ }
+ }
+ else
+ {
+ EventLosePlayer();
+ }
+}
+
+
+
+
+
+// What to do if player is seen
+// default to change state to chasing
+void cMonster::EventSeePlayer(cEntity * a_SeenPlayer)
+{
+ m_Target = a_SeenPlayer;
+ AddReference(m_Target);
+}
+
+
+
+
+
+void cMonster::EventLosePlayer(void)
+{
+ Dereference(m_Target);
+ m_Target = NULL;
+ m_EMState = IDLE;
+}
+
+
+
+
+
+// What to do if in Idle State
+void cMonster::InStateIdle(float a_Dt)
+{
+ idle_interval += a_Dt;
+ if (idle_interval > 1)
+ {
+ // at this interval the results are predictable
+ int rem = m_World->GetTickRandomNumber(6) + 1;
+ // LOGD("Moving: int: %3.3f rem: %i",idle_interval,rem);
+ idle_interval -= 1; // So nothing gets dropped when the server hangs for a few seconds
+ Vector3f Dist;
+ Dist.x = (float)(m_World->GetTickRandomNumber(10) - 5);
+ Dist.z = (float)(m_World->GetTickRandomNumber(10) - 5);
+ if ((Dist.SqrLength() > 2) && (rem >= 3))
+ {
+ m_Destination.x = (float)(GetPosX() + Dist.x);
+ m_Destination.z = (float)(GetPosZ() + Dist.z);
+ int PosY;
+ if (m_World->TryGetHeight((int)m_Destination.x, (int)m_Destination.z, PosY))
+ {
+ m_Destination.y = (float)PosY + 1.2f;
+ MoveToPosition(m_Destination);
+ }
+ }
+ }
+}
+
+
+
+
+
+// What to do if in Chasing State
+// This state should always be defined in each child class
+void cMonster::InStateChasing(float a_Dt)
+{
+ UNUSED(a_Dt);
+}
+
+
+
+
+
+// What to do if in Escaping State
+void cMonster::InStateEscaping(float a_Dt)
+{
+ UNUSED(a_Dt);
+
+ if (m_Target != NULL)
+ {
+ Vector3d newloc = GetPosition();
+ newloc.x = (m_Target->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance);
+ newloc.z = (m_Target->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
+ }
+}
+
+
+
+
+
+// Do attack here
+// a_Dt is passed so we can set attack rate
+void cMonster::Attack(float a_Dt)
+{
+ m_AttackInterval += a_Dt * m_AttackRate;
+ if ((m_Target != NULL) && (m_AttackInterval > 3.0))
+ {
+ // Setting this higher gives us more wiggle room for attackrate
+ m_AttackInterval = 0.0;
+ ((cPawn *)m_Target)->TakeDamage(*this);
+ }
+}
+
+
+
+
+
+// Checks for Players close by and if they are visible return the closest
+cPlayer * cMonster::FindClosestPlayer(void)
+{
+ return m_World->FindClosestPlayer(GetPosition(), m_SightDistance);
+}
+
+
+
+
+
+void cMonster::GetMonsterConfig(const AString & a_Name)
+{
+ cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name);
+}
+
+
+
+
+
+void cMonster::SetAttackRate(int ar)
+{
+ m_AttackRate = (float)ar;
+}
+
+
+
+
+
+void cMonster::SetAttackRange(float ar)
+{
+ m_AttackRange = ar;
+}
+
+
+
+
+
+void cMonster::SetAttackDamage(float ad)
+{
+ m_AttackDamage = ad;
+}
+
+
+
+
+
+void cMonster::SetSightDistance(float sd)
+{
+ m_SightDistance = sd;
+}
+
+
+
+
+
+AString cMonster::MobTypeToString(cMonster::eType a_MobType)
+{
+ // Mob types aren't sorted, so we need to search linearly:
+ for (int i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++)
+ {
+ if (g_MobTypeNames[i].m_Type == a_MobType)
+ {
+ return g_MobTypeNames[i].m_lcName;
+ }
+ }
+
+ // Not found:
+ return "";
+}
+
+
+
+
+
+cMonster::eType cMonster::StringToMobType(const AString & a_Name)
+{
+ AString lcName(a_Name);
+ StrToLower(lcName);
+
+ // Binary-search for the lowercase name:
+ int lo = 0, hi = ARRAYCOUNT(g_MobTypeNames) - 1;
+ while (hi - lo > 1)
+ {
+ int mid = (lo + hi) / 2;
+ int res = strcmp(g_MobTypeNames[mid].m_lcName, lcName.c_str());
+ if (res == 0)
+ {
+ return g_MobTypeNames[mid].m_Type;
+ }
+ if (res < 0)
+ {
+ lo = mid;
+ }
+ else
+ {
+ hi = mid;
+ }
+ }
+ // Range has collapsed to at most two elements, compare each:
+ if (strcmp(g_MobTypeNames[lo].m_lcName, lcName.c_str()) == 0)
+ {
+ return g_MobTypeNames[lo].m_Type;
+ }
+ if ((lo != hi) && (strcmp(g_MobTypeNames[hi].m_lcName, lcName.c_str()) == 0))
+ {
+ return g_MobTypeNames[hi].m_Type;
+ }
+
+ // Not found:
+ return mtInvalidType;
+}
+
+
+
+
+
+cMonster::eFamily cMonster::FamilyFromType(eType a_Type)
+{
+ switch (a_Type)
+ {
+ case mtBat: return mfAmbient;
+ case mtBlaze: return mfHostile;
+ case mtCaveSpider: return mfHostile;
+ case mtChicken: return mfPassive;
+ case mtCow: return mfPassive;
+ case mtCreeper: return mfHostile;
+ case mtEnderman: return mfHostile;
+ case mtGhast: return mfHostile;
+ case mtHorse: return mfPassive;
+ case mtMagmaCube: return mfHostile;
+ case mtMooshroom: return mfHostile;
+ case mtOcelot: return mfHostile;
+ case mtPig: return mfPassive;
+ case mtSheep: return mfPassive;
+ case mtSilverfish: return mfHostile;
+ case mtSkeleton: return mfHostile;
+ case mtSlime: return mfHostile;
+ case mtSpider: return mfHostile;
+ case mtSquid: return mfWater;
+ case mtVillager: return mfPassive;
+ case mtWitch: return mfHostile;
+ case mtWolf: return mfHostile;
+ case mtZombie: return mfHostile;
+ case mtZombiePigman: return mfHostile;
+ } ;
+ ASSERT(!"Unhandled mob type");
+ return mfMaxplusone;
+}
+
+
+
+
+
+int cMonster::GetSpawnDelay(cMonster::eFamily a_MobFamily)
+{
+ switch (a_MobFamily)
+ {
+ case mfHostile: return 40;
+ case mfPassive: return 40;
+ case mfAmbient: return 40;
+ case mfWater: return 400;
+ }
+ ASSERT(!"Unhandled mob family");
+ return -1;
+}
+
+
+
+
+
+cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType)
+{
+ cFastRandom Random;
+ cMonster * toReturn = NULL;
+
+ // Create the mob entity
+ switch (a_MobType)
+ {
+ case mtMagmaCube:
+ case mtSlime:
+ {
+ toReturn = new cSlime (Random.NextInt(2) + 1);
+ break;
+ }
+ case mtSkeleton:
+ {
+ // TODO: Actual detection of spawning in Nether
+ toReturn = new cSkeleton(Random.NextInt(1) == 0 ? false : true);
+ break;
+ }
+ case mtVillager:
+ {
+ int VillagerType = Random.NextInt(6);
+ if (VillagerType == 6)
+ {
+ // Give farmers a better chance of spawning
+ VillagerType = 0;
+ }
+
+ toReturn = new cVillager((cVillager::eVillagerType)VillagerType);
+ break;
+ }
+ case mtHorse:
+ {
+ // Horses take a type (species), a colour, and a style (dots, stripes, etc.)
+ int HorseType = Random.NextInt(7);
+ int HorseColor = Random.NextInt(6);
+ int HorseStyle = Random.NextInt(6);
+ int HorseTameTimes = Random.NextInt(6) + 1;
+
+ if ((HorseType == 5) || (HorseType == 6) || (HorseType == 7))
+ {
+ // Increase chances of normal horse (zero)
+ HorseType = 0;
+ }
+
+ toReturn = new cHorse(HorseType, HorseColor, HorseStyle, HorseTameTimes);
+ break;
+ }
+
+ case mtBat: toReturn = new cBat(); break;
+ case mtBlaze: toReturn = new cBlaze(); break;
+ case mtCaveSpider: toReturn = new cCavespider(); break;
+ case mtChicken: toReturn = new cChicken(); break;
+ case mtCow: toReturn = new cCow(); break;
+ case mtCreeper: toReturn = new cCreeper(); break;
+ case mtEnderman: toReturn = new cEnderman(); break;
+ case mtGhast: toReturn = new cGhast(); break;
+ case mtMooshroom: toReturn = new cMooshroom(); break;
+ case mtOcelot: toReturn = new cOcelot(); break;
+ case mtPig: toReturn = new cPig(); break;
+ case mtSheep: toReturn = new cSheep (Random.NextInt(15)); break; // Colour parameter
+ case mtSilverfish: toReturn = new cSilverfish(); break;
+ case mtSpider: toReturn = new cSpider(); break;
+ case mtSquid: toReturn = new cSquid(); break;
+ case mtWitch: toReturn = new cWitch(); break;
+ case mtWolf: toReturn = new cWolf(); break;
+ case mtZombie: toReturn = new cZombie(false); break; // TODO: Infected zombie parameter
+ case mtZombiePigman: toReturn = new cZombiePigman(); break;
+ default:
+ {
+ ASSERT(!"Unhandled mob type whilst trying to spawn mob!");
+ }
+ }
+ return toReturn;
+}
+
+
+
+
+
+void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth)
+{
+ MTRand r1;
+ int Count = r1.randInt() % (a_Max + 1 - a_Min) + a_Min;
+ if (Count > 0)
+ {
+ a_Drops.push_back(cItem(a_Item, Count, a_ItemHealth));
+ }
+}
+
+
+
+
+
+void cMonster::HandleDaylightBurning(cChunk & a_Chunk)
+{
+ if (!m_BurnsInDaylight)
+ {
+ return;
+ }
+
+ int RelY = (int)floor(GetPosY());
+ if ((RelY < 0) || (RelY >= cChunkDef::Height))
+ {
+ // Outside the world
+ return;
+ }
+
+ int RelX = (int)floor(GetPosX()) - GetChunkX() * cChunkDef::Width;
+ int RelZ = (int)floor(GetPosZ()) - GetChunkZ() * cChunkDef::Width;
+ if (
+ (a_Chunk.GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight
+ (a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand
+ (GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime
+ !IsOnFire() // Not already burning
+ )
+ {
+ // Burn for 100 ticks, then decide again
+ StartBurning(100);
+ }
+}
+
+
+
+
+cMonster::eFamily cMonster::GetMobFamily(void) const
+{
+ return FamilyFromType(m_MobType);
+}
+
+
+
+
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
new file mode 100644
index 000000000..29a705d11
--- /dev/null
+++ b/src/Mobs/Monster.h
@@ -0,0 +1,195 @@
+
+#pragma once
+
+#include "../Entities/Pawn.h"
+#include "../Defines.h"
+#include "../BlockID.h"
+#include "../Item.h"
+
+
+
+
+
+class Vector3f;
+class cClientHandle;
+class cWorld;
+
+
+
+
+// tolua_begin
+class cMonster :
+ public cPawn
+{
+ typedef cPawn super;
+public:
+ /// This identifies individual monster type, as well as their network type-ID
+ enum eType
+ {
+ mtInvalidType = -1,
+
+ mtBat = E_META_SPAWN_EGG_BAT,
+ mtBlaze = E_META_SPAWN_EGG_BLAZE,
+ mtCaveSpider = E_META_SPAWN_EGG_CAVE_SPIDER,
+ mtChicken = E_META_SPAWN_EGG_CHICKEN,
+ mtCow = E_META_SPAWN_EGG_COW,
+ mtCreeper = E_META_SPAWN_EGG_CREEPER,
+ mtEnderDragon = E_META_SPAWN_EGG_ENDER_DRAGON,
+ mtEnderman = E_META_SPAWN_EGG_ENDERMAN,
+ mtGhast = E_META_SPAWN_EGG_GHAST,
+ mtGiant = E_META_SPAWN_EGG_GIANT,
+ mtHorse = E_META_SPAWN_EGG_HORSE,
+ mtIronGolem = E_META_SPAWN_EGG_IRON_GOLEM,
+ mtMagmaCube = E_META_SPAWN_EGG_MAGMA_CUBE,
+ mtMooshroom = E_META_SPAWN_EGG_MOOSHROOM,
+ mtOcelot = E_META_SPAWN_EGG_OCELOT,
+ mtPig = E_META_SPAWN_EGG_PIG,
+ mtSheep = E_META_SPAWN_EGG_SHEEP,
+ mtSilverfish = E_META_SPAWN_EGG_SILVERFISH,
+ mtSkeleton = E_META_SPAWN_EGG_SKELETON,
+ mtSlime = E_META_SPAWN_EGG_SLIME,
+ mtSnowGolem = E_META_SPAWN_EGG_SNOW_GOLEM,
+ mtSpider = E_META_SPAWN_EGG_SPIDER,
+ mtSquid = E_META_SPAWN_EGG_SQUID,
+ mtVillager = E_META_SPAWN_EGG_VILLAGER,
+ mtWitch = E_META_SPAWN_EGG_WITCH,
+ mtWither = E_META_SPAWN_EGG_WITHER,
+ mtWolf = E_META_SPAWN_EGG_WOLF,
+ mtZombie = E_META_SPAWN_EGG_ZOMBIE,
+ mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN,
+ } ;
+
+ enum eFamily
+ {
+ mfHostile = 0, // Spider, Zombies ...
+ mfPassive = 1, // Cows, Pigs
+ mfAmbient = 2, // Bats
+ mfWater = 3, // Squid
+
+ mfMaxplusone, // Nothing. Be sure this is the last and the others are in order
+ } ;
+
+ // tolua_end
+
+ enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState;
+ enum MPersonality{PASSIVE,AGGRESSIVE,COWARDLY} m_EMPersonality;
+
+ float m_SightDistance;
+
+ /** Creates the mob object.
+ * If a_ConfigName is not empty, the configuration is loaded using GetMonsterConfig()
+ * a_MobType is the type of the mob (also used in the protocol ( http://wiki.vg/Entities#Mobs , 2012_12_22))
+ * a_SoundHurt and a_SoundDeath are assigned into m_SoundHurt and m_SoundDeath, respectively
+ */
+ cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
+
+ CLASS_PROTODEF(cMonster);
+
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+ virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+
+ virtual void KilledBy(cEntity * a_Killer) override;
+
+ virtual void MoveToPosition(const Vector3f & a_Position);
+ virtual bool ReachedDestination(void);
+
+ // tolua_begin
+ eType GetMobType(void) const {return m_MobType; }
+ eFamily GetMobFamily(void) const;
+ // tolua_end
+
+
+ const char * GetState();
+ void SetState(const AString & str);
+
+ virtual void CheckEventSeePlayer(void);
+ virtual void EventSeePlayer(cEntity * a_Player);
+ virtual cPlayer * FindClosestPlayer(); // non static is easier. also virtual so other mobs can implement their own searching algo
+
+ /// Reads the monster configuration for the specified monster name and assigns it to this object.
+ void GetMonsterConfig(const AString & a_Name);
+
+ virtual void EventLosePlayer(void);
+ virtual void CheckEventLostPlayer(void);
+
+ virtual void InStateIdle (float a_Dt);
+ virtual void InStateChasing (float a_Dt);
+ virtual void InStateEscaping(float a_Dt);
+
+ virtual void Attack(float a_Dt);
+
+ int GetAttackRate(){return (int)m_AttackRate;}
+ void SetAttackRate(int ar);
+ void SetAttackRange(float ar);
+ void SetAttackDamage(float ad);
+ void SetSightDistance(float sd);
+
+ /// Sets whether the mob burns in daylight. Only evaluated at next burn-decision tick
+ void SetBurnsInDaylight(bool a_BurnsInDaylight) { m_BurnsInDaylight = a_BurnsInDaylight; }
+
+ // Overridables to handle ageable mobs
+ virtual bool IsBaby (void) const { return false; }
+ virtual bool IsTame (void) const { return false; }
+ virtual bool IsSitting (void) const { return false; }
+
+ // tolua_begin
+
+ /// Translates MobType enum to a string, empty string if unknown
+ static AString MobTypeToString(eType a_MobType);
+
+ /// Translates MobType string to the enum, mtInvalidType if not recognized
+ static eType StringToMobType(const AString & a_MobTypeName);
+
+ /// Returns the mob family based on the type
+ static eFamily FamilyFromType(eType a_MobType);
+
+ /// Returns the spawn delay (number of game ticks between spawn attempts) for the given mob family
+ static int GetSpawnDelay(cMonster::eFamily a_MobFamily);
+
+ // tolua_end
+
+ /** Creates a new object of the specified mob.
+ a_MobType is the type of the mob to be created
+ Asserts and returns null if mob type is not specified
+ */
+ static cMonster * NewMonsterFromType(eType a_MobType);
+
+protected:
+
+ cEntity * m_Target;
+ float m_AttackRate;
+ float idle_interval;
+
+ Vector3f m_Destination;
+ bool m_bMovingToDestination;
+ bool m_bPassiveAggressive;
+
+ float m_DestinationTime;
+
+ float m_DestroyTimer;
+ float m_Jump;
+
+ eType m_MobType;
+
+ AString m_SoundHurt;
+ AString m_SoundDeath;
+
+ float m_SeePlayerInterval;
+ float m_AttackDamage;
+ float m_AttackRange;
+ float m_AttackInterval;
+
+ bool m_BurnsInDaylight;
+
+ void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0);
+
+ void HandleDaylightBurning(cChunk & a_Chunk);
+
+} ; // tolua_export
+
+
+
+
diff --git a/src/Mobs/Mooshroom.cpp b/src/Mobs/Mooshroom.cpp
new file mode 100644
index 000000000..940e2db44
--- /dev/null
+++ b/src/Mobs/Mooshroom.cpp
@@ -0,0 +1,33 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Mooshroom.h"
+
+
+
+
+
+// TODO: Milk Cow
+
+
+
+
+
+cMooshroom::cMooshroom(void) :
+ super("Mooshroom", mtMooshroom, "mob.cow.hurt", "mob.cow.hurt", 0.9, 1.3)
+{
+}
+
+
+
+
+
+void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_LEATHER);
+ AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF);
+}
+
+
+
+
diff --git a/src/Mobs/Mooshroom.h b/src/Mobs/Mooshroom.h
new file mode 100644
index 000000000..73f6348b6
--- /dev/null
+++ b/src/Mobs/Mooshroom.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cMooshroom :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cMooshroom(void);
+
+ CLASS_PROTODEF(cMooshroom);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Ocelot.h b/src/Mobs/Ocelot.h
new file mode 100644
index 000000000..adb7a1f75
--- /dev/null
+++ b/src/Mobs/Ocelot.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cOcelot :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cOcelot(void) :
+ super("Ocelot", mtOcelot, "mob.cat.hitt", "mob.cat.hitt", 0.6, 0.8)
+ {
+ }
+
+ CLASS_PROTODEF(cOcelot);
+} ;
+
+
+
+
diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp
new file mode 100644
index 000000000..28de65905
--- /dev/null
+++ b/src/Mobs/PassiveAggressiveMonster.cpp
@@ -0,0 +1,38 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "PassiveAggressiveMonster.h"
+
+#include "../Entities/Player.h"
+
+
+
+
+
+cPassiveAggressiveMonster::cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
+ super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height)
+{
+ m_EMPersonality = PASSIVE;
+}
+
+
+
+
+
+void cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ super::DoTakeDamage(a_TDI);
+
+ if ((m_Target != NULL) && (m_Target->IsPlayer()))
+ {
+ cPlayer * Player = (cPlayer *) m_Target;
+ if (Player->GetGameMode() != 1)
+ {
+ m_EMState = CHASING;
+ }
+ }
+}
+
+
+
+
diff --git a/src/Mobs/PassiveAggressiveMonster.h b/src/Mobs/PassiveAggressiveMonster.h
new file mode 100644
index 000000000..2c5ef30b1
--- /dev/null
+++ b/src/Mobs/PassiveAggressiveMonster.h
@@ -0,0 +1,23 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cPassiveAggressiveMonster :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
+
+ virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp
new file mode 100644
index 000000000..91ceb5a53
--- /dev/null
+++ b/src/Mobs/PassiveMonster.cpp
@@ -0,0 +1,59 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "PassiveMonster.h"
+#include "../MersenneTwister.h"
+#include "../World.h"
+
+
+
+
+
+cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
+ super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height)
+{
+ m_EMPersonality = PASSIVE;
+}
+
+
+
+
+
+void cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ super::DoTakeDamage(a_TDI);
+ if ((a_TDI.Attacker != this) && (a_TDI.Attacker != NULL))
+ {
+ m_EMState = ESCAPING;
+ }
+}
+
+
+
+
+
+void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ m_SeePlayerInterval += a_Dt;
+
+ if (m_SeePlayerInterval > 1) // Check every second
+ {
+ int rem = m_World->GetTickRandomNumber(3) + 1; // Check most of the time but miss occasionally
+
+ m_SeePlayerInterval = 0.0;
+ if (rem >= 2)
+ {
+ if (m_EMState == ESCAPING)
+ {
+ CheckEventLostPlayer();
+ }
+ }
+ }
+}
+
+
+
+
+
diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h
new file mode 100644
index 000000000..14a6be6b1
--- /dev/null
+++ b/src/Mobs/PassiveMonster.h
@@ -0,0 +1,27 @@
+
+#pragma once
+
+#include "Monster.h"
+
+
+
+
+
+class cPassiveMonster :
+ public cMonster
+{
+ typedef cMonster super;
+
+public:
+ cPassiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
+
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+ /// When hit by someone, run away
+ virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp
new file mode 100644
index 000000000..0871a38a9
--- /dev/null
+++ b/src/Mobs/Pig.cpp
@@ -0,0 +1,77 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Pig.h"
+#include "../Entities/Player.h"
+#include "../World.h"
+
+
+
+
+
+cPig::cPig(void) :
+ super("Pig", mtPig, "mob.pig.say", "mob.pig.death", 0.9, 0.9),
+ m_bIsSaddled(false)
+{
+}
+
+
+
+
+
+void cPig::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 1, 3, IsOnFire() ? E_ITEM_COOKED_PORKCHOP : E_ITEM_RAW_PORKCHOP);
+ if (m_bIsSaddled)
+ {
+ a_Drops.push_back(cItem(E_ITEM_SADDLE, 1));
+ }
+}
+
+
+
+
+
+void cPig::OnRightClicked(cPlayer & a_Player)
+{
+ if (m_bIsSaddled)
+ {
+ if (m_Attachee != NULL)
+ {
+ if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
+ {
+ // This player is already sitting in, they want out.
+ a_Player.Detach();
+ return;
+ }
+
+ if (m_Attachee->IsPlayer())
+ {
+ // Another player is already sitting in here, cannot attach
+ return;
+ }
+
+ // Detach whatever is sitting in this pig now:
+ m_Attachee->Detach();
+ }
+
+ // Attach the player to this pig
+ a_Player.AttachTo(this);
+ }
+ else if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE)
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+
+ // Set saddle state & broadcast metadata
+ m_bIsSaddled = true;
+ m_World->BroadcastEntityMetadata(*this);
+ }
+}
+
+
+
+
+
diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h
new file mode 100644
index 000000000..4fd0d8db8
--- /dev/null
+++ b/src/Mobs/Pig.h
@@ -0,0 +1,32 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cPig :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cPig(void);
+
+ CLASS_PROTODEF(cPig);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+ bool IsSaddled(void) const { return m_bIsSaddled; }
+
+private:
+
+ bool m_bIsSaddled;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp
new file mode 100644
index 000000000..bda4ccff8
--- /dev/null
+++ b/src/Mobs/Sheep.cpp
@@ -0,0 +1,62 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Sheep.h"
+#include "../BlockID.h"
+#include "../Entities/Player.h"
+#include "../World.h"
+
+
+
+
+
+cSheep::cSheep(int a_Color) :
+ super("Sheep", mtSheep, "mob.sheep.say", "mob.sheep.say", 0.6, 1.3),
+ m_IsSheared(false),
+ m_WoolColor(a_Color)
+{
+}
+
+
+
+
+
+void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ if (!m_IsSheared)
+ {
+ a_Drops.push_back(cItem(E_BLOCK_WOOL, 1, m_WoolColor));
+ }
+}
+
+
+
+
+
+void cSheep::OnRightClicked(cPlayer & a_Player)
+{
+ if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_SHEARS) && (!m_IsSheared))
+ {
+ m_IsSheared = true;
+ m_World->BroadcastEntityMetadata(*this);
+
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.UseEquippedItem();
+ }
+
+ cItems Drops;
+ int NumDrops = m_World->GetTickRandomNumber(2) + 1;
+ Drops.push_back(cItem(E_BLOCK_WOOL, NumDrops, m_WoolColor));
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10);
+ }
+ if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_DYE) && (m_WoolColor != 15 - a_Player.GetEquippedItem().m_ItemDamage))
+ {
+ m_WoolColor = 15 - a_Player.GetEquippedItem().m_ItemDamage;
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ m_World->BroadcastEntityMetadata(*this);
+ }
+}
diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h
new file mode 100644
index 000000000..8293a2c05
--- /dev/null
+++ b/src/Mobs/Sheep.h
@@ -0,0 +1,34 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cSheep :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cSheep(int a_Color);
+
+ CLASS_PROTODEF(cSheep);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+ bool IsSheared(void) const { return m_IsSheared; }
+ int GetFurColor(void) const { return m_WoolColor; }
+
+private:
+
+ bool m_IsSheared;
+ int m_WoolColor;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Silverfish.h b/src/Mobs/Silverfish.h
new file mode 100644
index 000000000..a6e11c49d
--- /dev/null
+++ b/src/Mobs/Silverfish.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cSilverfish :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cSilverfish(void) :
+ super("Silverfish", mtSilverfish, "mob.silverfish.hit", "mob.silverfish.kill", 0.3, 0.7)
+ {
+ }
+
+ CLASS_PROTODEF(cSilverfish);
+} ;
+
+
+
+
diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp
new file mode 100644
index 000000000..509c2191e
--- /dev/null
+++ b/src/Mobs/Skeleton.cpp
@@ -0,0 +1,70 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Skeleton.h"
+#include "../World.h"
+
+
+
+
+cSkeleton::cSkeleton(bool IsWither) :
+ super("Skeleton", mtSkeleton, "mob.skeleton.hurt", "mob.skeleton.death", 0.6, 1.8),
+ m_bIsWither(IsWither)
+{
+ SetBurnsInDaylight(true);
+}
+
+
+
+
+
+void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_ARROW);
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_BONE);
+}
+
+
+
+
+
+void cSkeleton::MoveToPosition(const Vector3f & a_Position)
+{
+ m_Destination = a_Position;
+
+ // If the destination is in the sun and if it is not night AND the skeleton isn't on fire then block the movement.
+ if (!IsOnFire() && m_World->GetTimeOfDay() < 13187 && m_World->GetBlockSkyLight((int) a_Position.x, (int) a_Position.y, (int) a_Position.z) == 15)
+ {
+ m_bMovingToDestination = false;
+ return;
+ }
+ m_bMovingToDestination = true;
+}
+
+
+
+
+
+void cSkeleton::Attack(float a_Dt)
+{
+ m_AttackInterval += a_Dt * m_AttackRate;
+
+ if (m_Target != NULL && m_AttackInterval > 3.0)
+ {
+ // Setting this higher gives us more wiggle room for attackrate
+ Vector3d Speed = GetLookVector() * 20;
+ Speed.y = Speed.y + 1;
+ cArrowEntity * Arrow = new cArrowEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed);
+ if (Arrow == NULL)
+ {
+ return;
+ }
+ if (!Arrow->Initialize(m_World))
+ {
+ delete Arrow;
+ return;
+ }
+ m_World->BroadcastSpawnEntity(*Arrow);
+ m_AttackInterval = 0.0;
+ }
+} \ No newline at end of file
diff --git a/src/Mobs/Skeleton.h b/src/Mobs/Skeleton.h
new file mode 100644
index 000000000..8f31b42e1
--- /dev/null
+++ b/src/Mobs/Skeleton.h
@@ -0,0 +1,33 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cSkeleton :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cSkeleton(bool IsWither);
+
+ CLASS_PROTODEF(cSkeleton);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void MoveToPosition(const Vector3f & a_Position) override;
+ virtual void Attack(float a_Dt) override;
+ bool IsWither(void) const { return m_bIsWither; };
+
+private:
+
+ bool m_bIsWither;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp
new file mode 100644
index 000000000..19f376c21
--- /dev/null
+++ b/src/Mobs/Slime.cpp
@@ -0,0 +1,29 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Slime.h"
+
+
+
+
+
+/// Creates a slime of the specified size; size is 1 .. 3, with 1 being the smallest
+cSlime::cSlime(int a_Size) :
+ super("Slime", mtSlime, "mob.slime.attack", "mob.slime.attack", 0.6 * a_Size, 0.6 * a_Size),
+ m_Size(a_Size)
+{
+}
+
+
+
+
+
+void cSlime::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ // TODO: only when tiny
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_SLIMEBALL);
+}
+
+
+
+
diff --git a/src/Mobs/Slime.h b/src/Mobs/Slime.h
new file mode 100644
index 000000000..782c3113f
--- /dev/null
+++ b/src/Mobs/Slime.h
@@ -0,0 +1,32 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cSlime :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ /// Creates a slime of the specified size; size is 1 .. 3, with 1 being the smallest
+ cSlime(int a_Size);
+
+ CLASS_PROTODEF(cSlime);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ int GetSize(void) const { return m_Size; }
+
+protected:
+
+ /// Size of the slime, 1 .. 3, with 1 being the smallest
+ int m_Size;
+} ;
+
+
+
+
diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp
new file mode 100644
index 000000000..9e199f87e
--- /dev/null
+++ b/src/Mobs/SnowGolem.cpp
@@ -0,0 +1,26 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "SnowGolem.h"
+
+
+
+
+
+cSnowGolem::cSnowGolem(void) :
+ super("SnowGolem", mtSnowGolem, "", "", 0.4, 1.8)
+{
+}
+
+
+
+
+
+void cSnowGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 5, E_ITEM_SNOWBALL);
+}
+
+
+
+
diff --git a/src/Mobs/SnowGolem.h b/src/Mobs/SnowGolem.h
new file mode 100644
index 000000000..d1344adfd
--- /dev/null
+++ b/src/Mobs/SnowGolem.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cSnowGolem :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cSnowGolem(void);
+
+ CLASS_PROTODEF(cSnowGolem);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Spider.cpp b/src/Mobs/Spider.cpp
new file mode 100644
index 000000000..b19a5dcef
--- /dev/null
+++ b/src/Mobs/Spider.cpp
@@ -0,0 +1,27 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Spider.h"
+
+
+
+
+
+cSpider::cSpider(void) :
+ super("Spider", mtSpider, "mob.spider.say", "mob.spider.death", 1.4, 0.9)
+{
+}
+
+
+
+
+
+void cSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_STRING);
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_SPIDER_EYE);
+}
+
+
+
+
diff --git a/src/Mobs/Spider.h b/src/Mobs/Spider.h
new file mode 100644
index 000000000..51e65d028
--- /dev/null
+++ b/src/Mobs/Spider.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cSpider :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cSpider(void);
+
+ CLASS_PROTODEF(cSpider);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Squid.cpp b/src/Mobs/Squid.cpp
new file mode 100644
index 000000000..a311108ae
--- /dev/null
+++ b/src/Mobs/Squid.cpp
@@ -0,0 +1,56 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Squid.h"
+#include "../Vector3d.h"
+#include "../Chunk.h"
+
+
+
+
+
+cSquid::cSquid(void) :
+ super("Squid", mtSquid, "", "", 0.95, 0.95)
+{
+}
+
+
+
+
+
+void cSquid::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ // Drops 0-3 Ink Sacs
+ AddRandomDropItem(a_Drops, 0, 3, E_ITEM_DYE, E_META_DYE_BLACK);
+}
+
+
+
+
+
+void cSquid::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ // We must first process current location, and only then tick, otherwise we risk processing a location in a chunk
+ // that is not where the entity currently resides (FS #411)
+
+ Vector3d Pos = GetPosition();
+
+ // TODO: Not a real behavior, but cool :D
+ int RelY = (int)floor(Pos.y);
+ if ((RelY < 0) || (RelY >= cChunkDef::Height))
+ {
+ return;
+ }
+ int RelX = (int)floor(Pos.x) - a_Chunk.GetPosX() * cChunkDef::Width;
+ int RelZ = (int)floor(Pos.z) - a_Chunk.GetPosZ() * cChunkDef::Width;
+ if (!IsBlockWater(a_Chunk.GetBlock(RelX, RelY, RelZ)) && !IsOnFire())
+ {
+ // Burn for 10 ticks, then decide again
+ StartBurning(10);
+ }
+
+ super::Tick(a_Dt, a_Chunk);
+}
+
+
+
diff --git a/src/Mobs/Squid.h b/src/Mobs/Squid.h
new file mode 100644
index 000000000..ad299b95c
--- /dev/null
+++ b/src/Mobs/Squid.h
@@ -0,0 +1,28 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cSquid :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+ cSquid();
+
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+ CLASS_PROTODEF(cSquid);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp
new file mode 100644
index 000000000..7f89fb6cc
--- /dev/null
+++ b/src/Mobs/Villager.cpp
@@ -0,0 +1,35 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Villager.h"
+#include "../World.h"
+
+
+
+
+
+cVillager::cVillager(eVillagerType VillagerType) :
+ super("Villager", mtVillager, "", "", 0.6, 1.8),
+ m_Type(VillagerType)
+{
+}
+
+
+
+
+
+void cVillager::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ super::DoTakeDamage(a_TDI);
+ if (a_TDI.Attacker->IsPlayer())
+ {
+ if (m_World->GetTickRandomNumber(5) == 3)
+ {
+ m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_VILLAGER_ANGRY);
+ }
+ }
+}
+
+
+
+
diff --git a/src/Mobs/Villager.h b/src/Mobs/Villager.h
new file mode 100644
index 000000000..4cd9aaa8e
--- /dev/null
+++ b/src/Mobs/Villager.h
@@ -0,0 +1,43 @@
+
+#pragma once
+
+#include "PassiveMonster.h"
+
+
+
+
+
+class cVillager :
+ public cPassiveMonster
+{
+ typedef cPassiveMonster super;
+
+public:
+
+ enum eVillagerType
+ {
+ vtFarmer = 0,
+ vtLibrarian = 1,
+ vtPriest = 2,
+ vtBlacksmith = 3,
+ vtButcher = 4,
+ vtGeneric = 5,
+ vtMax
+ } ;
+
+ cVillager(eVillagerType VillagerType);
+
+ CLASS_PROTODEF(cVillager);
+
+ virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+ int GetVilType(void) const { return m_Type; }
+
+private:
+
+ int m_Type;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Witch.cpp b/src/Mobs/Witch.cpp
new file mode 100644
index 000000000..25d27041f
--- /dev/null
+++ b/src/Mobs/Witch.cpp
@@ -0,0 +1,32 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Witch.h"
+
+
+
+
+
+cWitch::cWitch(void) :
+ super("Witch", mtWitch, "", "", 0.6, 1.8)
+{
+}
+
+
+
+
+
+void cWitch::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLASS_BOTTLE);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GLOWSTONE_DUST);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_GUNPOWDER);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_REDSTONE_DUST);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SPIDER_EYE);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_STICK);
+ AddRandomDropItem(a_Drops, 0, 6, E_ITEM_SUGAR);
+}
+
+
+
+
diff --git a/src/Mobs/Witch.h b/src/Mobs/Witch.h
new file mode 100644
index 000000000..4e637beea
--- /dev/null
+++ b/src/Mobs/Witch.h
@@ -0,0 +1,27 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cWitch :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cWitch();
+
+ CLASS_PROTODEF(cWitch);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+
+ bool IsAngry(void) const {return ((m_EMState == ATTACKING) || (m_EMState == CHASING)); }
+} ;
+
+
+
+
diff --git a/src/Mobs/Wither.cpp b/src/Mobs/Wither.cpp
new file mode 100644
index 000000000..c46e0beab
--- /dev/null
+++ b/src/Mobs/Wither.cpp
@@ -0,0 +1,26 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Wither.h"
+
+
+
+
+
+cWither::cWither(void) :
+ super("Wither", mtWither, "mob.wither.hurt", "mob.wither.death", 0.9, 4.0)
+{
+}
+
+
+
+
+
+void cWither::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 1, 1, E_ITEM_NETHER_STAR);
+}
+
+
+
+
diff --git a/src/Mobs/Wither.h b/src/Mobs/Wither.h
new file mode 100644
index 000000000..56effc6bb
--- /dev/null
+++ b/src/Mobs/Wither.h
@@ -0,0 +1,25 @@
+
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cWither :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cWither(void);
+
+ CLASS_PROTODEF(cWither);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+} ;
+
+
+
+
diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp
new file mode 100644
index 000000000..c86250142
--- /dev/null
+++ b/src/Mobs/Wolf.cpp
@@ -0,0 +1,189 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Wolf.h"
+#include "../World.h"
+#include "../Entities/Player.h"
+
+
+
+
+
+cWolf::cWolf(void) :
+ super("Wolf", mtWolf, "mob.wolf.hurt", "mob.wolf.death", 0.6, 0.8),
+ m_IsAngry(false),
+ m_IsTame(false),
+ m_IsSitting(false),
+ m_IsBegging(false),
+ m_OwnerName(""),
+ m_CollarColor(14)
+{
+}
+
+
+
+
+
+void cWolf::DoTakeDamage(TakeDamageInfo & a_TDI)
+{
+ super::DoTakeDamage(a_TDI);
+ if (!m_IsTame)
+ {
+ m_IsAngry = true;
+ }
+ m_World->BroadcastEntityMetadata(*this); // Broadcast health and possibly angry face
+}
+
+
+
+
+
+void cWolf::OnRightClicked(cPlayer & a_Player)
+{
+ if (!IsTame() && !IsAngry())
+ {
+ if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_BONE)
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+
+ if (m_World->GetTickRandomNumber(7) == 0)
+ {
+ SetMaxHealth(20);
+ SetIsTame(true);
+ SetOwner(a_Player.GetName());
+ m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMED);
+ }
+ else
+ {
+ m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMING);
+ }
+ }
+ }
+ else if (IsTame())
+ {
+ if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog?
+ {
+ if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_DYE)
+ {
+ SetCollarColor(15 - a_Player.GetEquippedItem().m_ItemDamage);
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ }
+ else if (IsSitting())
+ {
+ SetIsSitting(false);
+ }
+ else
+ {
+ SetIsSitting(true);
+ }
+ }
+ }
+
+ m_World->BroadcastEntityMetadata(*this);
+}
+
+
+
+
+
+void cWolf::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ if (!IsAngry())
+ {
+ cMonster::Tick(a_Dt, a_Chunk);
+ }
+ else
+ {
+ super::Tick(a_Dt, a_Chunk);
+ }
+
+ if (IsSitting())
+ {
+ m_bMovingToDestination = false;
+ }
+
+ cPlayer * a_Closest_Player = FindClosestPlayer();
+ if (a_Closest_Player != NULL)
+ {
+ switch (a_Closest_Player->GetEquippedItem().m_ItemType)
+ {
+ case E_ITEM_BONE:
+ case E_ITEM_RAW_BEEF:
+ case E_ITEM_STEAK:
+ case E_ITEM_RAW_CHICKEN:
+ case E_ITEM_COOKED_CHICKEN:
+ case E_ITEM_ROTTEN_FLESH:
+ {
+ if (!IsBegging())
+ {
+ SetIsBegging(true);
+ m_World->BroadcastEntityMetadata(*this);
+ }
+ Vector3f a_NewDestination = a_Closest_Player->GetPosition();
+ a_NewDestination.y = a_NewDestination.y + 1; // Look at the head of the player, not his feet.
+ m_Destination = Vector3f(a_NewDestination);
+ m_bMovingToDestination = false;
+ break;
+ }
+ default:
+ {
+ if (IsBegging())
+ {
+ SetIsBegging(false);
+ m_World->BroadcastEntityMetadata(*this);
+ }
+ }
+ }
+ }
+
+ if (IsTame())
+ {
+ TickFollowPlayer();
+ }
+}
+
+
+
+
+
+void cWolf::TickFollowPlayer()
+{
+ class cCallback :
+ public cPlayerListCallback
+ {
+ virtual bool Item(cPlayer * a_Player) override
+ {
+ OwnerPos = a_Player->GetPosition();
+ return false;
+ }
+ public:
+ Vector3f OwnerPos;
+ } Callback;
+ if (m_World->DoWithPlayer(m_OwnerName, Callback))
+ {
+ // The player is present in the world, follow them:
+ double Distance = (Callback.OwnerPos - GetPosition()).Length();
+ if (Distance < 3)
+ {
+ m_bMovingToDestination = false;
+ }
+ else if ((Distance > 30) && (!IsSitting()))
+ {
+ TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z);
+ }
+ else
+ {
+ m_Destination = Callback.OwnerPos;
+ }
+ }
+}
+
+
+
+
diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h
new file mode 100644
index 000000000..040e2cf7a
--- /dev/null
+++ b/src/Mobs/Wolf.h
@@ -0,0 +1,54 @@
+
+#pragma once
+
+#include "PassiveAggressiveMonster.h"
+#include "../Entities/Entity.h"
+
+
+
+
+
+class cWolf :
+ public cPassiveAggressiveMonster
+{
+ typedef cPassiveAggressiveMonster super;
+
+public:
+ cWolf(void);
+
+ CLASS_PROTODEF(cWolf);
+
+ virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void TickFollowPlayer();
+
+ // Get functions
+ bool IsSitting (void) const { return m_IsSitting; }
+ bool IsTame (void) const { return m_IsTame; }
+ bool IsBegging (void) const { return m_IsBegging; }
+ bool IsAngry (void) const { return m_IsAngry; }
+ AString GetOwner (void) const { return m_OwnerName; }
+ int GetCollarColor(void) const { return m_CollarColor; }
+
+ // Set functions
+ void SetIsSitting (bool a_IsSitting) { m_IsSitting = a_IsSitting; }
+ void SetIsTame (bool a_IsTame) { m_IsTame = a_IsTame; }
+ void SetIsBegging (bool a_IsBegging) { m_IsBegging = a_IsBegging; }
+ void SetIsAngry (bool a_IsAngry) { m_IsAngry = a_IsAngry; }
+ void SetOwner (AString a_NewOwner) { m_OwnerName = a_NewOwner; }
+ void SetCollarColor(int a_CollarColor) { m_CollarColor = a_CollarColor; }
+
+protected:
+
+ bool m_IsSitting;
+ bool m_IsTame;
+ bool m_IsBegging;
+ bool m_IsAngry;
+ AString m_OwnerName;
+ int m_CollarColor;
+} ;
+
+
+
+
diff --git a/src/Mobs/Zombie.cpp b/src/Mobs/Zombie.cpp
new file mode 100644
index 000000000..a485d2b55
--- /dev/null
+++ b/src/Mobs/Zombie.cpp
@@ -0,0 +1,47 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Zombie.h"
+#include "../World.h"
+#include "../LineBlockTracer.h"
+
+
+
+
+cZombie::cZombie(bool IsVillagerZombie) :
+ super("Zombie", mtZombie, "mob.zombie.hurt", "mob.zombie.death", 0.6, 1.8),
+ m_bIsConverting(false),
+ m_bIsVillagerZombie(IsVillagerZombie)
+{
+ SetBurnsInDaylight(true);
+}
+
+
+
+
+
+void cZombie::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 2, E_ITEM_ROTTEN_FLESH);
+
+ // TODO: Rare drops
+}
+
+
+
+
+
+void cZombie::MoveToPosition(const Vector3f & a_Position)
+{
+ m_Destination = a_Position;
+
+ // If the destination is in the sun and if it is not night AND the skeleton isn't on fire then block the movement.
+ if ((m_World->GetBlockSkyLight((int) a_Position.x, (int) a_Position.y, (int) a_Position.z) == 15) && (m_World->GetTimeOfDay() < 13187) && !IsOnFire())
+ {
+ m_bMovingToDestination = false;
+ return;
+ }
+ m_bMovingToDestination = true;
+}
+
+
diff --git a/src/Mobs/Zombie.h b/src/Mobs/Zombie.h
new file mode 100644
index 000000000..7e14fe42f
--- /dev/null
+++ b/src/Mobs/Zombie.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "AggressiveMonster.h"
+
+
+
+
+
+class cZombie :
+ public cAggressiveMonster
+{
+ typedef cAggressiveMonster super;
+
+public:
+ cZombie(bool IsVillagerZombie);
+
+ CLASS_PROTODEF(cZombie);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void MoveToPosition(const Vector3f & a_Position) override;
+
+ bool IsVillagerZombie(void) const {return m_bIsVillagerZombie; }
+ bool IsConverting(void) const {return m_bIsConverting; }
+
+private:
+
+ bool m_bIsVillagerZombie, m_bIsConverting;
+
+} ;
+
+
+
+
diff --git a/src/Mobs/Zombiepigman.cpp b/src/Mobs/Zombiepigman.cpp
new file mode 100644
index 000000000..6ac89ed4c
--- /dev/null
+++ b/src/Mobs/Zombiepigman.cpp
@@ -0,0 +1,45 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "Zombiepigman.h"
+#include "../World.h"
+
+
+
+
+
+cZombiePigman::cZombiePigman(void) :
+ super("ZombiePigman", mtZombiePigman, "mob.zombiepig.zpighurt", "mob.zombiepig.zpigdeath", 0.6, 1.8)
+{
+}
+
+
+
+
+
+void cZombiePigman::GetDrops(cItems & a_Drops, cEntity * a_Killer)
+{
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_ROTTEN_FLESH);
+ AddRandomDropItem(a_Drops, 0, 1, E_ITEM_GOLD_NUGGET);
+
+ // TODO: Rare drops
+}
+
+
+
+
+
+void cZombiePigman::KilledBy(cEntity * a_Killer)
+{
+ super::KilledBy(a_Killer);
+
+ if ((a_Killer != NULL) && (a_Killer->IsPlayer()))
+ {
+ // TODO: Anger all nearby zombie pigmen
+ // TODO: In vanilla, if one player angers ZPs, do they attack any nearby player, or only that one attacker?
+ }
+}
+
+
+
+
diff --git a/src/Mobs/Zombiepigman.h b/src/Mobs/Zombiepigman.h
new file mode 100644
index 000000000..67991d56a
--- /dev/null
+++ b/src/Mobs/Zombiepigman.h
@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "PassiveAggressiveMonster.h"
+
+
+
+
+
+class cZombiePigman :
+ public cPassiveAggressiveMonster
+{
+ typedef cPassiveAggressiveMonster super;
+
+public:
+ cZombiePigman(void);
+
+ CLASS_PROTODEF(cZombiePigman);
+
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ virtual void KilledBy(cEntity * a_Killer) override;
+} ;
+
+
+
+