summaryrefslogtreecommitdiffstats
path: root/src/Mobs/Behaviors/BehaviorBreeder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Mobs/Behaviors/BehaviorBreeder.cpp')
-rw-r--r--src/Mobs/Behaviors/BehaviorBreeder.cpp233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/Mobs/Behaviors/BehaviorBreeder.cpp b/src/Mobs/Behaviors/BehaviorBreeder.cpp
new file mode 100644
index 000000000..ea3e10026
--- /dev/null
+++ b/src/Mobs/Behaviors/BehaviorBreeder.cpp
@@ -0,0 +1,233 @@
+
+#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"
+
+iBehaviorBreeder::~iBehaviorBreeder()
+{
+
+}
+
+
+
+
+
+cBehaviorBreeder::cBehaviorBreeder(cMonster * a_Parent, cItems & a_BreedingItems) :
+ m_Parent(a_Parent),
+ m_LovePartner(nullptr),
+ m_LoveTimer(0),
+ m_LoveCooldown(0),
+ m_MatingTimer(0),
+ m_BreedingItems(a_BreedingItems)
+{
+ m_Parent = dynamic_cast<cMonster *>(m_ParentInterface);
+ ASSERT(m_Parent != nullptr);
+}
+
+
+
+
+
+bool cBehaviorBreeder::ActiveTick()
+{
+ 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.NextInt(6));
+
+ m_LovePartner->GetBehaviorBreeder()->ResetLoveMode();
+ ResetLoveMode();
+ }
+ return true;
+ }
+
+ // If we are in love mode and we have no partner
+ if (m_LoveTimer > 0)
+ {
+ class LookForLover : public cEntityCallback
+ {
+ public:
+ cMonster * m_Me;
+ LookForLover(cMonster * a_Me) :
+ m_Me(a_Me)
+ {
+ }
+
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ // If the entity is not a monster, don't breed with it
+ // Also, do not self-breed
+ if ((a_Entity->GetEntityType() != cEntity::eEntityType::etMonster) || (a_Entity == m_Me))
+ {
+ return false;
+ }
+
+ auto PotentialPartner = static_cast<cMonster*>(a_Entity);
+
+ // If the potential partner is not of the same species, don't breed with it
+ if (PotentialPartner->GetMobType() != m_Me->GetMobType())
+ {
+ return false;
+ }
+
+ auto PartnerBreedingBehavior = PotentialPartner->GetBehaviorBreeder();
+ auto MyBreedingBehavior = m_Me->GetBehaviorBreeder();
+
+ // If the potential partner is not in love
+ // Or they already have a mate, do not breed with them
+
+ if ((!PartnerBreedingBehavior->IsInLove()) || (PartnerBreedingBehavior->GetPartner() != nullptr))
+ {
+ return false;
+ }
+
+ // All conditions met, let's breed!
+ PartnerBreedingBehavior->EngageLoveMode(m_Me);
+ MyBreedingBehavior->EngageLoveMode(PotentialPartner);
+ return true;
+ }
+ } Callback(m_Parent);
+
+ World->ForEachEntityInBox(cBoundingBox(m_Parent->GetPosition(), 8, 8), Callback);
+ if (m_LovePartner != nullptr)
+ {
+ return true; // We found love and took control of the monster, prevent other Behaviors from doing so
+ }
+ }
+
+ return false;
+}
+
+
+
+
+
+void cBehaviorBreeder::Tick()
+{
+ if (m_MatingTimer > 0)
+ {
+ m_MatingTimer--;
+ }
+ if (m_LoveCooldown > 0)
+ {
+ m_LoveCooldown--;
+ }
+ if (m_LoveTimer > 0)
+ {
+ m_LoveTimer--;
+ }
+}
+
+
+
+
+
+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;
+ if (m_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);
+}