summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Defines.h16
-rw-r--r--src/Mobs/Monster.cpp205
-rw-r--r--src/Mobs/Monster.h50
-rw-r--r--src/Mobs/PassiveMonster.cpp188
-rw-r--r--src/Mobs/PassiveMonster.h40
-rw-r--r--src/Mobs/Sheep.cpp2
-rw-r--r--src/Mobs/Sheep.h2
-rw-r--r--src/Mobs/Wolf.cpp65
-rw-r--r--src/Mobs/Wolf.h17
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<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);
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<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);
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<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);
}
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<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());
+ }
+ }
+}
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;