From 32ee1708a24836b26cd700eb42ad8264a3ecae83 Mon Sep 17 00:00:00 2001 From: 12xx12 <44411062+12xx12@users.noreply.github.com> Date: Fri, 9 Oct 2020 22:49:25 +0200 Subject: 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> --- src/Defines.h | 16 +++- src/Mobs/Monster.cpp | 205 +++++++++++++++++++++++++++++++++++++++++++- src/Mobs/Monster.h | 50 +++++++++++ src/Mobs/PassiveMonster.cpp | 188 +--------------------------------------- src/Mobs/PassiveMonster.h | 40 --------- src/Mobs/Sheep.cpp | 2 +- src/Mobs/Sheep.h | 2 +- src/Mobs/Wolf.cpp | 65 +++++++++++++- src/Mobs/Wolf.h | 17 ++++ 9 files changed, 352 insertions(+), 233 deletions(-) diff --git a/src/Defines.h b/src/Defines.h index a9714c52f..dc2835517 100644 --- a/src/Defines.h +++ b/src/Defines.h @@ -12,6 +12,21 @@ typedef std::vector 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); diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index ec240b61c..740223bfa 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -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(20 * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every second + m_AttackCoolDownTicksLeft = static_cast(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::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(&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(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(*this); + auto & PotentialPartner = static_cast(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(a_Min, a_Max); diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 89c9871e9..edd0a96c3 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -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; diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp index 1a8aaa3bf..1843ceb73 100644 --- a/src/Mobs/PassiveMonster.cpp +++ b/src/Mobs/PassiveMonster.cpp @@ -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(&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(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(*this); - auto & PotentialPartner = static_cast(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); } diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h index 67d7399d8..15900a95d 100644 --- a/src/Mobs/PassiveMonster.h +++ b/src/Mobs/PassiveMonster.h @@ -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; }; diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp index 2a6d27969..7bcfe1836 100644 --- a/src/Mobs/Sheep.cpp +++ b/src/Mobs/Sheep.cpp @@ -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 { diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h index c0a83f3ec..79f94731c 100644 --- a/src/Mobs/Sheep.h +++ b/src/Mobs/Sheep.h @@ -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 { diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp index e2be2a8d9..c40dbf5e3 100644 --- a/src/Mobs/Wolf.cpp +++ b/src/Mobs/Wolf.cpp @@ -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(a_Parent1); + const auto Parent2 = static_cast(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()); + } + } +} diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h index 6ce708a52..d3b689ef3 100644 --- a/src/Mobs/Wolf.h +++ b/src/Mobs/Wolf.h @@ -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; -- cgit v1.2.3