summaryrefslogtreecommitdiffstats
path: root/src/Entities
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/Entities/CMakeLists.txt2
-rw-r--r--src/Entities/Entity.cpp36
-rw-r--r--src/Entities/Entity.h20
-rw-r--r--src/Entities/LeashKnot.cpp185
-rw-r--r--src/Entities/LeashKnot.h50
5 files changed, 292 insertions, 1 deletions
diff --git a/src/Entities/CMakeLists.txt b/src/Entities/CMakeLists.txt
index 488c8da59..aaab6ebe4 100644
--- a/src/Entities/CMakeLists.txt
+++ b/src/Entities/CMakeLists.txt
@@ -17,6 +17,7 @@ SET (SRCS
GhastFireballEntity.cpp
HangingEntity.cpp
ItemFrame.cpp
+ LeashKnot.cpp
Minecart.cpp
Painting.cpp
Pawn.cpp
@@ -45,6 +46,7 @@ SET (HDRS
GhastFireballEntity.h
HangingEntity.h
ItemFrame.h
+ LeashKnot.h
Minecart.h
Painting.h
Pawn.h
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index a38a6552d..56f7b33a3 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -159,6 +159,15 @@ bool cEntity::Initialize(OwnedEntity a_Self, cWorld & a_EntityWorld)
// Spawn the entity on the clients:
a_EntityWorld.BroadcastSpawnEntity(*this);
+ // If has any mob leashed broadcast every leashed entity to this
+ if (HasAnyMobLeashed())
+ {
+ for (auto LeashedMob : m_LeashedMobs)
+ {
+ m_World->BroadcastLeashEntity(*LeashedMob, *this);
+ }
+ }
+
return true;
}
@@ -218,6 +227,12 @@ void cEntity::Destroy(bool a_ShouldBroadcast)
ASSERT(GetParentChunk() != nullptr);
SetIsTicking(false);
+ // Unleash leashed mobs
+ while (!m_LeashedMobs.empty())
+ {
+ m_LeashedMobs.front()->Unleash(true, true);
+ }
+
if (a_ShouldBroadcast)
{
m_World->BroadcastDestroyEntity(*this);
@@ -2195,3 +2210,24 @@ void cEntity::SetPosition(const Vector3d & a_Position)
+
+void cEntity::AddLeashedMob(cMonster * a_Monster)
+{
+ // Not there already
+ ASSERT(std::find(m_LeashedMobs.begin(), m_LeashedMobs.end(), a_Monster) == m_LeashedMobs.end());
+
+ m_LeashedMobs.push_back(a_Monster);
+}
+
+
+
+
+void cEntity::RemoveLeashedMob(cMonster * a_Monster)
+{
+ ASSERT(a_Monster->GetLeashedTo() == this);
+
+ // Must exists
+ ASSERT(std::find(m_LeashedMobs.begin(), m_LeashedMobs.end(), a_Monster) != m_LeashedMobs.end());
+
+ m_LeashedMobs.remove(a_Monster);
+}
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index 8f433b816..db147f3fc 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -45,6 +45,7 @@ class cWorld;
class cClientHandle;
class cPlayer;
class cChunk;
+class cMonster;
@@ -87,6 +88,7 @@ public:
etFloater,
etItemFrame,
etPainting,
+ etLeashKnot,
// Common variations
etMob = etMonster, // DEPRECATED, use etMonster instead!
@@ -176,6 +178,7 @@ public:
bool IsExpOrb (void) const { return (m_EntityType == etExpOrb); }
bool IsFloater (void) const { return (m_EntityType == etFloater); }
bool IsItemFrame (void) const { return (m_EntityType == etItemFrame); }
+ bool IsLeashKnot (void) const { return (m_EntityType == etLeashKnot); }
bool IsPainting (void) const { return (m_EntityType == etPainting); }
/** Returns true if the entity is of the specified class or a subclass (cPawn's IsA("cEntity") returns true) */
@@ -267,7 +270,7 @@ public:
bool IsTicking(void) const;
/** Destroys the entity and schedules it for memory freeing; if a_ShouldBroadcast is set to true, broadcasts the DestroyEntity packet */
- void Destroy(bool a_ShouldBroadcast = true);
+ virtual void Destroy(bool a_ShouldBroadcast = true);
/** Makes this pawn take damage from an attack by a_Attacker. Damage values are calculated automatically and DoTakeDamage() called */
void TakeDamage(cEntity & a_Attacker);
@@ -519,6 +522,15 @@ public:
/** Set the entity's status to either ticking or not ticking. */
void SetIsTicking(bool a_IsTicking);
+ /** Adds a mob to the leashed list of mobs */
+ void AddLeashedMob(cMonster * a_Monster);
+
+ /** Removes a mob from the leashed list of mobs */
+ void RemoveLeashedMob(cMonster * a_Monster);
+
+ /** Returs whether the entity has any mob leashed to */
+ bool HasAnyMobLeashed() const { return m_LeashedMobs.size() > 0; }
+
protected:
/** Structure storing the portal delay timer and cooldown boolean */
struct sPortalCooldownData
@@ -668,6 +680,12 @@ private:
/** If a player hit a entity, the entity receive a invulnerable of 10 ticks.
While this ticks, a player can't hit this entity. */
int m_InvulnerableTicks;
+
+ typedef std::list<cMonster *> cMonsterList;
+
+ /** List of leashed mobs to this entity */
+ cMonsterList m_LeashedMobs;
+
} ; // tolua_export
diff --git a/src/Entities/LeashKnot.cpp b/src/Entities/LeashKnot.cpp
new file mode 100644
index 000000000..52bb1b4b3
--- /dev/null
+++ b/src/Entities/LeashKnot.cpp
@@ -0,0 +1,185 @@
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "LeashKnot.h"
+#include "ClientHandle.h"
+#include "Player.h"
+#include "Mobs/Monster.h"
+#include "BoundingBox.h"
+
+// Ticks to wait in Tick function to optimize calculations
+#define TICK_STEP 10
+
+
+
+
+
+cLeashKnot::cLeashKnot(eBlockFace a_BlockFace, double a_X, double a_Y, double a_Z) :
+ cHangingEntity(etLeashKnot, a_BlockFace, a_X, a_Y, a_Z),
+ m_ShouldSelfDestroy(false),
+ m_TicksToSelfDestroy(20 * 1)
+{
+}
+
+
+
+
+
+void cLeashKnot::OnRightClicked(cPlayer & a_Player)
+{
+ super::OnRightClicked(a_Player);
+
+ TiePlayersLeashedMobs(a_Player, true);
+
+ GetWorld()->BroadcastEntityMetadata(*this); // Update clients
+}
+
+
+
+
+
+void cLeashKnot::TiePlayersLeashedMobs(cPlayer & a_Player, bool a_ShouldBroadCast)
+{
+ // Check leashed nearby mobs to tie them to this knot
+ class LookForLeasheds : public cEntityCallback
+ {
+ public:
+ cLeashKnot * m_Knot;
+ cPlayer * m_Player;
+ bool m_ShouldBroadcast;
+
+ LookForLeasheds(cLeashKnot * a_Knot, cPlayer * a_PlayerLeashedTo, bool a_ShouldBroadcast) :
+ m_Knot(a_Knot),
+ m_Player(a_PlayerLeashedTo),
+ m_ShouldBroadcast(a_ShouldBroadcast)
+ {
+ }
+
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ // If the entity is not a monster skip it
+ if (a_Entity->GetEntityType() != cEntity::eEntityType::etMonster)
+ {
+ return false;
+ }
+
+ cMonster * PotentialLeashed = static_cast<cMonster*>(a_Entity);
+
+ // If can't be leashed skip it
+ if (!PotentialLeashed->CanBeLeashed())
+ {
+ return false;
+ }
+
+ // If it's not leashed to the player skip it
+ if (
+ !PotentialLeashed->IsLeashed() ||
+ !PotentialLeashed->GetLeashedTo()->IsPlayer() ||
+ (PotentialLeashed->GetLeashedTo()->GetUniqueID() != m_Player->GetUniqueID())
+ )
+ {
+ return false;
+ }
+
+ // All conditions met, unleash from player and leash to fence
+ PotentialLeashed->Unleash(false, false);
+ PotentialLeashed->LeashTo(m_Knot, m_ShouldBroadcast);
+ return false;
+ }
+ } LookForLeashedsCallback(this, &a_Player, a_ShouldBroadCast);
+
+ // taking world from player (instead from this) because this can be called before entity was initialized
+ a_Player.GetWorld()->ForEachEntityInBox(cBoundingBox(GetPosition(), 8, 8, -4), LookForLeashedsCallback);
+}
+
+
+
+
+
+
+void cLeashKnot::KilledBy(TakeDamageInfo & a_TDI)
+{
+ super::KilledBy(a_TDI);
+ m_World->BroadcastSoundEffect("entity.leashknot.break", GetPosX(), GetPosY(), GetPosZ(), 1, 1);
+ Destroy();
+ return;
+}
+
+
+
+
+
+void cLeashKnot::GetDrops(cItems & a_Items, cEntity * a_Killer)
+{
+ if ((a_Killer != nullptr) && a_Killer->IsPlayer() && !static_cast<cPlayer *>(a_Killer)->IsGameModeCreative())
+ {
+ a_Items.push_back(cItem(E_ITEM_LEASH));
+ }
+}
+
+
+
+
+
+void cLeashKnot::SpawnOn(cClientHandle & a_ClientHandle)
+{
+ super::SpawnOn(a_ClientHandle);
+ a_ClientHandle.SendSpawnObject(*this, 77, GetProtocolFacing(), static_cast<Byte>(GetYaw()), static_cast<Byte>(GetPitch()));
+ a_ClientHandle.SendEntityMetadata(*this);
+}
+
+
+
+
+void cLeashKnot::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
+{
+ m_TicksAlive++;
+
+ if ((m_TicksAlive % TICK_STEP) != 0)
+ {
+ return;
+ }
+
+ if (m_ShouldSelfDestroy)
+ {
+ m_TicksToSelfDestroy -= TICK_STEP;
+
+ if (m_TicksToSelfDestroy <= 0)
+ {
+ Destroy();
+ m_World->BroadcastSoundEffect("entity.leashknot.break", GetPosX(), GetPosY(), GetPosZ(), 1, 1);
+ }
+ }
+}
+
+
+
+
+
+cLeashKnot * cLeashKnot::FindKnotAtPos(cWorldInterface & a_WorldInterface, Vector3i a_BlockPos)
+{
+ class LookForKnot : public cEntityCallback
+ {
+ public:
+ cLeashKnot * m_LeashKnot = nullptr;
+
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ if (a_Entity->IsLeashKnot())
+ {
+ m_LeashKnot = reinterpret_cast<cLeashKnot *>(a_Entity);
+ return true;
+ }
+ return false;
+ }
+
+ } CallbackFindKnot;
+
+ a_WorldInterface.ForEachEntityInBox(cBoundingBox(a_BlockPos, 0.5, 1), CallbackFindKnot);
+
+ return CallbackFindKnot.m_LeashKnot;
+}
+
+
+
+
diff --git a/src/Entities/LeashKnot.h b/src/Entities/LeashKnot.h
new file mode 100644
index 000000000..1a854ef34
--- /dev/null
+++ b/src/Entities/LeashKnot.h
@@ -0,0 +1,50 @@
+
+#pragma once
+
+#include "HangingEntity.h"
+
+
+class cWorldInterface;
+
+
+
+
+// tolua_begin
+class cLeashKnot :
+ public cHangingEntity
+{
+ typedef cHangingEntity super;
+
+public:
+
+ // tolua_end
+
+ CLASS_PROTODEF(cLeashKnot)
+
+ cLeashKnot(eBlockFace a_BlockFace, double a_X, double a_Y, double a_Z);
+
+ /** Looks for mobs leashed to a player and ties them to this knot */
+ void TiePlayersLeashedMobs(cPlayer & a_Player, bool a_ShouldBroadCast);
+
+ void SetShouldSelfDestroy() { m_ShouldSelfDestroy = true; }
+
+ /** Returns the leash knot entity representing the knot at the specified position. Returns nullptr if there's no knot. */
+ static cLeashKnot * FindKnotAtPos(cWorldInterface & a_WorldInterface, Vector3i a_BlockPos);
+
+private:
+
+ /** When a fence is destroyed, the knot on it gets destroyed after a while. This flag turns on the countdown to self destroy. */
+ bool m_ShouldSelfDestroy;
+ int m_TicksToSelfDestroy;
+
+ virtual void OnRightClicked(cPlayer & a_Player) override;
+ virtual void KilledBy(TakeDamageInfo & a_TDI) override;
+ virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override;
+ virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
+ virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
+
+}; // tolua_export
+
+
+
+