summaryrefslogblamecommitdiffstats
path: root/src/Mobs/Behaviors/BehaviorBreeder.cpp
blob: 8ba991989ea168e92325e3a1a8b2929e5046b2e3 (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(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);
}





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.RandInt() % 6));

            m_LovePartner->GetBehaviorBreeder()->ResetLoveMode();
            ResetLoveMode();
        }
        return true;
    }

    // If we are in love mode and we have no partner
    if (m_LoveTimer > 0)
    {
        class LookForLover : public cEntityCallback
        {
        public:
            cMonster * m_Me;
            LookForLover(cMonster * a_Me) :
                m_Me(a_Me)
            {
            }

            virtual bool Item(cEntity * a_Entity) override
            {
                // If the entity is not a monster, don't breed with it
                // Also, do not self-breed
                if ((a_Entity->GetEntityType() != cEntity::eEntityType::etMonster) || (a_Entity == m_Me))
                {
                    return false;
                }

                auto PotentialPartner = static_cast<cMonster*>(a_Entity);

                // If the potential partner is not of the same species, don't breed with it
                if (PotentialPartner->GetMobType() != m_Me->GetMobType())
                {
                    return false;
                }

                auto PartnerBreedingBehavior = PotentialPartner->GetBehaviorBreeder();
                auto MyBreedingBehavior = m_Me->GetBehaviorBreeder();

                // If the potential partner is not in love
                // Or they already have a mate, do not breed with them

                if ((!PartnerBreedingBehavior->IsInLove()) || (PartnerBreedingBehavior->GetPartner() != nullptr))
                {
                    return false;
                }

                // All conditions met, let's breed!
                PartnerBreedingBehavior->EngageLoveMode(m_Me);
                MyBreedingBehavior->EngageLoveMode(PotentialPartner);
                return true;
            }
        } Callback(m_Parent);

        World->ForEachEntityInBox(cBoundingBox(m_Parent->GetPosition(), 8, 8), Callback);
        if (m_LovePartner != nullptr)
        {
            return true;  // We found love and took control of the monster, prevent other Behaviors from doing so
        }
    }

    return false;
}





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