summaryrefslogblamecommitdiffstats
path: root/src/Mobs/PassiveMonster.cpp
blob: 30b46500d57cfc9a47c1843885c0e3acf0c49210 (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 "PassiveMonster.h"
#include "../World.h"
#include "../Entities/Player.h"
#include "BoundingBox.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_EMPersonality = PASSIVE;
}





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;
	}
	return true;
}





void cPassiveMonster::EngageLoveMode(cPassiveMonster * a_Partner)
{
	m_LovePartner = a_Partner;
	m_MatingTimer = 50;  // about 3 seconds of mating
}





void cPassiveMonster::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_World->BroadcastEntityMetadata(*this);
}





void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
	super::Tick(a_Dt, a_Chunk);

	if (m_EMState == ESCAPING)
	{
		CheckEventLostPlayer();
	}
	if ((m_LovePartner != nullptr) && m_LovePartner->IsDestroyed())
	{
		m_LovePartner = nullptr;
	}

	// 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();
			MoveToPosition(Pos);
		}
		else
		{
			// 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);
			}

			cFastRandom Random;
			m_World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, 1 + Random.NextInt(6));

			m_LovePartner->ResetLoveMode();
			ResetLoveMode();
		}
	}
	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)
		{
			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);
				}
			}
		}
	}

	// If we are in love mode but we have no partner, search for a partner neabry
	if (m_LoveTimer > 0)
	{
		if (m_LovePartner == nullptr)
		{
			class LookForLover : public cEntityCallback
			{
			public:
				cEntity * m_Me;

				LookForLover(cEntity * a_Me) :
					m_Me(a_Me)
				{
				}

				virtual bool Item(cEntity * a_Entity) override
				{
					// if we're the same species as someone around and they don't have a partner, start mating with them
					if ((a_Entity->GetEntityType() == m_Me->GetEntityType()) && (a_Entity != m_Me))
					{
						cPassiveMonster * Me = static_cast<cPassiveMonster*>(m_Me);
						cPassiveMonster * Partner = static_cast<cPassiveMonster*>(a_Entity);
						if (Partner->IsInLove() && (Partner->GetPartner() == nullptr))
						{
							Partner->EngageLoveMode(Me);
							Me->EngageLoveMode(Partner);
							return true;
						}
					}
					return false;
				}
			} Callback(this);

			m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), 8, 8), Callback);
		}

		m_LoveTimer--;
	}
	if (m_MatingTimer > 0)
	{
		m_MatingTimer--;
	}
	if (m_LoveCooldown > 0)
	{
		m_LoveCooldown--;
	}
}





void cPassiveMonster::OnRightClicked(cPlayer & a_Player)
{
	super::OnRightClicked(a_Player);

	// If a player holding breeding items right-clicked me, go into love mode
	if ((m_LoveCooldown == 0) && !IsInLove() && !IsBaby())
	{
		short HeldItem = a_Player.GetEquippedItem().m_ItemType;
		cItems Items;
		GetBreedingItems(Items);
		if (Items.ContainsType(HeldItem))
		{
			if (!a_Player.IsGameModeCreative())
			{
				a_Player.GetInventory().RemoveOneEquippedItem();
			}
			m_LoveTimer = 20 * 30;  // half a minute
			m_World->BroadcastEntityStatus(*this, esMobInLove);
		}
	}
}