summaryrefslogblamecommitdiffstats
path: root/src/Mobs/Behaviors/BehaviorBreeder.cpp
blob: b32fafc8cd5f0cae506704474251a2e54e3749b9 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                                                                                              
                                      



                               
 












                                                           





 
                                                                             
 

                        







































                                                                                                           
                 
         





 
                                                                                 
 

                        











                                 





 
                                                                                         
 

                        
































































                                                                                                                                 

                                  



                                                                     







                                                         















                                                                                                                    





                                                           

                                                         







                                      



                                                   
 

                                                                                                                                                               







                                       
                               







                                               
                                    
 

#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() :
	m_LovePartner(nullptr),
	m_LoveTimer(0),
	m_LoveCooldown(0),
	m_MatingTimer(0)
{
}





void cBehaviorBreeder::AttachToMonster(cMonster & a_Parent)
{
	m_Parent = &a_Parent;
	m_Parent->AttachTickBehavior(this);
	m_Parent->AttachPostTickBehavior(this);
	m_Parent->AttachRightClickBehavior(this);
	m_Parent->AttachDestroyBehavior(this);
}





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);
}