diff options
author | Tiger Wang <ziwei.tiger@hotmail.co.uk> | 2013-11-27 22:35:13 +0100 |
---|---|---|
committer | Tiger Wang <ziwei.tiger@hotmail.co.uk> | 2013-11-27 22:35:13 +0100 |
commit | a6630d32394120a78af56bc612fa3c3449283248 (patch) | |
tree | 2c791266b0f213cd56961299da8d2258b8f85d8e /src/Mobs | |
parent | Fixed spawn point being generally in an ocean (diff) | |
parent | Voronoi-related biomegens use the new cVoronoiMap class. (diff) | |
download | cuberite-a6630d32394120a78af56bc612fa3c3449283248.tar cuberite-a6630d32394120a78af56bc612fa3c3449283248.tar.gz cuberite-a6630d32394120a78af56bc612fa3c3449283248.tar.bz2 cuberite-a6630d32394120a78af56bc612fa3c3449283248.tar.lz cuberite-a6630d32394120a78af56bc612fa3c3449283248.tar.xz cuberite-a6630d32394120a78af56bc612fa3c3449283248.tar.zst cuberite-a6630d32394120a78af56bc612fa3c3449283248.zip |
Diffstat (limited to 'src/Mobs')
65 files changed, 3543 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..bbcad46f0 --- /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) : + super("Giant", mtGiant, "mob.zombie.hurt", "mob.zombie.death", 3.6, 10.8) +{ + +} + + + + + +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..091623c8a --- /dev/null +++ b/src/Mobs/Monster.cpp @@ -0,0 +1,813 @@ + +#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 "../Entities/ExpOrb.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); + } + int Reward; + switch (m_MobType) + { + // Animals + case cMonster::mtChicken: + case cMonster::mtCow: + case cMonster::mtHorse: + case cMonster::mtPig: + case cMonster::mtSheep: + case cMonster::mtSquid: + case cMonster::mtMooshroom: + case cMonster::mtOcelot: + case cMonster::mtWolf: + { + Reward = m_World->GetTickRandomNumber(2) + 1; + } + + // Monsters + case cMonster::mtCaveSpider: + case cMonster::mtCreeper: + case cMonster::mtEnderman: + case cMonster::mtGhast: + case cMonster::mtSilverfish: + case cMonster::mtSkeleton: + case cMonster::mtSpider: + case cMonster::mtWitch: + case cMonster::mtZombie: + case cMonster::mtZombiePigman: + case cMonster::mtSlime: + case cMonster::mtMagmaCube: + { + Reward = 6 + (m_World->GetTickRandomNumber(2)); + } + case cMonster::mtBlaze: + { + Reward = 10; + } + + // Bosses + case cMonster::mtEnderDragon: + { + Reward = 12000; + } + case cMonster::mtWither: + { + Reward = 50; + } + + default: + { + Reward = 0; + } + } + m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Reward); + 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; +} ; + + + + |