summaryrefslogtreecommitdiffstats
path: root/src/Mobs/Monster.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/Mobs/Monster.cpp173
1 files changed, 172 insertions, 1 deletions
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);
+}