diff options
Diffstat (limited to 'src/Mobs')
59 files changed, 2361 insertions, 1576 deletions
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index fec14e6e9..9d9532573 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -5,105 +5,47 @@ #include "../World.h" #include "../Entities/Player.h" -#include "../LineBlockTracer.h" - - +#include "../Tracer.h" +#include "Behaviors/BehaviorAggressive.h" +#include "Behaviors/BehaviorChaser.h" +#include "Behaviors/BehaviorWanderer.h" 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) + super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height), m_BehaviorAggressive(this) { m_EMPersonality = AGGRESSIVE; + ASSERT(GetBehaviorChaser() != nullptr); } -// What to do if in Chasing State -void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) -{ - super::InStateChasing(a_Dt, a_Chunk); - - if (GetTarget() != nullptr) - { - MoveToPosition(GetTarget()->GetPosition()); - } -} - - - - - - -void cAggressiveMonster::EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk) -{ - if (!a_Player->CanMobsTarget()) - { - return; - } - - super::EventSeePlayer(a_Player, a_Chunk); - m_EMState = CHASING; -} - - - - void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); - if (!IsTicking()) - { - // The base class tick destroyed us - return; - } - - if (m_EMState == CHASING) - { - CheckEventLostPlayer(); - } - else - { - CheckEventSeePlayer(a_Chunk); - } - auto target = GetTarget(); - if (target == nullptr) - { - return; - } + /* cBehaviorChaser * BehaviorChaser = GetBehaviorChaser(); + cBehaviorWanderer * BehaviorWanderer = GetBehaviorWanderer(); - // TODO: Currently all mobs see through lava, but only Nether-native mobs should be able to. - Vector3d MyHeadPosition = GetPosition() + Vector3d(0, GetHeight(), 0); - Vector3d TargetPosition = target->GetPosition() + Vector3d(0, target->GetHeight(), 0); - if ( - TargetIsInRange() && - cLineBlockTracer::LineOfSightTrace(*GetWorld(), MyHeadPosition, TargetPosition, cLineBlockTracer::losAirWaterLava) && - (GetHealth() > 0.0) - ) + for (;;) { - // Attack if reached destination, target isn't null, and have a clear line of sight to target (so won't attack through walls) - Attack(a_Dt); - } -} - - + m_BehaviorAggressive.Tick(); + if (BehaviorChaser->Tick()) + { + break; + } + if ((BehaviorWanderer != nullptr) && BehaviorWanderer->ActiveTick(a_Dt, a_Chunk)) + { + break; + } - - -bool cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt) -{ - if ((GetTarget() == nullptr) || (m_AttackCoolDownTicksLeft != 0)) - { - return false; + ASSERT(!"Not a single Behavior took control, this is not normal. "); + break; } - // Setting this higher gives us more wiggle room for attackrate - ResetAttackCooldown(); - GetTarget()->TakeDamage(dtMobAttack, this, m_AttackDamage, 0); - - return true; + BehaviorChaser->Tick();*/ } diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h index 9ab8df06f..27ad88834 100644 --- a/src/Mobs/AggressiveMonster.h +++ b/src/Mobs/AggressiveMonster.h @@ -1,33 +1,22 @@ - #pragma once #include "Monster.h" +#include "Behaviors/BehaviorAggressive.h" - - +typedef std::string AString; class cAggressiveMonster : - public cMonster + public cMonster { - typedef cMonster super; + typedef cMonster super; public: - cAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - - virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual void InStateChasing(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - + cAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - virtual void EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk) override; + virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - /** Try to perform attack - returns true if attack was deemed successful (hit player, fired projectile, creeper exploded, etc.) even if it didn't actually do damage - return false if e.g. the mob is still in cooldown from a previous attack */ - virtual bool Attack(std::chrono::milliseconds a_Dt); +private: + cBehaviorAggressive m_BehaviorAggressive; } ; - - - - diff --git a/src/Mobs/Bat.cpp b/src/Mobs/Bat.cpp index e419ceb2d..906b99320 100644 --- a/src/Mobs/Bat.cpp +++ b/src/Mobs/Bat.cpp @@ -4,12 +4,13 @@ #include "Bat.h" #include "../Chunk.h" - cBat::cBat(void) : - super("Bat", mtBat, "entity.bat.hurt", "entity.bat.death", 0.5, 0.9) + super("Bat", mtBat, "entity.bat.hurt", "entity.bat.death", 0.5, 0.9) { - SetGravity(-2.0f); - SetAirDrag(0.05f); + SetGravity(-2.0f); + SetAirDrag(0.05f); + + } diff --git a/src/Mobs/Behaviors/Behavior.cpp b/src/Mobs/Behaviors/Behavior.cpp new file mode 100644 index 000000000..695343732 --- /dev/null +++ b/src/Mobs/Behaviors/Behavior.cpp @@ -0,0 +1,103 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Behavior.h" + + + + + +bool cBehavior::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + LOGD("ERROR: Probably forgot to implement cBehavior::IsControlDesired but implement cBehavior::Tick"); + return false; +} + + + + + +bool cBehavior::ControlStarting(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + return true; +} + + + + + +bool cBehavior::ControlEnding(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + return true; +} + + + + + +void cBehavior::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + LOGD("ERROR: Called a TICK on a behavior that doesn't have one."); + ASSERT(1 == 0); +} + + + + + +void cBehavior::PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + LOGD("ERROR: Called a PostTick on a behavior that doesn't have one."); + ASSERT(1 == 0); +} + + + + + +void cBehavior::PreTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + LOGD("ERROR: Called a PreTick on a behavior that doesn't have one."); + ASSERT(1 == 0); +} + + + + + +void cBehavior::onRightClicked() +{ + LOGD("ERROR: Called onRightClicked on a behavior that doesn't have one."); + ASSERT(1 == 0); +} + + + + + +void cBehavior::Destroyed() +{ + LOGD("ERROR: Called Destroyed on a behavior that doesn't have one."); + ASSERT(1 == 0); +} + + + + +void cBehavior::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + UNUSED(a_TDI); + LOGD("ERROR: Called DoTakeDamage on a behavior that doesn't have one."); + ASSERT(1 == 0); +} diff --git a/src/Mobs/Behaviors/Behavior.h b/src/Mobs/Behaviors/Behavior.h new file mode 100644 index 000000000..989addf8d --- /dev/null +++ b/src/Mobs/Behaviors/Behavior.h @@ -0,0 +1,22 @@ +#pragma once + +struct TakeDamageInfo; +class cChunk; +#include <chrono> + +class cBehavior +{ +public: + virtual bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + virtual bool ControlStarting(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + virtual bool ControlEnding(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + virtual void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + virtual void PreTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + + + virtual void onRightClicked(); + virtual void Destroyed(); + virtual void DoTakeDamage(TakeDamageInfo & a_TDI); + virtual ~cBehavior() {} +}; diff --git a/src/Mobs/Behaviors/BehaviorAggressive.cpp b/src/Mobs/Behaviors/BehaviorAggressive.cpp new file mode 100644 index 000000000..f7c553f52 --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorAggressive.cpp @@ -0,0 +1,39 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BehaviorAggressive.h" +#include "BehaviorChaser.h" +#include "../Monster.h" +#include "../../Chunk.h" +#include "../../Entities/Player.h" + + + +cBehaviorAggressive::cBehaviorAggressive(cMonster * a_Parent) : m_Parent(a_Parent) +{ + ASSERT(m_Parent != nullptr); + m_ParentChaser = m_Parent->GetBehaviorChaser(); + ASSERT(m_ParentChaser != nullptr); +} + + + + + +void cBehaviorAggressive::PreTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + // Target something new if we have no target + if (m_ParentChaser->GetTarget() == nullptr) + { + m_ParentChaser->SetTarget(FindNewTarget()); + } +} + + + + + +cPawn * cBehaviorAggressive::FindNewTarget() +{ + cPlayer * Closest = m_Parent->GetNearestPlayer(); + return Closest; // May be null +} diff --git a/src/Mobs/Behaviors/BehaviorAggressive.h b/src/Mobs/Behaviors/BehaviorAggressive.h new file mode 100644 index 000000000..5bf86b23f --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorAggressive.h @@ -0,0 +1,40 @@ +#pragma once + + +class cBehaviorAggressive; + +#include "Behavior.h" + +class cPawn; +class cMonster; +class cBehaviorChaser; + + + + + +/** The mob is agressive toward specific mobtypes, or toward the player. +This Behavior has a dependency on BehaviorChaser. */ +class cBehaviorAggressive : public cBehavior +{ + +public: + 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) + + // Functions our host Monster should invoke: + void PreTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + +private: + cPawn * FindNewTarget(); + + // Our parent + cMonster * m_Parent; + cBehaviorChaser * m_ParentChaser; + + // The mob we want to attack + cPawn * m_Target; +}; diff --git a/src/Mobs/Behaviors/BehaviorBreeder.cpp b/src/Mobs/Behaviors/BehaviorBreeder.cpp new file mode 100644 index 000000000..b2bd8da5d --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorBreeder.cpp @@ -0,0 +1,241 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BehaviorBreeder.h" +#include "../PassiveMonster.h" +#include "../../World.h" +#include "../Monster.h" +#include "../../Entities/Player.h" +#include "../../Item.h" +#include "../../BoundingBox.h" + +cBehaviorBreeder::cBehaviorBreeder(cMonster * a_Parent) : + m_Parent(a_Parent), + m_LovePartner(nullptr), + m_LoveTimer(0), + m_LoveCooldown(0), + m_MatingTimer(0) +{ + m_Parent = a_Parent; + ASSERT(m_Parent != nullptr); +} + + + + + +void cBehaviorBreeder::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + cWorld * World = m_Parent->GetWorld(); + // if we have a partner, mate + if (m_LovePartner != nullptr) + { + if (m_MatingTimer > 0) + { + // If we should still mate, keep bumping into them until baby is made + Vector3d Pos = m_LovePartner->GetPosition(); + m_Parent->MoveToPosition(Pos); + } + else + { + // Mating finished. Spawn baby + Vector3f Pos = (m_Parent->GetPosition() + m_LovePartner->GetPosition()) * 0.5; + UInt32 BabyID = World->SpawnMob(Pos.x, Pos.y, Pos.z, m_Parent->GetMobType(), true); + + class cBabyInheritCallback : + public cEntityCallback + { + public: + cMonster * Baby; + cBabyInheritCallback() : Baby(nullptr) { } + virtual bool Item(cEntity * a_Entity) override + { + Baby = static_cast<cMonster *>(a_Entity); + return true; + } + } Callback; + + m_Parent->GetWorld()->DoWithEntityByID(BabyID, Callback); + if (Callback.Baby != nullptr) + { + Callback.Baby->InheritFromParents(m_Parent, m_LovePartner); + } + + cFastRandom Random; + World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, 1 + (Random.RandInt() % 6)); + + m_LovePartner->GetBehaviorBreeder()->ResetLoveMode(); + ResetLoveMode(); + } + } +} + + + + + +void cBehaviorBreeder::PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + if (m_MatingTimer > 0) + { + m_MatingTimer--; + } + if (m_LoveCooldown > 0) + { + m_LoveCooldown--; + } + if (m_LoveTimer > 0) + { + m_LoveTimer--; + } +} + + + + + +bool cBehaviorBreeder::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + cWorld * World = m_Parent->GetWorld(); + + // if we have a love partner, we should control the mob + if (m_LovePartner != nullptr) + { + return true; + } + + // If we are in love mode and we have no partner, try to find one + if (m_LoveTimer > 0) + { + class LookForLover : public cEntityCallback + { + public: + cMonster * m_Me; + LookForLover(cMonster * a_Me) : + m_Me(a_Me) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + // If the entity is not a monster, don't breed with it + // Also, do not self-breed + if ((a_Entity->GetEntityType() != cEntity::eEntityType::etMonster) || (a_Entity == m_Me)) + { + return false; + } + + auto PotentialPartner = static_cast<cMonster*>(a_Entity); + + // If the potential partner is not of the same species, don't breed with it + if (PotentialPartner->GetMobType() != m_Me->GetMobType()) + { + return false; + } + + auto PartnerBreedingBehavior = PotentialPartner->GetBehaviorBreeder(); + auto MyBreedingBehavior = m_Me->GetBehaviorBreeder(); + + // If the potential partner is not in love + // Or they already have a mate, do not breed with them + + if ((!PartnerBreedingBehavior->IsInLove()) || (PartnerBreedingBehavior->GetPartner() != nullptr)) + { + return false; + } + + // All conditions met, let's breed! + PartnerBreedingBehavior->EngageLoveMode(m_Me); + MyBreedingBehavior->EngageLoveMode(PotentialPartner); + return true; + } + } Callback(m_Parent); + + World->ForEachEntityInBox(cBoundingBox(m_Parent->GetPosition(), 8, 8), Callback); + if (m_LovePartner != nullptr) + { + return true; // We found love and took control of the monster, prevent other Behaviors from doing so + } + } + + return false; +} + +void cBehaviorBreeder::Destroyed() +{ + if (m_LovePartner != nullptr) + { + m_LovePartner->GetBehaviorBreeder()->ResetLoveMode(); + } +} + + + + + +void cBehaviorBreeder::OnRightClicked(cPlayer & a_Player) +{ + // If a player holding breeding items right-clicked me, go into love mode + if ((m_LoveCooldown == 0) && !IsInLove() && !m_Parent->IsBaby()) + { + short HeldItem = a_Player.GetEquippedItem().m_ItemType; + cItems BreedingItems; + m_Parent->GetFollowedItems(BreedingItems); + if (BreedingItems.ContainsType(HeldItem)) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + m_LoveTimer = 20 * 30; // half a minute + m_Parent->GetWorld()->BroadcastEntityStatus(*m_Parent, cEntity::eEntityStatus::esMobInLove); + } + } +} + + + +void cBehaviorBreeder::EngageLoveMode(cMonster * a_Partner) +{ + m_LovePartner = a_Partner; + m_MatingTimer = 50; // about 3 seconds of mating +} + + + + + +void cBehaviorBreeder::ResetLoveMode() +{ + m_LovePartner = nullptr; + m_LoveTimer = 0; + m_MatingTimer = 0; + m_LoveCooldown = 20 * 60 * 5; // 5 minutes + + // when an animal is in love mode, the client only stops sending the hearts if we let them know it's in cooldown, which is done with the "age" metadata + m_Parent->GetWorld()->BroadcastEntityMetadata(*m_Parent); +} + + + + + +bool cBehaviorBreeder::IsInLove() const +{ + return m_LoveTimer > 0; +} + + + + + +bool cBehaviorBreeder::IsInLoveCooldown() const +{ + return (m_LoveCooldown > 0); +} diff --git a/src/Mobs/Behaviors/BehaviorBreeder.h b/src/Mobs/Behaviors/BehaviorBreeder.h new file mode 100644 index 000000000..cf030b8f3 --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorBreeder.h @@ -0,0 +1,60 @@ +#pragma once + +class cBehaviorBreeder; + +#include "Behavior.h" + +class cWorld; +class cMonster; +class cPlayer; +class cItems; + + + + + +/** Grants breeding capabilities to the mob. */ +class cBehaviorBreeder : public cBehavior +{ + +public: + cBehaviorBreeder(cMonster * a_Parent); + + // Functions our host Monster should invoke: + bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void OnRightClicked(cPlayer & a_Player); + void Destroyed() override; + + /** Returns the partner which the monster is currently mating with. */ + cMonster * GetPartner(void) const { return m_LovePartner; } + + /** Start the mating process. Causes the monster to keep bumping into the partner until m_MatingTimer reaches zero. */ + void EngageLoveMode(cMonster * a_Partner); + + /** Finish the mating process. Called after a baby is born. Resets all breeding related timers and sets m_LoveCooldown to 20 minutes. */ + void ResetLoveMode(); + + /** Returns whether the monster has just been fed and is ready to mate. If this is "true" and GetPartner isn't "nullptr", then the monster is mating. */ + bool IsInLove() const; + + /** Returns whether the monster is tired of breeding and is in the cooldown state. */ + bool IsInLoveCooldown() const; + +private: + /** Our parent */ + cMonster * m_Parent; + + /** The monster's breeding partner. */ + cMonster * m_LovePartner; + + /** If above 0, the monster is in love mode, and will breed if a nearby monster is also in love mode. Decrements by 1 per tick till reaching zero. */ + int m_LoveTimer; + + /** If above 0, the monster is in cooldown mode and will refuse to breed. Decrements by 1 per tick till reaching zero. */ + int m_LoveCooldown; + + /** The monster is engaged in mating, once this reaches zero, a baby will be born. Decrements by 1 per tick till reaching zero, then a baby is made and ResetLoveMode() is called. */ + int m_MatingTimer; +}; diff --git a/src/Mobs/Behaviors/BehaviorChaser.cpp b/src/Mobs/Behaviors/BehaviorChaser.cpp new file mode 100644 index 000000000..025c60dd0 --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorChaser.cpp @@ -0,0 +1,242 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BehaviorChaser.h" +#include "BehaviorStriker.h" +#include "../Monster.h" +#include "../../Entities/Pawn.h" +#include "../../Entities/Player.h" + + + +cBehaviorChaser::cBehaviorChaser(cMonster * a_Parent) : + m_Parent(a_Parent) + , m_AttackRate(3) + , m_AttackDamage(1) + , m_AttackRange(1) + , m_AttackCoolDownTicksLeft(0) + , m_TicksSinceLastDamaged(100) +{ + ASSERT(m_Parent != nullptr); +} + + + + +bool cBehaviorChaser::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + // If we have a target, we have something to do! Return true and control the mob Ticks. + // Otherwise return false. + return (GetTarget() != nullptr); +} + + + + + +void cBehaviorChaser::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + /* + * if ((GetTarget() != nullptr)) + { + ASSERT(GetTarget()->IsTicking()); + + if (GetTarget()->IsPlayer()) + { + if (!static_cast<cPlayer *>(GetTarget())->CanMobsTarget()) + { + SetTarget(nullptr); + } + } + } //mobTodo copied from monster.cpp + * */ + + ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == m_Parent->GetWorld()))); + // Stop targeting out of range targets + if (GetTarget() != nullptr) + { + if (TargetOutOfSight()) + { + SetTarget(nullptr); + } + else + { + if (TargetIsInStrikeRange()) + { + StrikeTarget(); + } + else + { + ApproachTarget(); // potential mobTodo: decoupling approaching from attacking + // Not important now, but important for future extensibility, e.g. + // cow chases wheat but using the netherman approacher to teleport around. + } + } + } +} + +void cBehaviorChaser::ApproachTarget() +{ + // potential mobTodo inheritence for creaper approachers, etc + m_Parent->MoveToPosition(m_Target->GetPosition()); +} + + + +void cBehaviorChaser::PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + if (m_TicksSinceLastDamaged < 100) + { + ++m_TicksSinceLastDamaged; + } + + if (m_AttackCoolDownTicksLeft > 0) + { + m_AttackCoolDownTicksLeft -= 1; + } +} + + + + + +void cBehaviorChaser::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn()) + { + if ( + (!a_TDI.Attacker->IsPlayer()) || + (static_cast<cPlayer *>(a_TDI.Attacker)->CanMobsTarget()) + ) + { + SetTarget(static_cast<cPawn*>(a_TDI.Attacker)); + } + m_TicksSinceLastDamaged = 0; + } +} + + + + + +void cBehaviorChaser::Destroyed() +{ + SetTarget(nullptr); +} + + + + + +void cBehaviorChaser::SetAttackRate(float a_AttackRate) +{ + m_AttackRate = a_AttackRate; +} + + + + + +void cBehaviorChaser::SetAttackRange(int a_AttackRange) +{ + m_AttackRange = a_AttackRange; +} + + + + + +void cBehaviorChaser::SetAttackDamage(int a_AttackDamage) +{ + m_AttackDamage = a_AttackDamage; +} + + + + +cPawn * cBehaviorChaser::GetTarget() +{ + return m_Target; +} + + + + + +void cBehaviorChaser::SetTarget(cPawn * a_Target) +{ + m_Target = a_Target; +} + + + + + +bool cBehaviorChaser::TargetIsInStrikeRange() +{ + ASSERT(m_Target != nullptr); + ASSERT(m_Parent != nullptr); + /* + #include "../../Tracer.h" + cTracer LineOfSight(m_Parent->GetWorld()); + Vector3d MyHeadPosition = m_Parent->GetPosition() + Vector3d(0, m_Parent->GetHeight(), 0); + Vector3d AttackDirection(m_ParentChaser->GetTarget()->GetPosition() + Vector3d(0, GetTarget()->GetHeight(), 0) - MyHeadPosition); + + + if (GetTarget() != nullptr) + { + MoveToPosition(GetTarget()->GetPosition()); + } + if (TargetIsInRange() && !LineOfSight.Trace(MyHeadPosition, AttackDirection, static_cast<int>(AttackDirection.Length())) && (GetHealth() > 0.0)) + { + // Attack if reached destination, target isn't null, and have a clear line of sight to target (so won't attack through walls) + Attack(a_Dt); + } + */ + + return ((m_Target->GetPosition() - m_Parent->GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange)); +} + + + + + +bool cBehaviorChaser::TargetOutOfSight() +{ + ASSERT(m_Target != nullptr); + if ((GetTarget()->GetPosition() - m_Parent->GetPosition()).Length() > m_Parent->GetSightDistance()) + { + return true; + } + return false; +} + + + + + +void cBehaviorChaser::ResetStrikeCooldown() +{ + m_AttackCoolDownTicksLeft = static_cast<int>(3 * 20 * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every 3 seconds +} + + + + + +void cBehaviorChaser::StrikeTarget() +{ + if (m_AttackCoolDownTicksLeft != 0) + { + cBehaviorStriker * Striker = m_Parent->GetBehaviorStriker(); + if (Striker != nullptr) + { + // Striker->Strike(m_Target); //mobTodo + } + ResetStrikeCooldown(); + } +} diff --git a/src/Mobs/Behaviors/BehaviorChaser.h b/src/Mobs/Behaviors/BehaviorChaser.h new file mode 100644 index 000000000..d0910e11e --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorChaser.h @@ -0,0 +1,65 @@ +#pragma once + +class cBehaviorChaser; + +#include "Behavior.h" + +class cMonster; +class cPawn; +class cBehaviorStriker; + + +/** Grants chase capability to the mob. Note that this is not the same as agression! +The mob may possess this trait and not attack anyone or only attack when provoked. +Unlike most traits, this one has several forms, and therefore it is an abstract type +You should use one of its derived classes, and you cannot use it directly. */ +class cBehaviorChaser : public cBehavior +{ + +public: + cBehaviorChaser(cMonster * a_Parent); + + // Functions our host Monster should invoke: + bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void Destroyed() override; + void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void DoTakeDamage(TakeDamageInfo & a_TDI) override; + + // Our host monster will call these once it loads its config file + void SetAttackRate(float a_AttackRate); + void SetAttackRange(int a_AttackRange); + void SetAttackDamage(int a_AttackDamage); + + /** Returns the target pointer, or a nullptr if we're not targeting anyone. */ + cPawn * GetTarget(); + + /** Sets the target. */ + void SetTarget(cPawn * a_Target); +protected: + void ApproachTarget(); + // virtual void ApproachTarget() = 0; //mobTodo +private: + + /** Our parent */ + cMonster * m_Parent; + + // The mob we want to attack + cPawn * m_Target; + + // Target stuff + bool TargetIsInStrikeRange(); + bool TargetOutOfSight(); + void StrikeTarget(); + + // Cooldown stuff + void ResetStrikeCooldown(); + + // Our attacking parameters (Set by the setter methods, loaded from a config file in cMonster) + float m_AttackRate; + int m_AttackDamage; + int m_AttackRange; + int m_AttackCoolDownTicksLeft; + + int m_TicksSinceLastDamaged; // How many ticks ago were we last damaged by a player? +}; diff --git a/src/Mobs/Behaviors/BehaviorCoward.cpp b/src/Mobs/Behaviors/BehaviorCoward.cpp new file mode 100644 index 000000000..43be5c49c --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorCoward.cpp @@ -0,0 +1,63 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BehaviorCoward.h" +#include "../Monster.h" +#include "../../World.h" +#include "../../Entities/Player.h" +#include "../../Entities/Entity.h" + +cBehaviorCoward::cBehaviorCoward(cMonster * a_Parent) : + m_Parent(a_Parent), + m_Attacker(nullptr) +{ + ASSERT(m_Parent != nullptr); +} + + + + +bool cBehaviorCoward::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + return (m_Attacker != nullptr); //mobTodo probably not so safe pointer (and cChaser m_Target too) +} + + + + + +void cBehaviorCoward::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + if (m_Attacker == nullptr) + { + return; + } + + // TODO NOT SAFE + if (m_Attacker->IsDestroyed() || (m_Attacker->GetPosition() - m_Parent->GetPosition()).Length() > m_Parent->GetSightDistance()) + { + // We lost the attacker + m_Attacker = nullptr; + } + + Vector3d newloc = m_Parent->GetPosition(); + newloc.x = (m_Attacker->GetPosition().x < newloc.x)? (newloc.x + m_Parent->GetSightDistance()): (newloc.x - m_Parent->GetSightDistance()); + newloc.z = (m_Attacker->GetPosition().z < newloc.z)? (newloc.z + m_Parent->GetSightDistance()): (newloc.z - m_Parent->GetSightDistance()); + m_Parent->MoveToPosition(newloc); +} + + + + + +void cBehaviorCoward::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + if ((a_TDI.Attacker != m_Parent) && (a_TDI.Attacker != nullptr)) + { + m_Attacker = a_TDI.Attacker; + } +} + diff --git a/src/Mobs/Behaviors/BehaviorCoward.h b/src/Mobs/Behaviors/BehaviorCoward.h new file mode 100644 index 000000000..048101d5d --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorCoward.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Behavior.h" + +// Makes the mob run away from any other mob that damages it + +//fwds +class cMonster; +class cItems; +class cEntity; +struct TakeDamageInfo; + +class cBehaviorCoward : cBehavior +{ +public: + cBehaviorCoward(cMonster * a_Parent); + + // Functions our host Monster should invoke: + bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void DoTakeDamage(TakeDamageInfo & a_TDI) override; + + +private: + cMonster * m_Parent; // Our Parent + cEntity * m_Attacker; // The entity we're running away from +}; diff --git a/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp b/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp new file mode 100644 index 000000000..b28c3c49c --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorDayLightBurner.cpp @@ -0,0 +1,97 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BehaviorDayLightBurner.h" +#include "../Monster.h" +#include "../../Entities/Player.h" +#include "../../Entities/Entity.h" + +#include "../../Chunk.h" + +cBehaviorDayLightBurner::cBehaviorDayLightBurner(cMonster * a_Parent) : m_Parent(a_Parent) +{ + ASSERT(m_Parent != nullptr); +} + +void cBehaviorDayLightBurner::PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + // mobTodo WouldBurn + bool WouldBurn = false; // TEMP + + int RelY = static_cast<int>(m_Parent->GetPosY()); + if ((RelY < 0) || (RelY >= cChunkDef::Height)) + { + // Outside the world + return; + } + if (!a_Chunk.IsLightValid()) + { + m_Parent->GetWorld()->QueueLightChunk(m_Parent->GetChunkX(), m_Parent->GetChunkZ()); + return; + } + + if (!m_Parent->IsOnFire() && WouldBurn) + { + // Burn for 100 ticks, then decide again + m_Parent->StartBurning(100); + } +} + + + + +bool cBehaviorDayLightBurner::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk) +{ + int RelY = FloorC(a_Location.y); + if (RelY <= 0) + { + // The mob is about to die, no point in burning + return false; + } + if (RelY >= cChunkDef::Height) + { + // Always burn above the world + return true; + } + + PREPARE_REL_AND_CHUNK(a_Location, a_Chunk); + if (!RelSuccess) + { + return false; + } + + if ( + (Chunk->GetBlock(Rel.x, Rel.y, Rel.z) != E_BLOCK_SOULSAND) && // Not on soulsand + (m_Parent->GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime + m_Parent->GetWorld()->IsWeatherSunnyAt(static_cast<int>(m_Parent->GetPosX()), static_cast<int>(m_Parent->GetPosZ())) // Not raining + ) + { + int MobHeight = 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; +} diff --git a/src/Mobs/Behaviors/BehaviorDayLightBurner.h b/src/Mobs/Behaviors/BehaviorDayLightBurner.h new file mode 100644 index 000000000..77fcce281 --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorDayLightBurner.h @@ -0,0 +1,26 @@ +#pragma once + +// mobTodo I just need vector3d +#include "Behavior.h" +#include "../../World.h" + +// fwds +class cMonster; +class cEntity; +class cChunk; + +class cBehaviorDayLightBurner : cBehavior +{ +public: + cBehaviorDayLightBurner(cMonster * a_Parent); + + void PostTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + bool WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk); + + // Functions our host Monster should invoke: + void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + +private: + cMonster * m_Parent; // Our Parent + cEntity * m_Attacker; // The entity we're running away from +}; diff --git a/src/Mobs/Behaviors/BehaviorItemDropper.cpp b/src/Mobs/Behaviors/BehaviorItemDropper.cpp new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorItemDropper.cpp @@ -0,0 +1 @@ + diff --git a/src/Mobs/Behaviors/BehaviorItemDropper.h b/src/Mobs/Behaviors/BehaviorItemDropper.h new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorItemDropper.h @@ -0,0 +1 @@ + diff --git a/src/Mobs/Behaviors/BehaviorItemFollower.cpp b/src/Mobs/Behaviors/BehaviorItemFollower.cpp new file mode 100644 index 000000000..c6cb0df04 --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorItemFollower.cpp @@ -0,0 +1,64 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BehaviorItemFollower.h" +#include "../Monster.h" +#include "../../World.h" +#include "../../Entities/Player.h" + + +cBehaviorItemFollower::cBehaviorItemFollower(cMonster * a_Parent) : + m_Parent(a_Parent) +{ + m_Parent = a_Parent; + ASSERT(m_Parent != nullptr); +} + + + + + +bool cBehaviorItemFollower::IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + cItems FollowedItems; + m_Parent->GetFollowedItems(FollowedItems); + if (FollowedItems.Size() > 0) + { + cPlayer * a_Closest_Player = m_Parent->GetNearestPlayer(); + if (a_Closest_Player != nullptr) + { + cItem EquippedItem = a_Closest_Player->GetEquippedItem(); + if (FollowedItems.ContainsType(EquippedItem)) + { + return true; // We take control of the monster. Now it'll call our tick + } + } + } + return false; +} + + + + + +void cBehaviorItemFollower::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + UNUSED(a_Dt); + UNUSED(a_Chunk); + cItems FollowedItems; + m_Parent->GetFollowedItems(FollowedItems); + if (FollowedItems.Size() > 0) + { + cPlayer * a_Closest_Player = m_Parent->GetNearestPlayer(); + if (a_Closest_Player != nullptr) + { + cItem EquippedItem = a_Closest_Player->GetEquippedItem(); + if (FollowedItems.ContainsType(EquippedItem)) + { + Vector3d PlayerPos = a_Closest_Player->GetPosition(); + m_Parent->MoveToPosition(PlayerPos); + } + } + } +} diff --git a/src/Mobs/Behaviors/BehaviorItemFollower.h b/src/Mobs/Behaviors/BehaviorItemFollower.h new file mode 100644 index 000000000..94f8bda15 --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorItemFollower.h @@ -0,0 +1,27 @@ +#pragma once + +// Makes the mob follow specific held items +class cBehaviorItemFollower; + +#include "Behavior.h" + +// fwds +class cMonster; +class cItems; + +class cBehaviorItemFollower : public cBehavior +{ +public: + cBehaviorItemFollower(cMonster * a_Parent); + + void GetBreedingItems(cItems & a_Items); + + // Functions our host Monster should invoke: + bool IsControlDesired(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + + +private: + /** Our parent */ + cMonster * m_Parent; +}; diff --git a/src/Mobs/Behaviors/BehaviorStriker.cpp b/src/Mobs/Behaviors/BehaviorStriker.cpp new file mode 100644 index 000000000..e09bb45f9 --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorStriker.cpp @@ -0,0 +1,7 @@ +/* +bool cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt) +{ + GetTarget()->TakeDamage(dtMobAttack, this, m_AttackDamage, 0); + + return true; +} */ diff --git a/src/Mobs/Behaviors/BehaviorStriker.h b/src/Mobs/Behaviors/BehaviorStriker.h new file mode 100644 index 000000000..6fc094476 --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorStriker.h @@ -0,0 +1,13 @@ +#pragma once +class cBehaviorStriker +{ + +}; + +/* +bool cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt) +{ + GetTarget()->TakeDamage(dtMobAttack, this, m_AttackDamage, 0); + + return true; +} */ diff --git a/src/Mobs/Behaviors/BehaviorWanderer.cpp b/src/Mobs/Behaviors/BehaviorWanderer.cpp new file mode 100644 index 000000000..53efd407c --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorWanderer.cpp @@ -0,0 +1,60 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules +#include "BehaviorWanderer.h" +#include "../Monster.h" +#include "../../Chunk.h" +#include "../../World.h" + +cBehaviorWanderer::cBehaviorWanderer(cMonster * a_Parent) + : m_Parent(a_Parent) + , m_IdleInterval(0) +{ + ASSERT(m_Parent != nullptr); +} + +bool cBehaviorWanderer::ActiveTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + if (m_Parent->IsPathFinderActivated()) + { + return true; // Still getting there + } + + m_IdleInterval += a_Dt; + + if (m_IdleInterval > std::chrono::seconds(1)) + { + // At this interval the results are predictable + int rem = m_Parent->GetWorld()->GetTickRandomNumber(6) + 1; + m_IdleInterval -= std::chrono::seconds(1); // So nothing gets dropped when the server hangs for a few seconds + + Vector3d Dist; + Dist.x = static_cast<double>(m_Parent->GetWorld()->GetTickRandomNumber(10)) - 5.0; + Dist.z = static_cast<double>(m_Parent->GetWorld()->GetTickRandomNumber(10)) - 5.0; + + if ((Dist.SqrLength() > 2) && (rem >= 3)) + { + + Vector3d Destination(m_Parent->GetPosX() + Dist.x, m_Parent->GetPosition().y, m_Parent->GetPosZ() + Dist.z); + + cChunk * Chunk = a_Chunk.GetNeighborChunk(static_cast<int>(Destination.x), static_cast<int>(Destination.z)); + if ((Chunk == nullptr) || !Chunk->IsValid()) + { + return true; + } + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + int RelX = static_cast<int>(Destination.x) - Chunk->GetPosX() * cChunkDef::Width; + int RelZ = static_cast<int>(Destination.z) - Chunk->GetPosZ() * cChunkDef::Width; + int YBelowUs = static_cast<int>(Destination.y) - 1; + if (YBelowUs >= 0) + { + Chunk->GetBlockTypeMeta(RelX, YBelowUs, RelZ, BlockType, BlockMeta); + if (BlockType != E_BLOCK_STATIONARY_WATER) // Idle mobs shouldn't enter water on purpose + { + m_Parent->MoveToPosition(Destination); + } + } + } + } + return true; +} diff --git a/src/Mobs/Behaviors/BehaviorWanderer.h b/src/Mobs/Behaviors/BehaviorWanderer.h new file mode 100644 index 000000000..258bf5f59 --- /dev/null +++ b/src/Mobs/Behaviors/BehaviorWanderer.h @@ -0,0 +1,24 @@ +#pragma once + +// The mob will wander around +#include <chrono> + +class cMonster; +class cEntity; +class cChunk; + +class cBehaviorWanderer +{ + +public: + cBehaviorWanderer(cMonster * a_Parent); + + // Functions our host Monster should invoke: + bool ActiveTick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + + +private: + cMonster * m_Parent; // Our Parent + + std::chrono::milliseconds m_IdleInterval; +}; diff --git a/src/Mobs/Behaviors/CMakeLists.txt b/src/Mobs/Behaviors/CMakeLists.txt new file mode 100644 index 000000000..017fc0f35 --- /dev/null +++ b/src/Mobs/Behaviors/CMakeLists.txt @@ -0,0 +1,35 @@ + +cmake_minimum_required (VERSION 2.6) +project (Cuberite) + +include_directories ("${PROJECT_SOURCE_DIR}/../") + +SET (SRCS + Behavior.cpp + BehaviorAggressive.cpp + BehaviorChaser.cpp + BehaviorBreeder.cpp + BehaviorDayLightBurner.cpp + BehaviorCoward.cpp + BehaviorItemDropper.cpp + BehaviorItemFollower.cpp + BehaviorStriker.cpp + BehaviorWanderer.cpp +) + +SET (HDRS + Behavior.h + BehaviorAggressive.h + BehaviorChaser.h + BehaviorBreeder.h + BehaviorDayLightBurner.h + BehaviorCoward.h + BehaviorItemDropper.h + BehaviorItemFollower.h + BehaviorStriker.h + BehaviorWanderer.h +) + +if(NOT MSVC) + add_library(Behaviors ${SRCS} ${HDRS}) +endif() diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp index a48bfa886..7ada21369 100644 --- a/src/Mobs/Blaze.cpp +++ b/src/Mobs/Blaze.cpp @@ -9,10 +9,10 @@ cBlaze::cBlaze(void) : - super("Blaze", mtBlaze, "entity.blaze.hurt", "entity.blaze.death", 0.6, 1.8) + super("Blaze", mtBlaze, "entity.blaze.hurt", "entity.blaze.death", 0.6, 1.8) { - SetGravity(-8.0f); - SetAirDrag(0.05f); + SetGravity(-8.0f); + SetAirDrag(0.05f); } @@ -21,36 +21,37 @@ cBlaze::cBlaze(void) : void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - if ((a_Killer != nullptr) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf"))) - { - unsigned int LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); - AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_BLAZE_ROD); - } + if ((a_Killer != nullptr) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf"))) + { + unsigned int LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_BLAZE_ROD); + } } - +// mobTODO +/* bool cBlaze::Attack(std::chrono::milliseconds a_Dt) { - if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) - { - // Setting this higher gives us more wiggle room for attackrate - Vector3d Speed = GetLookVector() * 20; - Speed.y = Speed.y + 1; - - auto FireCharge = cpp14::make_unique<cFireChargeEntity>(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); - auto FireChargePtr = FireCharge.get(); - if (!FireChargePtr->Initialize(std::move(FireCharge), *m_World)) - { - return false; - } - - ResetAttackCooldown(); - // ToDo: Shoot 3 fireballs instead of 1. - - return true; - } - return false; -} + if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) + { + // Setting this higher gives us more wiggle room for attackrate + Vector3d Speed = GetLookVector() * 20; + Speed.y = Speed.y + 1; + + auto FireCharge = cpp14::make_unique<cFireChargeEntity>(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); + auto FireChargePtr = FireCharge.get(); + if (!FireChargePtr->Initialize(std::move(FireCharge), *m_World)) + { + return false; + } + + ResetAttackCooldown(); + // ToDo: Shoot 3 fireballs instead of 1. + + return true; + } + return false; +}*/ diff --git a/src/Mobs/Blaze.h b/src/Mobs/Blaze.h index ca755b626..7e389fa6c 100644 --- a/src/Mobs/Blaze.h +++ b/src/Mobs/Blaze.h @@ -8,15 +8,14 @@ class cBlaze : - public cAggressiveMonster + public cAggressiveMonster { - typedef cAggressiveMonster super; + typedef cAggressiveMonster super; public: - cBlaze(void); + cBlaze(void); - CLASS_PROTODEF(cBlaze) + CLASS_PROTODEF(cBlaze) - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual bool Attack(std::chrono::milliseconds a_Dt) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; } ; diff --git a/src/Mobs/CaveSpider.cpp b/src/Mobs/CaveSpider.cpp index c6aa8af61..8d8e15d85 100644 --- a/src/Mobs/CaveSpider.cpp +++ b/src/Mobs/CaveSpider.cpp @@ -8,7 +8,7 @@ cCaveSpider::cCaveSpider(void) : - super("CaveSpider", mtCaveSpider, "entity.spider.hurt", "entity.spider.death", 0.7, 0.5) + super("CaveSpider", mtCaveSpider, "entity.spider.hurt", "entity.spider.death", 0.7, 0.5) { } @@ -18,34 +18,34 @@ cCaveSpider::cCaveSpider(void) : void cCaveSpider::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { - super::Tick(a_Dt, a_Chunk); - if (!IsTicking()) - { - // The base class tick destroyed us - return; - } - - m_EMPersonality = (GetWorld()->GetTimeOfDay() < (12000 + 1000)) ? PASSIVE : AGGRESSIVE; + super::Tick(a_Dt, a_Chunk); + if (!IsTicking()) + { + // The base class tick destroyed us + return; + } + + m_EMPersonality = (GetWorld()->GetTimeOfDay() < (12000 + 1000)) ? PASSIVE : AGGRESSIVE; } - +/* bool cCaveSpider::Attack(std::chrono::milliseconds a_Dt) { - if (!super::Attack(a_Dt)) - { - return false; - } - - if (GetTarget()->IsPawn()) - { - // TODO: Easy = no poison, Medium = 7 seconds, Hard = 15 seconds - static_cast<cPawn *>(GetTarget())->AddEntityEffect(cEntityEffect::effPoison, 7 * 20, 0); - } - return true; -} + if (!super::Attack(a_Dt)) + { + return false; + } + + if (GetTarget()->IsPawn()) + { + // TODO: Easy = no poison, Medium = 7 seconds, Hard = 15 seconds + static_cast<cPawn *>(GetTarget())->AddEntityEffect(cEntityEffect::effPoison, 7 * 20, 0); + } + return true; +}*/ @@ -53,16 +53,16 @@ bool cCaveSpider::Attack(std::chrono::milliseconds a_Dt) void cCaveSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - unsigned int LootingLevel = 0; - if (a_Killer != nullptr) - { - LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); - } - AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_STRING); - if ((a_Killer != nullptr) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf"))) - { - AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_SPIDER_EYE); - } + unsigned int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_STRING); + if ((a_Killer != nullptr) && (a_Killer->IsPlayer() || a_Killer->IsA("cWolf"))) + { + AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_SPIDER_EYE); + } } diff --git a/src/Mobs/CaveSpider.h b/src/Mobs/CaveSpider.h index cf4b8e17c..71ee0b244 100644 --- a/src/Mobs/CaveSpider.h +++ b/src/Mobs/CaveSpider.h @@ -7,18 +7,17 @@ class cCaveSpider : - public cAggressiveMonster + public cAggressiveMonster { - typedef cAggressiveMonster super; + typedef cAggressiveMonster super; public: - cCaveSpider(void); + cCaveSpider(void); - CLASS_PROTODEF(cCaveSpider) + CLASS_PROTODEF(cCaveSpider) - virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual bool Attack(std::chrono::milliseconds a_Dt) override; - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; } ; diff --git a/src/Mobs/Chicken.cpp b/src/Mobs/Chicken.cpp index 1068295e6..80cff7fb8 100644 --- a/src/Mobs/Chicken.cpp +++ b/src/Mobs/Chicken.cpp @@ -5,16 +5,12 @@ - - - - cChicken::cChicken(void) : - super("Chicken", mtChicken, "entity.chicken.hurt", "entity.chicken.death", 0.3, 0.4), - m_EggDropTimer(0) + super("Chicken", mtChicken, "entity.chicken.hurt", "entity.chicken.death", 0.3, 0.4), + m_EggDropTimer(0) { - SetGravity(-2.0f); - SetAirDrag(0.0f); + SetGravity(-2.0f); + SetAirDrag(0.0f); } @@ -22,36 +18,36 @@ cChicken::cChicken(void) : void cChicken::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { - super::Tick(a_Dt, a_Chunk); - if (!IsTicking()) - { - // The base class tick destroyed us - return; - } - - if (IsBaby()) - { - return; // Babies don't lay eggs - } - - if ((m_EggDropTimer == 6000) && GetRandomProvider().RandBool()) - { - cItems Drops; - m_EggDropTimer = 0; - Drops.push_back(cItem(E_ITEM_EGG, 1)); - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); - } - else if (m_EggDropTimer == 12000) - { - cItems Drops; - m_EggDropTimer = 0; - Drops.push_back(cItem(E_ITEM_EGG, 1)); - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); - } - else - { - m_EggDropTimer++; - } + super::Tick(a_Dt, a_Chunk); + if (!IsTicking()) + { + // The base class tick destroyed us + return; + } + + if (IsBaby()) + { + return; // Babies don't lay eggs + } + + if ((m_EggDropTimer == 6000) && GetRandomProvider().RandBool()) + { + cItems Drops; + m_EggDropTimer = 0; + Drops.push_back(cItem(E_ITEM_EGG, 1)); + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); + } + else if (m_EggDropTimer == 12000) + { + cItems Drops; + m_EggDropTimer = 0; + Drops.push_back(cItem(E_ITEM_EGG, 1)); + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); + } + else + { + m_EggDropTimer++; + } } @@ -60,13 +56,13 @@ void cChicken::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cChicken::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - unsigned int LootingLevel = 0; - if (a_Killer != nullptr) - { - LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); - } - AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_FEATHER); - AddRandomDropItem(a_Drops, 1, 1, IsOnFire() ? E_ITEM_COOKED_CHICKEN : E_ITEM_RAW_CHICKEN); + unsigned int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_FEATHER); + AddRandomDropItem(a_Drops, 1, 1, IsOnFire() ? E_ITEM_COOKED_CHICKEN : E_ITEM_RAW_CHICKEN); } @@ -75,7 +71,7 @@ void cChicken::GetDrops(cItems & a_Drops, cEntity * a_Killer) void cChicken::HandleFalling(void) { - // empty - chickens don't take fall damage + // empty - chickens don't take fall damage } diff --git a/src/Mobs/Chicken.h b/src/Mobs/Chicken.h index 3be338b15..b954c19ec 100644 --- a/src/Mobs/Chicken.h +++ b/src/Mobs/Chicken.h @@ -7,28 +7,28 @@ class cChicken : - public cPassiveMonster + public cPassiveMonster { - typedef cPassiveMonster super; + typedef cPassiveMonster super; public: - cChicken(void); + cChicken(void); - CLASS_PROTODEF(cChicken) + CLASS_PROTODEF(cChicken) - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual void GetFollowedItems(cItems & a_Items) override - { - a_Items.Add(E_ITEM_SEEDS); - } + virtual void GetFollowedItems(cItems & a_Items) override + { + a_Items.Add(E_ITEM_SEEDS); + } - virtual void HandleFalling(void) override; + virtual void HandleFalling(void) override; private: - int m_EggDropTimer; + int m_EggDropTimer; } ; diff --git a/src/Mobs/Cow.cpp b/src/Mobs/Cow.cpp index 9736fe440..c92f2369f 100644 --- a/src/Mobs/Cow.cpp +++ b/src/Mobs/Cow.cpp @@ -6,12 +6,8 @@ - - - - cCow::cCow(void) : - super("Cow", mtCow, "entity.cow.hurt", "entity.cow.death", 0.9, 1.3) + super("Cow", mtCow, "entity.cow.hurt", "entity.cow.death", 0.9, 1.3) { } @@ -21,13 +17,13 @@ cCow::cCow(void) : void cCow::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); - AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); + 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); + AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_STEAK : E_ITEM_RAW_BEEF); } @@ -36,15 +32,15 @@ void cCow::GetDrops(cItems & a_Drops, cEntity * a_Killer) void cCow::OnRightClicked(cPlayer & a_Player) { - super::OnRightClicked(a_Player); - - short HeldItem = a_Player.GetEquippedItem().m_ItemType; - if (HeldItem == E_ITEM_BUCKET) - { - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - a_Player.GetInventory().AddItem(E_ITEM_MILK); - } - } + super::OnRightClicked(a_Player); + + short HeldItem = a_Player.GetEquippedItem().m_ItemType; + if (HeldItem == E_ITEM_BUCKET) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + a_Player.GetInventory().AddItem(E_ITEM_MILK); + } + } } diff --git a/src/Mobs/Cow.h b/src/Mobs/Cow.h index 569c6e619..7b3cdb56e 100644 --- a/src/Mobs/Cow.h +++ b/src/Mobs/Cow.h @@ -8,22 +8,22 @@ class cCow : - public cPassiveMonster + public cPassiveMonster { - typedef cPassiveMonster super; + typedef cPassiveMonster super; public: - cCow(); + cCow(); - CLASS_PROTODEF(cCow) + CLASS_PROTODEF(cCow) - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void GetFollowedItems(cItems & a_Items) override - { - a_Items.Add(E_ITEM_WHEAT); - } + virtual void GetFollowedItems(cItems & a_Items) override + { + a_Items.Add(E_ITEM_WHEAT); + } } ; diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp index 84b68cf7c..65576ff25 100644 --- a/src/Mobs/Creeper.cpp +++ b/src/Mobs/Creeper.cpp @@ -26,6 +26,8 @@ cCreeper::cCreeper(void) : void cCreeper::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); + /* mobTodo + if (!IsTicking()) { // The base class tick destroyed us @@ -53,7 +55,8 @@ void cCreeper::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) m_World->DoExplosionAt((m_bIsCharged ? 5 : 3), GetPosX(), GetPosY(), GetPosZ(), false, esMonster, this); Destroy(); // Just in case we aren't killed by the explosion } - } + } */ + } @@ -125,7 +128,8 @@ bool cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI) - +// mobTODO +/* bool cCreeper::Attack(std::chrono::milliseconds a_Dt) { UNUSED(a_Dt); @@ -139,7 +143,7 @@ bool cCreeper::Attack(std::chrono::milliseconds a_Dt) return true; } return false; -} +}*/ diff --git a/src/Mobs/Creeper.h b/src/Mobs/Creeper.h index aea36def3..2ef72650b 100644 --- a/src/Mobs/Creeper.h +++ b/src/Mobs/Creeper.h @@ -8,29 +8,28 @@ class cCreeper : - public cAggressiveMonster + public cAggressiveMonster { - typedef cAggressiveMonster super; + typedef cAggressiveMonster super; public: - cCreeper(void); + cCreeper(void); - CLASS_PROTODEF(cCreeper) + CLASS_PROTODEF(cCreeper) - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual bool Attack(std::chrono::milliseconds a_Dt) override; - virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void OnRightClicked(cPlayer & a_Player) override; - bool IsBlowing(void) const {return m_bIsBlowing; } - bool IsCharged(void) const {return m_bIsCharged; } - bool IsBurnedWithFlintAndSteel(void) const {return m_BurnedWithFlintAndSteel; } + bool IsBlowing(void) const {return m_bIsBlowing; } + bool IsCharged(void) const {return m_bIsCharged; } + bool IsBurnedWithFlintAndSteel(void) const {return m_BurnedWithFlintAndSteel; } private: - bool m_bIsBlowing, m_bIsCharged, m_BurnedWithFlintAndSteel; - int m_ExplodingTimer; + bool m_bIsBlowing, m_bIsCharged, m_BurnedWithFlintAndSteel; + int m_ExplodingTimer; } ; diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp index 5cfe0d4cd..6aa72369f 100644 --- a/src/Mobs/Enderman.cpp +++ b/src/Mobs/Enderman.cpp @@ -98,67 +98,6 @@ void cEnderman::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cEnderman::CheckEventSeePlayer(cChunk & a_Chunk) -{ - if (GetTarget() != nullptr) - { - return; - } - - cPlayerLookCheck Callback(GetPosition(), m_SightDistance); - if (m_World->ForEachPlayer(Callback)) - { - return; - } - - ASSERT(Callback.GetPlayer() != nullptr); - - if (!CheckLight()) - { - // Insufficient light for enderman to become aggravated - // TODO: Teleport to a suitable location - return; - } - - if (!Callback.GetPlayer()->CanMobsTarget()) - { - return; - } - - // Target the player - cMonster::EventSeePlayer(Callback.GetPlayer(), a_Chunk); - m_EMState = CHASING; - m_bIsScreaming = true; - GetWorld()->BroadcastEntityMetadata(*this); -} - - - - - -void cEnderman::CheckEventLostPlayer(void) -{ - super::CheckEventLostPlayer(); - if (!CheckLight()) - { - EventLosePlayer(); - } -} - - - - - -void cEnderman::EventLosePlayer() -{ - super::EventLosePlayer(); - m_bIsScreaming = false; - GetWorld()->BroadcastEntityMetadata(*this); -} - - - - bool cEnderman::CheckLight() { @@ -197,7 +136,7 @@ void cEnderman::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) // Take damage when touching water, drowning damage seems to be most appropriate if (CheckRain() || IsSwimming()) { - EventLosePlayer(); + // EventLosePlayer(); //mobTodo TakeDamage(dtDrowning, nullptr, 1, 0); // TODO teleport to a safe location } diff --git a/src/Mobs/Enderman.h b/src/Mobs/Enderman.h index c9ffbeaba..0dc648468 100644 --- a/src/Mobs/Enderman.h +++ b/src/Mobs/Enderman.h @@ -8,35 +8,32 @@ class cEnderman : - public cPassiveAggressiveMonster + public cPassiveAggressiveMonster { - typedef cPassiveAggressiveMonster super; + typedef cPassiveAggressiveMonster super; public: - cEnderman(void); + cEnderman(void); - CLASS_PROTODEF(cEnderman) + CLASS_PROTODEF(cEnderman) - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void CheckEventSeePlayer(cChunk & a_Chunk) override; - virtual void CheckEventLostPlayer(void) override; - virtual void EventLosePlayer(void) override; - virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - bool IsScreaming(void) const {return m_bIsScreaming; } - BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; } - NIBBLETYPE GetCarriedMeta(void) const {return CarriedMeta; } + bool IsScreaming(void) const {return m_bIsScreaming; } + BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; } + NIBBLETYPE GetCarriedMeta(void) const {return CarriedMeta; } - /** Returns if the current sky light level is sufficient for the enderman to become aggravated */ - bool CheckLight(void); - /** Returns if the enderman gets hit by the rain */ - bool CheckRain(void); + /** Returns if the current sky light level is sufficient for the enderman to become aggravated */ + bool CheckLight(void); + /** Returns if the enderman gets hit by the rain */ + bool CheckRain(void); private: - bool m_bIsScreaming; - BLOCKTYPE CarriedBlock; - NIBBLETYPE CarriedMeta; + bool m_bIsScreaming; + BLOCKTYPE CarriedBlock; + NIBBLETYPE CarriedMeta; } ; diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp index 2488e63b1..97a27be5a 100644 --- a/src/Mobs/Ghast.cpp +++ b/src/Mobs/Ghast.cpp @@ -9,7 +9,7 @@ cGhast::cGhast(void) : - super("Ghast", mtGhast, "entity.ghast.hurt", "entity.ghast.death", 4, 4) + super("Ghast", mtGhast, "entity.ghast.hurt", "entity.ghast.death", 4, 4) { } @@ -19,39 +19,39 @@ cGhast::cGhast(void) : void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - unsigned int LootingLevel = 0; - if (a_Killer != nullptr) - { - LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); - } - AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GUNPOWDER); - AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_GHAST_TEAR); + unsigned int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GUNPOWDER); + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_GHAST_TEAR); } - -bool cGhast::Attack(std::chrono::milliseconds a_Dt) +// mobTODO +/*bool cGhast::Attack(std::chrono::milliseconds a_Dt) { - if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) - { - // Setting this higher gives us more wiggle room for attackrate - Vector3d Speed = GetLookVector() * 20; - Speed.y = Speed.y + 1; - - auto GhastBall = cpp14::make_unique<cGhastFireballEntity>(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); - auto GhastBallPtr = GhastBall.get(); - if (!GhastBallPtr->Initialize(std::move(GhastBall), *m_World)) - { - return false; - } - - ResetAttackCooldown(); - return true; - } - return false; -} + if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) + { + // Setting this higher gives us more wiggle room for attackrate + Vector3d Speed = GetLookVector() * 20; + Speed.y = Speed.y + 1; + + auto GhastBall = cpp14::make_unique<cGhastFireballEntity>(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); + auto GhastBallPtr = GhastBall.get(); + if (!GhastBallPtr->Initialize(std::move(GhastBall), *m_World)) + { + return false; + } + + ResetAttackCooldown(); + return true; + } + return false; +}*/ diff --git a/src/Mobs/Ghast.h b/src/Mobs/Ghast.h index a41a72ddc..c3423590a 100644 --- a/src/Mobs/Ghast.h +++ b/src/Mobs/Ghast.h @@ -8,19 +8,18 @@ class cGhast : - public cAggressiveMonster + public cAggressiveMonster { - typedef cAggressiveMonster super; + typedef cAggressiveMonster super; public: - cGhast(void); + cGhast(void); - CLASS_PROTODEF(cGhast) + CLASS_PROTODEF(cGhast) - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual bool Attack(std::chrono::milliseconds a_Dt) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - bool IsCharging(void) const {return false; } + bool IsCharging(void) const {return false; } } ; diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp index 13630b0e3..21a58489d 100644 --- a/src/Mobs/Horse.cpp +++ b/src/Mobs/Horse.cpp @@ -11,21 +11,21 @@ cHorse::cHorse(int Type, int Color, int Style, int TameTimes) : - super("Horse", mtHorse, "entity.horse.hurt", "entity.horse.death", 1.4, 1.6), - m_bHasChest(false), - m_bIsEating(false), - m_bIsRearing(false), - m_bIsMouthOpen(false), - m_bIsTame(false), - m_bIsSaddled(false), - m_Type(Type), - m_Color(Color), - m_Style(Style), - m_Armour(0), - m_TimesToTame(TameTimes), - m_TameAttemptTimes(0), - m_RearTickCount(0), - m_MaxSpeed(14.0) + super("Horse", mtHorse, "entity.horse.hurt", "entity.horse.death", 1.4, 1.6), + m_bHasChest(false), + m_bIsEating(false), + m_bIsRearing(false), + m_bIsMouthOpen(false), + m_bIsTame(false), + m_bIsSaddled(false), + m_Type(Type), + m_Color(Color), + m_Style(Style), + m_Armour(0), + m_TimesToTame(TameTimes), + m_TameAttemptTimes(0), + m_RearTickCount(0), + m_MaxSpeed(14.0) { } @@ -35,67 +35,67 @@ cHorse::cHorse(int Type, int Color, int Style, int TameTimes) : void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { - super::Tick(a_Dt, a_Chunk); - if (!IsTicking()) - { - // The base class tick destroyed us - return; - } - - auto & Random = GetRandomProvider(); - - if (!m_bIsMouthOpen) - { - if (Random.RandBool(0.02)) - { - m_bIsMouthOpen = true; - } - } - else - { - if (Random.RandBool(0.10)) - { - m_bIsMouthOpen = false; - } - } - - if ((m_Attachee != nullptr) && (!m_bIsTame)) - { - if (m_TameAttemptTimes < m_TimesToTame) - { - if (Random.RandBool(0.02)) - { - m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_EAST)); - m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_WEST)); - m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_EAST)); - m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_WEST)); - - m_World->BroadcastSoundEffect("entity.horse.angry", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 1.0f); - m_Attachee->Detach(); - m_bIsRearing = true; - } - } - else - { - m_World->GetBroadcaster().BroadcastParticleEffect("heart", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5); - m_bIsTame = true; - } - } - - if (m_bIsRearing) - { - if (m_RearTickCount == 20) - { - m_bIsRearing = false; - m_RearTickCount = 0; - } - else - { - m_RearTickCount++; - } - } - - m_World->BroadcastEntityMetadata(*this); + super::Tick(a_Dt, a_Chunk); + if (!IsTicking()) + { + // The base class tick destroyed us + return; + } + + auto & Random = GetRandomProvider(); + + if (!m_bIsMouthOpen) + { + if (Random.RandBool(0.02)) + { + m_bIsMouthOpen = true; + } + } + else + { + if (Random.RandBool(0.10)) + { + m_bIsMouthOpen = false; + } + } + + if ((m_Attachee != nullptr) && (!m_bIsTame)) + { + if (m_TameAttemptTimes < m_TimesToTame) + { + if (Random.RandBool(0.02)) + { + m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_EAST)); + m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_WEST)); + m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_EAST)); + m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::NORTH_WEST)); + + m_World->BroadcastSoundEffect("entity.horse.angry", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 1.0f); + m_Attachee->Detach(); + m_bIsRearing = true; + } + } + else + { + m_World->GetBroadcaster().BroadcastParticleEffect("heart", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5); + m_bIsTame = true; + } + } + + if (m_bIsRearing) + { + if (m_RearTickCount == 20) + { + m_bIsRearing = false; + m_RearTickCount = 0; + } + else + { + m_RearTickCount++; + } + } + + m_World->BroadcastEntityMetadata(*this); } @@ -104,63 +104,63 @@ void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cHorse::OnRightClicked(cPlayer & a_Player) { - super::OnRightClicked(a_Player); - - if (m_bIsTame) - { - if (!m_bIsSaddled) - { - if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE) - { - // Saddle the horse: - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - m_bIsSaddled = true; - m_World->BroadcastEntityMetadata(*this); - } - else - { - a_Player.AttachTo(this); - } - } - else - { - a_Player.AttachTo(this); - } - } - else if (a_Player.GetEquippedItem().IsEmpty()) - { - // Check if leashed / unleashed to player before try to ride - if (!m_IsLeashActionJustDone) - { - if (m_Attachee != nullptr) - { - if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) - { - a_Player.Detach(); - return; - } - - if (m_Attachee->IsPlayer()) - { - return; - } - - m_Attachee->Detach(); - } - - m_TameAttemptTimes++; - a_Player.AttachTo(this); - } - } - else - { - m_bIsRearing = true; - m_RearTickCount = 0; - m_World->BroadcastSoundEffect("entity.horse.angry", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); - } + super::OnRightClicked(a_Player); + + if (m_bIsTame) + { + if (!m_bIsSaddled) + { + if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE) + { + // Saddle the horse: + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + m_bIsSaddled = true; + m_World->BroadcastEntityMetadata(*this); + } + else + { + a_Player.AttachTo(this); + } + } + else + { + a_Player.AttachTo(this); + } + } + else if (a_Player.GetEquippedItem().IsEmpty()) + { + // Check if leashed / unleashed to player before try to ride + if (!m_IsLeashActionJustDone) + { + if (m_Attachee != nullptr) + { + if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) + { + a_Player.Detach(); + return; + } + + if (m_Attachee->IsPlayer()) + { + return; + } + + m_Attachee->Detach(); + } + + m_TameAttemptTimes++; + a_Player.AttachTo(this); + } + } + else + { + m_bIsRearing = true; + m_RearTickCount = 0; + m_World->BroadcastSoundEffect("entity.horse.angry", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); + } } @@ -169,29 +169,16 @@ void cHorse::OnRightClicked(cPlayer & a_Player) void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - unsigned int LootingLevel = 0; - if (a_Killer != nullptr) - { - LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); - } - AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER); - if (m_bIsSaddled) - { - a_Drops.push_back(cItem(E_ITEM_SADDLE, 1)); - } -} - - - - - -void cHorse::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) -{ - // If horse is tame and someone is sitting on it, don't walk around - if ((!m_bIsTame) || (m_Attachee == nullptr)) - { - super::InStateIdle(a_Dt, a_Chunk); - } + unsigned int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER); + if (m_bIsSaddled) + { + a_Drops.push_back(cItem(E_ITEM_SADDLE, 1)); + } } @@ -200,8 +187,8 @@ void cHorse::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cHorse::HandleSpeedFromAttachee(float a_Forward, float a_Sideways) { - if ((m_bIsTame) && (m_bIsSaddled)) - { - super::HandleSpeedFromAttachee(a_Forward * m_MaxSpeed, a_Sideways * m_MaxSpeed); - } + if ((m_bIsTame) && (m_bIsSaddled)) + { + super::HandleSpeedFromAttachee(a_Forward * m_MaxSpeed, a_Sideways * m_MaxSpeed); + } } diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h index 82026a0ee..e84243360 100644 --- a/src/Mobs/Horse.h +++ b/src/Mobs/Horse.h @@ -8,43 +8,42 @@ class cHorse : - public cPassiveMonster + public cPassiveMonster { - typedef cPassiveMonster super; + typedef cPassiveMonster super; public: - cHorse(int Type, int Color, int Style, int TameTimes); - - CLASS_PROTODEF(cHorse) - - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual void HandleSpeedFromAttachee(float a_Forward, float a_Sideways) override; - virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual void OnRightClicked(cPlayer & a_Player) override; - - bool IsSaddled (void) const {return m_bIsSaddled; } - bool IsChested (void) const {return m_bHasChest; } - bool IsEating (void) const {return m_bIsEating; } - bool IsRearing (void) const {return m_bIsRearing; } - bool IsMthOpen (void) const {return m_bIsMouthOpen; } - bool IsTame (void) const override {return m_bIsTame; } - int GetHorseType (void) const {return m_Type; } - int GetHorseColor (void) const {return m_Color; } - int GetHorseStyle (void) const {return m_Style; } - int GetHorseArmour (void) const {return m_Armour;} - - virtual void GetBreedingItems(cItems & a_Items) override - { - a_Items.Add(E_ITEM_GOLDEN_CARROT); - a_Items.Add(E_ITEM_GOLDEN_APPLE); - } + cHorse(int Type, int Color, int Style, int TameTimes); + + CLASS_PROTODEF(cHorse) + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + virtual void HandleSpeedFromAttachee(float a_Forward, float a_Sideways) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + + bool IsSaddled (void) const {return m_bIsSaddled; } + bool IsChested (void) const {return m_bHasChest; } + bool IsEating (void) const {return m_bIsEating; } + bool IsRearing (void) const {return m_bIsRearing; } + bool IsMthOpen (void) const {return m_bIsMouthOpen; } + bool IsTame (void) const override {return m_bIsTame; } + int GetHorseType (void) const {return m_Type; } + int GetHorseColor (void) const {return m_Color; } + int GetHorseStyle (void) const {return m_Style; } + int GetHorseArmour (void) const {return m_Armour;} + + virtual void GetBreedingItems(cItems & a_Items) override + { + a_Items.Add(E_ITEM_GOLDEN_CARROT); + a_Items.Add(E_ITEM_GOLDEN_APPLE); + } private: - bool m_bHasChest, m_bIsEating, m_bIsRearing, m_bIsMouthOpen, m_bIsTame, m_bIsSaddled; - int m_Type, m_Color, m_Style, m_Armour, m_TimesToTame, m_TameAttemptTimes, m_RearTickCount; - float m_MaxSpeed; + bool m_bHasChest, m_bIsEating, m_bIsRearing, m_bIsMouthOpen, m_bIsTame, m_bIsSaddled; + int m_Type, m_Color, m_Style, m_Armour, m_TimesToTame, m_TameAttemptTimes, m_RearTickCount; + float m_MaxSpeed; } ; diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index d1c2413c3..80da3b03a 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -19,6 +19,9 @@ #include "PathFinder.h" #include "../Entities/LeashKnot.h" +// Temporary pathfinder hack +#include "Behaviors/BehaviorDayLightBurner.h" + @@ -82,8 +85,8 @@ static const struct cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : super(etMonster, a_Width, a_Height) - , m_EMState(IDLE) , m_EMPersonality(AGGRESSIVE) + , m_NearestPlayerIsStale(true) , m_PathFinder(a_Width, a_Height) , m_PathfinderActivated(false) , m_JumpCoolDown(0) @@ -94,10 +97,6 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A , m_CustomNameAlwaysVisible(false) , m_SoundHurt(a_SoundHurt) , m_SoundDeath(a_SoundDeath) - , m_AttackRate(3) - , m_AttackDamage(1) - , m_AttackRange(1) - , m_AttackCoolDownTicksLeft(0) , m_SightDistance(25) , m_DropChanceWeapon(0.085f) , m_DropChanceHelmet(0.085f) @@ -105,8 +104,6 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A , m_DropChanceLeggings(0.085f) , m_DropChanceBoots(0.085f) , m_CanPickUpLoot(true) - , m_TicksSinceLastDamaged(100) - , m_BurnsInDaylight(false) , m_RelativeWalkSpeed(1) , m_Age(1) , m_AgingTimer(20 * 60 * 20) // about 20 minutes @@ -116,6 +113,8 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A , m_IsLeashActionJustDone(false) , m_CanBeLeashed(GetMobFamily() == eFamily::mfPassive) , m_Target(nullptr) + , m_CurrentTickControllingBehavior(nullptr) + , m_NewTickControllingBehavior(nullptr) { if (!a_ConfigName.empty()) { @@ -282,6 +281,7 @@ 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()) { @@ -290,12 +290,6 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } GET_AND_VERIFY_CURRENT_CHUNK(Chunk, POSX_TOINT, POSZ_TOINT); - ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == GetWorld()))); - if (m_AttackCoolDownTicksLeft > 0) - { - m_AttackCoolDownTicksLeft -= 1; - } - if (m_Health <= 0) { // The mob is dead, but we're still animating the "puff" they leave when they die @@ -307,26 +301,87 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) return; } - if (m_TicksSinceLastDamaged < 100) + // All behaviors can execute PostTick and PreTick. + // These are for bookkeeping or passive actions like laying eggs. + // They MUST NOT control mob movement or interefere with the main Tick. + for (cBehavior * Behavior : PreTickBehaviors) { - ++m_TicksSinceLastDamaged; + Behavior->PreTick(a_Dt, a_Chunk); } - if ((GetTarget() != nullptr)) - { - ASSERT(GetTarget()->IsTicking()); - if (GetTarget()->IsPlayer()) + // Note 1: Each monster tick, at most one Behavior executes its Tick method. + // Note 2: Each monster tick, exactly one of these is executed: + // ControlStarting, Tick, ControlEnding + + // If we're in a regular tick cycle + if (m_TickControllingBehaviorState == Normal) + { + // ask the behaviors sequentially if they are interested in controlling this mob + // Stop at the first one that says yes. + for (cBehavior * Behavior : TickBehaviors) { - if (!static_cast<cPlayer *>(GetTarget())->CanMobsTarget()) + if (Behavior->IsControlDesired(a_Dt, a_Chunk)) { - SetTarget(nullptr); - m_EMState = IDLE; + m_NewTickControllingBehavior = Behavior; + break; } } + ASSERT(m_NewTickControllingBehavior != nullptr); // it's not OK if no one asks for control + if (m_CurrentTickControllingBehavior == m_NewTickControllingBehavior) + { + // The Behavior asking for control is the same as the behavior from last tick. + // Nothing special, just tick it. + m_CurrentTickControllingBehavior->Tick(a_Dt, a_Chunk); + } + else + { + // The behavior asking for control is not the same as the behavior from last tick. + // Begin the control swapping process. + m_TickControllingBehaviorState = OldControlEnding; + } + + } + + // Make the current controlling behavior clean up + if (m_TickControllingBehaviorState == OldControlEnding) + { + if (m_CurrentTickControllingBehavior->ControlEnding(a_Dt, a_Chunk)) + { + // The current behavior told us it is ready for letting go of control + m_TickControllingBehaviorState = NewControlStarting; + } + else + { + // The current behavior is not ready for releasing control. We'll execute ControlEnding + // next tick too. + m_TickControllingBehaviorState = OldControlEnding; + } + } + // Make the new controlling behavior set up + else if (m_TickControllingBehaviorState == NewControlStarting) + { + if (m_NewTickControllingBehavior->ControlStarting(a_Dt, a_Chunk)) + { + // The new behavior told us it is ready for taking control + // The new behavior is now the current behavior. Next tick it will execute its Tick. + m_TickControllingBehaviorState = Normal; + m_CurrentTickControllingBehavior = m_NewTickControllingBehavior; + } + else + { + // The new behavior is not ready for taking control. + // We'll execute ControlStarting next tick too. + m_TickControllingBehaviorState = NewControlStarting; + } } - // Process the undead burning in daylight. - HandleDaylightBurning(*Chunk, WouldBurnAt(GetPosition(), *Chunk)); + // All behaviors can execute PostTick and PreTick. + // These are for bookkeeping or passive actions like laying eggs. + // They MUST NOT control mob movement or interefere with the main Tick. + for (cBehavior * Behavior : PostTickBehaviors) + { + Behavior->PostTick(a_Dt, a_Chunk); + } bool a_IsFollowingPath = false; if (m_PathfinderActivated) @@ -337,8 +392,9 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } else { + // mobToDo fix dont care // Note that m_NextWayPointPosition is actually returned by GetNextWayPoint) - switch (m_PathFinder.GetNextWayPoint(*Chunk, GetPosition(), &m_FinalDestination, &m_NextWayPointPosition, m_EMState == IDLE ? true : false)) + switch (m_PathFinder.GetNextWayPoint(*Chunk, GetPosition(), &m_FinalDestination, &m_NextWayPointPosition, false)) { case ePathFinderStatus::PATH_FOUND: { @@ -347,9 +403,13 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) 2. I was not hurt by a player recently. Then STOP. */ if ( - m_BurnsInDaylight && ((m_TicksSinceLastDamaged >= 100) || (m_EMState == IDLE)) && - WouldBurnAt(m_NextWayPointPosition, *Chunk) && - !WouldBurnAt(GetPosition(), *Chunk) + //mobTodo emstate + /* (GetBehaviorDayLightBurner() != nullptr) && (m_TicksSinceLastDamaged >= 100) && + GetBehaviorDayLightBurner()->WouldBurnAt(m_NextWayPointPosition, *Chunk) && + !(GetBehaviorDayLightBurner()->WouldBurnAt(GetPosition(), *Chunk)) */ + 1 == 0 + + // This logic should probably be in chaser ) { // If we burn in daylight, and we would burn at the next step, and we won't burn where we are right now, and we weren't provoked recently: @@ -377,28 +437,6 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) SetPitchAndYawFromDestination(a_IsFollowingPath); - switch (m_EMState) - { - case IDLE: - { - // If enemy passive we ignore checks for player visibility. - InStateIdle(a_Dt, a_Chunk); - break; - } - case CHASING: - { - // If we do not see a player anymore skip chasing action. - InStateChasing(a_Dt, a_Chunk); - break; - } - case ESCAPING: - { - InStateEscaping(a_Dt, a_Chunk); - break; - } - case ATTACKING: break; - } // switch (m_EMState) - // Leash calculations if ((m_TicksAlive % LEASH_ACTIONS_TICK_STEP) == 0) { @@ -555,22 +593,14 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) return false; } + if (!m_SoundHurt.empty() && (m_Health > 0)) { m_World->BroadcastSoundEffect(m_SoundHurt, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); } - if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn()) - { - if ( - (!a_TDI.Attacker->IsPlayer()) || - (static_cast<cPlayer *>(a_TDI.Attacker)->CanMobsTarget()) - ) - { - SetTarget(static_cast<cPawn*>(a_TDI.Attacker)); - } - m_TicksSinceLastDamaged = 0; - } + //mobTodo call all interested behaviors + return true; } @@ -696,159 +726,6 @@ void cMonster::OnRightClicked(cPlayer & a_Player) -// Checks to see if EventSeePlayer should be fired -// monster sez: Do I see the player -void cMonster::CheckEventSeePlayer(cChunk & a_Chunk) -{ - // TODO: Rewrite this to use cWorld's DoWithPlayers() - cPlayer * Closest = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance), false); - - if (Closest != nullptr) - { - EventSeePlayer(Closest, a_Chunk); - } -} - - - - - -void cMonster::CheckEventLostPlayer(void) -{ - if (GetTarget() != nullptr) - { - if ((GetTarget()->GetPosition() - GetPosition()).Length() > m_SightDistance) - { - EventLosePlayer(); - } - } - else - { - EventLosePlayer(); - } -} - - - - - -// What to do if player is seen -// default to change state to chasing -void cMonster::EventSeePlayer(cPlayer * a_SeenPlayer, cChunk & a_Chunk) -{ - UNUSED(a_Chunk); - SetTarget(a_SeenPlayer); -} - - - - - -void cMonster::EventLosePlayer(void) -{ - SetTarget(nullptr); - m_EMState = IDLE; -} - - - - - -void cMonster::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) -{ - if (m_PathfinderActivated) - { - return; // Still getting there - } - - m_IdleInterval += a_Dt; - - if (m_IdleInterval > std::chrono::seconds(1)) - { - auto & Random = GetRandomProvider(); - - // At this interval the results are predictable - int rem = Random.RandInt(1, 7); - m_IdleInterval -= std::chrono::seconds(1); // So nothing gets dropped when the server hangs for a few seconds - - Vector3d Dist; - Dist.x = static_cast<double>(Random.RandInt(-5, 5)); - Dist.z = static_cast<double>(Random.RandInt(-5, 5)); - - if ((Dist.SqrLength() > 2) && (rem >= 3)) - { - - Vector3d Destination(GetPosX() + Dist.x, GetPosition().y, GetPosZ() + Dist.z); - - cChunk * Chunk = a_Chunk.GetNeighborChunk(static_cast<int>(Destination.x), static_cast<int>(Destination.z)); - if ((Chunk == nullptr) || !Chunk->IsValid()) - { - return; - } - - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - int RelX = static_cast<int>(Destination.x) - Chunk->GetPosX() * cChunkDef::Width; - int RelZ = static_cast<int>(Destination.z) - Chunk->GetPosZ() * cChunkDef::Width; - int YBelowUs = static_cast<int>(Destination.y) - 1; - if (YBelowUs >= 0) - { - Chunk->GetBlockTypeMeta(RelX, YBelowUs, RelZ, BlockType, BlockMeta); - if (BlockType != E_BLOCK_STATIONARY_WATER) // Idle mobs shouldn't enter water on purpose - { - MoveToPosition(Destination); - } - } - } - } -} - - - - - -// What to do if in Chasing State -// This state should always be defined in each child class -void cMonster::InStateChasing(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) -{ - UNUSED(a_Dt); -} - - - - - -// What to do if in Escaping State -void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) -{ - UNUSED(a_Dt); - - if (GetTarget() != nullptr) - { - Vector3d newloc = GetPosition(); - newloc.x = (GetTarget()->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance); - newloc.z = (GetTarget()->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance); - MoveToPosition(newloc); - } - else - { - m_EMState = IDLE; // This shouldnt be required but just to be safe - } -} - - - - - -void cMonster::ResetAttackCooldown() -{ - m_AttackCoolDownTicksLeft = static_cast<int>(3 * 20 * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every 3 seconds -} - - - - - void cMonster::SetCustomName(const AString & a_CustomName) { m_CustomName = a_CustomName; @@ -1113,6 +990,126 @@ cPawn * cMonster::GetTarget() +bool cMonster::IsPathFinderActivated() const +{ + return m_PathfinderActivated; +} + + + + + +cBehaviorAggressive * cMonster::GetBehaviorAggressive() +{ + return nullptr; +} + + + + + +cBehaviorBreeder * cMonster::GetBehaviorBreeder() +{ + return nullptr; +} + + + + + +const cBehaviorBreeder * cMonster::GetBehaviorBreeder() const +{ + return nullptr; +} + + + + + +cBehaviorChaser * cMonster::GetBehaviorChaser() +{ + return nullptr; +} + + + + + +cBehaviorStriker * cMonster::GetBehaviorStriker() +{ + return nullptr; +} + + + + + +cBehaviorWanderer * cMonster::GetBehaviorWanderer() +{ + return nullptr; +} + + + + + +cBehaviorDayLightBurner * cMonster::GetBehaviorDayLightBurner() +{ + return nullptr; +} + + + + + +void cMonster::InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2) +{ + UNUSED(a_Parent1); + UNUSED(a_Parent2); + return; +} + + + + + +void cMonster::GetFollowedItems(cItems & a_Items) +{ + return; +} + + + + + +void cMonster::GetBreedingItems(cItems & a_Items) +{ + return GetFollowedItems(a_Items); +} + + + + + +cPlayer * cMonster::GetNearestPlayer() +{ + if (m_NearestPlayerIsStale) + { + // TODO: Rewrite this to use cWorld's DoWithPlayers() + m_NearestPlayer = GetWorld()->FindClosestPlayer(GetPosition(), static_cast<float>(GetSightDistance())); + m_NearestPlayerIsStale = false; + } + if ((m_NearestPlayer != nullptr) && (!m_NearestPlayer->IsTicking())) + { + m_NearestPlayer = nullptr; + } + return m_NearestPlayer; +} + + + + + std::unique_ptr<cMonster> cMonster::NewMonsterFromType(eMonsterType a_MobType) { auto & Random = GetRandomProvider(); @@ -1302,102 +1299,6 @@ void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingL -void cMonster::HandleDaylightBurning(cChunk & a_Chunk, bool WouldBurn) -{ - if (!m_BurnsInDaylight) - { - return; - } - - int RelY = POSY_TOINT; - if ((RelY < 0) || (RelY >= cChunkDef::Height)) - { - // Outside the world - return; - } - if (!a_Chunk.IsLightValid()) - { - m_World->QueueLightChunk(GetChunkX(), GetChunkZ()); - return; - } - - if (!IsOnFire() && WouldBurn) - { - // Burn for 100 ticks, then decide again - StartBurning(100); - } -} - - - - -bool cMonster::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk) -{ - // If the Y coord is out of range, return the most logical result without considering anything else: - int RelY = FloorC(a_Location.y); - if (RelY < 0) - { - // Never burn under the world - return false; - } - if (RelY >= cChunkDef::Height) - { - // Always burn above the world - return true; - } - if (RelY <= 0) - { - // The mob is about to die, no point in burning - return false; - } - - PREPARE_REL_AND_CHUNK(a_Location, a_Chunk); - if (!RelSuccess) - { - return false; - } - - if ( - (Chunk->GetBlock(Rel.x, Rel.y, Rel.z) != E_BLOCK_SOULSAND) && // Not on soulsand - (GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime - GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) // Not raining - ) - { - int MobHeight = FloorC(a_Location.y + GetHeight()) - 1; // The block Y coord of the mob's head - if (MobHeight >= cChunkDef::Height) - { - return true; - } - // Start with the highest block and scan down to 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; -} - - - - - cMonster::eFamily cMonster::GetMobFamily(void) const { return FamilyFromType(m_MobType); diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index d98706f8b..6e24220b5 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -4,12 +4,21 @@ #include "../Entities/Pawn.h" #include "MonsterTypes.h" #include "PathFinder.h" - +#include <vector> class cItem; class cClientHandle; +//Behavior fwds +class cPassiveMonster; +class cBehaviorAggressive; +class cBehaviorBreeder; +class cBehaviorChaser; +class cBehaviorStriker; +class cBehaviorWanderer; +class cBehaviorDayLightBurner; +class cBehavior; // tolua_begin class cMonster : @@ -31,7 +40,6 @@ public: // tolua_end - enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState; enum MPersonality{PASSIVE, AGGRESSIVE, COWARDLY} m_EMPersonality; /** Creates the mob object. @@ -69,9 +77,6 @@ public: eFamily GetMobFamily(void) const; // tolua_end - virtual void CheckEventSeePlayer(cChunk & a_Chunk); - virtual void EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk); - // tolua_begin /** Returns whether the mob can be leashed. */ @@ -109,18 +114,8 @@ public: /** Returns whether this mob is undead (skeleton, zombie, etc.) */ virtual bool IsUndead(void); - virtual void EventLosePlayer(void); - virtual void CheckEventLostPlayer(void); - - virtual void InStateIdle (std::chrono::milliseconds a_Dt, cChunk & a_Chunk); - virtual void InStateChasing (std::chrono::milliseconds a_Dt, cChunk & a_Chunk); - virtual void InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); - - int GetAttackRate() { return static_cast<int>(m_AttackRate); } - void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; } - void SetAttackRange(int a_AttackRange) { m_AttackRange = a_AttackRange; } - void SetAttackDamage(int a_AttackDamage) { m_AttackDamage = a_AttackDamage; } void SetSightDistance(int a_SightDistance) { m_SightDistance = a_SightDistance; } + int GetSightDistance() { return m_SightDistance; } float GetDropChanceWeapon() { return m_DropChanceWeapon; } float GetDropChanceHelmet() { return m_DropChanceHelmet; } @@ -134,10 +129,6 @@ public: void SetDropChanceLeggings(float a_DropChanceLeggings) { m_DropChanceLeggings = a_DropChanceLeggings; } void SetDropChanceBoots(float a_DropChanceBoots) { m_DropChanceBoots = a_DropChanceBoots; } void SetCanPickUpLoot(bool a_CanPickUpLoot) { m_CanPickUpLoot = a_CanPickUpLoot; } - void ResetAttackCooldown(); - - /** Sets whether the mob burns in daylight. Only evaluated at next burn-decision tick */ - void SetBurnsInDaylight(bool a_BurnsInDaylight) { m_BurnsInDaylight = a_BurnsInDaylight; } double GetRelativeWalkSpeed(void) const { return m_RelativeWalkSpeed; } // tolua_export void SetRelativeWalkSpeed(double a_WalkSpeed) { m_RelativeWalkSpeed = a_WalkSpeed; } // tolua_export @@ -211,7 +202,31 @@ public: /** Returns if this mob last target was a player to avoid destruction on player quit */ bool WasLastTargetAPlayer() const { return m_WasLastTargetAPlayer; } -protected: + bool IsPathFinderActivated() const; + + // Behavior getters (most are probably not used. mobTodo - cleanup most of them) + virtual 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); + + cPlayer * GetNearestPlayer(); + + protected: + + /** Whether or not m_NearestPlayer is stale. Always true at the beginning of a tick. + When true, GetNearestPlayer() actually searches for a player, updates m_NearestPlayer, and sets it to false. + otherwise it returns m_NearestPlayer. This means we only perform 1 search per tick. */ + bool m_NearestPlayerIsStale; + cPlayer * m_NearestPlayer; /** The pathfinder instance handles pathfinding for this monster. */ cPathFinder m_PathFinder; @@ -234,13 +249,6 @@ protected: /** Returns if the ultimate, final destination has been reached. */ bool ReachedFinalDestination(void) { return ((m_FinalDestination - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS); } - /** Returns whether or not the target is close enough for attack. */ - bool TargetIsInRange(void) - { - ASSERT(GetTarget() != nullptr); - return ((GetTarget()->GetPosition() - GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange)); - } - /** Returns whether the monster needs to jump to reach a given height. */ inline bool DoesPosYRequireJump(double a_PosY) { @@ -268,11 +276,7 @@ protected: AString m_SoundHurt; AString m_SoundDeath; - float m_AttackRate; - int m_AttackDamage; - int m_AttackRange; - int m_AttackCoolDownTicksLeft; - int m_SightDistance; + int m_SightDistance; // mobTodo what to do with this? float m_DropChanceWeapon; float m_DropChanceHelmet; @@ -280,11 +284,7 @@ protected: float m_DropChanceLeggings; float m_DropChanceBoots; bool m_CanPickUpLoot; - int m_TicksSinceLastDamaged; // How many ticks ago we were last damaged by a player? - void HandleDaylightBurning(cChunk & a_Chunk, bool WouldBurn); - bool WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk); - bool m_BurnsInDaylight; double m_RelativeWalkSpeed; int m_Age; @@ -328,4 +328,14 @@ private: /** Leash calculations inside Tick function */ void CalcLeashActions(); + std::vector<cBehavior*> PreTickBehaviors; + std::vector<cBehavior*> TickBehaviors; + std::vector<cBehavior*> PostTickBehaviors; + std::vector<cBehavior*> OnDestroyBehaviors; + std::vector<cBehavior*> OnRightClickBehaviors; + + cBehavior * m_CurrentTickControllingBehavior; + cBehavior * m_NewTickControllingBehavior; + enum TickState{NewControlStarting, OldControlEnding, Normal} m_TickControllingBehaviorState; + } ; // tolua_export diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp index 8715ba9c2..addde27a7 100644 --- a/src/Mobs/PassiveAggressiveMonster.cpp +++ b/src/Mobs/PassiveAggressiveMonster.cpp @@ -26,23 +26,13 @@ bool cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) return false; } + // mobtodo remove this class altogether if ((GetTarget() != nullptr) && (GetTarget()->IsPlayer())) { if (static_cast<cPlayer *>(GetTarget())->CanMobsTarget()) { - m_EMState = CHASING; + // m_EMState = CHASING; } } return true; } - - - - - -void cPassiveAggressiveMonster::EventSeePlayer(cPlayer *, cChunk & a_Chunk) -{ - // don't do anything, neutral mobs don't react to just seeing the player -} - - diff --git a/src/Mobs/PassiveAggressiveMonster.h b/src/Mobs/PassiveAggressiveMonster.h index 764e27779..726db09c5 100644 --- a/src/Mobs/PassiveAggressiveMonster.h +++ b/src/Mobs/PassiveAggressiveMonster.h @@ -8,15 +8,14 @@ class cPassiveAggressiveMonster : - public cAggressiveMonster + public cAggressiveMonster { - typedef cAggressiveMonster super; + typedef cAggressiveMonster super; public: - cPassiveAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + cPassiveAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual void EventSeePlayer(cPlayer *, cChunk & a_Chunk) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; } ; diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp index a2089e13f..81bf681ea 100644 --- a/src/Mobs/PassiveMonster.cpp +++ b/src/Mobs/PassiveMonster.cpp @@ -5,17 +5,13 @@ #include "../World.h" #include "../Entities/Player.h" #include "BoundingBox.h" -#include "../Items/ItemSpawnEgg.h" cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) : super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height), - m_LovePartner(nullptr), - m_LoveTimer(0), - m_LoveCooldown(0), - m_MatingTimer(0) + m_BehaviorBreeder(this), m_BehaviorItemFollower(this), m_BehaviorCoward(this), m_BehaviorWanderer(this) { m_EMPersonality = PASSIVE; } @@ -24,16 +20,22 @@ cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eMonsterType a_Mo +cPassiveMonster::~cPassiveMonster() +{ + +} + + + + + bool cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) { if (!super::DoTakeDamage(a_TDI)) { return false; } - if ((a_TDI.Attacker != this) && (a_TDI.Attacker != nullptr)) - { - m_EMState = ESCAPING; - } + m_BehaviorCoward.DoTakeDamage(a_TDI); return true; } @@ -41,179 +43,59 @@ bool cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) -void cPassiveMonster::EngageLoveMode(cPassiveMonster * a_Partner) +void cPassiveMonster::Destroyed() { - m_LovePartner = a_Partner; - m_MatingTimer = 50; // about 3 seconds of mating + m_BehaviorBreeder.Destroyed(); + super::Destroyed(); } -void cPassiveMonster::ResetLoveMode() +cBehaviorBreeder * cPassiveMonster::GetBehaviorBreeder() { - m_LovePartner = nullptr; - m_LoveTimer = 0; - m_MatingTimer = 0; - m_LoveCooldown = 20 * 60 * 5; // 5 minutes - - // when an animal is in love mode, the client only stops sending the hearts if we let them know it's in cooldown, which is done with the "age" metadata - m_World->BroadcastEntityMetadata(*this); + return &m_BehaviorBreeder; } -void cPassiveMonster::Destroyed() +const cBehaviorBreeder * cPassiveMonster::GetBehaviorBreeder() const { - if (m_LovePartner != nullptr) - { - m_LovePartner->ResetLoveMode(); - } - super::Destroyed(); + return static_cast<const cBehaviorBreeder *>(&m_BehaviorBreeder); } - - - void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); - if (!IsTicking()) - { - // The base class tick destroyed us - return; - } - - if (m_EMState == ESCAPING) - { - CheckEventLostPlayer(); - } - // if we have a partner, mate - if (m_LovePartner != nullptr) + for (;;) { - - if (m_MatingTimer > 0) + /*if (m_BehaviorCoward.Tick()) { - // If we should still mate, keep bumping into them until baby is made - Vector3d Pos = m_LovePartner->GetPosition(); - MoveToPosition(Pos); + break; } - else + if (m_BehaviorBreeder.Tick()) { - // Mating finished. Spawn baby - Vector3f Pos = (GetPosition() + m_LovePartner->GetPosition()) * 0.5; - UInt32 BabyID = m_World->SpawnMob(Pos.x, Pos.y, Pos.z, GetMobType(), true); - - class cBabyInheritCallback : - public cEntityCallback - { - public: - cPassiveMonster * Baby; - cBabyInheritCallback() : Baby(nullptr) { } - virtual bool Item(cEntity * a_Entity) override - { - Baby = static_cast<cPassiveMonster *>(a_Entity); - return true; - } - } Callback; - - m_World->DoWithEntityByID(BabyID, Callback); - if (Callback.Baby != nullptr) - { - Callback.Baby->InheritFromParents(this, m_LovePartner); - } - - m_World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, GetRandomProvider().RandInt(1, 6)); - - m_LovePartner->ResetLoveMode(); - ResetLoveMode(); + break; } - } - else - { - // We have no partner, so we just chase the player if they have our breeding item - cItems FollowedItems; - GetFollowedItems(FollowedItems); - if (FollowedItems.Size() > 0) + if (m_BehaviorItemFollower.Tick()) { - cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance)); - if (a_Closest_Player != nullptr) - { - cItem EquippedItem = a_Closest_Player->GetEquippedItem(); - if (FollowedItems.ContainsType(EquippedItem)) - { - Vector3d PlayerPos = a_Closest_Player->GetPosition(); - MoveToPosition(PlayerPos); - } - } + break; } - } - - // If we are in love mode but we have no partner, search for a partner neabry - if (m_LoveTimer > 0) - { - if (m_LovePartner == nullptr) + if (m_BehaviorWanderer.ActiveTick(a_Dt, a_Chunk)) { - class LookForLover : public cEntityCallback - { - public: - cEntity * m_Me; - - LookForLover(cEntity * a_Me) : - m_Me(a_Me) - { - } - - virtual bool Item(cEntity * a_Entity) override - { - // If the entity is not a monster, don't breed with it - // Also, do not self-breed - if ((a_Entity->GetEntityType() != etMonster) || (a_Entity == m_Me)) - { - return false; - } - - cPassiveMonster * Me = static_cast<cPassiveMonster*>(m_Me); - cPassiveMonster * PotentialPartner = static_cast<cPassiveMonster*>(a_Entity); - - // If the potential partner is not of the same species, don't breed with it - if (PotentialPartner->GetMobType() != Me->GetMobType()) - { - return false; - } - - // If the potential partner is not in love - // Or they already have a mate, do not breed with them - if ((!PotentialPartner->IsInLove()) || (PotentialPartner->GetPartner() != nullptr)) - { - return false; - } - - // All conditions met, let's breed! - PotentialPartner->EngageLoveMode(Me); - Me->EngageLoveMode(PotentialPartner); - return true; - } - } Callback(this); - - m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), 8, 8, -4), Callback); - } + break; + }*/ - m_LoveTimer--; - } - if (m_MatingTimer > 0) - { - m_MatingTimer--; - } - if (m_LoveCooldown > 0) - { - m_LoveCooldown--; + ASSERT(!"Not a single Behavior took control, this is not normal. "); + break; } + + m_BehaviorBreeder.PostTick(a_Dt, a_Chunk); } @@ -223,41 +105,5 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cPassiveMonster::OnRightClicked(cPlayer & a_Player) { super::OnRightClicked(a_Player); - - const cItem & EquippedItem = a_Player.GetEquippedItem(); - - // If a player holding breeding items right-clicked me, go into love mode - if ((m_LoveCooldown == 0) && !IsInLove() && !IsBaby()) - { - cItems Items; - GetBreedingItems(Items); - if (Items.ContainsType(EquippedItem.m_ItemType)) - { - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - m_LoveTimer = 20 * 30; // half a minute - m_World->BroadcastEntityStatus(*this, esMobInLove); - } - } - // If a player holding my spawn egg right-clicked me, spawn a new baby - if (EquippedItem.m_ItemType == E_ITEM_SPAWN_EGG) - { - eMonsterType MonsterType = cItemSpawnEggHandler::ItemDamageToMonsterType(EquippedItem.m_ItemDamage); - if ( - (MonsterType == m_MobType) && - (m_World->SpawnMob(GetPosX(), GetPosY(), GetPosZ(), m_MobType, true) != cEntity::INVALID_ID) // Spawning succeeded - ) - { - if (!a_Player.IsGameModeCreative()) - { - // The mob was spawned, "use" the item: - a_Player.GetInventory().RemoveOneEquippedItem(); - } - } - } + m_BehaviorBreeder.OnRightClicked(a_Player); } - - - diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h index 9a2627417..4a1d5513e 100644 --- a/src/Mobs/PassiveMonster.h +++ b/src/Mobs/PassiveMonster.h @@ -2,65 +2,35 @@ #pragma once #include "Monster.h" +#include "Behaviors/BehaviorBreeder.h" +#include "Behaviors/BehaviorItemFollower.h" +#include "Behaviors/BehaviorCoward.h" +#include "Behaviors/BehaviorWanderer.h" - - - -class cPassiveMonster : - public cMonster +typedef std::string AString; +class cPassiveMonster : public cMonster { - typedef cMonster super; + typedef cMonster super; public: - cPassiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - - virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual void OnRightClicked(cPlayer & a_Player) override; - - /** When hit by someone, run away */ - virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - - /** Returns the items that the animal of this class follows when a player holds it in hand. */ - virtual void GetFollowedItems(cItems & a_Items) { } - - /** Returns the items that make the animal breed - this is usually the same as the ones that make the animal follow, but not necessarily. */ - virtual void GetBreedingItems(cItems & a_Items) { GetFollowedItems(a_Items); } - - /** Called after the baby is born, allows the baby to inherit the parents' properties (color, etc.) */ - virtual void InheritFromParents(cPassiveMonster * a_Parent1, cPassiveMonster * a_Parent2) { } - - /** Returns the partner which the monster is currently mating with. */ - cPassiveMonster * GetPartner(void) const { return m_LovePartner; } - - /** Start the mating process. Causes the monster to keep bumping into the partner until m_MatingTimer reaches zero. */ - void EngageLoveMode(cPassiveMonster * a_Partner); - - /** Finish the mating process. Called after a baby is born. Resets all breeding related timers and sets m_LoveCooldown to 20 minutes. */ - void ResetLoveMode(); - - /** Returns whether the monster has just been fed and is ready to mate. If this is "true" and GetPartner isn't "nullptr", then the monster is mating. */ - bool IsInLove() const { return (m_LoveTimer > 0); } - - /** Returns whether the monster is tired of breeding and is in the cooldown state. */ - bool IsInLoveCooldown() const { return (m_LoveCooldown > 0); } - - virtual void Destroyed(void) override; - -protected: - /** The monster's breeding partner. */ - cPassiveMonster * m_LovePartner; - - /** If above 0, the monster is in love mode, and will breed if a nearby monster is also in love mode. Decrements by 1 per tick till reaching zero. */ - int m_LoveTimer; - - /** If above 0, the monster is in cooldown mode and will refuse to breed. Decrements by 1 per tick till reaching zero. */ - int m_LoveCooldown; - - /** The monster is engaged in mating, once this reaches zero, a baby will be born. Decrements by 1 per tick till reaching zero, then a baby is made and ResetLoveMode() is called. */ - int m_MatingTimer; + cPassiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, + const AString & a_SoundHurt, const AString & a_SoundDeath, + double a_Width, double a_Height); + virtual ~cPassiveMonster(); + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + + /** When hit by someone, run away */ + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; + + virtual void Destroyed(void) override; + + virtual cBehaviorBreeder * GetBehaviorBreeder() override; + virtual const cBehaviorBreeder * GetBehaviorBreeder() const override; +private: + cBehaviorBreeder m_BehaviorBreeder; + cBehaviorItemFollower m_BehaviorItemFollower; + cBehaviorCoward m_BehaviorCoward; + cBehaviorWanderer m_BehaviorWanderer; }; - - - - diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp index fef1adac6..312bf74e2 100644 --- a/src/Mobs/Sheep.cpp +++ b/src/Mobs/Sheep.cpp @@ -12,21 +12,21 @@ cSheep::cSheep(int a_Color) : - super("Sheep", mtSheep, "entity.sheep.hurt", "entity.sheep.death", 0.6, 1.3), - m_IsSheared(false), - m_WoolColor(a_Color), - m_TimeToStopEating(-1) + super("Sheep", mtSheep, "entity.sheep.hurt", "entity.sheep.death", 0.6, 1.3), + m_IsSheared(false), + m_WoolColor(a_Color), + m_TimeToStopEating(-1) { - // Generate random wool color. - if (m_WoolColor == -1) - { - m_WoolColor = GenerateNaturalRandomColor(); - } - - if ((m_WoolColor < 0) || (m_WoolColor > 15)) - { - m_WoolColor = 0; - } + // Generate random wool color. + if (m_WoolColor == -1) + { + m_WoolColor = GenerateNaturalRandomColor(); + } + + if ((m_WoolColor < 0) || (m_WoolColor > 15)) + { + m_WoolColor = 0; + } } @@ -35,17 +35,17 @@ cSheep::cSheep(int a_Color) : void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - if (!m_IsSheared) - { - a_Drops.push_back(cItem(E_BLOCK_WOOL, 1, static_cast<short>(m_WoolColor))); - } - - unsigned int LootingLevel = 0; - if (a_Killer != nullptr) - { - LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); - } - AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_COOKED_MUTTON : E_ITEM_RAW_MUTTON); + if (!m_IsSheared) + { + a_Drops.push_back(cItem(E_BLOCK_WOOL, 1, static_cast<short>(m_WoolColor))); + } + + unsigned int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_COOKED_MUTTON : E_ITEM_RAW_MUTTON); } @@ -54,30 +54,30 @@ void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer) void cSheep::OnRightClicked(cPlayer & a_Player) { - super::OnRightClicked(a_Player); - - const cItem & EquippedItem = a_Player.GetEquippedItem(); - if ((EquippedItem.m_ItemType == E_ITEM_SHEARS) && !IsSheared() && !IsBaby()) - { - m_IsSheared = true; - m_World->BroadcastEntityMetadata(*this); - a_Player.UseEquippedItem(); - - cItems Drops; - char NumDrops = GetRandomProvider().RandInt<char>(1, 3); - Drops.emplace_back(E_BLOCK_WOOL, NumDrops, static_cast<short>(m_WoolColor)); - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); - m_World->BroadcastSoundEffect("entity.sheep.shear", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 1.0f); - } - else if ((EquippedItem.m_ItemType == E_ITEM_DYE) && (m_WoolColor != 15 - EquippedItem.m_ItemDamage)) - { - m_WoolColor = 15 - EquippedItem.m_ItemDamage; - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - m_World->BroadcastEntityMetadata(*this); - } + super::OnRightClicked(a_Player); + + const cItem & EquippedItem = a_Player.GetEquippedItem(); + if ((EquippedItem.m_ItemType == E_ITEM_SHEARS) && !IsSheared() && !IsBaby()) + { + m_IsSheared = true; + m_World->BroadcastEntityMetadata(*this); + a_Player.UseEquippedItem(); + + cItems Drops; + char NumDrops = GetRandomProvider().RandInt<char>(1, 3); + Drops.emplace_back(E_BLOCK_WOOL, NumDrops, static_cast<short>(m_WoolColor)); + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); + m_World->BroadcastSoundEffect("entity.sheep.shear", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 1.0f); + } + else if ((EquippedItem.m_ItemType == E_ITEM_DYE) && (m_WoolColor != 15 - EquippedItem.m_ItemDamage)) + { + m_WoolColor = 15 - EquippedItem.m_ItemDamage; + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + m_World->BroadcastEntityMetadata(*this); + } } @@ -86,88 +86,88 @@ void cSheep::OnRightClicked(cPlayer & a_Player) void cSheep::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { - super::Tick(a_Dt, a_Chunk); - if (!IsTicking()) - { - // The base class tick destroyed us - return; - } - int PosX = POSX_TOINT; - int PosY = POSY_TOINT - 1; - int PosZ = POSZ_TOINT; - - if ((PosY <= 0) || (PosY >= cChunkDef::Height)) - { - return; - } - - if (m_TimeToStopEating > 0) - { - StopMovingToPosition(); - m_TimeToStopEating--; - - if (m_TimeToStopEating == 0) - { - if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS) // Make sure grass hasn't been destroyed in the meantime - { - // The sheep ate the grass so we change it to dirt - m_World->SetBlock(PosX, PosY, PosZ, E_BLOCK_DIRT, 0); - GetWorld()->BroadcastSoundParticleEffect(EffectID::PARTICLE_BLOCK_BREAK, PosX, PosY, PosX, E_BLOCK_GRASS); - m_IsSheared = false; - m_World->BroadcastEntityMetadata(*this); - } - } - } - else - { - if (GetRandomProvider().RandBool(1.0 / 600.0)) - { - if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS) - { - m_World->BroadcastEntityStatus(*this, esSheepEating); - m_TimeToStopEating = 40; - } - } - } + super::Tick(a_Dt, a_Chunk); + if (!IsTicking()) + { + // The base class tick destroyed us + return; + } + int PosX = POSX_TOINT; + int PosY = POSY_TOINT - 1; + int PosZ = POSZ_TOINT; + + if ((PosY <= 0) || (PosY >= cChunkDef::Height)) + { + return; + } + + if (m_TimeToStopEating > 0) + { + StopMovingToPosition(); + m_TimeToStopEating--; + + if (m_TimeToStopEating == 0) + { + if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS) // Make sure grass hasn't been destroyed in the meantime + { + // The sheep ate the grass so we change it to dirt + m_World->SetBlock(PosX, PosY, PosZ, E_BLOCK_DIRT, 0); + GetWorld()->BroadcastSoundParticleEffect(EffectID::PARTICLE_BLOCK_BREAK, PosX, PosY, PosX, E_BLOCK_GRASS); + m_IsSheared = false; + m_World->BroadcastEntityMetadata(*this); + } + } + } + else + { + if (GetRandomProvider().RandBool(1.0 / 600.0)) + { + if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS) + { + m_World->BroadcastEntityStatus(*this, esSheepEating); + m_TimeToStopEating = 40; + } + } + } } -void cSheep::InheritFromParents(cPassiveMonster * a_Parent1, cPassiveMonster * a_Parent2) +void cSheep::InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2) { - static const struct - { - short Parent1, Parent2, Child; - } ColorInheritance[] = - { - { E_META_WOOL_BLUE, E_META_WOOL_RED, E_META_WOOL_PURPLE }, - { E_META_WOOL_BLUE, E_META_WOOL_GREEN, E_META_WOOL_CYAN }, - { E_META_WOOL_YELLOW, E_META_WOOL_RED, E_META_WOOL_ORANGE }, - { E_META_WOOL_GREEN, E_META_WOOL_WHITE, E_META_WOOL_LIGHTGREEN }, - { E_META_WOOL_RED, E_META_WOOL_WHITE, E_META_WOOL_PINK }, - { E_META_WOOL_WHITE, E_META_WOOL_BLACK, E_META_WOOL_GRAY }, - { E_META_WOOL_PURPLE, E_META_WOOL_PINK, E_META_WOOL_MAGENTA }, - { E_META_WOOL_WHITE, E_META_WOOL_GRAY, E_META_WOOL_LIGHTGRAY }, - { E_META_WOOL_BLUE, E_META_WOOL_WHITE, E_META_WOOL_LIGHTBLUE }, - }; - cSheep * Parent1 = static_cast<cSheep *>(a_Parent1); - cSheep * Parent2 = static_cast<cSheep *>(a_Parent2); - for (size_t i = 0; i < ARRAYCOUNT(ColorInheritance); i++) - { - if ( - ((Parent1->GetFurColor() == ColorInheritance[i].Parent1) && (Parent2->GetFurColor() == ColorInheritance[i].Parent2)) || - ((Parent1->GetFurColor() == ColorInheritance[i].Parent2) && (Parent2->GetFurColor() == ColorInheritance[i].Parent1)) - ) - { - SetFurColor(ColorInheritance[i].Child); - m_World->BroadcastEntityMetadata(*this); - return; - } - } - SetFurColor(GetRandomProvider().RandBool() ? Parent1->GetFurColor() : Parent2->GetFurColor()); - m_World->BroadcastEntityMetadata(*this); + static const struct + { + short Parent1, Parent2, Child; + } ColorInheritance[] = + { + { E_META_WOOL_BLUE, E_META_WOOL_RED, E_META_WOOL_PURPLE }, + { E_META_WOOL_BLUE, E_META_WOOL_GREEN, E_META_WOOL_CYAN }, + { E_META_WOOL_YELLOW, E_META_WOOL_RED, E_META_WOOL_ORANGE }, + { E_META_WOOL_GREEN, E_META_WOOL_WHITE, E_META_WOOL_LIGHTGREEN }, + { E_META_WOOL_RED, E_META_WOOL_WHITE, E_META_WOOL_PINK }, + { E_META_WOOL_WHITE, E_META_WOOL_BLACK, E_META_WOOL_GRAY }, + { E_META_WOOL_PURPLE, E_META_WOOL_PINK, E_META_WOOL_MAGENTA }, + { E_META_WOOL_WHITE, E_META_WOOL_GRAY, E_META_WOOL_LIGHTGRAY }, + { E_META_WOOL_BLUE, E_META_WOOL_WHITE, E_META_WOOL_LIGHTBLUE }, + }; + cSheep * Parent1 = static_cast<cSheep *>(a_Parent1); + cSheep * Parent2 = static_cast<cSheep *>(a_Parent2); + for (size_t i = 0; i < ARRAYCOUNT(ColorInheritance); i++) + { + if ( + ((Parent1->GetFurColor() == ColorInheritance[i].Parent1) && (Parent2->GetFurColor() == ColorInheritance[i].Parent2)) || + ((Parent1->GetFurColor() == ColorInheritance[i].Parent2) && (Parent2->GetFurColor() == ColorInheritance[i].Parent1)) + ) + { + SetFurColor(ColorInheritance[i].Child); + m_World->BroadcastEntityMetadata(*this); + return; + } + } + SetFurColor(GetRandomProvider().RandBool() ? Parent1->GetFurColor() : Parent2->GetFurColor()); + m_World->BroadcastEntityMetadata(*this); } @@ -176,31 +176,31 @@ void cSheep::InheritFromParents(cPassiveMonster * a_Parent1, cPassiveMonster * a NIBBLETYPE cSheep::GenerateNaturalRandomColor(void) { - int Chance = GetRandomProvider().RandInt(100); - - if (Chance <= 81) - { - return E_META_WOOL_WHITE; - } - else if (Chance <= 86) - { - return E_META_WOOL_BLACK; - } - else if (Chance <= 91) - { - return E_META_WOOL_GRAY; - } - else if (Chance <= 96) - { - return E_META_WOOL_LIGHTGRAY; - } - else if (Chance <= 99) - { - return E_META_WOOL_BROWN; - } - else - { - return E_META_WOOL_PINK; - } + int Chance = GetRandomProvider().RandInt(100); + + if (Chance <= 81) + { + return E_META_WOOL_WHITE; + } + else if (Chance <= 86) + { + return E_META_WOOL_BLACK; + } + else if (Chance <= 91) + { + return E_META_WOOL_GRAY; + } + else if (Chance <= 96) + { + return E_META_WOOL_LIGHTGRAY; + } + else if (Chance <= 99) + { + return E_META_WOOL_BROWN; + } + else + { + return E_META_WOOL_PINK; + } } diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h index 02aa888f1..3e2069cf1 100644 --- a/src/Mobs/Sheep.h +++ b/src/Mobs/Sheep.h @@ -8,44 +8,44 @@ class cSheep : - public cPassiveMonster + public cPassiveMonster { - typedef cPassiveMonster super; + typedef cPassiveMonster super; public: - /** The number is the color of the sheep. - Use E_META_WOOL_* constants for the wool color. - If you type -1, the server will generate a random color - with the GenerateNaturalRandomColor() function. */ - cSheep(int a_Color = -1); + /** The number is the color of the sheep. + Use E_META_WOOL_* constants for the wool color. + If you type -1, the server will generate a random color + with the GenerateNaturalRandomColor() function. */ + cSheep(int a_Color = -1); - CLASS_PROTODEF(cSheep) + CLASS_PROTODEF(cSheep) - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual void InheritFromParents(cPassiveMonster * a_Parent1, cPassiveMonster * a_Parent2) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2) override; - virtual void GetFollowedItems(cItems & a_Items) override - { - a_Items.Add(E_ITEM_WHEAT); - } + virtual void GetFollowedItems(cItems & a_Items) override + { + a_Items.Add(E_ITEM_WHEAT); + } - /** Generates a random color for the sheep like the vanilla server. - The percent's where used are from the wiki: http://minecraft.gamepedia.com/Sheep#Breeding */ - static NIBBLETYPE GenerateNaturalRandomColor(void); + /** Generates a random color for the sheep like the vanilla server. + The percent's where used are from the wiki: http://minecraft.gamepedia.com/Sheep#Breeding */ + static NIBBLETYPE GenerateNaturalRandomColor(void); - bool IsSheared(void) const { return m_IsSheared; } - void SetSheared(bool a_IsSheared) { m_IsSheared = a_IsSheared; } + bool IsSheared(void) const { return m_IsSheared; } + void SetSheared(bool a_IsSheared) { m_IsSheared = a_IsSheared; } - int GetFurColor(void) const { return m_WoolColor; } - void SetFurColor(int a_WoolColor) { m_WoolColor = a_WoolColor; } + int GetFurColor(void) const { return m_WoolColor; } + void SetFurColor(int a_WoolColor) { m_WoolColor = a_WoolColor; } private: - bool m_IsSheared; - int m_WoolColor; - int m_TimeToStopEating; + bool m_IsSheared; + int m_WoolColor; + int m_TimeToStopEating; } ; diff --git a/src/Mobs/Silverfish.h b/src/Mobs/Silverfish.h index 90ef5ea5d..9988ca015 100644 --- a/src/Mobs/Silverfish.h +++ b/src/Mobs/Silverfish.h @@ -8,17 +8,17 @@ class cSilverfish : - public cAggressiveMonster + public cAggressiveMonster { - typedef cAggressiveMonster super; + typedef cAggressiveMonster super; public: - cSilverfish(void) : - super("Silverfish", mtSilverfish, "entity.silverfish.hurt", "entity.silverfish.death", 0.3, 0.7) - { - } + cSilverfish(void) : + super("Silverfish", mtSilverfish, "entity.silverfish.hurt", "entity.silverfish.death", 0.3, 0.7) + { + } - CLASS_PROTODEF(cSilverfish) + CLASS_PROTODEF(cSilverfish) } ; diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp index e48991a06..55616cc28 100644 --- a/src/Mobs/Skeleton.cpp +++ b/src/Mobs/Skeleton.cpp @@ -10,10 +10,10 @@ cSkeleton::cSkeleton(bool IsWither) : - super("Skeleton", mtSkeleton, "entity.skeleton.hurt", "entity.skeleton.death", 0.6, 1.8), - m_bIsWither(IsWither) + super("Skeleton", mtSkeleton, "entity.skeleton.hurt", "entity.skeleton.death", 0.6, 1.8), + m_bIsWither(IsWither) { - SetBurnsInDaylight(true); + } @@ -22,54 +22,54 @@ cSkeleton::cSkeleton(bool IsWither) : void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - unsigned int LootingLevel = 0; - if (a_Killer != nullptr) - { - LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); - } - if (IsWither()) - { - AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_COAL); - cItems RareDrops; - RareDrops.Add(cItem(E_ITEM_HEAD, 1, 1)); - AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel); - } - else - { - AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ARROW); - - } - AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_BONE); - AddRandomArmorDropItem(a_Drops, LootingLevel); - AddRandomWeaponDropItem(a_Drops, LootingLevel); + unsigned int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + if (IsWither()) + { + AddRandomUncommonDropItem(a_Drops, 33.0f, E_ITEM_COAL); + cItems RareDrops; + RareDrops.Add(cItem(E_ITEM_HEAD, 1, 1)); + AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel); + } + else + { + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ARROW); + + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_BONE); + AddRandomArmorDropItem(a_Drops, LootingLevel); + AddRandomWeaponDropItem(a_Drops, LootingLevel); } - -bool cSkeleton::Attack(std::chrono::milliseconds a_Dt) +//mobTodo +/*bool cSkeleton::Attack(std::chrono::milliseconds a_Dt) { - StopMovingToPosition(); // Todo handle this in a better way, the skeleton does some uneeded recalcs due to inStateChasing - auto & Random = GetRandomProvider(); - if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) - { - Vector3d Inaccuracy = Vector3d(Random.RandReal<double>(-0.25, 0.25), Random.RandReal<double>(-0.25, 0.25), Random.RandReal<double>(-0.25, 0.25)); - Vector3d Speed = (GetTarget()->GetPosition() + Inaccuracy - GetPosition()) * 5; - Speed.y += Random.RandInt(-1, 1); - - auto Arrow = cpp14::make_unique<cArrowEntity>(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); - auto ArrowPtr = Arrow.get(); - if (!ArrowPtr->Initialize(std::move(Arrow), *m_World)) - { - return false; - } - - ResetAttackCooldown(); - return true; - } - return false; -} + StopMovingToPosition(); // Todo handle this in a better way, the skeleton does some uneeded recalcs due to inStateChasing + auto & Random = GetRandomProvider(); + if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) + { + Vector3d Inaccuracy = Vector3d(Random.RandReal<double>(-0.25, 0.25), Random.RandReal<double>(-0.25, 0.25), Random.RandReal<double>(-0.25, 0.25)); + Vector3d Speed = (GetTarget()->GetPosition() + Inaccuracy - GetPosition()) * 5; + Speed.y += Random.RandInt(-1, 1); + + auto Arrow = cpp14::make_unique<cArrowEntity>(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); + auto ArrowPtr = Arrow.get(); + if (!ArrowPtr->Initialize(std::move(Arrow), *m_World)) + { + return false; + } + + ResetAttackCooldown(); + return true; + } + return false; +}*/ @@ -77,8 +77,8 @@ bool cSkeleton::Attack(std::chrono::milliseconds a_Dt) void cSkeleton::SpawnOn(cClientHandle & a_ClientHandle) { - super::SpawnOn(a_ClientHandle); - a_ClientHandle.SendEntityEquipment(*this, 0, cItem(E_ITEM_BOW)); + super::SpawnOn(a_ClientHandle); + a_ClientHandle.SendEntityEquipment(*this, 0, cItem(E_ITEM_BOW)); } diff --git a/src/Mobs/Skeleton.h b/src/Mobs/Skeleton.h index 0316fb9b5..91e094c4c 100644 --- a/src/Mobs/Skeleton.h +++ b/src/Mobs/Skeleton.h @@ -8,26 +8,26 @@ class cSkeleton : - public cAggressiveMonster + public cAggressiveMonster { - typedef cAggressiveMonster super; + typedef cAggressiveMonster super; public: - cSkeleton(bool IsWither); + cSkeleton(bool IsWither); - CLASS_PROTODEF(cSkeleton) + CLASS_PROTODEF(cSkeleton) - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual bool Attack(std::chrono::milliseconds a_Dt) override; - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + /*virtual bool Attack(std::chrono::milliseconds a_Dt) override;*/ + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual bool IsUndead(void) override { return true; } + virtual bool IsUndead(void) override { return true; } - bool IsWither(void) const { return m_bIsWither; } + bool IsWither(void) const { return m_bIsWither; } private: - bool m_bIsWither; + bool m_bIsWither; } ; diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp index 291a3a57f..92c181798 100644 --- a/src/Mobs/Slime.cpp +++ b/src/Mobs/Slime.cpp @@ -20,7 +20,7 @@ cSlime::cSlime(int a_Size) : m_Size(a_Size) { SetMaxHealth(a_Size * a_Size); - SetAttackDamage(a_Size); + // SetAttackDamage(a_Size); //mobTodo myBehavior.setaTTACKDamage } @@ -45,8 +45,8 @@ void cSlime::GetDrops(cItems & a_Drops, cEntity * a_Killer) - -bool cSlime::Attack(std::chrono::milliseconds a_Dt) +//mobTodo +/*bool cSlime::Attack(std::chrono::milliseconds a_Dt) { if (m_Size > 1) { @@ -55,7 +55,7 @@ bool cSlime::Attack(std::chrono::milliseconds a_Dt) } return false; -} +}*/ diff --git a/src/Mobs/Slime.h b/src/Mobs/Slime.h index c78461a02..26bc6716d 100644 --- a/src/Mobs/Slime.h +++ b/src/Mobs/Slime.h @@ -8,32 +8,32 @@ class cSlime : - public cAggressiveMonster + public cAggressiveMonster { - typedef cAggressiveMonster super; + typedef cAggressiveMonster super; public: - /** Creates a slime of the specified size; size can be 1, 2 or 4, with 1 is the smallest and 4 is the tallest. */ - cSlime(int a_Size); + /** Creates a slime of the specified size; size can be 1, 2 or 4, with 1 is the smallest and 4 is the tallest. */ + cSlime(int a_Size); - CLASS_PROTODEF(cSlime) + CLASS_PROTODEF(cSlime) - // cAggressiveMonster overrides: - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual bool Attack(std::chrono::milliseconds a_Dt) override; - virtual void KilledBy(TakeDamageInfo & a_TDI) override; + // cAggressiveMonster overrides: + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + // virtual bool Attack(std::chrono::milliseconds a_Dt) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; - int GetSize(void) const { return m_Size; } + int GetSize(void) const { return m_Size; } - /** Returns the text describing the slime's size, as used by the client's resource subsystem for sounds. - Returns either "big" or "small". */ - static AString GetSizeName(int a_Size); + /** Returns the text describing the slime's size, as used by the client's resource subsystem for sounds. + Returns either "big" or "small". */ + static AString GetSizeName(int a_Size); protected: - /** Size of the slime, with 1 being the smallest. - Vanilla uses sizes 1, 2 and 4 only. */ - int m_Size; + /** Size of the slime, with 1 being the smallest. + Vanilla uses sizes 1, 2 and 4 only. */ + int m_Size; } ; diff --git a/src/Mobs/Spider.cpp b/src/Mobs/Spider.cpp index 971ff22f6..608bcd853 100644 --- a/src/Mobs/Spider.cpp +++ b/src/Mobs/Spider.cpp @@ -35,32 +35,6 @@ void cSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cSpider::EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk) -{ - if (!GetWorld()->IsChunkLighted(GetChunkX(), GetChunkZ())) - { - return; - } - - PREPARE_REL_AND_CHUNK(GetPosition(), a_Chunk); - if (!RelSuccess) - { - return; - } - - if ( - a_Player->CanMobsTarget() && - !((Chunk->GetSkyLightAltered(Rel.x, Rel.y, Rel.z) > 11) || (Chunk->GetBlockLight(Rel.x, Rel.y, Rel.z) > 11)) - ) - { - super::EventSeePlayer(a_Player, a_Chunk); - } -} - - - - - bool cSpider::DoTakeDamage(TakeDamageInfo & a_TDI) { if (!super::DoTakeDamage(a_TDI)) @@ -68,6 +42,7 @@ bool cSpider::DoTakeDamage(TakeDamageInfo & a_TDI) return false; } + /* mobTodo // If the source of the damage is not from an pawn entity, switch to idle if ((a_TDI.Attacker == nullptr) || !a_TDI.Attacker->IsPawn()) { @@ -77,7 +52,7 @@ bool cSpider::DoTakeDamage(TakeDamageInfo & a_TDI) { // If the source of the damage is from a pawn entity, chase that entity m_EMState = CHASING; - } + }*/ return true; } diff --git a/src/Mobs/Spider.h b/src/Mobs/Spider.h index af2753012..296c9f261 100644 --- a/src/Mobs/Spider.h +++ b/src/Mobs/Spider.h @@ -8,18 +8,17 @@ class cSpider : - public cAggressiveMonster + public cAggressiveMonster { - typedef cAggressiveMonster super; + typedef cAggressiveMonster super; public: - cSpider(void); + cSpider(void); - CLASS_PROTODEF(cSpider) + CLASS_PROTODEF(cSpider) - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void EventSeePlayer(cPlayer *, cChunk & a_Chunk) override; - virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; } ; diff --git a/src/Mobs/Witch.h b/src/Mobs/Witch.h index 706fcd9b3..79cc993bc 100644 --- a/src/Mobs/Witch.h +++ b/src/Mobs/Witch.h @@ -18,8 +18,7 @@ public: CLASS_PROTODEF(cWitch) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - - bool IsAngry(void) const {return ((m_EMState == ATTACKING) || (m_EMState == CHASING)); } + bool IsAngry() const { return false; } } ; diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp index 33a9b31ee..d7ecb3d47 100644 --- a/src/Mobs/Wolf.cpp +++ b/src/Mobs/Wolf.cpp @@ -95,7 +95,7 @@ void cWolf::NotifyAlliesOfFight(cPawn * a_Opponent) m_World->DoWithPlayerByUUID(m_OwnerUUID, Callback); } -bool cWolf::Attack(std::chrono::milliseconds a_Dt) +/*bool cWolf::Attack(std::chrono::milliseconds a_Dt) { UNUSED(a_Dt); @@ -111,7 +111,7 @@ bool cWolf::Attack(std::chrono::milliseconds a_Dt) NotifyAlliesOfFight(static_cast<cPawn*>(GetTarget())); return super::Attack(a_Dt); -} +}*/ @@ -253,6 +253,9 @@ void cWolf::OnRightClicked(cPlayer & a_Player) void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { + //mobTodo behaviors! + + /* if (!IsAngry()) { cMonster::Tick(a_Dt, a_Chunk); @@ -326,7 +329,7 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) MoveToPosition(GetTarget()->GetPosition()); if (TargetIsInRange()) { - Attack(a_Dt); + // Attack(a_Dt); mobTodo } } } @@ -339,6 +342,7 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { StopMovingToPosition(); } + */ } @@ -395,13 +399,3 @@ void cWolf::TickFollowPlayer() } - -void cWolf::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) -{ - if (!IsTame()) - { - cMonster::InStateIdle(a_Dt, a_Chunk); - } -} - - diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h index e05fedbf8..120e10942 100644 --- a/src/Mobs/Wolf.h +++ b/src/Mobs/Wolf.h @@ -9,64 +9,62 @@ class cEntity; class cWolf : - public cPassiveAggressiveMonster + public cPassiveAggressiveMonster { - typedef cPassiveAggressiveMonster super; + typedef cPassiveAggressiveMonster super; public: - cWolf(void); - - CLASS_PROTODEF(cWolf) - - void NotifyAlliesOfFight(cPawn * a_Opponent); - virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual void TickFollowPlayer(); - virtual bool Attack(std::chrono::milliseconds a_Dt) override; - - // Get functions - bool IsSitting (void) const override { return m_IsSitting; } - bool IsTame (void) const override { return m_IsTame; } - bool IsBegging (void) const { return m_IsBegging; } - bool IsAngry (void) const { return m_IsAngry; } - AString GetOwnerName (void) const { return m_OwnerName; } - AString GetOwnerUUID (void) const { return m_OwnerUUID; } - int GetCollarColor(void) const { return m_CollarColor; } - - // Set functions - void SetIsSitting (bool a_IsSitting) { m_IsSitting = a_IsSitting; } - void SetIsTame (bool a_IsTame) { m_IsTame = a_IsTame; } - void SetIsBegging (bool a_IsBegging) { m_IsBegging = a_IsBegging; } - void SetIsAngry (bool a_IsAngry) { m_IsAngry = a_IsAngry; } - void SetCollarColor(int a_CollarColor) { m_CollarColor = a_CollarColor; } - void SetOwner (const AString & a_NewOwnerName, const AString & a_NewOwnerUUID) - { - m_OwnerName = a_NewOwnerName; - m_OwnerUUID = a_NewOwnerUUID; - } - - /** Notfies the wolf of a nearby fight. - The wolf may then decide to attack a_Opponent. - If a_IsPlayer is true, then the player whose ID is a_PlayerID is fighting a_Opponent - If false, then a wolf owned by the player whose ID is a_PlayerID is fighting a_Opponent - @param a_PlayerID The ID of the fighting player, or the ID of the owner whose wolf is fighting. - @param a_Opponent The opponent who is being faught. - @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; + cWolf(void); + + CLASS_PROTODEF(cWolf) + + void NotifyAlliesOfFight(cPawn * a_Opponent); + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void TickFollowPlayer(); + //virtual bool Attack(std::chrono::milliseconds a_Dt) override; + + // Get functions + bool IsSitting (void) const override { return m_IsSitting; } + bool IsTame (void) const override { return m_IsTame; } + bool IsBegging (void) const { return m_IsBegging; } + bool IsAngry (void) const { return m_IsAngry; } + AString GetOwnerName (void) const { return m_OwnerName; } + AString GetOwnerUUID (void) const { return m_OwnerUUID; } + int GetCollarColor(void) const { return m_CollarColor; } + + // Set functions + void SetIsSitting (bool a_IsSitting) { m_IsSitting = a_IsSitting; } + void SetIsTame (bool a_IsTame) { m_IsTame = a_IsTame; } + void SetIsBegging (bool a_IsBegging) { m_IsBegging = a_IsBegging; } + void SetIsAngry (bool a_IsAngry) { m_IsAngry = a_IsAngry; } + void SetCollarColor(int a_CollarColor) { m_CollarColor = a_CollarColor; } + void SetOwner (const AString & a_NewOwnerName, const AString & a_NewOwnerUUID) + { + m_OwnerName = a_NewOwnerName; + m_OwnerUUID = a_NewOwnerUUID; + } + + /** Notfies the wolf of a nearby fight. + The wolf may then decide to attack a_Opponent. + If a_IsPlayer is true, then the player whose ID is a_PlayerID is fighting a_Opponent + If false, then a wolf owned by the player whose ID is a_PlayerID is fighting a_Opponent + @param a_PlayerID The ID of the fighting player, or the ID of the owner whose wolf is fighting. + @param a_Opponent The opponent who is being faught. + @param a_IsPlayerInvolved Whether the fighter a player or a wolf. */ + void ReceiveNearbyFightInfo(AString a_PlayerID, cPawn * a_Opponent, bool a_IsPlayerInvolved); protected: - bool m_IsSitting; - bool m_IsTame; - bool m_IsBegging; - bool m_IsAngry; - AString m_OwnerName; - AString m_OwnerUUID; - int m_CollarColor; - int m_NotificationCooldown; + bool m_IsSitting; + bool m_IsTame; + bool m_IsBegging; + bool m_IsAngry; + AString m_OwnerName; + AString m_OwnerUUID; + int m_CollarColor; + int m_NotificationCooldown; } ; diff --git a/src/Mobs/Zombie.cpp b/src/Mobs/Zombie.cpp index 882e98bf1..6f889e182 100644 --- a/src/Mobs/Zombie.cpp +++ b/src/Mobs/Zombie.cpp @@ -10,11 +10,11 @@ cZombie::cZombie(bool a_IsVillagerZombie) : - super("Zombie", mtZombie, "entity.zombie.hurt", "entity.zombie.death", 0.6, 1.8), - m_IsVillagerZombie(a_IsVillagerZombie), - m_IsConverting(false) + super("Zombie", mtZombie, "entity.zombie.hurt", "entity.zombie.death", 0.6, 1.8), + m_IsVillagerZombie(a_IsVillagerZombie), + m_IsConverting(false) { - SetBurnsInDaylight(true); + } @@ -23,17 +23,17 @@ cZombie::cZombie(bool a_IsVillagerZombie) : void cZombie::GetDrops(cItems & a_Drops, cEntity * a_Killer) { - unsigned int LootingLevel = 0; - if (a_Killer != nullptr) - { - LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); - } - AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ROTTEN_FLESH); - cItems RareDrops; - RareDrops.Add(cItem(E_ITEM_IRON)); - RareDrops.Add(cItem(E_ITEM_CARROT)); - RareDrops.Add(cItem(E_ITEM_POTATO)); - AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel); - AddRandomArmorDropItem(a_Drops, LootingLevel); - AddRandomWeaponDropItem(a_Drops, LootingLevel); + unsigned int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_ROTTEN_FLESH); + cItems RareDrops; + RareDrops.Add(cItem(E_ITEM_IRON)); + RareDrops.Add(cItem(E_ITEM_CARROT)); + RareDrops.Add(cItem(E_ITEM_POTATO)); + AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel); + AddRandomArmorDropItem(a_Drops, LootingLevel); + AddRandomWeaponDropItem(a_Drops, LootingLevel); } |