summaryrefslogtreecommitdiffstats
path: root/src/Mobs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Mobs')
-rw-r--r--src/Mobs/Creeper.cpp2
-rw-r--r--src/Mobs/Horse.cpp28
-rw-r--r--src/Mobs/Monster.cpp173
-rw-r--r--src/Mobs/Monster.h48
-rw-r--r--src/Mobs/PassiveMonster.cpp2
5 files changed, 239 insertions, 14 deletions
diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp
index da4270af9..84b68cf7c 100644
--- a/src/Mobs/Creeper.cpp
+++ b/src/Mobs/Creeper.cpp
@@ -147,6 +147,8 @@ bool cCreeper::Attack(std::chrono::milliseconds a_Dt)
void cCreeper::OnRightClicked(cPlayer & a_Player)
{
+ super::OnRightClicked(a_Player);
+
if ((a_Player.GetEquippedItem().m_ItemType == E_ITEM_FLINT_AND_STEEL))
{
if (!a_Player.IsGameModeCreative())
diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp
index 24287ecc8..13630b0e3 100644
--- a/src/Mobs/Horse.cpp
+++ b/src/Mobs/Horse.cpp
@@ -132,24 +132,28 @@ void cHorse::OnRightClicked(cPlayer & a_Player)
}
else if (a_Player.GetEquippedItem().IsEmpty())
{
- if (m_Attachee != nullptr)
+ // Check if leashed / unleashed to player before try to ride
+ if (!m_IsLeashActionJustDone)
{
- if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
+ if (m_Attachee != nullptr)
{
- a_Player.Detach();
- return;
- }
+ if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
+ {
+ a_Player.Detach();
+ return;
+ }
- if (m_Attachee->IsPlayer())
- {
- return;
+ if (m_Attachee->IsPlayer())
+ {
+ return;
+ }
+
+ m_Attachee->Detach();
}
- m_Attachee->Detach();
+ m_TameAttemptTimes++;
+ a_Player.AttachTo(this);
}
-
- m_TameAttemptTimes++;
- a_Player.AttachTo(this);
}
else
{
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index 8077e41d6..d1c2413c3 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -11,11 +11,19 @@
#include "../Entities/Player.h"
#include "../Entities/ExpOrb.h"
#include "../MonsterConfig.h"
+#include "BoundingBox.h"
#include "../Chunk.h"
#include "../FastRandom.h"
#include "PathFinder.h"
+#include "../Entities/LeashKnot.h"
+
+
+
+
+// Ticks to wait to do leash calculations
+#define LEASH_ACTIONS_TICK_STEP 10
@@ -103,6 +111,10 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_Age(1)
, m_AgingTimer(20 * 60 * 20) // about 20 minutes
, m_WasLastTargetAPlayer(false)
+ , m_LeashedTo(nullptr)
+ , m_LeashToPos(nullptr)
+ , m_IsLeashActionJustDone(false)
+ , m_CanBeLeashed(GetMobFamily() == eFamily::mfPassive)
, m_Target(nullptr)
{
if (!a_ConfigName.empty())
@@ -124,6 +136,27 @@ cMonster::~cMonster()
+void cMonster::Destroy(bool a_ShouldBroadcast)
+{
+ if (IsLeashed())
+ {
+ cEntity * LeashedTo = GetLeashedTo();
+ Unleash(false, a_ShouldBroadcast);
+
+ // Remove leash knot if there are no more mobs leashed to
+ if (!LeashedTo->HasAnyMobLeashed() && LeashedTo->IsLeashKnot())
+ {
+ LeashedTo->Destroy();
+ }
+ }
+
+ super::Destroy(a_ShouldBroadcast);
+}
+
+
+
+
+
void cMonster::Destroyed()
{
SetTarget(nullptr); // Tell them we're no longer targeting them.
@@ -137,6 +170,11 @@ void cMonster::Destroyed()
void cMonster::SpawnOn(cClientHandle & a_Client)
{
a_Client.SendSpawnMob(*this);
+
+ if (IsLeashed())
+ {
+ a_Client.SendLeashEntity(*this, *this->GetLeashedTo());
+ }
}
@@ -201,6 +239,16 @@ void cMonster::MoveToWayPoint(cChunk & a_Chunk)
AddSpeedX(Distance.x);
AddSpeedZ(Distance.z);
}
+
+ // Speed up leashed mobs getting far from player
+ if (IsLeashed() && GetLeashedTo()->IsPlayer())
+ {
+ Distance = GetLeashedTo()->GetPosition() - GetPosition();
+ Distance.Normalize();
+ AddSpeedX(Distance.x);
+ AddSpeedZ(Distance.z);
+ }
+
}
@@ -283,7 +331,7 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
bool a_IsFollowingPath = false;
if (m_PathfinderActivated)
{
- if (ReachedFinalDestination())
+ if (ReachedFinalDestination() || (m_LeashToPos != nullptr))
{
StopMovingToPosition(); // Simply sets m_PathfinderActivated to false.
}
@@ -351,6 +399,12 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
case ATTACKING: break;
} // switch (m_EMState)
+ // Leash calculations
+ if ((m_TicksAlive % LEASH_ACTIONS_TICK_STEP) == 0)
+ {
+ CalcLeashActions();
+ }
+
BroadcastMovementUpdate();
if (m_AgingTimer > 0)
@@ -368,6 +422,39 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+void cMonster::CalcLeashActions()
+{
+ // This mob just spotted in the world and [m_LeashToPos not null] shows that should be leashed to a leash knot at m_LeashToPos.
+ // This keeps trying until knot is found. Leash knot may be in a different chunk that needn't or can't be loaded yet.
+ if (!IsLeashed() && (m_LeashToPos != nullptr))
+ {
+ auto LeashKnot = cLeashKnot::FindKnotAtPos(*m_World, { FloorC(m_LeashToPos->x), FloorC(m_LeashToPos->y), FloorC(m_LeashToPos->z) });
+ if (LeashKnot != nullptr)
+ {
+ LeashTo(LeashKnot);
+ SetLeashToPos(nullptr);
+ }
+ }
+ else if (IsLeashed()) // Mob is already leashed to an entity: follow it.
+ {
+ // TODO: leashed mobs in vanilla can move around up to 5 blocks distance from leash origin
+ MoveToPosition(m_LeashedTo->GetPosition());
+
+ // If distance to target > 10 break leash
+ Vector3f a_Distance(m_LeashedTo->GetPosition() - GetPosition());
+ double Distance(a_Distance.Length());
+ if (Distance > 10.0)
+ {
+ LOGD("Leash broken (distance)");
+ Unleash(false);
+ }
+ }
+}
+
+
+
+
+
void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath)
{
Vector3d BodyDistance;
@@ -583,6 +670,26 @@ void cMonster::OnRightClicked(cPlayer & a_Player)
a_Player.GetInventory().RemoveOneEquippedItem();
}
}
+
+ // Using leashes
+ m_IsLeashActionJustDone = false;
+ if (IsLeashed() && (GetLeashedTo() == &a_Player)) // a player can only unleash a mob leashed to him
+ {
+ Unleash(!a_Player.IsGameModeCreative());
+ }
+ else if (IsLeashed())
+ {
+ // Mob is already leashed but client anticipates the server action and draws a leash link, so we need to send current leash to cancel it
+ m_World->BroadcastLeashEntity(*this, *this->GetLeashedTo());
+ }
+ else if (CanBeLeashed() && (EquippedItem.m_ItemType == E_ITEM_LEASH))
+ {
+ if (!a_Player.IsGameModeCreative())
+ {
+ a_Player.GetInventory().RemoveOneEquippedItem();
+ }
+ LeashTo(&a_Player);
+ }
}
@@ -1295,3 +1402,67 @@ cMonster::eFamily cMonster::GetMobFamily(void) const
{
return FamilyFromType(m_MobType);
}
+
+
+
+
+
+void cMonster::LeashTo(cEntity * a_Entity, bool a_ShouldBroadcast)
+{
+ // Do nothing if already leashed
+ if (m_LeashedTo != nullptr)
+ {
+ return;
+ }
+
+ m_LeashedTo = a_Entity;
+
+ a_Entity->AddLeashedMob(this);
+
+ if (a_ShouldBroadcast)
+ {
+ m_World->BroadcastLeashEntity(*this, *a_Entity);
+ }
+
+ m_IsLeashActionJustDone = true;
+}
+
+
+
+
+
+void cMonster::Unleash(bool a_ShouldDropLeashPickup, bool a_ShouldBroadcast)
+{
+ // Do nothing if not leashed
+ if (m_LeashedTo == nullptr)
+ {
+ return;
+ }
+
+ m_LeashedTo->RemoveLeashedMob(this);
+
+ m_LeashedTo = nullptr;
+
+ if (a_ShouldDropLeashPickup)
+ {
+ cItems Pickups;
+ Pickups.Add(cItem(E_ITEM_LEASH, 1, 0));
+ GetWorld()->SpawnItemPickups(Pickups, GetPosX() + 0.5, GetPosY() + 0.5, GetPosZ() + 0.5);
+ }
+
+ if (a_ShouldBroadcast)
+ {
+ m_World->BroadcastUnleashEntity(*this);
+ }
+
+ m_IsLeashActionJustDone = true;
+}
+
+
+
+
+
+void cMonster::Unleash(bool a_ShouldDropLeashPickup)
+{
+ Unleash(a_ShouldDropLeashPickup, true);
+}
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index 268db6168..ab5b2cf2f 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -43,6 +43,8 @@ public:
virtual ~cMonster() override;
+ virtual void Destroy(bool a_ShouldBroadcast = true) override;
+
virtual void Destroyed() override;
CLASS_PROTODEF(cMonster)
@@ -71,6 +73,37 @@ public:
virtual void CheckEventSeePlayer(cChunk & a_Chunk);
virtual void EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk);
+ // tolua_begin
+
+ /** Returns whether the mob can be leashed. */
+ bool CanBeLeashed() const { return m_CanBeLeashed; }
+
+ /** Sets whether the mob can be leashed, for extensibility in plugins */
+ void SetCanBeLeashed(bool a_CanBeLeashed) { m_CanBeLeashed = a_CanBeLeashed; }
+
+ /** Returns whether the monster is leashed to an entity. */
+ bool IsLeashed() const { return (m_LeashedTo != nullptr); }
+
+ /** Leash the monster to an entity. */
+ void LeashTo(cEntity * a_Entity, bool a_ShouldBroadcast = true);
+
+ /** Unleash the monster. Overload for the Unleash(bool, bool) function for plugins */
+ void Unleash(bool a_ShouldDropLeashPickup);
+
+ /** Returns the entity to where this mob is leashed, returns nullptr if it's not leashed */
+ cEntity * GetLeashedTo() const { return m_LeashedTo; }
+
+ // tolua_end
+
+ /** Unleash the monster. */
+ void Unleash(bool a_ShouldDropLeashPickup, bool a_ShouldBroadcast);
+
+ /** Sets entity position to where is leashed this mob */
+ void SetLeashToPos(Vector3d * pos) { m_LeashToPos = std::unique_ptr<Vector3d>(pos); }
+
+ /** Gets entity position to where mob should be leashed */
+ Vector3d * GetLeashToPos() const { return m_LeashToPos.get(); }
+
/** Reads the monster configuration for the specified monster name and assigns it to this object. */
void GetMonsterConfig(const AString & a_Name);
@@ -260,6 +293,18 @@ protected:
bool m_WasLastTargetAPlayer;
+ /** Entity leashed to */
+ cEntity * m_LeashedTo;
+
+ /** Entity pos where this mob was leashed to. Used when deserializing the chunk in order to make the mob find the leash knot. */
+ std::unique_ptr<Vector3d> m_LeashToPos;
+
+ /** Mob has ben leashed or unleashed in current player action. Avoids double actions on horses. */
+ bool m_IsLeashActionJustDone;
+
+ /** Determines whether a monster can be leashed */
+ bool m_CanBeLeashed;
+
/** 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);
@@ -281,4 +326,7 @@ private:
it MUST be reset when the pointee changes worlds or is destroyed. */
cPawn * m_Target;
+ /** Leash calculations inside Tick function */
+ void CalcLeashActions();
+
} ; // tolua_export
diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp
index 73defaf7f..a2089e13f 100644
--- a/src/Mobs/PassiveMonster.cpp
+++ b/src/Mobs/PassiveMonster.cpp
@@ -201,7 +201,7 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
} Callback(this);
- m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), 8, 8), Callback);
+ m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), 8, 8, -4), Callback);
}
m_LoveTimer--;