Adding wolf breading and moving breeding functionality to cMonster (#4951)
* added wolf breading * mpoved breeding to monster * checkstyle * fixed my IDE "helping" * removed magic number and fixed faster aging * added flooring to age manipulation * fixed copiler error * fixed typo * moved tps to Defines.h * removed the TPS constant from the lua API exposure * added inline constexpr added explanation * fixed broken build * "fixed" build Co-authored-by: 12xx12 <12xx12100@gmail.com>
This commit is contained in:
parent
6fd35be67a
commit
32ee1708a2
@ -12,6 +12,21 @@ typedef std::vector<int> cSlotNums;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
|
||||||
|
#endif
|
||||||
|
/** Constant to calculate ticks from seconds "ticks per second" */
|
||||||
|
constexpr inline const int TPS = 20;
|
||||||
|
// This is not added to the lua API because it broke the build
|
||||||
|
#ifdef __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// tolua_begin
|
// tolua_begin
|
||||||
|
|
||||||
/** Experience Orb setup */
|
/** Experience Orb setup */
|
||||||
@ -384,7 +399,6 @@ enum eMessageType
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Returns a textual representation of the click action. */
|
/** Returns a textual representation of the click action. */
|
||||||
const char * ClickActionToString(int a_ClickAction);
|
const char * ClickActionToString(int a_ClickAction);
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
#include "../MonsterConfig.h"
|
#include "../MonsterConfig.h"
|
||||||
#include "../BoundingBox.h"
|
#include "../BoundingBox.h"
|
||||||
|
|
||||||
|
#include "Items/ItemSpawnEgg.h"
|
||||||
|
|
||||||
#include "../Chunk.h"
|
#include "../Chunk.h"
|
||||||
#include "../FastRandom.h"
|
#include "../FastRandom.h"
|
||||||
|
|
||||||
@ -22,8 +24,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Map for eType <-> string
|
/** Map for eType <-> string
|
||||||
Needs to be alpha-sorted by the strings, because binary search is used in StringToMobType()
|
Needs to be alpha-sorted by the strings, because binary search is used in StringToMobType()
|
||||||
The strings need to be lowercase (for more efficient comparisons in StringToMobType())
|
The strings need to be lowercase (for more efficient comparisons in StringToMobType())
|
||||||
@ -109,12 +109,16 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
|
|||||||
, m_BurnsInDaylight(false)
|
, m_BurnsInDaylight(false)
|
||||||
, m_RelativeWalkSpeed(1)
|
, m_RelativeWalkSpeed(1)
|
||||||
, m_Age(1)
|
, m_Age(1)
|
||||||
, m_AgingTimer(20 * 60 * 20) // about 20 minutes
|
, m_AgingTimer(TPS * 60 * 20) // about 20 minutes
|
||||||
, m_WasLastTargetAPlayer(false)
|
, m_WasLastTargetAPlayer(false)
|
||||||
, m_LeashedTo(nullptr)
|
, m_LeashedTo(nullptr)
|
||||||
, m_LeashToPos(nullptr)
|
, m_LeashToPos(nullptr)
|
||||||
, m_IsLeashActionJustDone(false)
|
, m_IsLeashActionJustDone(false)
|
||||||
, m_CanBeLeashed(GetMobFamily() == eFamily::mfPassive)
|
, m_CanBeLeashed(GetMobFamily() == eFamily::mfPassive)
|
||||||
|
, m_LovePartner(nullptr)
|
||||||
|
, m_LoveTimer(0)
|
||||||
|
, m_LoveCooldown(0)
|
||||||
|
, m_MatingTimer(0)
|
||||||
, m_Target(nullptr)
|
, m_Target(nullptr)
|
||||||
{
|
{
|
||||||
if (!a_ConfigName.empty())
|
if (!a_ConfigName.empty())
|
||||||
@ -163,6 +167,10 @@ void cMonster::OnRemoveFromWorld(cWorld & a_World)
|
|||||||
void cMonster::Destroyed()
|
void cMonster::Destroyed()
|
||||||
{
|
{
|
||||||
SetTarget(nullptr); // Tell them we're no longer targeting them.
|
SetTarget(nullptr); // Tell them we're no longer targeting them.
|
||||||
|
if (m_LovePartner != nullptr)
|
||||||
|
{
|
||||||
|
m_LovePartner->ResetLoveMode();
|
||||||
|
}
|
||||||
Super::Destroyed();
|
Super::Destroyed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -896,7 +904,7 @@ void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
|||||||
|
|
||||||
void cMonster::ResetAttackCooldown()
|
void cMonster::ResetAttackCooldown()
|
||||||
{
|
{
|
||||||
m_AttackCoolDownTicksLeft = static_cast<int>(20 * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every second
|
m_AttackCoolDownTicksLeft = static_cast<int>(TPS * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every second
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1248,6 +1256,195 @@ std::unique_ptr<cMonster> cMonster::NewMonsterFromType(eMonsterType a_MobType)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cMonster::EngageLoveMode(cMonster *a_Partner)
|
||||||
|
{
|
||||||
|
m_LovePartner = a_Partner;
|
||||||
|
m_MatingTimer = 50; // about 3 seconds of mating
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cMonster::ResetLoveMode()
|
||||||
|
{
|
||||||
|
m_LovePartner = nullptr;
|
||||||
|
m_LoveTimer = 0;
|
||||||
|
m_MatingTimer = 0;
|
||||||
|
m_LoveCooldown = TPS * 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 cMonster::LoveTick(void)
|
||||||
|
{
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
cMonster * Baby = nullptr;
|
||||||
|
|
||||||
|
m_World->DoWithEntityByID(BabyID, [&](cEntity & a_Entity)
|
||||||
|
{
|
||||||
|
Baby = static_cast<cMonster *>(&a_Entity);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Baby != nullptr)
|
||||||
|
{
|
||||||
|
Baby->InheritFromParents(this, m_LovePartner);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, GetRandomProvider().RandInt(1, 6));
|
||||||
|
|
||||||
|
m_World->DoWithPlayerByUUID(m_Feeder, [&] (cPlayer & a_Player)
|
||||||
|
{
|
||||||
|
a_Player.GetStatManager().AddValue(Statistic::AnimalsBred);
|
||||||
|
if (GetMobType() == eMonsterType::mtCow)
|
||||||
|
{
|
||||||
|
a_Player.AwardAchievement(Statistic::AchBreedCow);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
m_World->DoWithNearestPlayer(GetPosition(), static_cast<float>(m_SightDistance), [&](cPlayer & a_Player) -> bool
|
||||||
|
{
|
||||||
|
const cItem & EquippedItem = a_Player.GetEquippedItem();
|
||||||
|
if (FollowedItems.ContainsType(EquippedItem))
|
||||||
|
{
|
||||||
|
Vector3d PlayerPos = a_Player.GetPosition();
|
||||||
|
MoveToPosition(PlayerPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are in love mode but we have no partner, search for a partner neabry
|
||||||
|
if (m_LoveTimer > 0)
|
||||||
|
{
|
||||||
|
if (m_LovePartner == nullptr)
|
||||||
|
{
|
||||||
|
m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), 8, 8), [=](cEntity & a_Entity)
|
||||||
|
{
|
||||||
|
// If the entity is not a monster, don't breed with it
|
||||||
|
// Also, do not self-breed
|
||||||
|
if ((a_Entity.GetEntityType() != etMonster) || (&a_Entity == this))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto & Me = static_cast<cMonster &>(*this);
|
||||||
|
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() != 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
m_LoveTimer--;
|
||||||
|
}
|
||||||
|
if (m_MatingTimer > 0)
|
||||||
|
{
|
||||||
|
m_MatingTimer--;
|
||||||
|
}
|
||||||
|
if (m_LoveCooldown > 0)
|
||||||
|
{
|
||||||
|
m_LoveCooldown--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cMonster::RightClickFeed(cPlayer & 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 = TPS * 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Stores feeder UUID for statistic tracking
|
||||||
|
m_Feeder = a_Player.GetUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth)
|
void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth)
|
||||||
{
|
{
|
||||||
auto Count = GetRandomProvider().RandInt<unsigned int>(a_Min, a_Max);
|
auto Count = GetRandomProvider().RandInt<unsigned int>(a_Min, a_Max);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../Entities/Pawn.h"
|
#include "../Entities/Pawn.h"
|
||||||
|
#include "../UUID.h"
|
||||||
#include "MonsterTypes.h"
|
#include "MonsterTypes.h"
|
||||||
#include "PathFinder.h"
|
#include "PathFinder.h"
|
||||||
|
|
||||||
@ -217,6 +218,38 @@ public:
|
|||||||
/** Returns if this mob last target was a player to avoid destruction on player quit */
|
/** Returns if this mob last target was a player to avoid destruction on player quit */
|
||||||
bool WasLastTargetAPlayer() const { return m_WasLastTargetAPlayer; }
|
bool WasLastTargetAPlayer() const { return m_WasLastTargetAPlayer; }
|
||||||
|
|
||||||
|
/* the breeding processing */
|
||||||
|
|
||||||
|
/** 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(cMonster * a_Parent1, cMonster * a_Parent2) { }
|
||||||
|
|
||||||
|
/** 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 { 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); }
|
||||||
|
|
||||||
|
/** Does the whole love and breeding processing */
|
||||||
|
void LoveTick(void);
|
||||||
|
|
||||||
|
/** Right click call to process feeding */
|
||||||
|
void RightClickFeed(cPlayer & a_Player);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/** The pathfinder instance handles pathfinding for this monster. */
|
/** The pathfinder instance handles pathfinding for this monster. */
|
||||||
@ -330,6 +363,23 @@ protected:
|
|||||||
|
|
||||||
virtual void DoMoveToWorld(const cEntity::sWorldChangeInfo & a_WorldChangeInfo) override;
|
virtual void DoMoveToWorld(const cEntity::sWorldChangeInfo & a_WorldChangeInfo) override;
|
||||||
|
|
||||||
|
/* The breeding processing */
|
||||||
|
|
||||||
|
/** The monster's breeding partner. */
|
||||||
|
cMonster * m_LovePartner;
|
||||||
|
|
||||||
|
/** Remembers the player is was last fed by for statistics tracking */
|
||||||
|
cUUID m_Feeder;
|
||||||
|
|
||||||
|
/** 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;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** A pointer to the entity this mobile is aiming to reach.
|
/** A pointer to the entity this mobile is aiming to reach.
|
||||||
The validity of this pointer SHALL be guaranteed by the pointee;
|
The validity of this pointer SHALL be guaranteed by the pointee;
|
||||||
|
@ -11,11 +11,7 @@
|
|||||||
|
|
||||||
|
|
||||||
cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, const AString & a_SoundAmbient, double a_Width, double a_Height) :
|
cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, const AString & a_SoundAmbient, double a_Width, double a_Height) :
|
||||||
Super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_SoundAmbient, a_Width, a_Height),
|
Super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_SoundAmbient, a_Width, a_Height)
|
||||||
m_LovePartner(nullptr),
|
|
||||||
m_LoveTimer(0),
|
|
||||||
m_LoveCooldown(0),
|
|
||||||
m_MatingTimer(0)
|
|
||||||
{
|
{
|
||||||
m_EMPersonality = PASSIVE;
|
m_EMPersonality = PASSIVE;
|
||||||
}
|
}
|
||||||
@ -41,38 +37,8 @@ bool cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
m_Feeder = cUUID();
|
|
||||||
|
|
||||||
// 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::Destroyed()
|
void cPassiveMonster::Destroyed()
|
||||||
{
|
{
|
||||||
if (m_LovePartner != nullptr)
|
|
||||||
{
|
|
||||||
m_LovePartner->ResetLoveMode();
|
|
||||||
}
|
|
||||||
Super::Destroyed();
|
Super::Destroyed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,120 +60,7 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
|||||||
CheckEventLostPlayer();
|
CheckEventLostPlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we have a partner, mate
|
cMonster::LoveTick();
|
||||||
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);
|
|
||||||
|
|
||||||
cPassiveMonster * Baby = nullptr;
|
|
||||||
|
|
||||||
m_World->DoWithEntityByID(BabyID, [&](cEntity & a_Entity)
|
|
||||||
{
|
|
||||||
Baby = static_cast<cPassiveMonster *>(&a_Entity);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (Baby != nullptr)
|
|
||||||
{
|
|
||||||
Baby->InheritFromParents(this, m_LovePartner);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, GetRandomProvider().RandInt(1, 6));
|
|
||||||
|
|
||||||
m_World->DoWithPlayerByUUID(m_Feeder, [&] (cPlayer & a_Player)
|
|
||||||
{
|
|
||||||
a_Player.GetStatManager().AddValue(Statistic::AnimalsBred);
|
|
||||||
if (GetMobType() == eMonsterType::mtCow)
|
|
||||||
{
|
|
||||||
a_Player.AwardAchievement(Statistic::AchBreedCow);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
m_World->DoWithNearestPlayer(GetPosition(), static_cast<float>(m_SightDistance), [&](cPlayer & a_Player) -> bool
|
|
||||||
{
|
|
||||||
const cItem & EquippedItem = a_Player.GetEquippedItem();
|
|
||||||
if (FollowedItems.ContainsType(EquippedItem))
|
|
||||||
{
|
|
||||||
Vector3d PlayerPos = a_Player.GetPosition();
|
|
||||||
MoveToPosition(PlayerPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are in love mode but we have no partner, search for a partner neabry
|
|
||||||
if (m_LoveTimer > 0)
|
|
||||||
{
|
|
||||||
if (m_LovePartner == nullptr)
|
|
||||||
{
|
|
||||||
m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), 8, 8), [=](cEntity & a_Entity)
|
|
||||||
{
|
|
||||||
// If the entity is not a monster, don't breed with it
|
|
||||||
// Also, do not self-breed
|
|
||||||
if ((a_Entity.GetEntityType() != etMonster) || (&a_Entity == this))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto & Me = static_cast<cPassiveMonster&>(*this);
|
|
||||||
auto & 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;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_LoveTimer--;
|
|
||||||
}
|
|
||||||
if (m_MatingTimer > 0)
|
|
||||||
{
|
|
||||||
m_MatingTimer--;
|
|
||||||
}
|
|
||||||
if (m_LoveCooldown > 0)
|
|
||||||
{
|
|
||||||
m_LoveCooldown--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -217,42 +70,7 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
|||||||
void cPassiveMonster::OnRightClicked(cPlayer & a_Player)
|
void cPassiveMonster::OnRightClicked(cPlayer & a_Player)
|
||||||
{
|
{
|
||||||
Super::OnRightClicked(a_Player);
|
Super::OnRightClicked(a_Player);
|
||||||
|
Super::RightClickFeed(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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Stores feeder UUID for statistic tracking
|
|
||||||
m_Feeder = a_Player.GetUUID();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,47 +31,7 @@ public:
|
|||||||
/** When hit by someone, run away */
|
/** When hit by someone, run away */
|
||||||
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
|
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;
|
virtual void Destroyed(void) override;
|
||||||
|
|
||||||
protected:
|
|
||||||
/** The monster's breeding partner. */
|
|
||||||
cPassiveMonster * m_LovePartner;
|
|
||||||
|
|
||||||
/** Remembers the player is was last fed by for statistics tracking */
|
|
||||||
cUUID m_Feeder;
|
|
||||||
|
|
||||||
/** 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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ void cSheep::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cSheep::InheritFromParents(cPassiveMonster * a_Parent1, cPassiveMonster * a_Parent2)
|
void cSheep::InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2)
|
||||||
{
|
{
|
||||||
static const struct
|
static const struct
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ public:
|
|||||||
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
|
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
|
||||||
virtual void OnRightClicked(cPlayer & a_Player) override;
|
virtual void OnRightClicked(cPlayer & a_Player) override;
|
||||||
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) 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 InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2) override;
|
||||||
|
|
||||||
virtual void GetFollowedItems(cItems & a_Items) override
|
virtual void GetFollowedItems(cItems & a_Items) override
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "../World.h"
|
#include "../World.h"
|
||||||
#include "../Entities/Player.h"
|
#include "../Entities/Player.h"
|
||||||
#include "../Items/ItemHandler.h"
|
#include "../Items/ItemHandler.h"
|
||||||
|
#include "../Items/ItemSpawnEgg.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -16,7 +17,6 @@ cWolf::cWolf(void) :
|
|||||||
m_IsTame(false),
|
m_IsTame(false),
|
||||||
m_IsBegging(false),
|
m_IsBegging(false),
|
||||||
m_IsAngry(false),
|
m_IsAngry(false),
|
||||||
m_OwnerName(""),
|
|
||||||
m_CollarColor(E_META_DYE_ORANGE),
|
m_CollarColor(E_META_DYE_ORANGE),
|
||||||
m_NotificationCooldown(0)
|
m_NotificationCooldown(0)
|
||||||
{
|
{
|
||||||
@ -198,6 +198,10 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
|
|||||||
}
|
}
|
||||||
else if (IsTame())
|
else if (IsTame())
|
||||||
{
|
{
|
||||||
|
if (a_Player.GetUUID() == m_OwnerUUID)
|
||||||
|
{
|
||||||
|
cMonster::RightClickFeed(a_Player);
|
||||||
|
}
|
||||||
// Feed the wolf, restoring its health, or dye its collar:
|
// Feed the wolf, restoring its health, or dye its collar:
|
||||||
switch (EquippedItemType)
|
switch (EquippedItemType)
|
||||||
{
|
{
|
||||||
@ -208,6 +212,10 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
|
|||||||
case E_ITEM_RAW_CHICKEN:
|
case E_ITEM_RAW_CHICKEN:
|
||||||
case E_ITEM_COOKED_CHICKEN:
|
case E_ITEM_COOKED_CHICKEN:
|
||||||
case E_ITEM_ROTTEN_FLESH:
|
case E_ITEM_ROTTEN_FLESH:
|
||||||
|
case E_ITEM_RAW_MUTTON:
|
||||||
|
case E_ITEM_RAW_RABBIT:
|
||||||
|
case E_ITEM_COOKED_RABBIT:
|
||||||
|
case E_ITEM_COOKED_MUTTON:
|
||||||
{
|
{
|
||||||
if (m_Health < m_MaxHealth)
|
if (m_Health < m_MaxHealth)
|
||||||
{
|
{
|
||||||
@ -217,6 +225,13 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
|
|||||||
a_Player.GetInventory().RemoveOneEquippedItem();
|
a_Player.GetInventory().RemoveOneEquippedItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (a_Player.GetUUID() == m_OwnerUUID) // Is the player the owner of the dog?
|
||||||
|
{
|
||||||
|
if (IsBaby())
|
||||||
|
{
|
||||||
|
m_AgingTimer = FloorC(m_AgingTimer * 0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case E_ITEM_DYE:
|
case E_ITEM_DYE:
|
||||||
@ -231,6 +246,11 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Multiplication is handled in cMonster. Just prevents from sitting down.
|
||||||
|
case E_ITEM_SPAWN_EGG:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
if (a_Player.GetUUID() == m_OwnerUUID) // Is the player the owner of the dog?
|
if (a_Player.GetUUID() == m_OwnerUUID) // Is the player the owner of the dog?
|
||||||
@ -241,6 +261,21 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((EquippedItemType == E_ITEM_SPAWN_EGG) && (!IsTame()))
|
||||||
|
{
|
||||||
|
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_World->BroadcastEntityMetadata(*this);
|
m_World->BroadcastEntityMetadata(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,6 +372,8 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
|||||||
{
|
{
|
||||||
StopMovingToPosition();
|
StopMovingToPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cMonster::LoveTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -400,3 +437,29 @@ void cWolf::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cWolf::InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2)
|
||||||
|
{
|
||||||
|
const auto Parent1 = static_cast<cWolf *>(a_Parent1);
|
||||||
|
const auto Parent2 = static_cast<cWolf *>(a_Parent2);
|
||||||
|
if (Parent1->GetOwnerUUID() == Parent2->GetOwnerUUID())
|
||||||
|
{
|
||||||
|
SetOwner(Parent1->GetOwnerName(), Parent2->GetOwnerUUID());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto Parent1Age = Parent1->GetAge();
|
||||||
|
auto Parent2Age = Parent2->GetAge();
|
||||||
|
|
||||||
|
if (Parent1Age > Parent2Age)
|
||||||
|
{
|
||||||
|
SetOwner(Parent2->GetOwnerName(), Parent2->GetOwnerUUID());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetOwner(Parent1->GetOwnerName(), Parent1->GetOwnerUUID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -59,6 +59,23 @@ public:
|
|||||||
|
|
||||||
virtual void InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
|
virtual void InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
|
||||||
|
|
||||||
|
virtual void InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2) override;
|
||||||
|
virtual void GetBreedingItems(cItems & a_Items) override
|
||||||
|
{
|
||||||
|
a_Items.Add(E_ITEM_RAW_BEEF);
|
||||||
|
a_Items.Add(E_ITEM_STEAK);
|
||||||
|
a_Items.Add(E_ITEM_RAW_PORKCHOP);
|
||||||
|
a_Items.Add(E_ITEM_COOKED_PORKCHOP);
|
||||||
|
a_Items.Add(E_ITEM_RAW_CHICKEN);
|
||||||
|
a_Items.Add(E_ITEM_COOKED_CHICKEN);
|
||||||
|
a_Items.Add(E_ITEM_RAW_MUTTON);
|
||||||
|
a_Items.Add(E_ITEM_COOKED_MUTTON);
|
||||||
|
a_Items.Add(E_ITEM_RAW_RABBIT);
|
||||||
|
a_Items.Add(E_ITEM_COOKED_RABBIT);
|
||||||
|
a_Items.Add(E_ITEM_ROTTEN_FLESH);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
bool m_IsSitting;
|
bool m_IsSitting;
|
||||||
|
Loading…
Reference in New Issue
Block a user