diff options
Diffstat (limited to 'src')
33 files changed, 1876 insertions, 1956 deletions
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index 2cb59237c..1cb32fc70 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -13,10 +13,10 @@ cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : - super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height), m_BehaviorAggressive(this) + super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height), m_BehaviorAggressive(this) { - m_EMPersonality = AGGRESSIVE; - ASSERT(GetBehaviorChaser() != nullptr); + m_EMPersonality = AGGRESSIVE; + ASSERT(GetBehaviorChaser() != nullptr); } @@ -26,26 +26,26 @@ cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eMonsterTyp void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { - super::Tick(a_Dt, a_Chunk); - - cBehaviorChaser * BehaviorChaser = GetBehaviorChaser(); - cBehaviorWanderer * BehaviorWanderer = GetBehaviorWanderer(); - - for (;;) - { - m_BehaviorAggressive.ActiveTick(); - if (BehaviorChaser->ActiveTick()) - { - break; - } - if ((BehaviorWanderer != nullptr) && BehaviorWanderer->ActiveTick(a_Dt, a_Chunk)) - { - break; - } - - ASSERT(!"Not a single Behavior took control, this is not normal. "); - break; - } - - BehaviorChaser->Tick(); + super::Tick(a_Dt, a_Chunk); + + cBehaviorChaser * BehaviorChaser = GetBehaviorChaser(); + cBehaviorWanderer * BehaviorWanderer = GetBehaviorWanderer(); + + for (;;) + { + m_BehaviorAggressive.Tick(); + if (BehaviorChaser->ActiveTick()) + { + break; + } + if ((BehaviorWanderer != nullptr) && BehaviorWanderer->ActiveTick(a_Dt, a_Chunk)) + { + break; + } + + ASSERT(!"Not a single Behavior took control, this is not normal. "); + break; + } + + BehaviorChaser->Tick(); } diff --git a/src/Mobs/Behaviors/Behavior.cpp b/src/Mobs/Behaviors/Behavior.cpp new file mode 100644 index 000000000..e7c8a2c8b --- /dev/null +++ b/src/Mobs/Behaviors/Behavior.cpp @@ -0,0 +1,80 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Behavior.h" + + + + + +bool cBehavior::IsControlDesired() +{ + return false; +} + + + + + +bool cBehavior::ControlStarting() +{ + return true; +} + + + + + +bool cBehavior::ControlEnding() +{ + return true; +} + + + + + +void cBehavior::Tick() +{ + LOGD("ERROR: Called a TICK on a behavior that doesn't have one."); + ASSERT(1 == 0); +} + + + + + +void cBehavior::PostTick() +{ + LOGD("ERROR: Called a PostTick on a behavior that doesn't have one."); + ASSERT(1 == 0); +} + + + + + +void cBehavior::PreTick() +{ + LOGD("ERROR: Called a PreTick on a behavior that doesn't have one."); + ASSERT(1 == 0); +} + + + + + +void cBehavior::onRightClicked() +{ + LOGD("ERROR: Called onRightClicked on a behavior that doesn't have one."); + ASSERT(1 == 0); +} + + + + + +void cBehavior::Destroyed() +{ + LOGD("ERROR: Called Destroyed on a behavior that doesn't have one."); + ASSERT(1 == 0); +} diff --git a/src/Mobs/Behaviors/Behavior.h b/src/Mobs/Behaviors/Behavior.h new file mode 100644 index 000000000..c3e5cad8e --- /dev/null +++ b/src/Mobs/Behaviors/Behavior.h @@ -0,0 +1,13 @@ +class cBehavior +{ +public: + virtual bool IsControlDesired(); + virtual bool ControlStarting(); + virtual bool ControlEnding(); + virtual void Tick(); + virtual void PostTick(); + virtual void PreTick(); + virtual void onRightClicked(); + virtual void Destroyed(); + virtual ~cBehavior() = 0; +}; diff --git a/src/Mobs/Behaviors/BehaviorAggressive.cpp b/src/Mobs/Behaviors/BehaviorAggressive.cpp index 2113cb1e6..b2c7cc9ef 100644 --- a/src/Mobs/Behaviors/BehaviorAggressive.cpp +++ b/src/Mobs/Behaviors/BehaviorAggressive.cpp @@ -10,23 +10,22 @@ cBehaviorAggressive::cBehaviorAggressive(cMonster * a_Parent) : m_Parent(a_Parent) { - ASSERT(m_Parent != nullptr); - m_ParentChaser = m_Parent->GetBehaviorChaser(); - ASSERT(m_ParentChaser != nullptr); + ASSERT(m_Parent != nullptr); + m_ParentChaser = m_Parent->GetBehaviorChaser(); + ASSERT(m_ParentChaser != nullptr); } -bool cBehaviorAggressive::ActiveTick() +void cBehaviorAggressive::PreTick() { - // Target something new if we have no target - if (m_ParentChaser->GetTarget() == nullptr) - { - m_ParentChaser->SetTarget(FindNewTarget()); - } - return false; + // Target something new if we have no target + if (m_ParentChaser->GetTarget() == nullptr) + { + m_ParentChaser->SetTarget(FindNewTarget()); + } } @@ -35,7 +34,7 @@ bool cBehaviorAggressive::ActiveTick() void cBehaviorAggressive::Destroyed() { - m_Target = nullptr; + m_ParentChaser->SetTarget(nullptr); } @@ -44,6 +43,6 @@ void cBehaviorAggressive::Destroyed() cPawn * cBehaviorAggressive::FindNewTarget() { - cPlayer * Closest = m_Parent->GetNearestPlayer(); - return Closest; // May be null + cPlayer * Closest = m_Parent->GetNearestPlayer(); + return Closest; // May be null } diff --git a/src/Mobs/Behaviors/BehaviorAggressive.h b/src/Mobs/Behaviors/BehaviorAggressive.h index ca2ad577b..14dfc1ab0 100644 --- a/src/Mobs/Behaviors/BehaviorAggressive.h +++ b/src/Mobs/Behaviors/BehaviorAggressive.h @@ -1,31 +1,40 @@ -// The mob is agressive toward specific mobtypes, or toward the player. -// This Behavior has a dependency on BehaviorChaser. #pragma once + + +class cBehaviorAggressive; + +#include "Behavior.h" + class cPawn; class cMonster; class cBehaviorChaser; -class cBehaviorAggressive + + + + +/** The mob is agressive toward specific mobtypes, or toward the player. +This Behavior has a dependency on BehaviorChaser. */ +class cBehaviorAggressive : public cBehavior { public: - cBehaviorAggressive(cMonster * a_Parent); + cBehaviorAggressive(cMonster * a_Parent); - // cBehaviorAggressive(cMonster * a_Parent, bool a_HatesPlayer); - // TODO agression toward specific players, and specific mobtypes, etc - // Agression under specific conditions (nighttime, etc) + // cBehaviorAggressive(cMonster * a_Parent, bool a_HatesPlayer); + // TODO agression toward specific players, and specific mobtypes, etc + // Agression under specific conditions (nighttime, etc) - // Functions our host Monster should invoke: - bool ActiveTick(); - void Destroyed(); + // Functions our host Monster should invoke: + void PreTick() override; private: - cPawn * FindNewTarget(); + cPawn * FindNewTarget(); - // Our parent - cMonster * m_Parent; - cBehaviorChaser * m_ParentChaser; + // Our parent + cMonster * m_Parent; + cBehaviorChaser * m_ParentChaser; - // The mob we want to attack - cPawn * m_Target; + // The mob we want to attack + cPawn * m_Target; }; diff --git a/src/Mobs/Behaviors/BehaviorBreeder.cpp b/src/Mobs/Behaviors/BehaviorBreeder.cpp index 8ba991989..34f5142e0 100644 --- a/src/Mobs/Behaviors/BehaviorBreeder.cpp +++ b/src/Mobs/Behaviors/BehaviorBreeder.cpp @@ -10,153 +10,163 @@ #include "../../BoundingBox.h" cBehaviorBreeder::cBehaviorBreeder(cMonster * a_Parent) : - m_Parent(a_Parent), - m_LovePartner(nullptr), - m_LoveTimer(0), - m_LoveCooldown(0), - m_MatingTimer(0) + m_Parent(a_Parent), + m_LovePartner(nullptr), + m_LoveTimer(0), + m_LoveCooldown(0), + m_MatingTimer(0) { - m_Parent = a_Parent; - ASSERT(m_Parent != nullptr); + m_Parent = a_Parent; + ASSERT(m_Parent != nullptr); } -bool cBehaviorBreeder::ActiveTick() +void cBehaviorBreeder::Tick() { - cWorld * World = m_Parent->GetWorld(); - // if we have a partner, mate - if (m_LovePartner != nullptr) - { - if (m_MatingTimer > 0) - { - // If we should still mate, keep bumping into them until baby is made - Vector3d Pos = m_LovePartner->GetPosition(); - m_Parent->MoveToPosition(Pos); - } - else - { - // Mating finished. Spawn baby - Vector3f Pos = (m_Parent->GetPosition() + m_LovePartner->GetPosition()) * 0.5; - UInt32 BabyID = World->SpawnMob(Pos.x, Pos.y, Pos.z, m_Parent->GetMobType(), true); - - class cBabyInheritCallback : - public cEntityCallback - { - public: - cMonster * Baby; - cBabyInheritCallback() : Baby(nullptr) { } - virtual bool Item(cEntity * a_Entity) override - { - Baby = static_cast<cMonster *>(a_Entity); - return true; - } - } Callback; - - m_Parent->GetWorld()->DoWithEntityByID(BabyID, Callback); - if (Callback.Baby != nullptr) - { - Callback.Baby->InheritFromParents(m_Parent, m_LovePartner); - } - - cFastRandom Random; - World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, 1 + (Random.RandInt() % 6)); - - m_LovePartner->GetBehaviorBreeder()->ResetLoveMode(); - ResetLoveMode(); - } - return true; - } - - // If we are in love mode and we have no partner - if (m_LoveTimer > 0) - { - class LookForLover : public cEntityCallback - { - public: - cMonster * m_Me; - LookForLover(cMonster * a_Me) : - m_Me(a_Me) - { - } - - virtual bool Item(cEntity * a_Entity) override - { - // If the entity is not a monster, don't breed with it - // Also, do not self-breed - if ((a_Entity->GetEntityType() != cEntity::eEntityType::etMonster) || (a_Entity == m_Me)) - { - return false; - } - - auto PotentialPartner = static_cast<cMonster*>(a_Entity); - - // If the potential partner is not of the same species, don't breed with it - if (PotentialPartner->GetMobType() != m_Me->GetMobType()) - { - return false; - } - - auto PartnerBreedingBehavior = PotentialPartner->GetBehaviorBreeder(); - auto MyBreedingBehavior = m_Me->GetBehaviorBreeder(); - - // If the potential partner is not in love - // Or they already have a mate, do not breed with them - - if ((!PartnerBreedingBehavior->IsInLove()) || (PartnerBreedingBehavior->GetPartner() != nullptr)) - { - return false; - } - - // All conditions met, let's breed! - PartnerBreedingBehavior->EngageLoveMode(m_Me); - MyBreedingBehavior->EngageLoveMode(PotentialPartner); - return true; - } - } Callback(m_Parent); - - World->ForEachEntityInBox(cBoundingBox(m_Parent->GetPosition(), 8, 8), Callback); - if (m_LovePartner != nullptr) - { - return true; // We found love and took control of the monster, prevent other Behaviors from doing so - } - } - - return false; + cWorld * World = m_Parent->GetWorld(); + // if we have a partner, mate + if (m_LovePartner != nullptr) + { + if (m_MatingTimer > 0) + { + // If we should still mate, keep bumping into them until baby is made + Vector3d Pos = m_LovePartner->GetPosition(); + m_Parent->MoveToPosition(Pos); + } + else + { + // Mating finished. Spawn baby + Vector3f Pos = (m_Parent->GetPosition() + m_LovePartner->GetPosition()) * 0.5; + UInt32 BabyID = World->SpawnMob(Pos.x, Pos.y, Pos.z, m_Parent->GetMobType(), true); + + class cBabyInheritCallback : + public cEntityCallback + { + public: + cMonster * Baby; + cBabyInheritCallback() : Baby(nullptr) { } + virtual bool Item(cEntity * a_Entity) override + { + Baby = static_cast<cMonster *>(a_Entity); + return true; + } + } Callback; + + m_Parent->GetWorld()->DoWithEntityByID(BabyID, Callback); + if (Callback.Baby != nullptr) + { + Callback.Baby->InheritFromParents(m_Parent, m_LovePartner); + } + + cFastRandom Random; + World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, 1 + (Random.RandInt() % 6)); + + m_LovePartner->GetBehaviorBreeder()->ResetLoveMode(); + ResetLoveMode(); + }d + } } -void cBehaviorBreeder::Tick() +void cBehaviorBreeder::PostTick() { - if (m_MatingTimer > 0) - { - m_MatingTimer--; - } - if (m_LoveCooldown > 0) - { - m_LoveCooldown--; - } - if (m_LoveTimer > 0) - { - m_LoveTimer--; - } + if (m_MatingTimer > 0) + { + m_MatingTimer--; + } + if (m_LoveCooldown > 0) + { + m_LoveCooldown--; + } + if (m_LoveTimer > 0) + { + m_LoveTimer--; + } } +bool cBehaviorBreeder::IsControlDesired() +{ + cWorld * World = m_Parent->GetWorld(); + + // if we have a love partner, we should control the mob + if (m_LovePartner != nullptr) + { + return true; + } + + // If we are in love mode and we have no partner, try to find one + if (m_LoveTimer > 0) + { + class LookForLover : public cEntityCallback + { + public: + cMonster * m_Me; + LookForLover(cMonster * a_Me) : + m_Me(a_Me) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + // If the entity is not a monster, don't breed with it + // Also, do not self-breed + if ((a_Entity->GetEntityType() != cEntity::eEntityType::etMonster) || (a_Entity == m_Me)) + { + return false; + } + + auto PotentialPartner = static_cast<cMonster*>(a_Entity); + + // If the potential partner is not of the same species, don't breed with it + if (PotentialPartner->GetMobType() != m_Me->GetMobType()) + { + return false; + } + + auto PartnerBreedingBehavior = PotentialPartner->GetBehaviorBreeder(); + auto MyBreedingBehavior = m_Me->GetBehaviorBreeder(); + + // If the potential partner is not in love + // Or they already have a mate, do not breed with them + + if ((!PartnerBreedingBehavior->IsInLove()) || (PartnerBreedingBehavior->GetPartner() != nullptr)) + { + return false; + } + + // All conditions met, let's breed! + PartnerBreedingBehavior->EngageLoveMode(m_Me); + MyBreedingBehavior->EngageLoveMode(PotentialPartner); + return true; + } + } Callback(m_Parent); + + World->ForEachEntityInBox(cBoundingBox(m_Parent->GetPosition(), 8, 8), Callback); + if (m_LovePartner != nullptr) + { + return true; // We found love and took control of the monster, prevent other Behaviors from doing so + } + } + + return false; +} + void cBehaviorBreeder::Destroyed() { - if (m_LovePartner != nullptr) - { - m_LovePartner->GetBehaviorBreeder()->ResetLoveMode(); - } + if (m_LovePartner != nullptr) + { + m_LovePartner->GetBehaviorBreeder()->ResetLoveMode(); + } } @@ -165,30 +175,30 @@ void cBehaviorBreeder::Destroyed() void cBehaviorBreeder::OnRightClicked(cPlayer & a_Player) { - // If a player holding breeding items right-clicked me, go into love mode - if ((m_LoveCooldown == 0) && !IsInLove() && !m_Parent->IsBaby()) - { - short HeldItem = a_Player.GetEquippedItem().m_ItemType; - cItems BreedingItems; - m_Parent->GetFollowedItems(BreedingItems); - if (BreedingItems.ContainsType(HeldItem)) - { - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - m_LoveTimer = 20 * 30; // half a minute - m_Parent->GetWorld()->BroadcastEntityStatus(*m_Parent, cEntity::eEntityStatus::esMobInLove); - } - } + // If a player holding breeding items right-clicked me, go into love mode + if ((m_LoveCooldown == 0) && !IsInLove() && !m_Parent->IsBaby()) + { + short HeldItem = a_Player.GetEquippedItem().m_ItemType; + cItems BreedingItems; + m_Parent->GetFollowedItems(BreedingItems); + if (BreedingItems.ContainsType(HeldItem)) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + m_LoveTimer = 20 * 30; // half a minute + m_Parent->GetWorld()->BroadcastEntityStatus(*m_Parent, cEntity::eEntityStatus::esMobInLove); + } + } } void cBehaviorBreeder::EngageLoveMode(cMonster * a_Partner) { - m_LovePartner = a_Partner; - m_MatingTimer = 50; // about 3 seconds of mating + m_LovePartner = a_Partner; + m_MatingTimer = 50; // about 3 seconds of mating } @@ -197,13 +207,13 @@ void cBehaviorBreeder::EngageLoveMode(cMonster * a_Partner) void cBehaviorBreeder::ResetLoveMode() { - m_LovePartner = nullptr; - m_LoveTimer = 0; - m_MatingTimer = 0; - m_LoveCooldown = 20 * 60 * 5; // 5 minutes + m_LovePartner = nullptr; + m_LoveTimer = 0; + m_MatingTimer = 0; + m_LoveCooldown = 20 * 60 * 5; // 5 minutes - // when an animal is in love mode, the client only stops sending the hearts if we let them know it's in cooldown, which is done with the "age" metadata - m_Parent->GetWorld()->BroadcastEntityMetadata(*m_Parent); + // when an animal is in love mode, the client only stops sending the hearts if we let them know it's in cooldown, which is done with the "age" metadata + m_Parent->GetWorld()->BroadcastEntityMetadata(*m_Parent); } @@ -212,7 +222,7 @@ void cBehaviorBreeder::ResetLoveMode() bool cBehaviorBreeder::IsInLove() const { - return m_LoveTimer > 0; + return m_LoveTimer > 0; } @@ -221,5 +231,5 @@ bool cBehaviorBreeder::IsInLove() const bool cBehaviorBreeder::IsInLoveCooldown() const { - return (m_LoveCooldown > 0); + return (m_LoveCooldown > 0); } diff --git a/src/Mobs/Behaviors/BehaviorBreeder.h b/src/Mobs/Behaviors/BehaviorBreeder.h index 51ed0501d..d95840f5e 100644 --- a/src/Mobs/Behaviors/BehaviorBreeder.h +++ b/src/Mobs/Behaviors/BehaviorBreeder.h @@ -1,9 +1,9 @@ #pragma once -// Grants breeding capabilities to the mob - class cBehaviorBreeder; +#include "Behavior.h" + class cWorld; class cMonster; class cPlayer; @@ -13,46 +13,48 @@ class cItems; -class cBehaviorBreeder +/** Grants breeding capabilities to the mob. */ +class cBehaviorBreeder : public cBehavior { public: - cBehaviorBreeder(cMonster * a_Parent); + cBehaviorBreeder(cMonster * a_Parent); - // Functions our host Monster should invoke: - void Tick(); - bool ActiveTick(); - void OnRightClicked(cPlayer & a_Player); - void Destroyed(); + // Functions our host Monster should invoke: + bool IsControlDesired() override; + void Tick() override; + void PostTick() override; + void OnRightClicked(cPlayer & a_Player); + void Destroyed() override; - /** Returns the partner which the monster is currently mating with. */ - cMonster * GetPartner(void) const { return m_LovePartner; } + /** Returns the partner which the monster is currently mating with. */ + cMonster * GetPartner(void) const { return m_LovePartner; } - /** Start the mating process. Causes the monster to keep bumping into the partner until m_MatingTimer reaches zero. */ - void EngageLoveMode(cMonster * a_Partner); + /** Start the mating process. Causes the monster to keep bumping into the partner until m_MatingTimer reaches zero. */ + void EngageLoveMode(cMonster * a_Partner); - /** Finish the mating process. Called after a baby is born. Resets all breeding related timers and sets m_LoveCooldown to 20 minutes. */ - void ResetLoveMode(); + /** Finish the mating process. Called after a baby is born. Resets all breeding related timers and sets m_LoveCooldown to 20 minutes. */ + void ResetLoveMode(); - /** Returns whether the monster has just been fed and is ready to mate. If this is "true" and GetPartner isn't "nullptr", then the monster is mating. */ - bool IsInLove() const; + /** Returns whether the monster has just been fed and is ready to mate. If this is "true" and GetPartner isn't "nullptr", then the monster is mating. */ + bool IsInLove() const; - /** Returns whether the monster is tired of breeding and is in the cooldown state. */ - bool IsInLoveCooldown() const; + /** Returns whether the monster is tired of breeding and is in the cooldown state. */ + bool IsInLoveCooldown() const; private: - /** Our parent */ - cMonster * m_Parent; + /** Our parent */ + cMonster * m_Parent; - /** The monster's breeding partner. */ - cMonster * m_LovePartner; + /** The monster's breeding partner. */ + cMonster * m_LovePartner; - /** If above 0, the monster is in love mode, and will breed if a nearby monster is also in love mode. Decrements by 1 per tick till reaching zero. */ - int m_LoveTimer; + /** If above 0, the monster is in love mode, and will breed if a nearby monster is also in love mode. Decrements by 1 per tick till reaching zero. */ + int m_LoveTimer; - /** If above 0, the monster is in cooldown mode and will refuse to breed. Decrements by 1 per tick till reaching zero. */ - int m_LoveCooldown; + /** If above 0, the monster is in cooldown mode and will refuse to breed. Decrements by 1 per tick till reaching zero. */ + int m_LoveCooldown; - /** The monster is engaged in mating, once this reaches zero, a baby will be born. Decrements by 1 per tick till reaching zero, then a baby is made and ResetLoveMode() is called. */ - int m_MatingTimer; + /** The monster is engaged in mating, once this reaches zero, a baby will be born. Decrements by 1 per tick till reaching zero, then a baby is made and ResetLoveMode() is called. */ + int m_MatingTimer; }; diff --git a/src/Mobs/Behaviors/BehaviorChaser.cpp b/src/Mobs/Behaviors/BehaviorChaser.cpp index b022495eb..2cdafe98f 100644 --- a/src/Mobs/Behaviors/BehaviorChaser.cpp +++ b/src/Mobs/Behaviors/BehaviorChaser.cpp @@ -10,58 +10,62 @@ cBehaviorChaser::cBehaviorChaser(cMonster * a_Parent) : - m_Parent(a_Parent) + m_Parent(a_Parent) , m_AttackRate(3) , m_AttackDamage(1) , m_AttackRange(1) , m_AttackCoolDownTicksLeft(0) , m_TicksSinceLastDamaged(50) { - ASSERT(m_Parent != nullptr); - m_StrikeBehavior = m_Parent->GetBehaviorStriker(); - ASSERT(m_StrikeBehavior != nullptr); // The monster that has an Attacker behavior must also have a Striker behavior + ASSERT(m_Parent != nullptr); } -bool cBehaviorChaser::ActiveTick() +bool cBehaviorChaser::Tick() { - // Stop targeting out of range targets - if (GetTarget() != nullptr) - { - if (TargetOutOfSight()) - { - SetTarget(nullptr); - } - else - { - if (TargetIsInStrikeRange()) - { - StrikeTarget(); - } - else - { - ApproachTarget(); - } - return true; - } - } - return false; + // Stop targeting out of range targets + if (GetTarget() != nullptr) + { + if (TargetOutOfSight()) + { + SetTarget(nullptr); + } + else + { + if (TargetIsInStrikeRange()) + { + StrikeTarget(); + } + else + { + ApproachTarget(); // potential mobTodo: decoupling approaching from attacking + // Not important now, but important for future extensibility, e.g. + // cow chases wheat but using the netherman approacher to teleport around. + } + return true; + } + } + return false; } +void cBehaviorChaser::ApproachTarget() +{ + // potential mobTodo inheritence for creaper approachers, etc + m_Parent->MoveToPosition(m_Target->GetPosition()); +} - -void cBehaviorChaser::Tick() +void cBehaviorChaser::PostTick() { - ++m_TicksSinceLastDamaged; - if (m_AttackCoolDownTicksLeft > 0) - { - m_AttackCoolDownTicksLeft -= 1; - } + ++m_TicksSinceLastDamaged; + if (m_AttackCoolDownTicksLeft > 0) + { + m_AttackCoolDownTicksLeft -= 1; + } } @@ -70,7 +74,7 @@ void cBehaviorChaser::Tick() void cBehaviorChaser::Destroyed() { - m_Target = nullptr; + SetTarget(nullptr); } @@ -79,7 +83,7 @@ void cBehaviorChaser::Destroyed() void cBehaviorChaser::SetAttackRate(float a_AttackRate) { - m_AttackRate = a_AttackRate; + m_AttackRate = a_AttackRate; } @@ -88,7 +92,7 @@ void cBehaviorChaser::SetAttackRate(float a_AttackRate) void cBehaviorChaser::SetAttackRange(int a_AttackRange) { - m_AttackRange = a_AttackRange; + m_AttackRange = a_AttackRange; } @@ -97,7 +101,7 @@ void cBehaviorChaser::SetAttackRange(int a_AttackRange) void cBehaviorChaser::SetAttackDamage(int a_AttackDamage) { - m_AttackDamage = a_AttackDamage; + m_AttackDamage = a_AttackDamage; } @@ -105,7 +109,7 @@ void cBehaviorChaser::SetAttackDamage(int a_AttackDamage) cPawn * cBehaviorChaser::GetTarget() { - return m_Target; + return m_Target; } @@ -114,16 +118,7 @@ cPawn * cBehaviorChaser::GetTarget() void cBehaviorChaser::SetTarget(cPawn * a_Target) { - m_Target = a_Target; -} - - - - - -cBehaviorChaser::~cBehaviorChaser() -{ - + m_Target = a_Target; } @@ -132,26 +127,26 @@ cBehaviorChaser::~cBehaviorChaser() bool cBehaviorChaser::TargetIsInStrikeRange() { - ASSERT(m_Target != nullptr); - ASSERT(m_Parent != nullptr); - /* - #include "../../Tracer.h" - cTracer LineOfSight(m_Parent->GetWorld()); - Vector3d MyHeadPosition = m_Parent->GetPosition() + Vector3d(0, m_Parent->GetHeight(), 0); - Vector3d AttackDirection(m_ParentChaser->GetTarget()->GetPosition() + Vector3d(0, GetTarget()->GetHeight(), 0) - MyHeadPosition); - - - if (GetTarget() != nullptr) - { - MoveToPosition(GetTarget()->GetPosition()); - } - if (TargetIsInRange() && !LineOfSight.Trace(MyHeadPosition, AttackDirection, static_cast<int>(AttackDirection.Length())) && (GetHealth() > 0.0)) - { - // Attack if reached destination, target isn't null, and have a clear line of sight to target (so won't attack through walls) - Attack(a_Dt); - } - */ - return ((m_Target->GetPosition() - m_Parent->GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange)); + ASSERT(m_Target != nullptr); + ASSERT(m_Parent != nullptr); + /* + #include "../../Tracer.h" + cTracer LineOfSight(m_Parent->GetWorld()); + Vector3d MyHeadPosition = m_Parent->GetPosition() + Vector3d(0, m_Parent->GetHeight(), 0); + Vector3d AttackDirection(m_ParentChaser->GetTarget()->GetPosition() + Vector3d(0, GetTarget()->GetHeight(), 0) - MyHeadPosition); + + + if (GetTarget() != nullptr) + { + MoveToPosition(GetTarget()->GetPosition()); + } + if (TargetIsInRange() && !LineOfSight.Trace(MyHeadPosition, AttackDirection, static_cast<int>(AttackDirection.Length())) && (GetHealth() > 0.0)) + { + // Attack if reached destination, target isn't null, and have a clear line of sight to target (so won't attack through walls) + Attack(a_Dt); + } + */ + return ((m_Target->GetPosition() - m_Parent->GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange)); } @@ -160,12 +155,12 @@ bool cBehaviorChaser::TargetIsInStrikeRange() bool cBehaviorChaser::TargetOutOfSight() { - ASSERT(m_Target != nullptr); - if ((GetTarget()->GetPosition() - m_Parent->GetPosition()).Length() > m_Parent->GetSightDistance()) - { - return true; - } - return false; + ASSERT(m_Target != nullptr); + if ((GetTarget()->GetPosition() - m_Parent->GetPosition()).Length() > m_Parent->GetSightDistance()) + { + return true; + } + return false; } @@ -174,7 +169,7 @@ bool cBehaviorChaser::TargetOutOfSight() void cBehaviorChaser::ResetStrikeCooldown() { - m_AttackCoolDownTicksLeft = static_cast<int>(3 * 20 * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every 3 seconds + m_AttackCoolDownTicksLeft = static_cast<int>(3 * 20 * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every 3 seconds } @@ -183,10 +178,13 @@ void cBehaviorChaser::ResetStrikeCooldown() void cBehaviorChaser::StrikeTarget() { - if (m_AttackCoolDownTicksLeft != 0) - { - // mobTodo - // m_StrikeBehavior->Strike(m_Target); // LogicParrot Todo animations (via counter passing?) - ResetStrikeCooldown(); - } + if (m_AttackCoolDownTicksLeft != 0) + { + cBehaviorStriker * Striker = m_Parent->GetBehaviorStriker(); + if (Striker != nullptr) + { + Striker->Strike(m_Target); + } + ResetStrikeCooldown(); + } } diff --git a/src/Mobs/Behaviors/BehaviorChaser.h b/src/Mobs/Behaviors/BehaviorChaser.h index fff5ebfa3..e924a9db3 100644 --- a/src/Mobs/Behaviors/BehaviorChaser.h +++ b/src/Mobs/Behaviors/BehaviorChaser.h @@ -1,12 +1,15 @@ - #pragma once +class cBehaviorChaser; + +#include "Behavior.h" + class cMonster; class cPawn; class cBehaviorStriker; -/** Grants attack capability to the mob. Note that this is not the same as agression! +/** Grants chase capability to the mob. Note that this is not the same as agression! The mob may possess this trait and not attack anyone or only attack when provoked. Unlike most traits, this one has several forms, and therefore it is an abstract type You should use one of its derived classes, and you cannot use it directly. */ @@ -17,9 +20,9 @@ public: cBehaviorChaser(cMonster * a_Parent); // Functions our host Monster should invoke: - bool ActiveTick(); - void Destroyed(); - void Tick(); + bool Tick() override; + void Destroyed() override; + void PostTick() override; // Our host monster will call these once it loads its config file void SetAttackRate(float a_AttackRate); @@ -34,12 +37,12 @@ public: virtual ~cBehaviorChaser(); protected: - virtual void ApproachTarget() = 0; + void ApproachTarget(); + // virtual void ApproachTarget() = 0; private: /** Our parent */ cMonster * m_Parent; - cBehaviorStriker * m_StrikeBehavior; // The mob we want to attack cPawn * m_Target; diff --git a/src/Mobs/Behaviors/BehaviorCoward.cpp b/src/Mobs/Behaviors/BehaviorCoward.cpp index 017227340..ec3626807 100644 --- a/src/Mobs/Behaviors/BehaviorCoward.cpp +++ b/src/Mobs/Behaviors/BehaviorCoward.cpp @@ -17,7 +17,7 @@ cBehaviorCoward::cBehaviorCoward(cMonster * a_Parent) : -bool cBehaviorCoward::ActiveTick() +bool cBehaviorCoward::Tick() { if (m_Attacker == nullptr) { diff --git a/src/Mobs/Behaviors/BehaviorCoward.h b/src/Mobs/Behaviors/BehaviorCoward.h index 227715c00..ed0ad3ed5 100644 --- a/src/Mobs/Behaviors/BehaviorCoward.h +++ b/src/Mobs/Behaviors/BehaviorCoward.h @@ -1,5 +1,7 @@ #pragma once +#include "Behavior.h" + // Makes the mob run away from any other mob that damages it //fwds @@ -14,7 +16,7 @@ public: cBehaviorCoward(cMonster * a_Parent); // Functions our host Monster should invoke: - bool ActiveTick(); + bool Tick(); void DoTakeDamage(TakeDamageInfo & a_TDI); diff --git a/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp b/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp index 809f0190f..062d60bae 100644 --- a/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp +++ b/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp @@ -9,28 +9,28 @@ cBehaviorDayLightBurner::cBehaviorDayLightBurner(cMonster * a_Parent) : m_Parent(a_Parent) { - ASSERT(m_Parent != nullptr); + ASSERT(m_Parent != nullptr); } -void cBehaviorDayLightBurner::Tick(cChunk & a_Chunk, bool WouldBurn) +void cBehaviorDayLightBurner::PostTick(cChunk & a_Chunk, bool WouldBurn) { - int RelY = static_cast<int>(m_Parent->GetPosY()); - if ((RelY < 0) || (RelY >= cChunkDef::Height)) - { - // Outside the world - return; - } - if (!a_Chunk.IsLightValid()) - { - m_Parent->GetWorld()->QueueLightChunk(m_Parent->GetChunkX(), m_Parent->GetChunkZ()); - return; - } + int RelY = static_cast<int>(m_Parent->GetPosY()); + if ((RelY < 0) || (RelY >= cChunkDef::Height)) + { + // Outside the world + return; + } + if (!a_Chunk.IsLightValid()) + { + m_Parent->GetWorld()->QueueLightChunk(m_Parent->GetChunkX(), m_Parent->GetChunkZ()); + return; + } - if (!m_Parent->IsOnFire() && WouldBurn) - { - // Burn for 100 ticks, then decide again - m_Parent->StartBurning(100); - } + if (!m_Parent->IsOnFire() && WouldBurn) + { + // Burn for 100 ticks, then decide again + m_Parent->StartBurning(100); + } } @@ -38,57 +38,57 @@ void cBehaviorDayLightBurner::Tick(cChunk & a_Chunk, bool WouldBurn) bool cBehaviorDayLightBurner::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk) { - int RelY = FloorC(a_Location.y); - if (RelY <= 0) - { - // The mob is about to die, no point in burning - return false; - } - if (RelY >= cChunkDef::Height) - { - // Always burn above the world - return true; - } + int RelY = FloorC(a_Location.y); + if (RelY <= 0) + { + // The mob is about to die, no point in burning + return false; + } + if (RelY >= cChunkDef::Height) + { + // Always burn above the world + return true; + } - PREPARE_REL_AND_CHUNK(a_Location, a_Chunk); - if (!RelSuccess) - { - return false; - } + PREPARE_REL_AND_CHUNK(a_Location, a_Chunk); + if (!RelSuccess) + { + return false; + } - if ( - (Chunk->GetBlock(Rel.x, Rel.y, Rel.z) != E_BLOCK_SOULSAND) && // Not on soulsand - (m_Parent->GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime - m_Parent->GetWorld()->IsWeatherSunnyAt(static_cast<int>(m_Parent->GetPosX()), static_cast<int>(m_Parent->GetPosZ())) // Not raining - ) - { - int MobHeight = static_cast<int>(a_Location.y) + static_cast<int>(round(m_Parent->GetHeight())) - 1; // The height of the mob head - if (MobHeight >= cChunkDef::Height) - { - return true; - } - // Start with the highest block and scan down to the mob's head. - // If a non transparent is found, return false (do not burn). Otherwise return true. - // Note that this loop is not a performance concern as transparent blocks are rare and the loop almost always bailes out - // instantly.(An exception is e.g. standing under a long column of glass). - int CurrentBlock = Chunk->GetHeight(Rel.x, Rel.z); - while (CurrentBlock >= MobHeight) - { - BLOCKTYPE Block = Chunk->GetBlock(Rel.x, CurrentBlock, Rel.z); - if ( - // Do not burn if a block above us meets one of the following conditions: - (!cBlockInfo::IsTransparent(Block)) || - (Block == E_BLOCK_LEAVES) || - (Block == E_BLOCK_NEW_LEAVES) || - (IsBlockWater(Block)) - ) - { - return false; - } - --CurrentBlock; - } - return true; + if ( + (Chunk->GetBlock(Rel.x, Rel.y, Rel.z) != E_BLOCK_SOULSAND) && // Not on soulsand + (m_Parent->GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime + m_Parent->GetWorld()->IsWeatherSunnyAt(static_cast<int>(m_Parent->GetPosX()), static_cast<int>(m_Parent->GetPosZ())) // Not raining + ) + { + int MobHeight = static_cast<int>(a_Location.y) + static_cast<int>(round(m_Parent->GetHeight())) - 1; // The height of the mob head + if (MobHeight >= cChunkDef::Height) + { + return true; + } + // Start with the highest block and scan down to the mob's head. + // If a non transparent is found, return false (do not burn). Otherwise return true. + // Note that this loop is not a performance concern as transparent blocks are rare and the loop almost always bailes out + // instantly.(An exception is e.g. standing under a long column of glass). + int CurrentBlock = Chunk->GetHeight(Rel.x, Rel.z); + while (CurrentBlock >= MobHeight) + { + BLOCKTYPE Block = Chunk->GetBlock(Rel.x, CurrentBlock, Rel.z); + if ( + // Do not burn if a block above us meets one of the following conditions: + (!cBlockInfo::IsTransparent(Block)) || + (Block == E_BLOCK_LEAVES) || + (Block == E_BLOCK_NEW_LEAVES) || + (IsBlockWater(Block)) + ) + { + return false; + } + --CurrentBlock; + } + return true; - } - return false; + } + return false; } diff --git a/src/Mobs/Behaviors/BehaviorDayLightBurner.h b/src/Mobs/Behaviors/BehaviorDayLightBurner.h index d967b5f68..f059965c5 100644 --- a/src/Mobs/Behaviors/BehaviorDayLightBurner.h +++ b/src/Mobs/Behaviors/BehaviorDayLightBurner.h @@ -11,15 +11,15 @@ class cChunk; class cBehaviorDayLightBurner { public: - cBehaviorDayLightBurner(cMonster * a_Parent); + cBehaviorDayLightBurner(cMonster * a_Parent); - void Tick(cChunk & a_Chunk, bool WouldBurn); - bool WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk); + void PostTick(cChunk & a_Chunk, bool WouldBurn); + bool WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk); - // Functions our host Monster should invoke: - void Tick(); + // Functions our host Monster should invoke: + void Tick(); private: - cMonster * m_Parent; // Our Parent - cEntity * m_Attacker; // The entity we're running away from + cMonster * m_Parent; // Our Parent + cEntity * m_Attacker; // The entity we're running away from }; diff --git a/src/Mobs/Behaviors/BehaviorItemFollower.cpp b/src/Mobs/Behaviors/BehaviorItemFollower.cpp index 833050e69..2b0377c46 100644 --- a/src/Mobs/Behaviors/BehaviorItemFollower.cpp +++ b/src/Mobs/Behaviors/BehaviorItemFollower.cpp @@ -7,33 +7,33 @@ cBehaviorItemFollower::cBehaviorItemFollower(cMonster * a_Parent) : - m_Parent(a_Parent) + m_Parent(a_Parent) { - m_Parent = a_Parent; - ASSERT(m_Parent != nullptr); + m_Parent = a_Parent; + ASSERT(m_Parent != nullptr); } -bool cBehaviorItemFollower::ActiveTick() +bool cBehaviorItemFollower::Tick() { - cItems FollowedItems; - m_Parent->GetFollowedItems(FollowedItems); - if (FollowedItems.Size() > 0) - { - cPlayer * a_Closest_Player = m_Parent->GetNearestPlayer(); - if (a_Closest_Player != nullptr) - { - cItem EquippedItem = a_Closest_Player->GetEquippedItem(); - if (FollowedItems.ContainsType(EquippedItem)) - { - Vector3d PlayerPos = a_Closest_Player->GetPosition(); - m_Parent->MoveToPosition(PlayerPos); - return true; // We took control of the monster, prevent other Behaviors from doing so - } - } - } - return false; + cItems FollowedItems; + m_Parent->GetFollowedItems(FollowedItems); + if (FollowedItems.Size() > 0) + { + cPlayer * a_Closest_Player = m_Parent->GetNearestPlayer(); + if (a_Closest_Player != nullptr) + { + cItem EquippedItem = a_Closest_Player->GetEquippedItem(); + if (FollowedItems.ContainsType(EquippedItem)) + { + Vector3d PlayerPos = a_Closest_Player->GetPosition(); + m_Parent->MoveToPosition(PlayerPos); + return true; // We took control of the monster, prevent other Behaviors from doing so + } + } + } + return false; } diff --git a/src/Mobs/Behaviors/BehaviorItemFollower.h b/src/Mobs/Behaviors/BehaviorItemFollower.h index ee2bda638..09acfe473 100644 --- a/src/Mobs/Behaviors/BehaviorItemFollower.h +++ b/src/Mobs/Behaviors/BehaviorItemFollower.h @@ -1,24 +1,26 @@ #pragma once // Makes the mob follow specific held items - class cBehaviorItemFollower; -//fwds + +#include "Behavior.h" + +// fwds class cMonster; class cItems; class cBehaviorItemFollower { public: - cBehaviorItemFollower(cMonster * a_Parent); + cBehaviorItemFollower(cMonster * a_Parent); - void GetBreedingItems(cItems & a_Items); + void GetBreedingItems(cItems & a_Items); - // Functions our host Monster should invoke: - bool ActiveTick(); + // Functions our host Monster should invoke: + void Tick(); private: - /** Our parent */ - cMonster * m_Parent; + /** Our parent */ + cMonster * m_Parent; }; diff --git a/src/Mobs/Behaviors/CMakeLists.txt b/src/Mobs/Behaviors/CMakeLists.txt index 3e0a063d1..017fc0f35 100644 --- a/src/Mobs/Behaviors/CMakeLists.txt +++ b/src/Mobs/Behaviors/CMakeLists.txt @@ -5,6 +5,7 @@ project (Cuberite) include_directories ("${PROJECT_SOURCE_DIR}/../") SET (SRCS + Behavior.cpp BehaviorAggressive.cpp BehaviorChaser.cpp BehaviorBreeder.cpp @@ -17,6 +18,7 @@ SET (SRCS ) SET (HDRS + Behavior.h BehaviorAggressive.h BehaviorChaser.h BehaviorBreeder.h diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp index 5cfe0d4cd..6aa72369f 100644 --- a/src/Mobs/Enderman.cpp +++ b/src/Mobs/Enderman.cpp @@ -98,67 +98,6 @@ void cEnderman::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cEnderman::CheckEventSeePlayer(cChunk & a_Chunk) -{ - if (GetTarget() != nullptr) - { - return; - } - - cPlayerLookCheck Callback(GetPosition(), m_SightDistance); - if (m_World->ForEachPlayer(Callback)) - { - return; - } - - ASSERT(Callback.GetPlayer() != nullptr); - - if (!CheckLight()) - { - // Insufficient light for enderman to become aggravated - // TODO: Teleport to a suitable location - return; - } - - if (!Callback.GetPlayer()->CanMobsTarget()) - { - return; - } - - // Target the player - cMonster::EventSeePlayer(Callback.GetPlayer(), a_Chunk); - m_EMState = CHASING; - m_bIsScreaming = true; - GetWorld()->BroadcastEntityMetadata(*this); -} - - - - - -void cEnderman::CheckEventLostPlayer(void) -{ - super::CheckEventLostPlayer(); - if (!CheckLight()) - { - EventLosePlayer(); - } -} - - - - - -void cEnderman::EventLosePlayer() -{ - super::EventLosePlayer(); - m_bIsScreaming = false; - GetWorld()->BroadcastEntityMetadata(*this); -} - - - - bool cEnderman::CheckLight() { @@ -197,7 +136,7 @@ void cEnderman::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) // Take damage when touching water, drowning damage seems to be most appropriate if (CheckRain() || IsSwimming()) { - EventLosePlayer(); + // EventLosePlayer(); //mobTodo TakeDamage(dtDrowning, nullptr, 1, 0); // TODO teleport to a safe location } diff --git a/src/Mobs/Enderman.h b/src/Mobs/Enderman.h index c9ffbeaba..0dc648468 100644 --- a/src/Mobs/Enderman.h +++ b/src/Mobs/Enderman.h @@ -8,35 +8,32 @@ class cEnderman : - public cPassiveAggressiveMonster + public cPassiveAggressiveMonster { - typedef cPassiveAggressiveMonster super; + typedef cPassiveAggressiveMonster super; public: - cEnderman(void); + cEnderman(void); - CLASS_PROTODEF(cEnderman) + CLASS_PROTODEF(cEnderman) - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void CheckEventSeePlayer(cChunk & a_Chunk) override; - virtual void CheckEventLostPlayer(void) override; - virtual void EventLosePlayer(void) override; - virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - bool IsScreaming(void) const {return m_bIsScreaming; } - BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; } - NIBBLETYPE GetCarriedMeta(void) const {return CarriedMeta; } + bool IsScreaming(void) const {return m_bIsScreaming; } + BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; } + NIBBLETYPE GetCarriedMeta(void) const {return CarriedMeta; } - /** Returns if the current sky light level is sufficient for the enderman to become aggravated */ - bool CheckLight(void); - /** Returns if the enderman gets hit by the rain */ - bool CheckRain(void); + /** Returns if the current sky light level is sufficient for the enderman to become aggravated */ + bool CheckLight(void); + /** Returns if the enderman gets hit by the rain */ + bool CheckRain(void); private: - bool m_bIsScreaming; - BLOCKTYPE CarriedBlock; - NIBBLETYPE CarriedMeta; + bool m_bIsScreaming; + BLOCKTYPE CarriedBlock; + NIBBLETYPE CarriedMeta; } ; diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp index 13630b0e3..21a58489d 100644 --- a/src/Mobs/Horse.cpp +++ b/src/Mobs/Horse.cpp @@ -11,21 +11,21 @@ cHorse::cHorse(int Type, int Color, int Style, int TameTimes) : - super("Horse", mtHorse, "entity.horse.hurt", "entity.horse.death", 1.4, 1.6), - m_bHasChest(false), - m_bIsEating(false), - m_bIsRearing(false), - m_bIsMouthOpen(false), - m_bIsTame(false), - m_bIsSaddled(false), - m_Type(Type), - m_Color(Color), - m_Style(Style), - m_Armour(0), - m_TimesToTame(TameTimes), - m_TameAttemptTimes(0), - m_RearTickCount(0), - m_MaxSpeed(14.0) + super("Horse", mtHorse, "entity.horse.hurt", "entity.horse.death", 1.4, 1.6), + m_bHasChest(false), + m_bIsEating(false), + m_bIsRearing(false), + m_bIsMouthOpen(false), + m_bIsTame(false), + m_bIsSaddled(false), + m_Type(Type), + m_Color(Color), + m_Style(Style), + m_Armour(0), + m_TimesToTame(TameTimes), + m_TameAttemptTimes(0), + m_RearTickCount(0), + m_MaxSpeed(14.0) { } @@ -35,67 +35,67 @@ cHorse::cHorse(int Type, int Color, int Style, int TameTimes) : void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { - super::Tick(a_Dt, a_Chunk); - if (!IsTicking()) - { - // The base class tick destroyed us - return; - } - - auto & Random = GetRandomProvider(); - - if (!m_bIsMouthOpen) - { - if (Random.RandBool(0.02)) - { - m_bIsMouthOpen = true; - } - } - else - { - if (Random.RandBool(0.10)) - { - m_bIsMouthOpen = false; - } - } - - if ((m_Attachee != nullptr) && (!m_bIsTame)) - { - if (m_TameAttemptTimes < m_TimesToTame) - { - if (Random.RandBool(0.02)) - { - m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_EAST)); - m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_WEST)); - m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_EAST)); - m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_WEST)); - - m_World->BroadcastSoundEffect("entity.horse.angry", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 1.0f); - m_Attachee->Detach(); - m_bIsRearing = true; - } - } - else - { - m_World->GetBroadcaster().BroadcastParticleEffect("heart", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5); - m_bIsTame = true; - } - } - - if (m_bIsRearing) - { - if (m_RearTickCount == 20) - { - m_bIsRearing = false; - m_RearTickCount = 0; - } - else - { - m_RearTickCount++; - } - } - - m_World->BroadcastEntityMetadata(*this); + super::Tick(a_Dt, a_Chunk); + if (!IsTicking()) + { + // The base class tick destroyed us + return; + } + + auto & Random = GetRandomProvider(); + + if (!m_bIsMouthOpen) + { + if (Random.RandBool(0.02)) + { + m_bIsMouthOpen = true; + } + } + else + { + if (Random.RandBool(0.10)) + { + m_bIsMouthOpen = false; + } + } + + if ((m_Attachee != nullptr) && (!m_bIsTame)) + { + if (m_TameAttemptTimes < m_TimesToTame) + { + if (Random.RandBool(0.02)) + { + m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_EAST)); + m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_WEST)); + m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_EAST)); + m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_WEST)); + + m_World->BroadcastSoundEffect("entity.horse.angry", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 1.0f); + m_Attachee->Detach(); + m_bIsRearing = true; + } + } + else + { + m_World->GetBroadcaster().BroadcastParticleEffect("heart", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5); + m_bIsTame = true; + } + } + + if (m_bIsRearing) + { + if (m_RearTickCount == 20) + { + m_bIsRearing = false; + m_RearTickCount = 0; + } + else + { + m_RearTickCount++; + } + } + + m_World->BroadcastEntityMetadata(*this); } @@ -104,63 +104,63 @@ void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cHorse::OnRightClicked(cPlayer & a_Player) { - super::OnRightClicked(a_Player); - - if (m_bIsTame) - { - if (!m_bIsSaddled) - { - if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE) - { - // Saddle the horse: - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - m_bIsSaddled = true; - m_World->BroadcastEntityMetadata(*this); - } - else - { - a_Player.AttachTo(this); - } - } - else - { - a_Player.AttachTo(this); - } - } - else if (a_Player.GetEquippedItem().IsEmpty()) - { - // Check if leashed / unleashed to player before try to ride - if (!m_IsLeashActionJustDone) - { - if (m_Attachee != nullptr) - { - if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) - { - a_Player.Detach(); - return; - } - - if (m_Attachee->IsPlayer()) - { - return; - } - - m_Attachee->Detach(); - } - - m_TameAttemptTimes++; - a_Player.AttachTo(this); - } - } - else - { - m_bIsRearing = true; - m_RearTickCount = 0; - m_World->BroadcastSoundEffect("entity.horse.angry", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); - } + super::OnRightClicked(a_Player); + + if (m_bIsTame) + { + if (!m_bIsSaddled) + { + if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE) + { + // Saddle the horse: + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + m_bIsSaddled = true; + m_World->BroadcastEntityMetadata(*this); + } + else + { + a_Player.AttachTo(this); + } + } + else + { + a_Player.AttachTo(this); + } + } + else if (a_Player.GetEquippedItem().IsEmpty()) + { + // Check if leashed / unleashed to player before try to ride + if (!m_IsLeashActionJustDone) + { + if (m_Attachee != nullptr) + { + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + return; + } + + m_Attachee->Detach(); + } + + m_TameAttemptTimes++; + a_Player.AttachTo(this); + } + } + else + { + m_bIsRearing = true; + m_RearTickCount = 0; + m_World->BroadcastSoundEffect("entity.horse.angry", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); + } } @@ -169,29 +169,16 @@ void cHorse::OnRightClicked(cPlayer & a_Player) void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - unsigned int LootingLevel = 0; - if (a_Killer != nullptr) - { - LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); - } - AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER); - if (m_bIsSaddled) - { - a_Drops.push_back(cItem(E_ITEM_SADDLE, 1)); - } -} - - - - - -void cHorse::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) -{ - // If horse is tame and someone is sitting on it, don't walk around - if ((!m_bIsTame) || (m_Attachee == nullptr)) - { - super::InStateIdle(a_Dt, a_Chunk); - } + unsigned int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER); + if (m_bIsSaddled) + { + a_Drops.push_back(cItem(E_ITEM_SADDLE, 1)); + } } @@ -200,8 +187,8 @@ void cHorse::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cHorse::HandleSpeedFromAttachee(float a_Forward, float a_Sideways) { - if ((m_bIsTame) && (m_bIsSaddled)) - { - super::HandleSpeedFromAttachee(a_Forward * m_MaxSpeed, a_Sideways * m_MaxSpeed); - } + if ((m_bIsTame) && (m_bIsSaddled)) + { + super::HandleSpeedFromAttachee(a_Forward * m_MaxSpeed, a_Sideways * m_MaxSpeed); + } } diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h index 82026a0ee..e84243360 100644 --- a/src/Mobs/Horse.h +++ b/src/Mobs/Horse.h @@ -8,43 +8,42 @@ class cHorse : - public cPassiveMonster + public cPassiveMonster { - typedef cPassiveMonster super; + typedef cPassiveMonster super; public: - cHorse(int Type, int Color, int Style, int TameTimes); - - CLASS_PROTODEF(cHorse) - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual void HandleSpeedFromAttachee(float a_Forward, float a_Sideways) override; - virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual void OnRightClicked(cPlayer & a_Player) override; - - bool IsSaddled (void) const {return m_bIsSaddled; } - bool IsChested (void) const {return m_bHasChest; } - bool IsEating (void) const {return m_bIsEating; } - bool IsRearing (void) const {return m_bIsRearing; } - bool IsMthOpen (void) const {return m_bIsMouthOpen; } - bool IsTame (void) const override {return m_bIsTame; } - int GetHorseType (void) const {return m_Type; } - int GetHorseColor (void) const {return m_Color; } - int GetHorseStyle (void) const {return m_Style; } - int GetHorseArmour (void) const {return m_Armour;} - - virtual void GetBreedingItems(cItems & a_Items) override - { - a_Items.Add(E_ITEM_GOLDEN_CARROT); - a_Items.Add(E_ITEM_GOLDEN_APPLE); - } + cHorse(int Type, int Color, int Style, int TameTimes); + + CLASS_PROTODEF(cHorse) + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + virtual void HandleSpeedFromAttachee(float a_Forward, float a_Sideways) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + + bool IsSaddled (void) const {return m_bIsSaddled; } + bool IsChested (void) const {return m_bHasChest; } + bool IsEating (void) const {return m_bIsEating; } + bool IsRearing (void) const {return m_bIsRearing; } + bool IsMthOpen (void) const {return m_bIsMouthOpen; } + bool IsTame (void) const override {return m_bIsTame; } + int GetHorseType (void) const {return m_Type; } + int GetHorseColor (void) const {return m_Color; } + int GetHorseStyle (void) const {return m_Style; } + int GetHorseArmour (void) const {return m_Armour;} + + virtual void GetBreedingItems(cItems & a_Items) override + { + a_Items.Add(E_ITEM_GOLDEN_CARROT); + a_Items.Add(E_ITEM_GOLDEN_APPLE); + } private: - bool m_bHasChest, m_bIsEating, m_bIsRearing, m_bIsMouthOpen, m_bIsTame, m_bIsSaddled; - int m_Type, m_Color, m_Style, m_Armour, m_TimesToTame, m_TameAttemptTimes, m_RearTickCount; - float m_MaxSpeed; + bool m_bHasChest, m_bIsEating, m_bIsRearing, m_bIsMouthOpen, m_bIsTame, m_bIsSaddled; + int m_Type, m_Color, m_Style, m_Armour, m_TimesToTame, m_TameAttemptTimes, m_RearTickCount; + float m_MaxSpeed; } ; diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 757a0d700..2becf21d1 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -37,43 +37,43 @@ m_VanillaName is the name that vanilla use for this mob. */ static const struct { - eMonsterType m_Type; - const char * m_lcName; - const char * m_VanillaName; - const char * m_VanillaNameNBT; + eMonsterType m_Type; + const char * m_lcName; + const char * m_VanillaName; + const char * m_VanillaNameNBT; } g_MobTypeNames[] = { - {mtBat, "bat", "Bat", "bat"}, - {mtBlaze, "blaze", "Blaze", "blaze"}, - {mtCaveSpider, "cavespider", "CaveSpider", "cave_spider"}, - {mtChicken, "chicken", "Chicken", "chicken"}, - {mtCow, "cow", "Cow", "cow"}, - {mtCreeper, "creeper", "Creeper", "creeper"}, - {mtEnderman, "enderman", "Enderman", "enderman"}, - {mtEnderDragon, "enderdragon", "EnderDragon", "ender_dragon"}, - {mtGhast, "ghast", "Ghast", "ghast"}, - {mtGiant, "giant", "Giant", "giant"}, - {mtGuardian, "guardian", "Guardian", "guardian"}, - {mtHorse, "horse", "EntityHorse", "horse"}, - {mtIronGolem, "irongolem", "VillagerGolem", "iron_golem"}, - {mtMagmaCube, "magmacube", "LavaSlime", "magma_cube"}, - {mtMooshroom, "mooshroom", "MushroomCow", "mooshroom"}, - {mtOcelot, "ocelot", "Ozelot", "ocelot"}, - {mtPig, "pig", "Pig", "pig"}, - {mtRabbit, "rabbit", "Rabbit", "rabbit"}, - {mtSheep, "sheep", "Sheep", "sheep"}, - {mtSilverfish, "silverfish", "Silverfish", "silverfish"}, - {mtSkeleton, "skeleton", "Skeleton", "skeleton"}, - {mtSlime, "slime", "Slime", "slime"}, - {mtSnowGolem, "snowgolem", "SnowMan", "snow_golem"}, - {mtSpider, "spider", "Spider", "spider"}, - {mtSquid, "squid", "Squid", "squid"}, - {mtVillager, "villager", "Villager", "villager"}, - {mtWitch, "witch", "Witch", "witch"}, - {mtWither, "wither", "WitherBoss", "wither"}, - {mtWolf, "wolf", "Wolf", "wolf"}, - {mtZombie, "zombie", "Zombie", "zombie"}, - {mtZombiePigman, "zombiepigman", "PigZombie", "zombie_pigman"}, + {mtBat, "bat", "Bat", "bat"}, + {mtBlaze, "blaze", "Blaze", "blaze"}, + {mtCaveSpider, "cavespider", "CaveSpider", "cave_spider"}, + {mtChicken, "chicken", "Chicken", "chicken"}, + {mtCow, "cow", "Cow", "cow"}, + {mtCreeper, "creeper", "Creeper", "creeper"}, + {mtEnderman, "enderman", "Enderman", "enderman"}, + {mtEnderDragon, "enderdragon", "EnderDragon", "ender_dragon"}, + {mtGhast, "ghast", "Ghast", "ghast"}, + {mtGiant, "giant", "Giant", "giant"}, + {mtGuardian, "guardian", "Guardian", "guardian"}, + {mtHorse, "horse", "EntityHorse", "horse"}, + {mtIronGolem, "irongolem", "VillagerGolem", "iron_golem"}, + {mtMagmaCube, "magmacube", "LavaSlime", "magma_cube"}, + {mtMooshroom, "mooshroom", "MushroomCow", "mooshroom"}, + {mtOcelot, "ocelot", "Ozelot", "ocelot"}, + {mtPig, "pig", "Pig", "pig"}, + {mtRabbit, "rabbit", "Rabbit", "rabbit"}, + {mtSheep, "sheep", "Sheep", "sheep"}, + {mtSilverfish, "silverfish", "Silverfish", "silverfish"}, + {mtSkeleton, "skeleton", "Skeleton", "skeleton"}, + {mtSlime, "slime", "Slime", "slime"}, + {mtSnowGolem, "snowgolem", "SnowMan", "snow_golem"}, + {mtSpider, "spider", "Spider", "spider"}, + {mtSquid, "squid", "Squid", "squid"}, + {mtVillager, "villager", "Villager", "villager"}, + {mtWitch, "witch", "Witch", "witch"}, + {mtWither, "wither", "WitherBoss", "wither"}, + {mtWolf, "wolf", "Wolf", "wolf"}, + {mtZombie, "zombie", "Zombie", "zombie"}, + {mtZombiePigman, "zombiepigman", "PigZombie", "zombie_pigman"}, } ; @@ -84,46 +84,46 @@ static const struct // cMonster: cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) - : super(etMonster, a_Width, a_Height) - , m_EMState(IDLE) - , m_EMPersonality(AGGRESSIVE) - , m_NearestPlayerIsStale(true) - , m_PathFinder(a_Width, a_Height) - , m_PathfinderActivated(false) - , m_JumpCoolDown(0) - , m_IdleInterval(0) - , m_DestroyTimer(0) - , m_MobType(a_MobType) - , m_CustomName("") - , m_CustomNameAlwaysVisible(false) - , m_SoundHurt(a_SoundHurt) - , m_SoundDeath(a_SoundDeath) - , m_AttackRate(3) - , m_AttackDamage(1) - , m_AttackRange(1) - , m_AttackCoolDownTicksLeft(0) - , m_SightDistance(25) - , m_DropChanceWeapon(0.085f) - , m_DropChanceHelmet(0.085f) - , m_DropChanceChestplate(0.085f) - , m_DropChanceLeggings(0.085f) - , m_DropChanceBoots(0.085f) - , m_CanPickUpLoot(true) - , m_TicksSinceLastDamaged(100) - , m_RelativeWalkSpeed(1) - , m_Age(1) - , m_AgingTimer(20 * 60 * 20) // about 20 minutes - , m_WasLastTargetAPlayer(false) - , m_LeashedTo(nullptr) - , m_LeashToPos(nullptr) - , m_IsLeashActionJustDone(false) - , m_CanBeLeashed(GetMobFamily() == eFamily::mfPassive) - , m_Target(nullptr) + : super(etMonster, a_Width, a_Height) + , m_EMPersonality(AGGRESSIVE) + , m_NearestPlayerIsStale(true) + , m_PathFinder(a_Width, a_Height) + , m_PathfinderActivated(false) + , m_JumpCoolDown(0) + , m_IdleInterval(0) + , m_DestroyTimer(0) + , m_MobType(a_MobType) + , m_CustomName("") + , m_CustomNameAlwaysVisible(false) + , m_SoundHurt(a_SoundHurt) + , m_SoundDeath(a_SoundDeath) + , m_AttackRate(3) + , m_AttackDamage(1) + , m_AttackRange(1) + , m_AttackCoolDownTicksLeft(0) + , m_SightDistance(25) + , m_DropChanceWeapon(0.085f) + , m_DropChanceHelmet(0.085f) + , m_DropChanceChestplate(0.085f) + , m_DropChanceLeggings(0.085f) + , m_DropChanceBoots(0.085f) + , m_CanPickUpLoot(true) + , m_TicksSinceLastDamaged(100) + , m_RelativeWalkSpeed(1) + , m_Age(1) + , m_AgingTimer(20 * 60 * 20) // about 20 minutes + , m_WasLastTargetAPlayer(false) + , m_LeashedTo(nullptr) + , m_LeashToPos(nullptr) + , m_IsLeashActionJustDone(false) + , m_CanBeLeashed(GetMobFamily() == eFamily::mfPassive) + , m_Target(nullptr) + , m_CurrentTickControllingBehavior(nullptr) { - if (!a_ConfigName.empty()) - { - GetMonsterConfig(a_ConfigName); - } + if (!a_ConfigName.empty()) + { + GetMonsterConfig(a_ConfigName); + } } @@ -132,7 +132,7 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A cMonster::~cMonster() { - ASSERT(GetTarget() == nullptr); + ASSERT(GetTarget() == nullptr); } @@ -141,19 +141,19 @@ cMonster::~cMonster() void cMonster::Destroy(bool a_ShouldBroadcast) { - if (IsLeashed()) - { - cEntity * LeashedTo = GetLeashedTo(); - Unleash(false, a_ShouldBroadcast); - - // Remove leash knot if there are no more mobs leashed to - if (!LeashedTo->HasAnyMobLeashed() && LeashedTo->IsLeashKnot()) - { - LeashedTo->Destroy(); - } - } - - super::Destroy(a_ShouldBroadcast); + if (IsLeashed()) + { + cEntity * LeashedTo = GetLeashedTo(); + Unleash(false, a_ShouldBroadcast); + + // Remove leash knot if there are no more mobs leashed to + if (!LeashedTo->HasAnyMobLeashed() && LeashedTo->IsLeashKnot()) + { + LeashedTo->Destroy(); + } + } + + super::Destroy(a_ShouldBroadcast); } @@ -162,8 +162,8 @@ void cMonster::Destroy(bool a_ShouldBroadcast) void cMonster::Destroyed() { - SetTarget(nullptr); // Tell them we're no longer targeting them. - super::Destroyed(); + SetTarget(nullptr); // Tell them we're no longer targeting them. + super::Destroyed(); } @@ -172,12 +172,12 @@ void cMonster::Destroyed() void cMonster::SpawnOn(cClientHandle & a_Client) { - a_Client.SendSpawnMob(*this); + a_Client.SendSpawnMob(*this); - if (IsLeashed()) - { - a_Client.SendLeashEntity(*this, *this->GetLeashedTo()); - } + if (IsLeashed()) + { + a_Client.SendLeashEntity(*this, *this->GetLeashedTo()); + } } @@ -186,71 +186,71 @@ void cMonster::SpawnOn(cClientHandle & a_Client) void cMonster::MoveToWayPoint(cChunk & a_Chunk) { - if ((m_NextWayPointPosition - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS) - { - return; - } - - if (m_JumpCoolDown <= 0) - { - if (DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y))) - { - if ( - (IsOnGround() && (GetSpeed().SqrLength() <= 0.5)) || // If walking on the ground, we need to slow down first, otherwise we miss the jump - IsSwimming() - ) - { - m_bOnGround = false; - m_JumpCoolDown = 20; - AddPosY(1.6); // Jump!! - SetSpeedY(1); - SetSpeedX(3.2 * (m_NextWayPointPosition.x - GetPosition().x)); // Move forward in a preset speed. - SetSpeedZ(3.2 * (m_NextWayPointPosition.z - GetPosition().z)); // The numbers were picked based on trial and error - } - } - } - else - { - --m_JumpCoolDown; - } - - Vector3d Distance = m_NextWayPointPosition - GetPosition(); - if ((std::abs(Distance.x) > 0.05) || (std::abs(Distance.z) > 0.05)) - { - Distance.y = 0; - Distance.Normalize(); - - if (m_bOnGround) - { - Distance *= 2.5f; - } - else if (IsSwimming()) - { - Distance *= 1.3f; - } - else - { - // Don't let the mob move too much if he's falling. - Distance *= 0.25f; - } - // Apply walk speed: - Distance *= m_RelativeWalkSpeed; - /* Reduced default speed. - Close to Vanilla, easier for mobs to follow m_NextWayPointPositions, hence - better pathfinding. */ - Distance *= 0.5; - AddSpeedX(Distance.x); - AddSpeedZ(Distance.z); - } - - // Speed up leashed mobs getting far from player - if (IsLeashed() && GetLeashedTo()->IsPlayer()) - { - Distance = GetLeashedTo()->GetPosition() - GetPosition(); - Distance.Normalize(); - AddSpeedX(Distance.x); - AddSpeedZ(Distance.z); - } + if ((m_NextWayPointPosition - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS) + { + return; + } + + if (m_JumpCoolDown <= 0) + { + if (DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y))) + { + if ( + (IsOnGround() && (GetSpeed().SqrLength() <= 0.5)) || // If walking on the ground, we need to slow down first, otherwise we miss the jump + IsSwimming() + ) + { + m_bOnGround = false; + m_JumpCoolDown = 20; + AddPosY(1.6); // Jump!! + SetSpeedY(1); + SetSpeedX(3.2 * (m_NextWayPointPosition.x - GetPosition().x)); // Move forward in a preset speed. + SetSpeedZ(3.2 * (m_NextWayPointPosition.z - GetPosition().z)); // The numbers were picked based on trial and error + } + } + } + else + { + --m_JumpCoolDown; + } + + Vector3d Distance = m_NextWayPointPosition - GetPosition(); + if ((std::abs(Distance.x) > 0.05) || (std::abs(Distance.z) > 0.05)) + { + Distance.y = 0; + Distance.Normalize(); + + if (m_bOnGround) + { + Distance *= 2.5f; + } + else if (IsSwimming()) + { + Distance *= 1.3f; + } + else + { + // Don't let the mob move too much if he's falling. + Distance *= 0.25f; + } + // Apply walk speed: + Distance *= m_RelativeWalkSpeed; + /* Reduced default speed. + Close to Vanilla, easier for mobs to follow m_NextWayPointPositions, hence + better pathfinding. */ + Distance *= 0.5; + AddSpeedX(Distance.x); + AddSpeedZ(Distance.z); + } + + // Speed up leashed mobs getting far from player + if (IsLeashed() && GetLeashedTo()->IsPlayer()) + { + Distance = GetLeashedTo()->GetPosition() - GetPosition(); + Distance.Normalize(); + AddSpeedX(Distance.x); + AddSpeedZ(Distance.z); + } } @@ -266,8 +266,8 @@ void cMonster::MoveToWayPoint(cChunk & a_Chunk) void cMonster::MoveToPosition(const Vector3d & a_Position) { - m_FinalDestination = a_Position; - m_PathfinderActivated = true; + m_FinalDestination = a_Position; + m_PathfinderActivated = true; } @@ -276,7 +276,7 @@ void cMonster::MoveToPosition(const Vector3d & a_Position) void cMonster::StopMovingToPosition() { - m_PathfinderActivated = false; + m_PathfinderActivated = false; } @@ -285,138 +285,117 @@ void cMonster::StopMovingToPosition() void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { - m_NearestPlayerIsStale = true; - super::Tick(a_Dt, a_Chunk); - if (!IsTicking()) - { - // The base class tick destroyed us - return; - } - GET_AND_VERIFY_CURRENT_CHUNK(Chunk, POSX_TOINT, POSZ_TOINT); - - ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == GetWorld()))); - if (m_AttackCoolDownTicksLeft > 0) - { - m_AttackCoolDownTicksLeft -= 1; - } - - if (m_Health <= 0) - { - // The mob is dead, but we're still animating the "puff" they leave when they die - m_DestroyTimer += a_Dt; - if (m_DestroyTimer > std::chrono::seconds(1)) - { - Destroy(true); - } - return; - } - - if (m_TicksSinceLastDamaged < 100) - { - ++m_TicksSinceLastDamaged; - } - if ((GetTarget() != nullptr)) - { - ASSERT(GetTarget()->IsTicking()); - - if (GetTarget()->IsPlayer()) - { - if (!static_cast<cPlayer *>(GetTarget())->CanMobsTarget()) - { - SetTarget(nullptr); - m_EMState = IDLE; - } - } - } - - bool a_IsFollowingPath = false; - if (m_PathfinderActivated) - { - if (ReachedFinalDestination() || (m_LeashToPos != nullptr)) - { - StopMovingToPosition(); // Simply sets m_PathfinderActivated to false. - } - else - { - // Note that m_NextWayPointPosition is actually returned by GetNextWayPoint) - switch (m_PathFinder.GetNextWayPoint(*Chunk, GetPosition(), &m_FinalDestination, &m_NextWayPointPosition, m_EMState == IDLE ? true : false)) - { - case ePathFinderStatus::PATH_FOUND: - { - /* If I burn in daylight, and I won't burn where I'm standing, and I'll burn in my next position, and at least one of those is true: - 1. I am idle - 2. I was not hurt by a player recently. - Then STOP. */ - if ( - (GetBehaviorDayLightBurner() != nullptr) && ((m_TicksSinceLastDamaged >= 100) || (m_EMState == IDLE)) && - GetBehaviorDayLightBurner()->WouldBurnAt(m_NextWayPointPosition, *Chunk) && - !(GetBehaviorDayLightBurner()->WouldBurnAt(GetPosition(), *Chunk)) - ) - { - // If we burn in daylight, and we would burn at the next step, and we won't burn where we are right now, and we weren't provoked recently: - StopMovingToPosition(); - } - else - { - a_IsFollowingPath = true; // Used for proper body / head orientation only. - MoveToWayPoint(*Chunk); - } - break; - } - case ePathFinderStatus::PATH_NOT_FOUND: - { - StopMovingToPosition(); - break; - } - default: - { - - } - } - } - } - - SetPitchAndYawFromDestination(a_IsFollowingPath); - - switch (m_EMState) - { - case IDLE: - { - // If enemy passive we ignore checks for player visibility. - InStateIdle(a_Dt, a_Chunk); - break; - } - case CHASING: - { - // If we do not see a player anymore skip chasing action. - InStateChasing(a_Dt, a_Chunk); - break; - } - case ESCAPING: - { - InStateEscaping(a_Dt, a_Chunk); - break; - } - case ATTACKING: break; - } // switch (m_EMState) - - // Leash calculations - if ((m_TicksAlive % LEASH_ACTIONS_TICK_STEP) == 0) - { - CalcLeashActions(); - } - - BroadcastMovementUpdate(); - - if (m_AgingTimer > 0) - { - m_AgingTimer--; - if ((m_AgingTimer <= 0) && IsBaby()) - { - SetAge(1); - m_World->BroadcastEntityMetadata(*this); - } - } + m_NearestPlayerIsStale = true; + super::Tick(a_Dt, a_Chunk); + if (!IsTicking()) + { + // The base class tick destroyed us + return; + } + GET_AND_VERIFY_CURRENT_CHUNK(Chunk, POSX_TOINT, POSZ_TOINT); + + ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == GetWorld()))); + if (m_AttackCoolDownTicksLeft > 0) + { + m_AttackCoolDownTicksLeft -= 1; + } + + if (m_Health <= 0) + { + // The mob is dead, but we're still animating the "puff" they leave when they die + m_DestroyTimer += a_Dt; + if (m_DestroyTimer > std::chrono::seconds(1)) + { + Destroy(true); + } + return; + } + + if (m_TicksSinceLastDamaged < 100) + { + ++m_TicksSinceLastDamaged; + } + if ((GetTarget() != nullptr)) + { + ASSERT(GetTarget()->IsTicking()); + + if (GetTarget()->IsPlayer()) + { + if (!static_cast<cPlayer *>(GetTarget())->CanMobsTarget()) + { + SetTarget(nullptr); + } + } + } + + bool a_IsFollowingPath = false; + if (m_PathfinderActivated) + { + if (ReachedFinalDestination() || (m_LeashToPos != nullptr)) + { + StopMovingToPosition(); // Simply sets m_PathfinderActivated to false. + } + else + { + // mobToDo fix dont care + // Note that m_NextWayPointPosition is actually returned by GetNextWayPoint) + switch (m_PathFinder.GetNextWayPoint(*Chunk, GetPosition(), &m_FinalDestination, &m_NextWayPointPosition, false)) + { + case ePathFinderStatus::PATH_FOUND: + { + /* If I burn in daylight, and I won't burn where I'm standing, and I'll burn in my next position, and at least one of those is true: + 1. I am idle + 2. I was not hurt by a player recently. + Then STOP. */ + if ( + //mobTodo emstate + (GetBehaviorDayLightBurner() != nullptr) && (m_TicksSinceLastDamaged >= 100) && + GetBehaviorDayLightBurner()->WouldBurnAt(m_NextWayPointPosition, *Chunk) && + !(GetBehaviorDayLightBurner()->WouldBurnAt(GetPosition(), *Chunk)) + ) + { + // If we burn in daylight, and we would burn at the next step, and we won't burn where we are right now, and we weren't provoked recently: + StopMovingToPosition(); + } + else + { + a_IsFollowingPath = true; // Used for proper body / head orientation only. + MoveToWayPoint(*Chunk); + } + break; + } + case ePathFinderStatus::PATH_NOT_FOUND: + { + StopMovingToPosition(); + break; + } + default: + { + + } + } + } + } + + SetPitchAndYawFromDestination(a_IsFollowingPath); + + // Leash calculations + if ((m_TicksAlive % LEASH_ACTIONS_TICK_STEP) == 0) + { + CalcLeashActions(); + } + + BroadcastMovementUpdate(); + + if (m_AgingTimer > 0) + { + m_AgingTimer--; + if ((m_AgingTimer <= 0) && IsBaby()) + { + SetAge(1); + m_World->BroadcastEntityMetadata(*this); + } + } } @@ -425,31 +404,31 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cMonster::CalcLeashActions() { - // This mob just spotted in the world and [m_LeashToPos not null] shows that should be leashed to a leash knot at m_LeashToPos. - // This keeps trying until knot is found. Leash knot may be in a different chunk that needn't or can't be loaded yet. - if (!IsLeashed() && (m_LeashToPos != nullptr)) - { - auto LeashKnot = cLeashKnot::FindKnotAtPos(*m_World, { FloorC(m_LeashToPos->x), FloorC(m_LeashToPos->y), FloorC(m_LeashToPos->z) }); - if (LeashKnot != nullptr) - { - LeashTo(LeashKnot); - SetLeashToPos(nullptr); - } - } - else if (IsLeashed()) // Mob is already leashed to an entity: follow it. - { - // TODO: leashed mobs in vanilla can move around up to 5 blocks distance from leash origin - MoveToPosition(m_LeashedTo->GetPosition()); - - // If distance to target > 10 break leash - Vector3f a_Distance(m_LeashedTo->GetPosition() - GetPosition()); - double Distance(a_Distance.Length()); - if (Distance > 10.0) - { - LOGD("Leash broken (distance)"); - Unleash(false); - } - } + // This mob just spotted in the world and [m_LeashToPos not null] shows that should be leashed to a leash knot at m_LeashToPos. + // This keeps trying until knot is found. Leash knot may be in a different chunk that needn't or can't be loaded yet. + if (!IsLeashed() && (m_LeashToPos != nullptr)) + { + auto LeashKnot = cLeashKnot::FindKnotAtPos(*m_World, { FloorC(m_LeashToPos->x), FloorC(m_LeashToPos->y), FloorC(m_LeashToPos->z) }); + if (LeashKnot != nullptr) + { + LeashTo(LeashKnot); + SetLeashToPos(nullptr); + } + } + else if (IsLeashed()) // Mob is already leashed to an entity: follow it. + { + // TODO: leashed mobs in vanilla can move around up to 5 blocks distance from leash origin + MoveToPosition(m_LeashedTo->GetPosition()); + + // If distance to target > 10 break leash + Vector3f a_Distance(m_LeashedTo->GetPosition() - GetPosition()); + double Distance(a_Distance.Length()); + if (Distance > 10.0) + { + LOGD("Leash broken (distance)"); + Unleash(false); + } + } } @@ -458,52 +437,52 @@ void cMonster::CalcLeashActions() void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath) { - Vector3d BodyDistance; - if (!a_IsFollowingPath && (GetTarget() != nullptr)) - { - BodyDistance = GetTarget()->GetPosition() - GetPosition(); - } - else - { - BodyDistance = m_NextWayPointPosition - GetPosition(); - } - double BodyRotation, BodyPitch; - BodyDistance.Normalize(); - VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, BodyRotation, BodyPitch); - SetYaw(BodyRotation); - - Vector3d HeadDistance; - if (GetTarget() != nullptr) - { - if (GetTarget()->IsPlayer()) // Look at a player - { - HeadDistance = GetTarget()->GetPosition() - GetPosition(); - } - else // Look at some other entity - { - HeadDistance = GetTarget()->GetPosition() - GetPosition(); - // HeadDistance.y = GetTarget()->GetPosY() + GetHeight(); - } - } - else // Look straight - { - HeadDistance = BodyDistance; - HeadDistance.y = 0; - } - - double HeadRotation, HeadPitch; - HeadDistance.Normalize(); - VectorToEuler(HeadDistance.x, HeadDistance.y, HeadDistance.z, HeadRotation, HeadPitch); - if ((std::abs(BodyRotation - HeadRotation) < 70) && (std::abs(HeadPitch) < 60)) - { - SetHeadYaw(HeadRotation); - SetPitch(-HeadPitch); - } - else - { - SetHeadYaw(BodyRotation); - SetPitch(0); - } + Vector3d BodyDistance; + if (!a_IsFollowingPath && (GetTarget() != nullptr)) + { + BodyDistance = GetTarget()->GetPosition() - GetPosition(); + } + else + { + BodyDistance = m_NextWayPointPosition - GetPosition(); + } + double BodyRotation, BodyPitch; + BodyDistance.Normalize(); + VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, BodyRotation, BodyPitch); + SetYaw(BodyRotation); + + Vector3d HeadDistance; + if (GetTarget() != nullptr) + { + if (GetTarget()->IsPlayer()) // Look at a player + { + HeadDistance = GetTarget()->GetPosition() - GetPosition(); + } + else // Look at some other entity + { + HeadDistance = GetTarget()->GetPosition() - GetPosition(); + // HeadDistance.y = GetTarget()->GetPosY() + GetHeight(); + } + } + else // Look straight + { + HeadDistance = BodyDistance; + HeadDistance.y = 0; + } + + double HeadRotation, HeadPitch; + HeadDistance.Normalize(); + VectorToEuler(HeadDistance.x, HeadDistance.y, HeadDistance.z, HeadRotation, HeadPitch); + if ((std::abs(BodyRotation - HeadRotation) < 70) && (std::abs(HeadPitch) < 60)) + { + SetHeadYaw(HeadRotation); + SetPitch(-HeadPitch); + } + else + { + SetHeadYaw(BodyRotation); + SetPitch(0); + } } @@ -512,8 +491,8 @@ void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath) void cMonster::HandleFalling() { - m_bTouchGround = IsOnGround(); - super::HandleFalling(); + m_bTouchGround = IsOnGround(); + super::HandleFalling(); } @@ -522,27 +501,27 @@ void cMonster::HandleFalling() int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ) { - int PosY = POSY_TOINT; - PosY = Clamp(PosY, 0, cChunkDef::Height); - - if (!cBlockInfo::IsSolid(m_World->GetBlock(FloorC(a_PosX), PosY, FloorC(a_PosZ)))) - { - while (!cBlockInfo::IsSolid(m_World->GetBlock(FloorC(a_PosX), PosY, FloorC(a_PosZ))) && (PosY > 0)) - { - PosY--; - } - - return PosY + 1; - } - else - { - while ((PosY < cChunkDef::Height) && cBlockInfo::IsSolid(m_World->GetBlock(static_cast<int>(floor(a_PosX)), PosY, static_cast<int>(floor(a_PosZ))))) - { - PosY++; - } - - return PosY; - } + int PosY = POSY_TOINT; + PosY = Clamp(PosY, 0, cChunkDef::Height); + + if (!cBlockInfo::IsSolid(m_World->GetBlock(FloorC(a_PosX), PosY, FloorC(a_PosZ)))) + { + while (!cBlockInfo::IsSolid(m_World->GetBlock(FloorC(a_PosX), PosY, FloorC(a_PosZ))) && (PosY > 0)) + { + PosY--; + } + + return PosY + 1; + } + else + { + while ((PosY < cChunkDef::Height) && cBlockInfo::IsSolid(m_World->GetBlock(static_cast<int>(floor(a_PosX)), PosY, static_cast<int>(floor(a_PosZ))))) + { + PosY++; + } + + return PosY; + } } @@ -551,28 +530,28 @@ int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ) bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) { - if (!super::DoTakeDamage(a_TDI)) - { - return false; - } - - if (!m_SoundHurt.empty() && (m_Health > 0)) - { - m_World->BroadcastSoundEffect(m_SoundHurt, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); - } - - if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn()) - { - if ( - (!a_TDI.Attacker->IsPlayer()) || - (static_cast<cPlayer *>(a_TDI.Attacker)->CanMobsTarget()) - ) - { - SetTarget(static_cast<cPawn*>(a_TDI.Attacker)); - } - m_TicksSinceLastDamaged = 0; - } - return true; + if (!super::DoTakeDamage(a_TDI)) + { + return false; + } + + if (!m_SoundHurt.empty() && (m_Health > 0)) + { + m_World->BroadcastSoundEffect(m_SoundHurt, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); + } + + if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn()) + { + if ( + (!a_TDI.Attacker->IsPlayer()) || + (static_cast<cPlayer *>(a_TDI.Attacker)->CanMobsTarget()) + ) + { + SetTarget(static_cast<cPawn*>(a_TDI.Attacker)); + } + m_TicksSinceLastDamaged = 0; + } + return true; } @@ -581,77 +560,77 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) void cMonster::KilledBy(TakeDamageInfo & a_TDI) { - super::KilledBy(a_TDI); - if (m_SoundHurt != "") - { - m_World->BroadcastSoundEffect(m_SoundDeath, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); - } - int Reward; - switch (m_MobType) - { - // Animals - case mtChicken: - case mtCow: - case mtHorse: - case mtPig: - case mtRabbit: - case mtSheep: - case mtSquid: - case mtMooshroom: - case mtOcelot: - case mtWolf: - { - Reward = GetRandomProvider().RandInt(1, 3); - break; - } - - // Monsters - case mtCaveSpider: - case mtCreeper: - case mtEnderman: - case mtGhast: - case mtGuardian: - case mtSilverfish: - case mtSkeleton: - case mtSpider: - case mtWitch: - case mtZombie: - case mtZombiePigman: - case mtSlime: - case mtMagmaCube: - { - Reward = GetRandomProvider().RandInt(6, 8); - break; - } - case mtBlaze: - { - Reward = 10; - break; - } - - // Bosses - case mtEnderDragon: - { - Reward = 12000; - break; - } - case mtWither: - { - Reward = 50; - break; - } - - default: - { - Reward = 0; - break; - } - } - if ((a_TDI.Attacker != nullptr) && (!IsBaby())) - { - m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Reward); - } - m_DestroyTimer = std::chrono::milliseconds(0); + super::KilledBy(a_TDI); + if (m_SoundHurt != "") + { + m_World->BroadcastSoundEffect(m_SoundDeath, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); + } + int Reward; + switch (m_MobType) + { + // Animals + case mtChicken: + case mtCow: + case mtHorse: + case mtPig: + case mtRabbit: + case mtSheep: + case mtSquid: + case mtMooshroom: + case mtOcelot: + case mtWolf: + { + Reward = GetRandomProvider().RandInt(1, 3); + break; + } + + // Monsters + case mtCaveSpider: + case mtCreeper: + case mtEnderman: + case mtGhast: + case mtGuardian: + case mtSilverfish: + case mtSkeleton: + case mtSpider: + case mtWitch: + case mtZombie: + case mtZombiePigman: + case mtSlime: + case mtMagmaCube: + { + Reward = GetRandomProvider().RandInt(6, 8); + break; + } + case mtBlaze: + { + Reward = 10; + break; + } + + // Bosses + case mtEnderDragon: + { + Reward = 12000; + break; + } + case mtWither: + { + Reward = 50; + break; + } + + default: + { + Reward = 0; + break; + } + } + if ((a_TDI.Attacker != nullptr) && (!IsBaby())) + { + m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Reward); + } + m_DestroyTimer = std::chrono::milliseconds(0); } @@ -660,94 +639,37 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI) void cMonster::OnRightClicked(cPlayer & a_Player) { - super::OnRightClicked(a_Player); - - const cItem & EquippedItem = a_Player.GetEquippedItem(); - if ((EquippedItem.m_ItemType == E_ITEM_NAME_TAG) && !EquippedItem.m_CustomName.empty()) - { - SetCustomName(EquippedItem.m_CustomName); - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - } - - // Using leashes - m_IsLeashActionJustDone = false; - if (IsLeashed() && (GetLeashedTo() == &a_Player)) // a player can only unleash a mob leashed to him - { - Unleash(!a_Player.IsGameModeCreative()); - } - else if (IsLeashed()) - { - // Mob is already leashed but client anticipates the server action and draws a leash link, so we need to send current leash to cancel it - m_World->BroadcastLeashEntity(*this, *this->GetLeashedTo()); - } - else if (CanBeLeashed() && (EquippedItem.m_ItemType == E_ITEM_LEASH)) - { - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - LeashTo(&a_Player); - } -} - - - - - -// Checks to see if EventSeePlayer should be fired -// monster sez: Do I see the player -void cMonster::CheckEventSeePlayer(cChunk & a_Chunk) -{ - // TODO: Rewrite this to use cWorld's DoWithPlayers() - cPlayer * Closest = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance), false); - - if (Closest != nullptr) - { - EventSeePlayer(Closest, a_Chunk); - } -} - - - - - -void cMonster::CheckEventLostPlayer(void) -{ - if (GetTarget() != nullptr) - { - if ((GetTarget()->GetPosition() - GetPosition()).Length() > m_SightDistance) - { - EventLosePlayer(); - } - } - else - { - EventLosePlayer(); - } -} - - - - - -// What to do if player is seen -// default to change state to chasing -void cMonster::EventSeePlayer(cPlayer * a_SeenPlayer, cChunk & a_Chunk) -{ - UNUSED(a_Chunk); - SetTarget(a_SeenPlayer); -} - - - - - -void cMonster::EventLosePlayer(void) -{ - SetTarget(nullptr); + super::OnRightClicked(a_Player); + + const cItem & EquippedItem = a_Player.GetEquippedItem(); + if ((EquippedItem.m_ItemType == E_ITEM_NAME_TAG) && !EquippedItem.m_CustomName.empty()) + { + SetCustomName(EquippedItem.m_CustomName); + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + } + + // Using leashes + m_IsLeashActionJustDone = false; + if (IsLeashed() && (GetLeashedTo() == &a_Player)) // a player can only unleash a mob leashed to him + { + Unleash(!a_Player.IsGameModeCreative()); + } + else if (IsLeashed()) + { + // Mob is already leashed but client anticipates the server action and draws a leash link, so we need to send current leash to cancel it + m_World->BroadcastLeashEntity(*this, *this->GetLeashedTo()); + } + else if (CanBeLeashed() && (EquippedItem.m_ItemType == E_ITEM_LEASH)) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + LeashTo(&a_Player); + } } @@ -756,7 +678,7 @@ void cMonster::EventLosePlayer(void) void cMonster::ResetAttackCooldown() { - m_AttackCoolDownTicksLeft = static_cast<int>(3 * 20 * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every 3 seconds + m_AttackCoolDownTicksLeft = static_cast<int>(3 * 20 * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every 3 seconds } @@ -765,18 +687,18 @@ void cMonster::ResetAttackCooldown() void cMonster::SetCustomName(const AString & a_CustomName) { - m_CustomName = a_CustomName; - - // The maximal length is 64 - if (a_CustomName.length() > 64) - { - m_CustomName = a_CustomName.substr(0, 64); - } - - if (m_World != nullptr) - { - m_World->BroadcastEntityMetadata(*this); - } + m_CustomName = a_CustomName; + + // The maximal length is 64 + if (a_CustomName.length() > 64) + { + m_CustomName = a_CustomName.substr(0, 64); + } + + if (m_World != nullptr) + { + m_World->BroadcastEntityMetadata(*this); + } } @@ -785,11 +707,11 @@ void cMonster::SetCustomName(const AString & a_CustomName) void cMonster::SetCustomNameAlwaysVisible(bool a_CustomNameAlwaysVisible) { - m_CustomNameAlwaysVisible = a_CustomNameAlwaysVisible; - if (m_World != nullptr) - { - m_World->BroadcastEntityMetadata(*this); - } + m_CustomNameAlwaysVisible = a_CustomNameAlwaysVisible; + if (m_World != nullptr) + { + m_World->BroadcastEntityMetadata(*this); + } } @@ -798,7 +720,7 @@ void cMonster::SetCustomNameAlwaysVisible(bool a_CustomNameAlwaysVisible) void cMonster::GetMonsterConfig(const AString & a_Name) { - cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name); + cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name); } @@ -807,7 +729,7 @@ void cMonster::GetMonsterConfig(const AString & a_Name) bool cMonster::IsUndead(void) { - return false; + return false; } @@ -816,17 +738,17 @@ bool cMonster::IsUndead(void) AString cMonster::MobTypeToString(eMonsterType a_MobType) { - // Mob types aren't sorted, so we need to search linearly: - for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) - { - if (g_MobTypeNames[i].m_Type == a_MobType) - { - return g_MobTypeNames[i].m_lcName; - } - } - - // Not found: - return ""; + // Mob types aren't sorted, so we need to search linearly: + for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (g_MobTypeNames[i].m_Type == a_MobType) + { + return g_MobTypeNames[i].m_lcName; + } + } + + // Not found: + return ""; } @@ -835,17 +757,17 @@ AString cMonster::MobTypeToString(eMonsterType a_MobType) AString cMonster::MobTypeToVanillaName(eMonsterType a_MobType) { - // Mob types aren't sorted, so we need to search linearly: - for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) - { - if (g_MobTypeNames[i].m_Type == a_MobType) - { - return g_MobTypeNames[i].m_VanillaName; - } - } - - // Not found: - return ""; + // Mob types aren't sorted, so we need to search linearly: + for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (g_MobTypeNames[i].m_Type == a_MobType) + { + return g_MobTypeNames[i].m_VanillaName; + } + } + + // Not found: + return ""; } @@ -854,17 +776,17 @@ AString cMonster::MobTypeToVanillaName(eMonsterType a_MobType) AString cMonster::MobTypeToVanillaNBT(eMonsterType a_MobType) { - // Mob types aren't sorted, so we need to search linearly: - for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) - { - if (g_MobTypeNames[i].m_Type == a_MobType) - { - return g_MobTypeNames[i].m_VanillaNameNBT; - } - } - - // Not found: - return ""; + // Mob types aren't sorted, so we need to search linearly: + for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (g_MobTypeNames[i].m_Type == a_MobType) + { + return g_MobTypeNames[i].m_VanillaNameNBT; + } + } + + // Not found: + return ""; } @@ -873,37 +795,37 @@ AString cMonster::MobTypeToVanillaNBT(eMonsterType a_MobType) eMonsterType cMonster::StringToMobType(const AString & a_Name) { - AString lcName = StrToLower(a_Name); - - // Search Cuberite name: - for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) - { - if (strcmp(g_MobTypeNames[i].m_lcName, lcName.c_str()) == 0) - { - return g_MobTypeNames[i].m_Type; - } - } - - // Not found. Search Vanilla name: - for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) - { - if (strcmp(StrToLower(g_MobTypeNames[i].m_VanillaName).c_str(), lcName.c_str()) == 0) - { - return g_MobTypeNames[i].m_Type; - } - } - - // Search in NBT name - for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) - { - if (strcmp(StrToLower(g_MobTypeNames[i].m_VanillaNameNBT).c_str(), lcName.c_str()) == 0) - { - return g_MobTypeNames[i].m_Type; - } - } - - // Not found: - return mtInvalidType; + AString lcName = StrToLower(a_Name); + + // Search Cuberite name: + for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (strcmp(g_MobTypeNames[i].m_lcName, lcName.c_str()) == 0) + { + return g_MobTypeNames[i].m_Type; + } + } + + // Not found. Search Vanilla name: + for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (strcmp(StrToLower(g_MobTypeNames[i].m_VanillaName).c_str(), lcName.c_str()) == 0) + { + return g_MobTypeNames[i].m_Type; + } + } + + // Search in NBT name + for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (strcmp(StrToLower(g_MobTypeNames[i].m_VanillaNameNBT).c_str(), lcName.c_str()) == 0) + { + return g_MobTypeNames[i].m_Type; + } + } + + // Not found: + return mtInvalidType; } @@ -912,46 +834,46 @@ eMonsterType cMonster::StringToMobType(const AString & a_Name) cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type) { - // Passive-agressive mobs are counted in mob spawning code as passive - - 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 mtEnderDragon: return mfNoSpawn; - case mtEnderman: return mfHostile; - case mtGhast: return mfHostile; - case mtGiant: return mfNoSpawn; - case mtGuardian: return mfWater; // Just because they have special spawning conditions. If Watertemples have been added, this needs to be edited! - case mtHorse: return mfPassive; - case mtIronGolem: return mfPassive; - case mtMagmaCube: return mfHostile; - case mtMooshroom: return mfHostile; - case mtOcelot: return mfPassive; - case mtPig: return mfPassive; - case mtRabbit: return mfPassive; - case mtSheep: return mfPassive; - case mtSilverfish: return mfHostile; - case mtSkeleton: return mfHostile; - case mtSlime: return mfHostile; - case mtSnowGolem: return mfNoSpawn; - case mtSpider: return mfHostile; - case mtSquid: return mfWater; - case mtVillager: return mfPassive; - case mtWitch: return mfHostile; - case mtWither: return mfNoSpawn; - case mtWolf: return mfHostile; - case mtZombie: return mfHostile; - case mtZombiePigman: return mfHostile; - - case mtInvalidType: break; - } - ASSERT(!"Unhandled mob type"); - return mfUnhandled; + // Passive-agressive mobs are counted in mob spawning code as passive + + 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 mtEnderDragon: return mfNoSpawn; + case mtEnderman: return mfHostile; + case mtGhast: return mfHostile; + case mtGiant: return mfNoSpawn; + case mtGuardian: return mfWater; // Just because they have special spawning conditions. If Watertemples have been added, this needs to be edited! + case mtHorse: return mfPassive; + case mtIronGolem: return mfPassive; + case mtMagmaCube: return mfHostile; + case mtMooshroom: return mfHostile; + case mtOcelot: return mfPassive; + case mtPig: return mfPassive; + case mtRabbit: return mfPassive; + case mtSheep: return mfPassive; + case mtSilverfish: return mfHostile; + case mtSkeleton: return mfHostile; + case mtSlime: return mfHostile; + case mtSnowGolem: return mfNoSpawn; + case mtSpider: return mfHostile; + case mtSquid: return mfWater; + case mtVillager: return mfPassive; + case mtWitch: return mfHostile; + case mtWither: return mfNoSpawn; + case mtWolf: return mfHostile; + case mtZombie: return mfHostile; + case mtZombiePigman: return mfHostile; + + case mtInvalidType: break; + } + ASSERT(!"Unhandled mob type"); + return mfUnhandled; } @@ -960,17 +882,17 @@ cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type) 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; - case mfNoSpawn: return -1; - case mfUnhandled: break; - } - ASSERT(!"Unhandled mob family"); - return -1; + switch (a_MobFamily) + { + case mfHostile: return 40; + case mfPassive: return 40; + case mfAmbient: return 40; + case mfWater: return 400; + case mfNoSpawn: return -1; + case mfUnhandled: break; + } + ASSERT(!"Unhandled mob family"); + return -1; } @@ -981,27 +903,27 @@ int cMonster::GetSpawnDelay(cMonster::eFamily a_MobFamily) /** Sets the target. */ void cMonster::SetTarget (cPawn * a_NewTarget) { - ASSERT((a_NewTarget == nullptr) || (IsTicking())); - if (m_Target == a_NewTarget) - { - return; - } - cPawn * OldTarget = m_Target; - m_Target = a_NewTarget; - - if (OldTarget != nullptr) - { - // Notify the old target that we are no longer targeting it. - OldTarget->NoLongerTargetingMe(this); - } - - if (a_NewTarget != nullptr) - { - ASSERT(a_NewTarget->IsTicking()); - // Notify the new target that we are now targeting it. - m_Target->TargetingMe(this); - m_WasLastTargetAPlayer = m_Target->IsPlayer(); - } + ASSERT((a_NewTarget == nullptr) || (IsTicking())); + if (m_Target == a_NewTarget) + { + return; + } + cPawn * OldTarget = m_Target; + m_Target = a_NewTarget; + + if (OldTarget != nullptr) + { + // Notify the old target that we are no longer targeting it. + OldTarget->NoLongerTargetingMe(this); + } + + if (a_NewTarget != nullptr) + { + ASSERT(a_NewTarget->IsTicking()); + // Notify the new target that we are now targeting it. + m_Target->TargetingMe(this); + m_WasLastTargetAPlayer = m_Target->IsPlayer(); + } } @@ -1011,7 +933,7 @@ void cMonster::SetTarget (cPawn * a_NewTarget) void cMonster::UnsafeUnsetTarget() { - m_Target = nullptr; + m_Target = nullptr; } @@ -1020,7 +942,7 @@ void cMonster::UnsafeUnsetTarget() cPawn * cMonster::GetTarget() { - return m_Target; + return m_Target; } @@ -1029,7 +951,7 @@ cPawn * cMonster::GetTarget() bool cMonster::IsPathFinderActivated() const { - return m_PathfinderActivated; + return m_PathfinderActivated; } @@ -1038,7 +960,7 @@ bool cMonster::IsPathFinderActivated() const cBehaviorAggressive * cMonster::GetBehaviorAggressive() { - return nullptr; + return nullptr; } @@ -1047,7 +969,7 @@ cBehaviorAggressive * cMonster::GetBehaviorAggressive() cBehaviorBreeder * cMonster::GetBehaviorBreeder() { - return nullptr; + return nullptr; } @@ -1056,7 +978,7 @@ cBehaviorBreeder * cMonster::GetBehaviorBreeder() const cBehaviorBreeder * cMonster::GetBehaviorBreeder() const { - return nullptr; + return nullptr; } @@ -1065,7 +987,7 @@ const cBehaviorBreeder * cMonster::GetBehaviorBreeder() const cBehaviorChaser * cMonster::GetBehaviorChaser() { - return nullptr; + return nullptr; } @@ -1074,7 +996,7 @@ cBehaviorChaser * cMonster::GetBehaviorChaser() cBehaviorStriker * cMonster::GetBehaviorStriker() { - return nullptr; + return nullptr; } @@ -1083,7 +1005,7 @@ cBehaviorStriker * cMonster::GetBehaviorStriker() cBehaviorWanderer * cMonster::GetBehaviorWanderer() { - return nullptr; + return nullptr; } @@ -1092,7 +1014,7 @@ cBehaviorWanderer * cMonster::GetBehaviorWanderer() cBehaviorDayLightBurner * cMonster::GetBehaviorDayLightBurner() { - return nullptr; + return nullptr; } @@ -1101,9 +1023,9 @@ cBehaviorDayLightBurner * cMonster::GetBehaviorDayLightBurner() void cMonster::InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2) { - UNUSED(a_Parent1); - UNUSED(a_Parent2); - return; + UNUSED(a_Parent1); + UNUSED(a_Parent2); + return; } @@ -1112,7 +1034,7 @@ void cMonster::InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2) void cMonster::GetFollowedItems(cItems & a_Items) { - return; + return; } @@ -1121,7 +1043,7 @@ void cMonster::GetFollowedItems(cItems & a_Items) void cMonster::GetBreedingItems(cItems & a_Items) { - return GetFollowedItems(a_Items); + return GetFollowedItems(a_Items); } @@ -1130,17 +1052,17 @@ void cMonster::GetBreedingItems(cItems & a_Items) cPlayer * cMonster::GetNearestPlayer() { - if (m_NearestPlayerIsStale) - { - // TODO: Rewrite this to use cWorld's DoWithPlayers() - m_NearestPlayer = GetWorld()->FindClosestPlayer(GetPosition(), static_cast<float>(GetSightDistance())); - m_NearestPlayerIsStale = false; - } - if ((m_NearestPlayer != nullptr) && (!m_NearestPlayer->IsTicking())) - { - m_NearestPlayer = nullptr; - } - return m_NearestPlayer; + if (m_NearestPlayerIsStale) + { + // TODO: Rewrite this to use cWorld's DoWithPlayers() + m_NearestPlayer = GetWorld()->FindClosestPlayer(GetPosition(), static_cast<float>(GetSightDistance())); + m_NearestPlayerIsStale = false; + } + if ((m_NearestPlayer != nullptr) && (!m_NearestPlayer->IsTicking())) + { + m_NearestPlayer = nullptr; + } + return m_NearestPlayer; } @@ -1149,84 +1071,84 @@ cPlayer * cMonster::GetNearestPlayer() std::unique_ptr<cMonster> cMonster::NewMonsterFromType(eMonsterType a_MobType) { - auto & Random = GetRandomProvider(); - - // Create the mob entity - switch (a_MobType) - { - case mtMagmaCube: - { - return cpp14::make_unique<cMagmaCube>(1 << Random.RandInt(2)); // Size 1, 2 or 4 - } - case mtSlime: - { - return cpp14::make_unique<cSlime>(1 << Random.RandInt(2)); // Size 1, 2 or 4 - } - case mtSkeleton: - { - // TODO: Actual detection of spawning in Nether - return cpp14::make_unique<cSkeleton>(false); - } - case mtVillager: - { - int VillagerType = Random.RandInt(6); - if (VillagerType == 6) - { - // Give farmers a better chance of spawning - VillagerType = 0; - } - - return cpp14::make_unique<cVillager>(static_cast<cVillager::eVillagerType>(VillagerType)); - } - case mtHorse: - { - // Horses take a type (species), a colour, and a style (dots, stripes, etc.) - int HorseType = Random.RandInt(7); - int HorseColor = Random.RandInt(6); - int HorseStyle = Random.RandInt(4); - int HorseTameTimes = Random.RandInt(1, 6); - - if ((HorseType == 5) || (HorseType == 6) || (HorseType == 7)) - { - // Increase chances of normal horse (zero) - HorseType = 0; - } - - return cpp14::make_unique<cHorse>(HorseType, HorseColor, HorseStyle, HorseTameTimes); - } - - case mtBat: return cpp14::make_unique<cBat>(); - case mtBlaze: return cpp14::make_unique<cBlaze>(); - case mtCaveSpider: return cpp14::make_unique<cCaveSpider>(); - case mtChicken: return cpp14::make_unique<cChicken>(); - case mtCow: return cpp14::make_unique<cCow>(); - case mtCreeper: return cpp14::make_unique < cCreeper>(); - case mtEnderDragon: return cpp14::make_unique<cEnderDragon>(); - case mtEnderman: return cpp14::make_unique<cEnderman>(); - case mtGhast: return cpp14::make_unique<cGhast>(); - case mtGiant: return cpp14::make_unique<cGiant>(); - case mtGuardian: return cpp14::make_unique<cGuardian>(); - case mtIronGolem: return cpp14::make_unique<cIronGolem>(); - case mtMooshroom: return cpp14::make_unique<cMooshroom>(); - case mtOcelot: return cpp14::make_unique<cOcelot>(); - case mtPig: return cpp14::make_unique<cPig>(); - case mtRabbit: return cpp14::make_unique<cRabbit>(); - case mtSheep: return cpp14::make_unique<cSheep>(); - case mtSilverfish: return cpp14::make_unique<cSilverfish>(); - case mtSnowGolem: return cpp14::make_unique<cSnowGolem>(); - case mtSpider: return cpp14::make_unique<cSpider>(); - case mtSquid: return cpp14::make_unique<cSquid>(); - case mtWitch: return cpp14::make_unique<cWitch>(); - case mtWither: return cpp14::make_unique<cWither>(); - case mtWolf: return cpp14::make_unique<cWolf>(); - case mtZombie: return cpp14::make_unique<cZombie>(false); // TODO: Infected zombie parameter - case mtZombiePigman: return cpp14::make_unique<cZombiePigman>(); - default: - { - ASSERT(!"Unhandled mob type whilst trying to spawn mob!"); - return nullptr; - } - } + auto & Random = GetRandomProvider(); + + // Create the mob entity + switch (a_MobType) + { + case mtMagmaCube: + { + return cpp14::make_unique<cMagmaCube>(1 << Random.RandInt(2)); // Size 1, 2 or 4 + } + case mtSlime: + { + return cpp14::make_unique<cSlime>(1 << Random.RandInt(2)); // Size 1, 2 or 4 + } + case mtSkeleton: + { + // TODO: Actual detection of spawning in Nether + return cpp14::make_unique<cSkeleton>(false); + } + case mtVillager: + { + int VillagerType = Random.RandInt(6); + if (VillagerType == 6) + { + // Give farmers a better chance of spawning + VillagerType = 0; + } + + return cpp14::make_unique<cVillager>(static_cast<cVillager::eVillagerType>(VillagerType)); + } + case mtHorse: + { + // Horses take a type (species), a colour, and a style (dots, stripes, etc.) + int HorseType = Random.RandInt(7); + int HorseColor = Random.RandInt(6); + int HorseStyle = Random.RandInt(4); + int HorseTameTimes = Random.RandInt(1, 6); + + if ((HorseType == 5) || (HorseType == 6) || (HorseType == 7)) + { + // Increase chances of normal horse (zero) + HorseType = 0; + } + + return cpp14::make_unique<cHorse>(HorseType, HorseColor, HorseStyle, HorseTameTimes); + } + + case mtBat: return cpp14::make_unique<cBat>(); + case mtBlaze: return cpp14::make_unique<cBlaze>(); + case mtCaveSpider: return cpp14::make_unique<cCaveSpider>(); + case mtChicken: return cpp14::make_unique<cChicken>(); + case mtCow: return cpp14::make_unique<cCow>(); + case mtCreeper: return cpp14::make_unique < cCreeper>(); + case mtEnderDragon: return cpp14::make_unique<cEnderDragon>(); + case mtEnderman: return cpp14::make_unique<cEnderman>(); + case mtGhast: return cpp14::make_unique<cGhast>(); + case mtGiant: return cpp14::make_unique<cGiant>(); + case mtGuardian: return cpp14::make_unique<cGuardian>(); + case mtIronGolem: return cpp14::make_unique<cIronGolem>(); + case mtMooshroom: return cpp14::make_unique<cMooshroom>(); + case mtOcelot: return cpp14::make_unique<cOcelot>(); + case mtPig: return cpp14::make_unique<cPig>(); + case mtRabbit: return cpp14::make_unique<cRabbit>(); + case mtSheep: return cpp14::make_unique<cSheep>(); + case mtSilverfish: return cpp14::make_unique<cSilverfish>(); + case mtSnowGolem: return cpp14::make_unique<cSnowGolem>(); + case mtSpider: return cpp14::make_unique<cSpider>(); + case mtSquid: return cpp14::make_unique<cSquid>(); + case mtWitch: return cpp14::make_unique<cWitch>(); + case mtWither: return cpp14::make_unique<cWither>(); + case mtWolf: return cpp14::make_unique<cWolf>(); + case mtZombie: return cpp14::make_unique<cZombie>(false); // TODO: Infected zombie parameter + case mtZombiePigman: return cpp14::make_unique<cZombiePigman>(); + default: + { + ASSERT(!"Unhandled mob type whilst trying to spawn mob!"); + return nullptr; + } + } } @@ -1235,17 +1157,17 @@ std::unique_ptr<cMonster> cMonster::NewMonsterFromType(eMonsterType a_MobType) void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth) { - auto Count = GetRandomProvider().RandInt<unsigned int>(a_Min, a_Max); - auto MaxStackSize = static_cast<unsigned char>(ItemHandler(a_Item)->GetMaxStackSize()); - while (Count > MaxStackSize) - { - a_Drops.emplace_back(a_Item, MaxStackSize, a_ItemHealth); - Count -= MaxStackSize; - } - if (Count > 0) - { - a_Drops.emplace_back(a_Item, Count, a_ItemHealth); - } + auto Count = GetRandomProvider().RandInt<unsigned int>(a_Min, a_Max); + auto MaxStackSize = static_cast<unsigned char>(ItemHandler(a_Item)->GetMaxStackSize()); + while (Count > MaxStackSize) + { + a_Drops.emplace_back(a_Item, MaxStackSize, a_ItemHealth); + Count -= MaxStackSize; + } + if (Count > 0) + { + a_Drops.emplace_back(a_Item, Count, a_ItemHealth); + } } @@ -1254,10 +1176,10 @@ void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned void cMonster::AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short a_Item, short a_ItemHealth) { - if (GetRandomProvider().RandBool(a_Chance / 100.0)) - { - a_Drops.push_back(cItem(a_Item, 1, a_ItemHealth)); - } + if (GetRandomProvider().RandBool(a_Chance / 100.0)) + { + a_Drops.push_back(cItem(a_Item, 1, a_ItemHealth)); + } } @@ -1266,12 +1188,12 @@ void cMonster::AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short void cMonster::AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, unsigned int a_LootingLevel) { - auto & r1 = GetRandomProvider(); - if (r1.RandBool((5 + a_LootingLevel) / 200.0)) - { - size_t Rare = r1.RandInt<size_t>(a_Items.Size() - 1); - a_Drops.push_back(a_Items.at(Rare)); - } + auto & r1 = GetRandomProvider(); + if (r1.RandBool((5 + a_LootingLevel) / 200.0)) + { + size_t Rare = r1.RandInt<size_t>(a_Items.Size() - 1); + a_Drops.push_back(a_Items.at(Rare)); + } } @@ -1280,41 +1202,41 @@ void cMonster::AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, unsigne void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLevel) { - auto & r1 = GetRandomProvider(); - - double LootingBonus = a_LootingLevel / 100.0; - - if (r1.RandBool(m_DropChanceHelmet + LootingBonus)) - { - if (!GetEquippedHelmet().IsEmpty()) - { - a_Drops.push_back(GetEquippedHelmet()); - } - } - - if (r1.RandBool(m_DropChanceChestplate + LootingBonus)) - { - if (!GetEquippedChestplate().IsEmpty()) - { - a_Drops.push_back(GetEquippedChestplate()); - } - } - - if (r1.RandBool(m_DropChanceLeggings + LootingBonus)) - { - if (!GetEquippedLeggings().IsEmpty()) - { - a_Drops.push_back(GetEquippedLeggings()); - } - } - - if (r1.RandBool(m_DropChanceBoots + LootingBonus)) - { - if (!GetEquippedBoots().IsEmpty()) - { - a_Drops.push_back(GetEquippedBoots()); - } - } + auto & r1 = GetRandomProvider(); + + double LootingBonus = a_LootingLevel / 100.0; + + if (r1.RandBool(m_DropChanceHelmet + LootingBonus)) + { + if (!GetEquippedHelmet().IsEmpty()) + { + a_Drops.push_back(GetEquippedHelmet()); + } + } + + if (r1.RandBool(m_DropChanceChestplate + LootingBonus)) + { + if (!GetEquippedChestplate().IsEmpty()) + { + a_Drops.push_back(GetEquippedChestplate()); + } + } + + if (r1.RandBool(m_DropChanceLeggings + LootingBonus)) + { + if (!GetEquippedLeggings().IsEmpty()) + { + a_Drops.push_back(GetEquippedLeggings()); + } + } + + if (r1.RandBool(m_DropChanceBoots + LootingBonus)) + { + if (!GetEquippedBoots().IsEmpty()) + { + a_Drops.push_back(GetEquippedBoots()); + } + } } @@ -1323,13 +1245,13 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLe void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingLevel) { - if (GetRandomProvider().RandBool(m_DropChanceWeapon + (a_LootingLevel / 100.0))) - { - if (!GetEquippedWeapon().IsEmpty()) - { - a_Drops.push_back(GetEquippedWeapon()); - } - } + if (GetRandomProvider().RandBool(m_DropChanceWeapon + (a_LootingLevel / 100.0))) + { + if (!GetEquippedWeapon().IsEmpty()) + { + a_Drops.push_back(GetEquippedWeapon()); + } + } } @@ -1338,7 +1260,7 @@ void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingL cMonster::eFamily cMonster::GetMobFamily(void) const { - return FamilyFromType(m_MobType); + return FamilyFromType(m_MobType); } @@ -1347,22 +1269,22 @@ cMonster::eFamily cMonster::GetMobFamily(void) const void cMonster::LeashTo(cEntity * a_Entity, bool a_ShouldBroadcast) { - // Do nothing if already leashed - if (m_LeashedTo != nullptr) - { - return; - } + // Do nothing if already leashed + if (m_LeashedTo != nullptr) + { + return; + } - m_LeashedTo = a_Entity; + m_LeashedTo = a_Entity; - a_Entity->AddLeashedMob(this); + a_Entity->AddLeashedMob(this); - if (a_ShouldBroadcast) - { - m_World->BroadcastLeashEntity(*this, *a_Entity); - } + if (a_ShouldBroadcast) + { + m_World->BroadcastLeashEntity(*this, *a_Entity); + } - m_IsLeashActionJustDone = true; + m_IsLeashActionJustDone = true; } @@ -1371,29 +1293,29 @@ void cMonster::LeashTo(cEntity * a_Entity, bool a_ShouldBroadcast) void cMonster::Unleash(bool a_ShouldDropLeashPickup, bool a_ShouldBroadcast) { - // Do nothing if not leashed - if (m_LeashedTo == nullptr) - { - return; - } + // Do nothing if not leashed + if (m_LeashedTo == nullptr) + { + return; + } - m_LeashedTo->RemoveLeashedMob(this); + m_LeashedTo->RemoveLeashedMob(this); - m_LeashedTo = nullptr; + m_LeashedTo = nullptr; - if (a_ShouldDropLeashPickup) - { - cItems Pickups; - Pickups.Add(cItem(E_ITEM_LEASH, 1, 0)); - GetWorld()->SpawnItemPickups(Pickups, GetPosX() + 0.5, GetPosY() + 0.5, GetPosZ() + 0.5); - } + if (a_ShouldDropLeashPickup) + { + cItems Pickups; + Pickups.Add(cItem(E_ITEM_LEASH, 1, 0)); + GetWorld()->SpawnItemPickups(Pickups, GetPosX() + 0.5, GetPosY() + 0.5, GetPosZ() + 0.5); + } - if (a_ShouldBroadcast) - { - m_World->BroadcastUnleashEntity(*this); - } + if (a_ShouldBroadcast) + { + m_World->BroadcastUnleashEntity(*this); + } - m_IsLeashActionJustDone = true; + m_IsLeashActionJustDone = true; } @@ -1402,5 +1324,5 @@ void cMonster::Unleash(bool a_ShouldDropLeashPickup, bool a_ShouldBroadcast) void cMonster::Unleash(bool a_ShouldDropLeashPickup) { - Unleash(a_ShouldDropLeashPickup, true); + Unleash(a_ShouldDropLeashPickup, true); } diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index dde51e941..2ff411040 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -4,7 +4,7 @@ #include "../Entities/Pawn.h" #include "MonsterTypes.h" #include "PathFinder.h" - +#include <vector> class cItem; class cClientHandle; @@ -18,336 +18,339 @@ class cBehaviorChaser; class cBehaviorStriker; class cBehaviorWanderer; class cBehaviorDayLightBurner; +class cBehavior; // tolua_begin class cMonster : - public cPawn + public cPawn { - typedef cPawn super; + typedef cPawn super; public: - enum eFamily - { - mfHostile = 0, // Spider, Zombies ... - mfPassive = 1, // Cows, Pigs - mfAmbient = 2, // Bats - mfWater = 3, // Squid, Guardian - - mfNoSpawn, - mfUnhandled, // Nothing. Be sure this is the last and the others are in order - } ; + enum eFamily + { + mfHostile = 0, // Spider, Zombies ... + mfPassive = 1, // Cows, Pigs + mfAmbient = 2, // Bats + mfWater = 3, // Squid, Guardian - // tolua_end + mfNoSpawn, + mfUnhandled, // Nothing. Be sure this is the last and the others are in order + } ; - enum MPersonality{PASSIVE, AGGRESSIVE, COWARDLY} m_EMPersonality; + // tolua_end - /** Creates the mob object. - If a_ConfigName is not empty, the configuration is loaded using GetMonsterConfig() - a_MobType is the type of the mob (also used in the protocol ( http://wiki.vg/Entities#Mobs 2012_12_22)) - a_SoundHurt and a_SoundDeath are assigned into m_SoundHurt and m_SoundDeath, respectively - */ - cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + enum MPersonality{PASSIVE, AGGRESSIVE, COWARDLY} m_EMPersonality; - virtual ~cMonster() override; + /** Creates the mob object. + If a_ConfigName is not empty, the configuration is loaded using GetMonsterConfig() + a_MobType is the type of the mob (also used in the protocol ( http://wiki.vg/Entities#Mobs 2012_12_22)) + a_SoundHurt and a_SoundDeath are assigned into m_SoundHurt and m_SoundDeath, respectively + */ + cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - virtual void Destroy(bool a_ShouldBroadcast = true) override; + virtual ~cMonster() override; - virtual void Destroyed() override; + virtual void Destroy(bool a_ShouldBroadcast = true) override; - CLASS_PROTODEF(cMonster) + virtual void Destroyed() override; - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + CLASS_PROTODEF(cMonster) - virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual void KilledBy(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; - virtual void HandleFalling(void) override; + virtual void OnRightClicked(cPlayer & a_Player) override; - /** Engage pathfinder and tell it to calculate a path to a given position, and move the mob accordingly - Currently, the mob will only start moving to a new position after the position it is currently going to is reached. */ - virtual void MoveToPosition(const Vector3d & a_Position); // tolua_export + virtual void HandleFalling(void) override; - // tolua_begin - eMonsterType GetMobType(void) const { return m_MobType; } - eFamily GetMobFamily(void) const; - // tolua_end + /** Engage pathfinder and tell it to calculate a path to a given position, and move the mob accordingly + Currently, the mob will only start moving to a new position after the position it is currently going to is reached. */ + virtual void MoveToPosition(const Vector3d & a_Position); // tolua_export - virtual void CheckEventSeePlayer(cChunk & a_Chunk); - virtual void EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk); + // tolua_begin + eMonsterType GetMobType(void) const { return m_MobType; } + eFamily GetMobFamily(void) const; + // tolua_end - // tolua_begin + // tolua_begin - /** Returns whether the mob can be leashed. */ - bool CanBeLeashed() const { return m_CanBeLeashed; } + /** Returns whether the mob can be leashed. */ + bool CanBeLeashed() const { return m_CanBeLeashed; } - /** Sets whether the mob can be leashed, for extensibility in plugins */ - void SetCanBeLeashed(bool a_CanBeLeashed) { m_CanBeLeashed = a_CanBeLeashed; } + /** Sets whether the mob can be leashed, for extensibility in plugins */ + void SetCanBeLeashed(bool a_CanBeLeashed) { m_CanBeLeashed = a_CanBeLeashed; } - /** Returns whether the monster is leashed to an entity. */ - bool IsLeashed() const { return (m_LeashedTo != nullptr); } + /** Returns whether the monster is leashed to an entity. */ + bool IsLeashed() const { return (m_LeashedTo != nullptr); } - /** Leash the monster to an entity. */ - void LeashTo(cEntity * a_Entity, bool a_ShouldBroadcast = true); + /** Leash the monster to an entity. */ + void LeashTo(cEntity * a_Entity, bool a_ShouldBroadcast = true); - /** Unleash the monster. Overload for the Unleash(bool, bool) function for plugins */ - void Unleash(bool a_ShouldDropLeashPickup); + /** Unleash the monster. Overload for the Unleash(bool, bool) function for plugins */ + void Unleash(bool a_ShouldDropLeashPickup); - /** Returns the entity to where this mob is leashed, returns nullptr if it's not leashed */ - cEntity * GetLeashedTo() const { return m_LeashedTo; } + /** Returns the entity to where this mob is leashed, returns nullptr if it's not leashed */ + cEntity * GetLeashedTo() const { return m_LeashedTo; } - // tolua_end + // tolua_end - /** Unleash the monster. */ - void Unleash(bool a_ShouldDropLeashPickup, bool a_ShouldBroadcast); + /** Unleash the monster. */ + void Unleash(bool a_ShouldDropLeashPickup, bool a_ShouldBroadcast); - /** Sets entity position to where is leashed this mob */ - void SetLeashToPos(Vector3d * pos) { m_LeashToPos = std::unique_ptr<Vector3d>(pos); } + /** Sets entity position to where is leashed this mob */ + void SetLeashToPos(Vector3d * pos) { m_LeashToPos = std::unique_ptr<Vector3d>(pos); } - /** Gets entity position to where mob should be leashed */ - Vector3d * GetLeashToPos() const { return m_LeashToPos.get(); } + /** Gets entity position to where mob should be leashed */ + Vector3d * GetLeashToPos() const { return m_LeashToPos.get(); } - /** Reads the monster configuration for the specified monster name and assigns it to this object. */ - void GetMonsterConfig(const AString & a_Name); + /** Reads the monster configuration for the specified monster name and assigns it to this object. */ + void GetMonsterConfig(const AString & a_Name); - /** Returns whether this mob is undead (skeleton, zombie, etc.) */ - virtual bool IsUndead(void); + /** Returns whether this mob is undead (skeleton, zombie, etc.) */ + virtual bool IsUndead(void); - virtual void EventLosePlayer(void); - virtual void CheckEventLostPlayer(void); + int GetAttackRate() { return static_cast<int>(m_AttackRate); } + void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; } + void SetAttackRange(int a_AttackRange) { m_AttackRange = a_AttackRange; } + void SetAttackDamage(int a_AttackDamage) { m_AttackDamage = a_AttackDamage; } + void SetSightDistance(int a_SightDistance) { m_SightDistance = a_SightDistance; } + int GetSightDistance() { return m_SightDistance; } - int GetAttackRate() { return static_cast<int>(m_AttackRate); } - void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; } - void SetAttackRange(int a_AttackRange) { m_AttackRange = a_AttackRange; } - void SetAttackDamage(int a_AttackDamage) { m_AttackDamage = a_AttackDamage; } - void SetSightDistance(int a_SightDistance) { m_SightDistance = a_SightDistance; } - int GetSightDistance() { return m_SightDistance; } + float GetDropChanceWeapon() { return m_DropChanceWeapon; } + float GetDropChanceHelmet() { return m_DropChanceHelmet; } + float GetDropChanceChestplate() { return m_DropChanceChestplate; } + float GetDropChanceLeggings() { return m_DropChanceLeggings; } + float GetDropChanceBoots() { return m_DropChanceBoots; } + bool CanPickUpLoot() { return m_CanPickUpLoot; } + void SetDropChanceWeapon(float a_DropChanceWeapon) { m_DropChanceWeapon = a_DropChanceWeapon; } + void SetDropChanceHelmet(float a_DropChanceHelmet) { m_DropChanceHelmet = a_DropChanceHelmet; } + void SetDropChanceChestplate(float a_DropChanceChestplate) { m_DropChanceChestplate = a_DropChanceChestplate; } + void SetDropChanceLeggings(float a_DropChanceLeggings) { m_DropChanceLeggings = a_DropChanceLeggings; } + void SetDropChanceBoots(float a_DropChanceBoots) { m_DropChanceBoots = a_DropChanceBoots; } + void SetCanPickUpLoot(bool a_CanPickUpLoot) { m_CanPickUpLoot = a_CanPickUpLoot; } + void ResetAttackCooldown(); - float GetDropChanceWeapon() { return m_DropChanceWeapon; } - float GetDropChanceHelmet() { return m_DropChanceHelmet; } - float GetDropChanceChestplate() { return m_DropChanceChestplate; } - float GetDropChanceLeggings() { return m_DropChanceLeggings; } - float GetDropChanceBoots() { return m_DropChanceBoots; } - bool CanPickUpLoot() { return m_CanPickUpLoot; } - void SetDropChanceWeapon(float a_DropChanceWeapon) { m_DropChanceWeapon = a_DropChanceWeapon; } - void SetDropChanceHelmet(float a_DropChanceHelmet) { m_DropChanceHelmet = a_DropChanceHelmet; } - void SetDropChanceChestplate(float a_DropChanceChestplate) { m_DropChanceChestplate = a_DropChanceChestplate; } - void SetDropChanceLeggings(float a_DropChanceLeggings) { m_DropChanceLeggings = a_DropChanceLeggings; } - void SetDropChanceBoots(float a_DropChanceBoots) { m_DropChanceBoots = a_DropChanceBoots; } - void SetCanPickUpLoot(bool a_CanPickUpLoot) { m_CanPickUpLoot = a_CanPickUpLoot; } - void ResetAttackCooldown(); + double GetRelativeWalkSpeed(void) const { return m_RelativeWalkSpeed; } // tolua_export + void SetRelativeWalkSpeed(double a_WalkSpeed) { m_RelativeWalkSpeed = a_WalkSpeed; } // tolua_export - double GetRelativeWalkSpeed(void) const { return m_RelativeWalkSpeed; } // tolua_export - void SetRelativeWalkSpeed(double a_WalkSpeed) { m_RelativeWalkSpeed = a_WalkSpeed; } // tolua_export + // Overridables to handle ageable mobs + virtual bool IsTame (void) const { return false; } + virtual bool IsSitting (void) const { return false; } - // Overridables to handle ageable mobs - virtual bool IsTame (void) const { return false; } - virtual bool IsSitting (void) const { return false; } + // tolua_begin + bool IsBaby (void) const { return m_Age < 0; } + int GetAge (void) const { return m_Age; } + void SetAge(int a_Age) { m_Age = a_Age; } + // tolua_end - // tolua_begin - bool IsBaby (void) const { return m_Age < 0; } - int GetAge (void) const { return m_Age; } - void SetAge(int a_Age) { m_Age = a_Age; } - // tolua_end + // tolua_begin - // tolua_begin + /** Returns true if the monster has a custom name. */ + bool HasCustomName(void) const { return !m_CustomName.empty(); } - /** Returns true if the monster has a custom name. */ - bool HasCustomName(void) const { return !m_CustomName.empty(); } + /** Gets the custom name of the monster. If no custom name is set, the function returns an empty string. */ + const AString & GetCustomName(void) const { return m_CustomName; } - /** Gets the custom name of the monster. If no custom name is set, the function returns an empty string. */ - const AString & GetCustomName(void) const { return m_CustomName; } + /** Sets the custom name of the monster. You see the name over the monster. + If you want to disable the custom name, simply set an empty string. */ + void SetCustomName(const AString & a_CustomName); - /** Sets the custom name of the monster. You see the name over the monster. - If you want to disable the custom name, simply set an empty string. */ - void SetCustomName(const AString & a_CustomName); + /** Is the custom name of this monster always visible? If not, you only see the name when you sight the mob. */ + bool IsCustomNameAlwaysVisible(void) const { return m_CustomNameAlwaysVisible; } - /** Is the custom name of this monster always visible? If not, you only see the name when you sight the mob. */ - bool IsCustomNameAlwaysVisible(void) const { return m_CustomNameAlwaysVisible; } + /** Sets the custom name visiblity of this monster. + If it's false, you only see the name when you sight the mob. If it's true, you always see the custom name. */ + void SetCustomNameAlwaysVisible(bool a_CustomNameAlwaysVisible); - /** Sets the custom name visiblity of this monster. - If it's false, you only see the name when you sight the mob. If it's true, you always see the custom name. */ - void SetCustomNameAlwaysVisible(bool a_CustomNameAlwaysVisible); + /** Translates MobType enum to a string, empty string if unknown */ + static AString MobTypeToString(eMonsterType a_MobType); - /** Translates MobType enum to a string, empty string if unknown */ - static AString MobTypeToString(eMonsterType a_MobType); + /** Translates MobType enum to the vanilla name of the mob, empty string if unknown. */ + static AString MobTypeToVanillaName(eMonsterType a_MobType); - /** Translates MobType enum to the vanilla name of the mob, empty string if unknown. */ - static AString MobTypeToVanillaName(eMonsterType a_MobType); + /** Translates MobType string to the enum, mtInvalidType if not recognized */ + static eMonsterType StringToMobType(const AString & a_MobTypeName); - /** Translates MobType string to the enum, mtInvalidType if not recognized */ - static eMonsterType StringToMobType(const AString & a_MobTypeName); + /** Returns the mob family based on the type */ + static eFamily FamilyFromType(eMonsterType a_MobType); - /** Returns the mob family based on the type */ - static eFamily FamilyFromType(eMonsterType 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); - /** 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 - // tolua_end + /** Translates the MobType enum to the vanilla nbt name */ + static AString MobTypeToVanillaNBT(eMonsterType a_MobType); - /** Translates the MobType enum to the vanilla nbt name */ - static AString MobTypeToVanillaNBT(eMonsterType a_MobType); + /** Sets the target that this mob will chase. Pass a nullptr to unset. */ + void SetTarget (cPawn * a_NewTarget); - /** Sets the target that this mob will chase. Pass a nullptr to unset. */ - void SetTarget (cPawn * a_NewTarget); + /** Unset the target without notifying the target entity. Do not use this, use SetTarget(nullptr) instead. + This is only used by cPawn internally. */ + void UnsafeUnsetTarget(); - /** Unset the target without notifying the target entity. Do not use this, use SetTarget(nullptr) instead. - This is only used by cPawn internally. */ - void UnsafeUnsetTarget(); + /** Returns the current target. */ + cPawn * GetTarget(); - /** Returns the current target. */ - cPawn * GetTarget(); + /** 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 std::unique_ptr<cMonster> NewMonsterFromType(eMonsterType a_MobType); - /** 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 std::unique_ptr<cMonster> NewMonsterFromType(eMonsterType a_MobType); + /** Returns if this mob last target was a player to avoid destruction on player quit */ + bool WasLastTargetAPlayer() const { return m_WasLastTargetAPlayer; } - /** Returns if this mob last target was a player to avoid destruction on player quit */ - bool WasLastTargetAPlayer() const { return m_WasLastTargetAPlayer; } + bool IsPathFinderActivated() const; - bool IsPathFinderActivated() const; - // Behavior getters - virtual cBehaviorAggressive * GetBehaviorAggressive(); - virtual cBehaviorBreeder * GetBehaviorBreeder(); - virtual const cBehaviorBreeder * GetBehaviorBreeder() const; - virtual cBehaviorChaser * GetBehaviorChaser(); - virtual cBehaviorStriker * GetBehaviorStriker(); - virtual cBehaviorWanderer * GetBehaviorWanderer(); - virtual cBehaviorDayLightBurner * GetBehaviorDayLightBurner(); + // Behavior getters (most are probably not used. mobTodo - cleanup most of them) + virtual cBehaviorAggressive * GetBehaviorAggressive(); + virtual cBehaviorBreeder * GetBehaviorBreeder(); + virtual const cBehaviorBreeder * GetBehaviorBreeder() const; + virtual cBehaviorChaser * GetBehaviorChaser(); + virtual cBehaviorStriker * GetBehaviorStriker(); + virtual cBehaviorWanderer * GetBehaviorWanderer(); + virtual cBehaviorDayLightBurner * GetBehaviorDayLightBurner(); - // Polymorphic behavior functions ("Skin-specific") - virtual void InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2); - virtual void GetFollowedItems(cItems & a_Items); - virtual void GetBreedingItems(cItems & a_Items); + // Polymorphic behavior functions ("Skin-specific") + virtual void InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2); + virtual void GetFollowedItems(cItems & a_Items); + virtual void GetBreedingItems(cItems & a_Items); - cPlayer * GetNearestPlayer(); + cPlayer * GetNearestPlayer(); - protected: + protected: - /** Whether or not m_NearestPlayer is stale. Always true at the beginning of a tick. - When true, GetNearestPlayer() actually searches for a player, updates m_NearestPlayer, and sets it to false. - otherwise it returns m_NearestPlayer. This means we only perform 1 search per tick. */ - bool m_NearestPlayerIsStale; - cPlayer * m_NearestPlayer; + /** Whether or not m_NearestPlayer is stale. Always true at the beginning of a tick. + When true, GetNearestPlayer() actually searches for a player, updates m_NearestPlayer, and sets it to false. + otherwise it returns m_NearestPlayer. This means we only perform 1 search per tick. */ + bool m_NearestPlayerIsStale; + cPlayer * m_NearestPlayer; - /** The pathfinder instance handles pathfinding for this monster. */ - cPathFinder m_PathFinder; + /** The pathfinder instance handles pathfinding for this monster. */ + cPathFinder m_PathFinder; - /** Stores if pathfinder is being used - set when final destination is set, and unset when stopped moving to final destination */ - bool m_PathfinderActivated; + /** Stores if pathfinder is being used - set when final destination is set, and unset when stopped moving to final destination */ + bool m_PathfinderActivated; - /** Coordinates of the next position that should be reached */ - Vector3d m_NextWayPointPosition; + /** Coordinates of the next position that should be reached */ + Vector3d m_NextWayPointPosition; - /** Coordinates for the ultimate, final destination. */ - Vector3d m_FinalDestination; + /** Coordinates for the ultimate, final destination. */ + Vector3d m_FinalDestination; - /** Finds the lowest non-air block position (not the highest, as cWorld::GetHeight does) - If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1 - If current Y is solid, goes up to find first nonsolid block, and returns that. - If no suitable position is found, returns cChunkDef::Height. */ - int FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ); + /** Finds the lowest non-air block position (not the highest, as cWorld::GetHeight does) + If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1 + If current Y is solid, goes up to find first nonsolid block, and returns that. + If no suitable position is found, returns cChunkDef::Height. */ + int FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ); - /** Returns if the ultimate, final destination has been reached. */ - bool ReachedFinalDestination(void) { return ((m_FinalDestination - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS); } + /** Returns if the ultimate, final destination has been reached. */ + bool ReachedFinalDestination(void) { return ((m_FinalDestination - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS); } - /** Returns whether or not the target is close enough for attack. */ - bool TargetIsInRange(void) - { - ASSERT(GetTarget() != nullptr); - return ((GetTarget()->GetPosition() - GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange)); - } + /** Returns whether or not the target is close enough for attack. */ + bool TargetIsInRange(void) + { + ASSERT(GetTarget() != nullptr); + return ((GetTarget()->GetPosition() - GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange)); + } - /** Returns whether the monster needs to jump to reach a given height. */ - inline bool DoesPosYRequireJump(double a_PosY) - { - return (a_PosY > GetPosY() + 0.8); // Assume that differences up to 0.8 blocks can be walked instead of jumped - } + /** Returns whether the monster needs to jump to reach a given height. */ + inline bool DoesPosYRequireJump(double a_PosY) + { + return (a_PosY > GetPosY() + 0.8); // Assume that differences up to 0.8 blocks can be walked instead of jumped + } - /** Move in a straight line to the next waypoint in the path, will jump if needed. */ - void MoveToWayPoint(cChunk & a_Chunk); + /** Move in a straight line to the next waypoint in the path, will jump if needed. */ + void MoveToWayPoint(cChunk & a_Chunk); - /** Stops pathfinding. Calls ResetPathFinding and sets m_IsFollowingPath to false */ - void StopMovingToPosition(); + /** Stops pathfinding. Calls ResetPathFinding and sets m_IsFollowingPath to false */ + void StopMovingToPosition(); - /** Sets the body yaw and head yaw */ - void SetPitchAndYawFromDestination(bool a_IsFollowingPath); + /** Sets the body yaw and head yaw */ + void SetPitchAndYawFromDestination(bool a_IsFollowingPath); - int m_JumpCoolDown; + int m_JumpCoolDown; - std::chrono::milliseconds m_IdleInterval; - std::chrono::milliseconds m_DestroyTimer; + std::chrono::milliseconds m_IdleInterval; + std::chrono::milliseconds m_DestroyTimer; - eMonsterType m_MobType; - AString m_CustomName; - bool m_CustomNameAlwaysVisible; + eMonsterType m_MobType; + AString m_CustomName; + bool m_CustomNameAlwaysVisible; - AString m_SoundHurt; - AString m_SoundDeath; + AString m_SoundHurt; + AString m_SoundDeath; - float m_AttackRate; - int m_AttackDamage; - int m_AttackRange; - int m_AttackCoolDownTicksLeft; - int m_SightDistance; + float m_AttackRate; + int m_AttackDamage; + int m_AttackRange; + int m_AttackCoolDownTicksLeft; + int m_SightDistance; - float m_DropChanceWeapon; - float m_DropChanceHelmet; - float m_DropChanceChestplate; - float m_DropChanceLeggings; - float m_DropChanceBoots; - bool m_CanPickUpLoot; - int m_TicksSinceLastDamaged; // How many ticks ago we were last damaged by a player? + float m_DropChanceWeapon; + float m_DropChanceHelmet; + float m_DropChanceChestplate; + float m_DropChanceLeggings; + float m_DropChanceBoots; + bool m_CanPickUpLoot; + int m_TicksSinceLastDamaged; // How many ticks ago we were last damaged by a player? - double m_RelativeWalkSpeed; + double m_RelativeWalkSpeed; - int m_Age; - int m_AgingTimer; + int m_Age; + int m_AgingTimer; - bool m_WasLastTargetAPlayer; + bool m_WasLastTargetAPlayer; - /** Entity leashed to */ - cEntity * m_LeashedTo; + /** Entity leashed to */ + cEntity * m_LeashedTo; - /** Entity pos where this mob was leashed to. Used when deserializing the chunk in order to make the mob find the leash knot. */ - std::unique_ptr<Vector3d> m_LeashToPos; + /** Entity pos where this mob was leashed to. Used when deserializing the chunk in order to make the mob find the leash knot. */ + std::unique_ptr<Vector3d> m_LeashToPos; - /** Mob has ben leashed or unleashed in current player action. Avoids double actions on horses. */ - bool m_IsLeashActionJustDone; + /** Mob has ben leashed or unleashed in current player action. Avoids double actions on horses. */ + bool m_IsLeashActionJustDone; - /** Determines whether a monster can be leashed */ - bool m_CanBeLeashed; + /** Determines whether a monster can be leashed */ + bool m_CanBeLeashed; - /** Adds a random number of a_Item between a_Min and a_Max to itemdrops a_Drops */ - void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0); + /** Adds a random number of a_Item between a_Min and a_Max to itemdrops a_Drops */ + void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0); - /** Adds a item a_Item with the chance of a_Chance (in percent) to itemdrops a_Drops */ - void AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short a_Item, short a_ItemHealth = 0); + /** Adds a item a_Item with the chance of a_Chance (in percent) to itemdrops a_Drops */ + void AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short a_Item, short a_ItemHealth = 0); - /** Adds one rare item out of the list of rare items a_Items modified by the looting level a_LootingLevel(I-III or custom) to the itemdrop a_Drops */ - void AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, unsigned int a_LootingLevel); + /** Adds one rare item out of the list of rare items a_Items modified by the looting level a_LootingLevel(I-III or custom) to the itemdrop a_Drops */ + void AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, unsigned int a_LootingLevel); - /** Adds armor that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if picked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop */ - void AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLevel); + /** Adds armor that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if picked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop */ + void AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLevel); - /** Adds weapon that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if picked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop */ - void AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingLevel); + /** Adds weapon that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if picked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop */ + void AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingLevel); private: - /** A pointer to the entity this mobile is aiming to reach. - The validity of this pointer SHALL be guaranteed by the pointee; - it MUST be reset when the pointee changes worlds or is destroyed. */ - cPawn * m_Target; + /** A pointer to the entity this mobile is aiming to reach. + The validity of this pointer SHALL be guaranteed by the pointee; + it MUST be reset when the pointee changes worlds or is destroyed. */ + cPawn * m_Target; + + /** Leash calculations inside Tick function */ + void CalcLeashActions(); + + std::vector<cBehavior*> TickBehaviors; + std::vector<cBehavior*> OnDestroyBehaviors; + std::vector<cBehavior*> OnRightClickBehaviors; - /** Leash calculations inside Tick function */ - void CalcLeashActions(); + cBehavior * m_CurrentTickControllingBehavior; + enum TickState{ControlStarting, ControlEnding, Normal} m_TickControllingBehaviorState; } ; // tolua_export diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp index 8715ba9c2..addde27a7 100644 --- a/src/Mobs/PassiveAggressiveMonster.cpp +++ b/src/Mobs/PassiveAggressiveMonster.cpp @@ -26,23 +26,13 @@ bool cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) return false; } + // mobtodo remove this class altogether if ((GetTarget() != nullptr) && (GetTarget()->IsPlayer())) { if (static_cast<cPlayer *>(GetTarget())->CanMobsTarget()) { - m_EMState = CHASING; + // m_EMState = CHASING; } } return true; } - - - - - -void cPassiveAggressiveMonster::EventSeePlayer(cPlayer *, cChunk & a_Chunk) -{ - // don't do anything, neutral mobs don't react to just seeing the player -} - - diff --git a/src/Mobs/PassiveAggressiveMonster.h b/src/Mobs/PassiveAggressiveMonster.h index 764e27779..726db09c5 100644 --- a/src/Mobs/PassiveAggressiveMonster.h +++ b/src/Mobs/PassiveAggressiveMonster.h @@ -8,15 +8,14 @@ class cPassiveAggressiveMonster : - public cAggressiveMonster + public cAggressiveMonster { - typedef cAggressiveMonster super; + typedef cAggressiveMonster super; public: - cPassiveAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + cPassiveAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual void EventSeePlayer(cPlayer *, cChunk & a_Chunk) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; } ; diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp index 5eded6b6f..c5dab6d80 100644 --- a/src/Mobs/PassiveMonster.cpp +++ b/src/Mobs/PassiveMonster.cpp @@ -78,7 +78,7 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { break; } - if (m_BehaviorBreeder.ActiveTick()) + if (m_BehaviorBreeder.Tick()) { break; } @@ -95,7 +95,7 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) break; } - m_BehaviorBreeder.Tick(); + m_BehaviorBreeder.PostTick(); } diff --git a/src/Mobs/Spider.cpp b/src/Mobs/Spider.cpp index b614fcbde..608bcd853 100644 --- a/src/Mobs/Spider.cpp +++ b/src/Mobs/Spider.cpp @@ -9,7 +9,7 @@ cSpider::cSpider(void) : - super("Spider", mtSpider, "entity.spider.hurt", "entity.spider.death", 1.4, 0.9) + super("Spider", mtSpider, "entity.spider.hurt", "entity.spider.death", 1.4, 0.9) { } @@ -19,42 +19,16 @@ cSpider::cSpider(void) : void cSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - unsigned int LootingLevel = 0; - if (a_Killer != nullptr) - { - LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); - } - AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_STRING); - if ((a_Killer != nullptr) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf"))) - { - AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_SPIDER_EYE); - } -} - - - - - -void cSpider::EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk) -{ - if (!GetWorld()->IsChunkLighted(GetChunkX(), GetChunkZ())) - { - return; - } - - PREPARE_REL_AND_CHUNK(GetPosition(), a_Chunk); - if (!RelSuccess) - { - return; - } - - if ( - a_Player->CanMobsTarget() && - !((Chunk->GetSkyLightAltered(Rel.x, Rel.y, Rel.z) > 11) || (Chunk->GetBlockLight(Rel.x, Rel.y, Rel.z) > 11)) - ) - { - super::EventSeePlayer(a_Player, a_Chunk); - } + unsigned int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_STRING); + if ((a_Killer != nullptr) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf"))) + { + AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_SPIDER_EYE); + } } @@ -63,21 +37,22 @@ void cSpider::EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk) bool cSpider::DoTakeDamage(TakeDamageInfo & a_TDI) { - if (!super::DoTakeDamage(a_TDI)) - { - return false; - } - - // If the source of the damage is not from an pawn entity, switch to idle - if ((a_TDI.Attacker == nullptr) || !a_TDI.Attacker->IsPawn()) - { - m_EMState = IDLE; - } - else - { - // If the source of the damage is from a pawn entity, chase that entity - m_EMState = CHASING; - } - - return true; + if (!super::DoTakeDamage(a_TDI)) + { + return false; + } + + /* mobTodo + // If the source of the damage is not from an pawn entity, switch to idle + if ((a_TDI.Attacker == nullptr) || !a_TDI.Attacker->IsPawn()) + { + m_EMState = IDLE; + } + else + { + // If the source of the damage is from a pawn entity, chase that entity + m_EMState = CHASING; + }*/ + + return true; } diff --git a/src/Mobs/Spider.h b/src/Mobs/Spider.h index af2753012..296c9f261 100644 --- a/src/Mobs/Spider.h +++ b/src/Mobs/Spider.h @@ -8,18 +8,17 @@ class cSpider : - public cAggressiveMonster + public cAggressiveMonster { - typedef cAggressiveMonster super; + typedef cAggressiveMonster super; public: - cSpider(void); + cSpider(void); - CLASS_PROTODEF(cSpider) + CLASS_PROTODEF(cSpider) - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void EventSeePlayer(cPlayer *, cChunk & a_Chunk) override; - virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; } ; diff --git a/src/Mobs/Witch.h b/src/Mobs/Witch.h index 706fcd9b3..79cc993bc 100644 --- a/src/Mobs/Witch.h +++ b/src/Mobs/Witch.h @@ -18,8 +18,7 @@ public: CLASS_PROTODEF(cWitch) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - - bool IsAngry(void) const {return ((m_EMState == ATTACKING) || (m_EMState == CHASING)); } + bool IsAngry() const { return false; } } ; diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp index cc0ee02a4..a92fb2071 100644 --- a/src/Mobs/Wolf.cpp +++ b/src/Mobs/Wolf.cpp @@ -395,13 +395,3 @@ void cWolf::TickFollowPlayer() } - -void cWolf::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) -{ - if (!IsTame()) - { - cMonster::InStateIdle(a_Dt, a_Chunk); - } -} - - diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h index f001fb559..120e10942 100644 --- a/src/Mobs/Wolf.h +++ b/src/Mobs/Wolf.h @@ -55,8 +55,6 @@ public: @param a_IsPlayerInvolved Whether the fighter a player or a wolf. */ void ReceiveNearbyFightInfo(AString a_PlayerID, cPawn * a_Opponent, bool a_IsPlayerInvolved); - virtual void InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - protected: bool m_IsSitting; diff --git a/src/Protocol/Protocol_1_10.cpp b/src/Protocol/Protocol_1_10.cpp index bc6b89635..e3d05f5ae 100644 --- a/src/Protocol/Protocol_1_10.cpp +++ b/src/Protocol/Protocol_1_10.cpp @@ -923,7 +923,8 @@ void cProtocol_1_10_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_ auto & Witch = reinterpret_cast<const cWitch &>(a_Mob); a_Pkt.WriteBEUInt8(WITCH_AGGRESIVE); a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); - a_Pkt.WriteBool(Witch.IsAngry()); + // a_Pkt.WriteBool(Witch.IsAngry()); //mobTodo + a_Pkt.WriteBool(0); break; } // case mtWitch diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp index 68d5c3f9d..862e20419 100644 --- a/src/Protocol/Protocol_1_8.cpp +++ b/src/Protocol/Protocol_1_8.cpp @@ -3576,7 +3576,8 @@ void cProtocol_1_8_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M { auto & Witch = reinterpret_cast<const cWitch &>(a_Mob); a_Pkt.WriteBEUInt8(0x15); - a_Pkt.WriteBEUInt8(Witch.IsAngry() ? 1 : 0); + // a_Pkt.WriteBEUInt8(Witch.IsAngry() ? 1 : 0); // mobTodo + a_Pkt.WriteBEUInt8(0); break; } // case mtWitch diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index 167bc4ddf..e4b02e02c 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -4018,7 +4018,8 @@ void cProtocol_1_9_0::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_M auto & Witch = reinterpret_cast<const cWitch &>(a_Mob); a_Pkt.WriteBEUInt8(11); // Index 11: Is angry a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); - a_Pkt.WriteBool(Witch.IsAngry()); + // a_Pkt.WriteBool(Witch.IsAngry()); // mobTodo + a_Pkt.WriteBool(0); break; } // case mtWitch |