From b18f6637b6c58db20353cd3e77584b646ab36b5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Beltr=C3=A1n?= Date: Mon, 21 Aug 2017 10:46:41 +0200 Subject: Fully implemented leashes (#3798) --- src/Entities/CMakeLists.txt | 2 + src/Entities/Entity.cpp | 36 +++++++++ src/Entities/Entity.h | 20 ++++- src/Entities/LeashKnot.cpp | 185 ++++++++++++++++++++++++++++++++++++++++++++ src/Entities/LeashKnot.h | 50 ++++++++++++ 5 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 src/Entities/LeashKnot.cpp create mode 100644 src/Entities/LeashKnot.h (limited to 'src/Entities') 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 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(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(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(GetYaw()), static_cast(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(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 + + + + -- cgit v1.2.3