1
0

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:
12xx12 2020-10-09 22:49:25 +02:00 committed by GitHub
parent 6fd35be67a
commit 32ee1708a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 352 additions and 233 deletions

View File

@ -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
/** Experience Orb setup */
@ -384,7 +399,6 @@ enum eMessageType
/** Returns a textual representation of the click action. */
const char * ClickActionToString(int a_ClickAction);

View File

@ -14,6 +14,8 @@
#include "../MonsterConfig.h"
#include "../BoundingBox.h"
#include "Items/ItemSpawnEgg.h"
#include "../Chunk.h"
#include "../FastRandom.h"
@ -22,8 +24,6 @@
/** Map for eType <-> string
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())
@ -109,12 +109,16 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_BurnsInDaylight(false)
, m_RelativeWalkSpeed(1)
, m_Age(1)
, m_AgingTimer(20 * 60 * 20) // about 20 minutes
, m_AgingTimer(TPS * 60 * 20) // about 20 minutes
, m_WasLastTargetAPlayer(false)
, m_LeashedTo(nullptr)
, m_LeashToPos(nullptr)
, m_IsLeashActionJustDone(false)
, m_CanBeLeashed(GetMobFamily() == eFamily::mfPassive)
, m_LovePartner(nullptr)
, m_LoveTimer(0)
, m_LoveCooldown(0)
, m_MatingTimer(0)
, m_Target(nullptr)
{
if (!a_ConfigName.empty())
@ -163,6 +167,10 @@ void cMonster::OnRemoveFromWorld(cWorld & a_World)
void cMonster::Destroyed()
{
SetTarget(nullptr); // Tell them we're no longer targeting them.
if (m_LovePartner != nullptr)
{
m_LovePartner->ResetLoveMode();
}
Super::Destroyed();
}
@ -896,7 +904,7 @@ void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
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)
{
auto Count = GetRandomProvider().RandInt<unsigned int>(a_Min, a_Max);

View File

@ -2,6 +2,7 @@
#pragma once
#include "../Entities/Pawn.h"
#include "../UUID.h"
#include "MonsterTypes.h"
#include "PathFinder.h"
@ -217,6 +218,38 @@ public:
/** Returns if this mob last target was a player to avoid destruction on player quit */
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:
/** The pathfinder instance handles pathfinding for this monster. */
@ -330,6 +363,23 @@ protected:
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:
/** A pointer to the entity this mobile is aiming to reach.
The validity of this pointer SHALL be guaranteed by the pointee;

View File

@ -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) :
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)
Super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_SoundAmbient, a_Width, a_Height)
{
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()
{
if (m_LovePartner != nullptr)
{
m_LovePartner->ResetLoveMode();
}
Super::Destroyed();
}
@ -94,120 +60,7 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
CheckEventLostPlayer();
}
// 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);
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--;
}
cMonster::LoveTick();
}
@ -217,42 +70,7 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
void cPassiveMonster::OnRightClicked(cPlayer & a_Player)
{
Super::OnRightClicked(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();
Super::RightClickFeed(a_Player);
}

View File

@ -31,47 +31,7 @@ public:
/** When hit by someone, run away */
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;
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;
};

View File

@ -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
{

View File

@ -25,7 +25,7 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
virtual void OnRightClicked(cPlayer & a_Player) 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
{

View File

@ -5,6 +5,7 @@
#include "../World.h"
#include "../Entities/Player.h"
#include "../Items/ItemHandler.h"
#include "../Items/ItemSpawnEgg.h"
@ -16,7 +17,6 @@ cWolf::cWolf(void) :
m_IsTame(false),
m_IsBegging(false),
m_IsAngry(false),
m_OwnerName(""),
m_CollarColor(E_META_DYE_ORANGE),
m_NotificationCooldown(0)
{
@ -198,6 +198,10 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
}
else if (IsTame())
{
if (a_Player.GetUUID() == m_OwnerUUID)
{
cMonster::RightClickFeed(a_Player);
}
// Feed the wolf, restoring its health, or dye its collar:
switch (EquippedItemType)
{
@ -208,6 +212,10 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
case E_ITEM_RAW_CHICKEN:
case E_ITEM_COOKED_CHICKEN:
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)
{
@ -217,6 +225,13 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
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;
}
case E_ITEM_DYE:
@ -231,6 +246,11 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
}
break;
}
// Multiplication is handled in cMonster. Just prevents from sitting down.
case E_ITEM_SPAWN_EGG:
{
break;
}
default:
{
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);
}
@ -337,6 +372,8 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
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());
}
}
}

View File

@ -59,6 +59,23 @@ public:
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:
bool m_IsSitting;