summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSafwat Halaby <SafwatHalaby@users.noreply.github.com>2015-11-30 07:58:02 +0100
committerSafwat Halaby <SafwatHalaby@users.noreply.github.com>2015-11-30 07:58:02 +0100
commit1a9c023d6c69d5aa37ed0d3726e0f4af77b974f9 (patch)
tree0da79374832cae75ae5d9e6822712b3b62cbdbe8
parentUpdated DumpInfo plugin (diff)
parentimplement breeding (diff)
downloadcuberite-1a9c023d6c69d5aa37ed0d3726e0f4af77b974f9.tar
cuberite-1a9c023d6c69d5aa37ed0d3726e0f4af77b974f9.tar.gz
cuberite-1a9c023d6c69d5aa37ed0d3726e0f4af77b974f9.tar.bz2
cuberite-1a9c023d6c69d5aa37ed0d3726e0f4af77b974f9.tar.lz
cuberite-1a9c023d6c69d5aa37ed0d3726e0f4af77b974f9.tar.xz
cuberite-1a9c023d6c69d5aa37ed0d3726e0f4af77b974f9.tar.zst
cuberite-1a9c023d6c69d5aa37ed0d3726e0f4af77b974f9.zip
-rw-r--r--src/Entities/Entity.h2
-rw-r--r--src/Mobs/Cow.cpp6
-rw-r--r--src/Mobs/Horse.cpp2
-rw-r--r--src/Mobs/Horse.h6
-rw-r--r--src/Mobs/Monster.cpp11
-rw-r--r--src/Mobs/Monster.h1
-rw-r--r--src/Mobs/Ocelot.h5
-rw-r--r--src/Mobs/PassiveMonster.cpp149
-rw-r--r--src/Mobs/PassiveMonster.h18
-rw-r--r--src/Mobs/Pig.cpp2
-rw-r--r--src/Protocol/Protocol18x.cpp26
11 files changed, 209 insertions, 19 deletions
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index 63dfabb53..c4f66893d 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -126,6 +126,8 @@ public:
// Informs client to explode a firework based on its metadata
esFireworkExploding = 17,
+ // Passive mob is in "love mode"
+ esMobInLove = 18,
} ;
static const int FIRE_TICKS_PER_DAMAGE = 10; ///< Ticks to wait between damaging an entity when it stands in fire
diff --git a/src/Mobs/Cow.cpp b/src/Mobs/Cow.cpp
index a45010201..dec8eface 100644
--- a/src/Mobs/Cow.cpp
+++ b/src/Mobs/Cow.cpp
@@ -36,7 +36,10 @@ void cCow::GetDrops(cItems & a_Drops, cEntity * a_Killer)
void cCow::OnRightClicked(cPlayer & a_Player)
{
- if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_BUCKET))
+ super::OnRightClicked(a_Player);
+
+ short HeldItem = a_Player.GetEquippedItem().m_ItemType;
+ if (HeldItem == E_ITEM_BUCKET)
{
if (!a_Player.IsGameModeCreative())
{
@@ -45,4 +48,3 @@ void cCow::OnRightClicked(cPlayer & a_Player)
}
}
}
-
diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp
index 8b76d7c50..a338f12bd 100644
--- a/src/Mobs/Horse.cpp
+++ b/src/Mobs/Horse.cpp
@@ -93,6 +93,8 @@ void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
void cHorse::OnRightClicked(cPlayer & a_Player)
{
+ super::OnRightClicked(a_Player);
+
if (!m_bIsSaddled && m_bIsTame)
{
if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_SADDLE)
diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h
index 27168ebae..987ab71a9 100644
--- a/src/Mobs/Horse.h
+++ b/src/Mobs/Horse.h
@@ -32,6 +32,12 @@ public:
int GetHorseStyle (void) const {return m_Style; }
int GetHorseArmour (void) const {return m_Armour;}
+ virtual void GetBreedingItems(cItems & a_Items) override
+ {
+ a_Items.Add(E_ITEM_GOLDEN_CARROT);
+ a_Items.Add(E_ITEM_GOLDEN_APPLE);
+ }
+
private:
bool m_bHasChest, m_bIsEating, m_bIsRearing, m_bIsMouthOpen, m_bIsTame, m_bIsSaddled;
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index 060b934ec..d1173c41c 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -104,6 +104,7 @@ 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
{
if (!a_ConfigName.empty())
{
@@ -505,6 +506,16 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
} // switch (m_EMState)
BroadcastMovementUpdate();
+
+ if (m_AgingTimer > 0)
+ {
+ m_AgingTimer--;
+ if ((m_AgingTimer <= 0) && IsBaby())
+ {
+ SetAge(1);
+ m_World->BroadcastEntityMetadata(*this);
+ }
+ }
}
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index 963ac9148..7b6c0c488 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -280,6 +280,7 @@ protected:
double m_RelativeWalkSpeed;
int m_Age;
+ int m_AgingTimer;
/** Adds a random number of a_Item between a_Min and a_Max to itemdrops a_Drops */
void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0);
diff --git a/src/Mobs/Ocelot.h b/src/Mobs/Ocelot.h
index f2727d354..796af2050 100644
--- a/src/Mobs/Ocelot.h
+++ b/src/Mobs/Ocelot.h
@@ -18,6 +18,11 @@ public:
{
}
+ virtual void GetBreedingItems(cItems & a_Items) override
+ {
+ a_Items.Add(E_ITEM_RAW_FISH);
+ }
+
CLASS_PROTODEF(cOcelot)
} ;
diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp
index a3d51da35..e39f6e23d 100644
--- a/src/Mobs/PassiveMonster.cpp
+++ b/src/Mobs/PassiveMonster.cpp
@@ -4,12 +4,17 @@
#include "PassiveMonster.h"
#include "../World.h"
#include "../Entities/Player.h"
+#include "BoundingBox.h"
cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
- super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height)
+ super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height),
+ m_LovePartner(nullptr),
+ m_LoveTimer(0),
+ m_LoveCooldown(0),
+ m_MatingTimer(0)
{
m_EMPersonality = PASSIVE;
}
@@ -35,6 +40,31 @@ 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
+
+ // 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::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
@@ -43,25 +73,122 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
CheckEventLostPlayer();
}
- cItems FollowedItems;
- GetFollowedItems(FollowedItems);
- if (FollowedItems.Size() <= 0)
+ if ((m_LovePartner != nullptr) && m_LovePartner->IsDestroyed())
{
- return;
+ m_LovePartner = nullptr;
}
- cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance));
- if (a_Closest_Player != nullptr)
+ if (m_LovePartner != nullptr)
{
- cItem EquippedItem = a_Closest_Player->GetEquippedItem();
- if (FollowedItems.ContainsType(EquippedItem))
+ // if we have a partner, bump into them until baby is made
+ if (m_MatingTimer > 0)
{
- Vector3d PlayerPos = a_Closest_Player->GetPosition();
- MoveToPosition(PlayerPos);
+ Vector3d Pos = m_LovePartner->GetPosition();
+ MoveToPosition(Pos);
+ }
+ else
+ {
+ // spawn baby
+ Vector3f Pos = (GetPosition() + m_LovePartner->GetPosition()) * 0.5;
+ m_World->SpawnMob(Pos.x, Pos.y, Pos.z, GetMobType(), true);
+
+ cFastRandom Random;
+ m_World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, 1 + Random.NextInt(6));
+
+ m_LovePartner->ResetLoveMode();
+ ResetLoveMode();
}
}
+ else
+ {
+ cItems FollowedItems;
+ GetFollowedItems(FollowedItems);
+ if (FollowedItems.Size() > 0)
+ {
+ cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance));
+ if (a_Closest_Player != nullptr)
+ {
+ cItem EquippedItem = a_Closest_Player->GetEquippedItem();
+ if (FollowedItems.ContainsType(EquippedItem))
+ {
+ Vector3d PlayerPos = a_Closest_Player->GetPosition();
+ MoveToPosition(PlayerPos);
+ }
+ }
+ }
+ }
+
+ if (m_LoveTimer > 0)
+ {
+ if (m_LovePartner == nullptr)
+ {
+ class LookForLover : public cEntityCallback
+ {
+ public:
+ cEntity * m_Me;
+
+ LookForLover(cEntity * a_Me) :
+ m_Me(a_Me)
+ {
+ }
+
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ // if we're the same species as someone around and they dont have a partner, swipe right
+ if ((a_Entity->GetEntityType() == m_Me->GetEntityType()) && (a_Entity != m_Me))
+ {
+ cPassiveMonster * Me = static_cast<cPassiveMonster*>(m_Me);
+ cPassiveMonster * Partner = static_cast<cPassiveMonster*>(a_Entity);
+ if (Partner->IsInLove() && (Partner->GetPartner() == nullptr))
+ {
+ Partner->EngageLoveMode(Me);
+ Me->EngageLoveMode(Partner);
+ return true;
+ }
+ }
+ return false;
+ }
+ } Callback(this);
+
+ m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), 8, 8), Callback);
+ }
+
+ m_LoveTimer--;
+ }
+ if (m_MatingTimer > 0)
+ {
+ m_MatingTimer--;
+ }
+ if (m_LoveCooldown > 0)
+ {
+ m_LoveCooldown--;
+ }
}
+void cPassiveMonster::OnRightClicked(cPlayer & a_Player)
+{
+ super::OnRightClicked(a_Player);
+
+ // if right clicked on the player with breeding items, go into lovemode
+ if ((m_LoveCooldown == 0) && !IsInLove() && !IsBaby())
+ {
+ short HeldItem = a_Player.GetEquippedItem().m_ItemType;
+ cItems Items;
+ GetBreedingItems(Items);
+ if (Items.ContainsType(HeldItem))
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ m_LoveTimer = 20 * 30; // half a minute
+ m_World->BroadcastEntityStatus(*this, esMobInLove);
+ }
+ }
+}
+
+
+
diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h
index a7e574a1d..ecce4ceb6 100644
--- a/src/Mobs/PassiveMonster.h
+++ b/src/Mobs/PassiveMonster.h
@@ -16,6 +16,7 @@ public:
cPassiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+ virtual void OnRightClicked(cPlayer & a_Player) override;
/** When hit by someone, run away */
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
@@ -23,7 +24,22 @@ public:
/** 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); }
+
+ cPassiveMonster * GetPartner(void) const { return m_LovePartner; }
+ void EngageLoveMode(cPassiveMonster * a_Partner);
+ void ResetLoveMode();
+
+ bool IsInLove() const { return (m_LoveTimer > 0); }
+ bool IsInLoveCooldown() const { return (m_LoveCooldown > 0); }
+
+protected:
+ cPassiveMonster * m_LovePartner;
+ int m_LoveTimer;
+ int m_LoveCooldown;
+ int m_MatingTimer;
+};
diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp
index 21c8e923a..b67b29d87 100644
--- a/src/Mobs/Pig.cpp
+++ b/src/Mobs/Pig.cpp
@@ -39,6 +39,8 @@ void cPig::GetDrops(cItems & a_Drops, cEntity * a_Killer)
void cPig::OnRightClicked(cPlayer & a_Player)
{
+ super::OnRightClicked(a_Player);
+
if (m_bIsSaddled)
{
if (m_Attachee != nullptr)
diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp
index 373269509..833746c00 100644
--- a/src/Protocol/Protocol18x.cpp
+++ b/src/Protocol/Protocol18x.cpp
@@ -3368,7 +3368,7 @@ void cProtocol180::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob)
a_Pkt.WriteBEUInt8(0x56); // Int at index 22
a_Pkt.WriteBEInt32(Horse.GetHorseArmour());
a_Pkt.WriteBEUInt8(0x0c);
- a_Pkt.WriteBEInt8(Horse.IsBaby() ? -1 : 0);
+ a_Pkt.WriteBEInt8(Horse.IsBaby() ? -1 : (Horse.IsInLoveCooldown() ? 1 : 0));
break;
} // case mtHorse
@@ -3384,15 +3384,31 @@ void cProtocol180::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob)
{
auto & Ocelot = reinterpret_cast<const cOcelot &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
- a_Pkt.WriteBEInt8(Ocelot.IsBaby() ? -1 : 0);
+ a_Pkt.WriteBEInt8(Ocelot.IsBaby() ? -1 : (Ocelot.IsInLoveCooldown() ? 1 : 0));
break;
} // case mtOcelot
+ case mtCow:
+ {
+ auto & Cow = reinterpret_cast<const cCow &>(a_Mob);
+ a_Pkt.WriteBEUInt8(0x0c);
+ a_Pkt.WriteBEInt8(Cow.IsBaby() ? -1 : (Cow.IsInLoveCooldown() ? 1 : 0));
+ break;
+ } // case mtCow
+
+ case mtChicken:
+ {
+ auto & Chicken = reinterpret_cast<const cChicken &>(a_Mob);
+ a_Pkt.WriteBEUInt8(0x0c);
+ a_Pkt.WriteBEInt8(Chicken.IsBaby() ? -1 : (Chicken.IsInLoveCooldown() ? 1 : 0));
+ break;
+ } // case mtChicken
+
case mtPig:
{
auto & Pig = reinterpret_cast<const cPig &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
- a_Pkt.WriteBEInt8(Pig.IsBaby() ? -1 : 0);
+ a_Pkt.WriteBEInt8(Pig.IsBaby() ? -1 : (Pig.IsInLoveCooldown() ? 1 : 0));
a_Pkt.WriteBEUInt8(0x10);
a_Pkt.WriteBEUInt8(Pig.IsSaddled() ? 1 : 0);
break;
@@ -3402,7 +3418,7 @@ void cProtocol180::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob)
{
auto & Sheep = reinterpret_cast<const cSheep &>(a_Mob);
a_Pkt.WriteBEUInt8(0x0c);
- a_Pkt.WriteBEInt8(Sheep.IsBaby() ? -1 : 0);
+ a_Pkt.WriteBEInt8(Sheep.IsBaby() ? -1 : (Sheep.IsInLoveCooldown() ? 1 : 0));
a_Pkt.WriteBEUInt8(0x10);
Byte SheepMetadata = 0;
@@ -3421,7 +3437,7 @@ void cProtocol180::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob)
a_Pkt.WriteBEUInt8(0x12);
a_Pkt.WriteBEUInt8(Rabbit.GetRabbitTypeAsNumber());
a_Pkt.WriteBEUInt8(0x0c);
- a_Pkt.WriteBEInt8(Rabbit.IsBaby() ? -1 : 0);
+ a_Pkt.WriteBEInt8(Rabbit.IsBaby() ? -1 : (Rabbit.IsInLoveCooldown() ? 1 : 0));
break;
} // case mtRabbit