1
0

Fully implemented leashes (#3798)

This commit is contained in:
Pablo Beltrán
2017-08-21 10:46:41 +02:00
committed by Mattes D
parent f81e6f6b6d
commit b18f6637b6
42 changed files with 1022 additions and 21 deletions

View File

@@ -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);
}