Fully implemented leashes (#3798)
This commit is contained in:
parent
f81e6f6b6d
commit
b18f6637b6
@ -45,6 +45,7 @@ SamJBarney
|
||||
Schwertspize
|
||||
Seadragon91 (Lukas Pioch)
|
||||
Sofapriester
|
||||
Spekdrum (Pablo Beltran)
|
||||
SphinxC0re
|
||||
structinf (xdot)
|
||||
sweetgiorni
|
||||
|
@ -3655,6 +3655,16 @@ local Hash = cCryptoHash.sha1HexString("DataToHash")
|
||||
},
|
||||
Notes = "Returns true if the entity is an item frame.",
|
||||
},
|
||||
IsLeashKnot =
|
||||
{
|
||||
Returns =
|
||||
{
|
||||
{
|
||||
Type = "boolean",
|
||||
},
|
||||
},
|
||||
Notes = "Returns true if the entity is a leash knot.",
|
||||
},
|
||||
IsMinecart =
|
||||
{
|
||||
Returns =
|
||||
@ -4327,7 +4337,11 @@ local Hash = cCryptoHash.sha1HexString("DataToHash")
|
||||
},
|
||||
etItemFrame =
|
||||
{
|
||||
Notes = "",
|
||||
Notes = "The entity is an item frame",
|
||||
},
|
||||
etLeashKnot =
|
||||
{
|
||||
Notes = "The entity is a leash knot",
|
||||
},
|
||||
etMinecart =
|
||||
{
|
||||
@ -8620,6 +8634,16 @@ a_Player:OpenWindow(Window);
|
||||
]],
|
||||
Functions =
|
||||
{
|
||||
CanBeLeashed =
|
||||
{
|
||||
Returns =
|
||||
{
|
||||
{
|
||||
Type = "boolean",
|
||||
},
|
||||
},
|
||||
Notes = "Returns whether the mob can be leashed.",
|
||||
},
|
||||
FamilyFromType =
|
||||
{
|
||||
IsStatic = true,
|
||||
@ -8659,6 +8683,17 @@ a_Player:OpenWindow(Window);
|
||||
},
|
||||
Notes = "Gets the custom name of the monster. If no custom name is set, the function returns an empty string.",
|
||||
},
|
||||
GetLeashedTo =
|
||||
{
|
||||
Returns =
|
||||
{
|
||||
{
|
||||
Name = "LeashedTo",
|
||||
Type = "cEntity",
|
||||
},
|
||||
},
|
||||
Notes = "Returns the entity to where this mob is leashed, returns nil if it's not leashed",
|
||||
},
|
||||
GetMobFamily =
|
||||
{
|
||||
Returns =
|
||||
@ -8739,6 +8774,27 @@ a_Player:OpenWindow(Window);
|
||||
},
|
||||
Notes = "Is the custom name of this monster always visible? If not, you only see the name when you sight the mob.",
|
||||
},
|
||||
IsLeashed =
|
||||
{
|
||||
Returns =
|
||||
{
|
||||
{
|
||||
Type = "boolean",
|
||||
},
|
||||
},
|
||||
Notes = "Returns whether the monster is leashed to an entity.",
|
||||
},
|
||||
LeashTo =
|
||||
{
|
||||
Params =
|
||||
{
|
||||
{
|
||||
Name = "Entity",
|
||||
Type = "cEntity",
|
||||
}
|
||||
},
|
||||
Notes = "Leash the monster to an entity.",
|
||||
},
|
||||
MobTypeToString =
|
||||
{
|
||||
IsStatic = true,
|
||||
@ -8797,6 +8853,17 @@ a_Player:OpenWindow(Window);
|
||||
},
|
||||
Notes = "Sets the age of the monster",
|
||||
},
|
||||
SetCanBeLeashed =
|
||||
{
|
||||
Params =
|
||||
{
|
||||
{
|
||||
Name = "CanBeLeashed",
|
||||
Type = "boolean",
|
||||
}
|
||||
},
|
||||
Notes = "Sets whether the mob can be leashed, for extensibility in plugins"
|
||||
},
|
||||
SetCustomName =
|
||||
{
|
||||
Params =
|
||||
@ -8849,6 +8916,17 @@ a_Player:OpenWindow(Window);
|
||||
},
|
||||
Notes = "Returns the mob type ({{Globals#eMonsterType|mtXXX}} constant) parsed from the string type (\"creeper\"), or mtInvalidType if unrecognized.",
|
||||
},
|
||||
Unleash =
|
||||
{
|
||||
Params =
|
||||
{
|
||||
{
|
||||
Name = "ShouldDropLeashPickup",
|
||||
Type = "boolean",
|
||||
},
|
||||
},
|
||||
Notes = "Unleash the monster.",
|
||||
},
|
||||
},
|
||||
Constants =
|
||||
{
|
||||
@ -15534,6 +15612,10 @@ end
|
||||
{
|
||||
Notes = "The itemtype for lead"
|
||||
},
|
||||
E_ITEM_LEASH =
|
||||
{
|
||||
Notes = "The itemtype for lead (E_ITEM_LEAD synonym)"
|
||||
},
|
||||
E_ITEM_LEATHER =
|
||||
{
|
||||
Notes = "The itemtype for leather"
|
||||
|
@ -86,6 +86,7 @@ $cfile "../Entities/Floater.h"
|
||||
$cfile "../Entities/GhastFireballEntity.h"
|
||||
$cfile "../Entities/HangingEntity.h"
|
||||
$cfile "../Entities/ItemFrame.h"
|
||||
$cfile "../Entities/LeashKnot.h"
|
||||
$cfile "../Entities/Player.h"
|
||||
$cfile "../Entities/Painting.h"
|
||||
$cfile "../Entities/Pickup.h"
|
||||
|
@ -109,6 +109,7 @@ set(BINDING_DEPENDENCIES
|
||||
../Entities/GhastFireballEntity.h
|
||||
../Entities/HangingEntity.h
|
||||
../Entities/ItemFrame.h
|
||||
../Entities/LeashKnot.h
|
||||
../Entities/Pawn.h
|
||||
../Entities/Player.h
|
||||
../Entities/Painting.h
|
||||
|
@ -993,6 +993,7 @@ void cLuaState::Push(cEntity * a_Entity)
|
||||
case cEntity::etExpOrb:
|
||||
case cEntity::etItemFrame:
|
||||
case cEntity::etPainting:
|
||||
case cEntity::etLeashKnot:
|
||||
{
|
||||
// Push the generic entity class type:
|
||||
tolua_pushusertype(m_LuaState, a_Entity, "cEntity");
|
||||
|
@ -459,6 +459,7 @@ enum ENUM_ITEM_ID : short
|
||||
E_ITEM_GOLD_HORSE_ARMOR = 418,
|
||||
E_ITEM_DIAMOND_HORSE_ARMOR = 419,
|
||||
E_ITEM_LEAD = 420,
|
||||
E_ITEM_LEASH = E_ITEM_LEAD,
|
||||
E_ITEM_NAME_TAG = 421,
|
||||
E_ITEM_MINECART_WITH_COMMAND_BLOCK = 422,
|
||||
E_ITEM_RAW_MUTTON = 423,
|
||||
|
@ -3,7 +3,10 @@
|
||||
|
||||
#include "BlockHandler.h"
|
||||
#include "../BoundingBox.h"
|
||||
|
||||
#include "../EffectID.h"
|
||||
#include "Entities/LeashKnot.h"
|
||||
#include "BoundingBox.h"
|
||||
#include "../Mobs/PassiveMonster.h"
|
||||
|
||||
|
||||
|
||||
@ -72,6 +75,63 @@ public:
|
||||
|
||||
return PlacementBox;
|
||||
}
|
||||
|
||||
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override
|
||||
{
|
||||
auto LeashKnot = cLeashKnot::FindKnotAtPos(*a_Player.GetWorld(), { a_BlockX, a_BlockY, a_BlockZ });
|
||||
auto KnotAlreadyExists = (LeashKnot != nullptr);
|
||||
|
||||
if (KnotAlreadyExists)
|
||||
{
|
||||
// Check leashed nearby mobs to leash them to the knot
|
||||
LeashKnot->TiePlayersLeashedMobs(a_Player, KnotAlreadyExists);
|
||||
}
|
||||
// New knot? needs to init and produce sound effect
|
||||
else
|
||||
{
|
||||
auto NewLeashKnot = cpp14::make_unique<cLeashKnot>(a_BlockFace, a_BlockX, a_BlockY, a_BlockZ);
|
||||
auto NewLeashKnotPtr = NewLeashKnot.get();
|
||||
|
||||
NewLeashKnotPtr->TiePlayersLeashedMobs(a_Player, KnotAlreadyExists);
|
||||
|
||||
// Only put the knot in the world if any mob has been leashed to
|
||||
if (NewLeashKnotPtr->HasAnyMobLeashed())
|
||||
{
|
||||
if (!NewLeashKnotPtr->Initialize(std::move(NewLeashKnot), *a_Player.GetWorld()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
a_Player.GetWorld()->BroadcastSoundEffect("entity.leashknot.place", a_Player.GetPosX(), a_Player.GetPosY(), a_Player.GetPosZ(), 1, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
|
||||
{
|
||||
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
|
||||
}
|
||||
|
||||
virtual bool IsUseable(void) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
auto LeashKnot = cLeashKnot::FindKnotAtPos(a_WorldInterface, { a_BlockX, a_BlockY, a_BlockZ });
|
||||
|
||||
if (LeashKnot != nullptr)
|
||||
{
|
||||
LeashKnot->SetShouldSelfDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -53,6 +53,11 @@ public:
|
||||
/** Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true */
|
||||
virtual bool ForEachPlayer(cItemCallback<cPlayer> & a_Callback) = 0;
|
||||
|
||||
/** Calls the callback for each entity that has a nonempty intersection with the specified boundingbox.
|
||||
Returns true if all entities processed, false if the callback aborted by returning true.
|
||||
If any chunk in the box is missing, ignores the entities in that chunk silently. */
|
||||
virtual bool ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback) = 0;
|
||||
|
||||
virtual void SetTimeOfDay(int a_TimeOfDay) = 0;
|
||||
|
||||
/** Returns true if it is raining, stormy or snowing at the specified location. This takes into account biomes. */
|
||||
|
@ -39,6 +39,16 @@ cBoundingBox::cBoundingBox(Vector3d a_Pos, double a_Radius, double a_Height) :
|
||||
|
||||
|
||||
|
||||
cBoundingBox::cBoundingBox(Vector3d a_Pos, double a_Radius, double a_Height, double a_VerticalOffset) :
|
||||
m_Min(a_Pos.x - a_Radius, a_Pos.y + a_VerticalOffset, a_Pos.z - a_Radius),
|
||||
m_Max(a_Pos.x + a_Radius, a_Pos.y + a_VerticalOffset + a_Height, a_Pos.z + a_Radius)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cBoundingBox::cBoundingBox(Vector3d a_Pos, double a_CubeLength) :
|
||||
m_Min(a_Pos.x - a_CubeLength / 2, a_Pos.y - a_CubeLength / 2, a_Pos.z - a_CubeLength / 2),
|
||||
m_Max(a_Pos.x + a_CubeLength / 2, a_Pos.y + a_CubeLength / 2, a_Pos.z + a_CubeLength / 2)
|
||||
|
@ -26,6 +26,10 @@ public:
|
||||
cBoundingBox(double a_MinX, double a_MaxX, double a_MinY, double a_MaxY, double a_MinZ, double a_MaxZ);
|
||||
cBoundingBox(Vector3d a_Min, Vector3d a_Max);
|
||||
cBoundingBox(Vector3d a_Pos, double a_Radius, double a_Height);
|
||||
/** Constructor that allows to define a bounding box given a center point (a_Pos), a horizontal radius (a_Radius),
|
||||
a height starting from given center point (a_Height) and a vertical offset (a_VerticalOffset) to adjust the vertical starting point.
|
||||
For example: cBoundingBox([0, 0, 0], 6, 6, -3) would create a bounding cube from (-3, -3, -3) to (3, 3, 3). */
|
||||
cBoundingBox(Vector3d a_Pos, double a_Radius, double a_Height, double a_VerticalOffset);
|
||||
cBoundingBox(Vector3d a_Pos, double a_CubeLength);
|
||||
cBoundingBox(const cBoundingBox & a_Orig);
|
||||
|
||||
|
@ -2683,6 +2683,28 @@ void cChunk::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity & a_V
|
||||
|
||||
|
||||
|
||||
void cChunk::BroadcastLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo)
|
||||
{
|
||||
for (auto ClientHandle : m_LoadedByClient)
|
||||
{
|
||||
ClientHandle->SendLeashEntity(a_Entity, a_EntityLeashedTo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunk::BroadcastUnleashEntity(const cEntity & a_Entity)
|
||||
{
|
||||
for (auto ClientHandle : m_LoadedByClient)
|
||||
{
|
||||
ClientHandle->SendUnleashEntity(a_Entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunk::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude)
|
||||
{
|
||||
|
@ -368,12 +368,14 @@ public:
|
||||
void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastEntityAnimation (const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastLeashEntity (const cEntity & a_Entity, const cEntity & a_EntityLeashedTo);
|
||||
void BroadcastParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount, cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastSoundParticleEffect(const EffectID a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastUnleashEntity (const cEntity & a_Entity);
|
||||
void BroadcastUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
|
||||
void SendBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client);
|
||||
|
@ -275,6 +275,35 @@ void cChunkMap::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity &
|
||||
|
||||
|
||||
|
||||
void cChunkMap::BroadcastLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo)
|
||||
{
|
||||
cCSLock Lock(m_CSChunks);
|
||||
cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
|
||||
if (Chunk == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Chunk->BroadcastLeashEntity(a_Entity, a_EntityLeashedTo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkMap::BroadcastUnleashEntity(const cEntity & a_Entity)
|
||||
{
|
||||
cCSLock Lock(m_CSChunks);
|
||||
cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
|
||||
if (Chunk == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Chunk->BroadcastUnleashEntity(a_Entity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkMap::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude)
|
||||
{
|
||||
|
@ -88,12 +88,14 @@ public:
|
||||
void BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastEntityAnimation(const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo);
|
||||
void BroadcastParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount, cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastSoundParticleEffect(const EffectID a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastUnleashEntity(const cEntity & a_Entity);
|
||||
void BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
|
||||
/** Sends the block entity, if it is at the coords specified, to a_Client */
|
||||
|
@ -2249,6 +2249,24 @@ void cClientHandle::SendAttachEntity(const cEntity & a_Entity, const cEntity & a
|
||||
|
||||
|
||||
|
||||
void cClientHandle::SendLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo)
|
||||
{
|
||||
m_Protocol->SendLeashEntity(a_Entity, a_EntityLeashedTo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cClientHandle::SendUnleashEntity(const cEntity & a_Entity)
|
||||
{
|
||||
m_Protocol->SendUnleashEntity(a_Entity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cClientHandle::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
|
||||
{
|
||||
m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType);
|
||||
|
@ -179,6 +179,7 @@ public: // tolua_export
|
||||
void SendHealth (void);
|
||||
void SendHideTitle (void); // tolua_export
|
||||
void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item);
|
||||
void SendLeashEntity (const cEntity & a_Entity, const cEntity & a_EntityLeashedTo);
|
||||
void SendMapData (const cMap & a_Map, int a_DataStartX, int a_DataStartY);
|
||||
void SendPaintingSpawn (const cPainting & a_Painting);
|
||||
void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount);
|
||||
@ -216,6 +217,7 @@ public: // tolua_export
|
||||
void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks); // tolua_export
|
||||
void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle); // tolua_export
|
||||
void SendUnleashEntity (const cEntity & a_Entity);
|
||||
void SendUnloadChunk (int a_ChunkX, int a_ChunkZ);
|
||||
void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity);
|
||||
void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
185
src/Entities/LeashKnot.cpp
Normal file
185
src/Entities/LeashKnot.cpp
Normal file
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
50
src/Entities/LeashKnot.h
Normal file
50
src/Entities/LeashKnot.h
Normal file
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -614,6 +614,7 @@ char cItemHandler::GetMaxStackSize(void)
|
||||
case E_ITEM_IRON: return 64;
|
||||
case E_ITEM_IRON_NUGGET: return 64;
|
||||
case E_ITEM_ITEM_FRAME: return 64;
|
||||
case E_ITEM_LEAD: return 64;
|
||||
case E_ITEM_LEATHER: return 64;
|
||||
case E_ITEM_MAGMA_CREAM: return 64;
|
||||
case E_ITEM_MAP: return 64;
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
m_Attachee->Detach();
|
||||
}
|
||||
|
||||
if (m_Attachee->IsPlayer())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_Attachee->Detach();
|
||||
m_TameAttemptTimes++;
|
||||
a_Player.AttachTo(this);
|
||||
}
|
||||
|
||||
m_TameAttemptTimes++;
|
||||
a_Player.AttachTo(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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--;
|
||||
|
@ -91,6 +91,7 @@ public:
|
||||
virtual void SendHideTitle (void) = 0;
|
||||
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0;
|
||||
virtual void SendKeepAlive (UInt32 a_PingID) = 0;
|
||||
virtual void SendLeashEntity (const cEntity & a_Entity, const cEntity & a_EntityLeashedTo) = 0;
|
||||
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0;
|
||||
virtual void SendLoginSuccess (void) = 0;
|
||||
virtual void SendMapData (const cMap & a_Map, int a_DataStartX, int a_DataStartY) = 0;
|
||||
@ -134,6 +135,7 @@ public:
|
||||
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0;
|
||||
virtual void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) = 0;
|
||||
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) = 0;
|
||||
virtual void SendUnleashEntity (const cEntity & a_Entity) = 0;
|
||||
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0;
|
||||
virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) = 0;
|
||||
virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) = 0;
|
||||
|
@ -468,6 +468,26 @@ void cProtocolRecognizer::SendKeepAlive(UInt32 a_PingID)
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo)
|
||||
{
|
||||
ASSERT(m_Protocol != nullptr);
|
||||
m_Protocol->SendLeashEntity(a_Entity, a_EntityLeashedTo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendUnleashEntity(const cEntity & a_Entity)
|
||||
{
|
||||
ASSERT(m_Protocol != nullptr);
|
||||
m_Protocol->SendUnleashEntity(a_Entity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
|
||||
{
|
||||
ASSERT(m_Protocol != nullptr);
|
||||
|
@ -87,6 +87,7 @@ public:
|
||||
virtual void SendHideTitle (void) override;
|
||||
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
|
||||
virtual void SendKeepAlive (UInt32 a_PingID) override;
|
||||
virtual void SendLeashEntity (const cEntity & a_Entity, const cEntity & a_EntityLeashedTo) override;
|
||||
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
|
||||
virtual void SendLoginSuccess (void) override;
|
||||
virtual void SendMapData (const cMap & a_Map, int a_DataStartX, int a_DataStartY) override;
|
||||
@ -130,6 +131,7 @@ public:
|
||||
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
|
||||
virtual void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) override;
|
||||
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) override;
|
||||
virtual void SendUnleashEntity (const cEntity & a_Entity) override;
|
||||
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
|
||||
virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override;
|
||||
virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
|
||||
|
@ -1252,6 +1252,30 @@ void cProtocol_1_12::SendScoreUpdate(const AString & a_Objective, const AString
|
||||
|
||||
|
||||
|
||||
void cProtocol_1_12::SendLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo)
|
||||
{
|
||||
ASSERT(m_State == 3); // In game mode?
|
||||
cPacketizer Pkt(*this, 0x3c); // Set Attach Entity packet
|
||||
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
|
||||
Pkt.WriteBEUInt32(a_EntityLeashedTo.GetUniqueID());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol_1_12::SendUnleashEntity(const cEntity & a_Entity)
|
||||
{
|
||||
ASSERT(m_State == 3); // In game mode?
|
||||
cPacketizer Pkt(*this, 0x3c); // Set Attach Entity packet
|
||||
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
|
||||
Pkt.WriteBEInt32(-1); // Unleash a_Entity
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol_1_12::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
|
||||
{
|
||||
// Send the Join Game packet:
|
||||
|
@ -48,6 +48,7 @@ public:
|
||||
virtual void SendExperience(void) override;
|
||||
virtual void SendHealth(void) override;
|
||||
virtual void SendHideTitle(void) override;
|
||||
virtual void SendLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo) override;
|
||||
virtual void SendLogin(const cPlayer & a_Player, const cWorld & a_World) override;
|
||||
virtual void SendPlayerMaxSpeed(void) override;
|
||||
virtual void SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID) override;
|
||||
@ -62,6 +63,7 @@ public:
|
||||
virtual void SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) override;
|
||||
virtual void SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) override;
|
||||
virtual void SendUpdateBlockEntity(cBlockEntity & a_BlockEntity) override;
|
||||
virtual void SendUnleashEntity(const cEntity & a_Entity) override;
|
||||
protected:
|
||||
virtual bool HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType) override;
|
||||
virtual void HandlePacketAdvancementTab(cByteBuffer & a_ByteBuffer);
|
||||
|
@ -629,6 +629,34 @@ void cProtocol_1_8_0::SendKeepAlive(UInt32 a_PingID)
|
||||
|
||||
|
||||
|
||||
void cProtocol_1_8_0::SendLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo)
|
||||
{
|
||||
ASSERT(m_State == 3); // In game mode?
|
||||
|
||||
cPacketizer Pkt(*this, 0x1b); // Attach Entity packet
|
||||
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
|
||||
Pkt.WriteBEUInt32(a_EntityLeashedTo.GetUniqueID());
|
||||
Pkt.WriteBool(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol_1_8_0::SendUnleashEntity(const cEntity & a_Entity)
|
||||
{
|
||||
ASSERT(m_State == 3); // In game mode?
|
||||
|
||||
cPacketizer Pkt(*this, 0x1b); // Attach Entity packet
|
||||
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
|
||||
Pkt.WriteBEInt32(-1);
|
||||
Pkt.WriteBool(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol_1_8_0::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
|
||||
{
|
||||
// Send the Join Game packet:
|
||||
|
@ -80,6 +80,7 @@ public:
|
||||
virtual void SendHideTitle (void) override;
|
||||
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
|
||||
virtual void SendKeepAlive (UInt32 a_PingID) override;
|
||||
virtual void SendLeashEntity (const cEntity & a_Entity, const cEntity & a_EntityLeashedTo) override;
|
||||
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
|
||||
virtual void SendLoginSuccess (void) override;
|
||||
virtual void SendMapData (const cMap & a_Map, int a_DataStartX, int a_DataStartY) override;
|
||||
@ -123,6 +124,7 @@ public:
|
||||
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
|
||||
virtual void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) override;
|
||||
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) override;
|
||||
virtual void SendUnleashEntity (const cEntity & a_Entity) override;
|
||||
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
|
||||
virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override;
|
||||
virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
|
||||
|
@ -648,6 +648,30 @@ void cProtocol_1_9_0::SendKeepAlive(UInt32 a_PingID)
|
||||
|
||||
|
||||
|
||||
void cProtocol_1_9_0::SendLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo)
|
||||
{
|
||||
ASSERT(m_State == 3); // In game mode?
|
||||
cPacketizer Pkt(*this, 0x3a); // Set Attach Entity packet
|
||||
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
|
||||
Pkt.WriteBEUInt32(a_EntityLeashedTo.GetUniqueID());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol_1_9_0::SendUnleashEntity(const cEntity & a_Entity)
|
||||
{
|
||||
ASSERT(m_State == 3); // In game mode?
|
||||
cPacketizer Pkt(*this, 0x3a); // Set Attach Entity packet
|
||||
Pkt.WriteBEUInt32(a_Entity.GetUniqueID());
|
||||
Pkt.WriteBEInt32(-1); // Unleash a_Entity
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cProtocol_1_9_0::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
|
||||
{
|
||||
// Send the Join Game packet:
|
||||
|
@ -86,6 +86,7 @@ public:
|
||||
virtual void SendHideTitle (void) override;
|
||||
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
|
||||
virtual void SendKeepAlive (UInt32 a_PingID) override;
|
||||
virtual void SendLeashEntity (const cEntity & a_Entity, const cEntity & a_EntityLeashedTo) override;
|
||||
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
|
||||
virtual void SendLoginSuccess (void) override;
|
||||
virtual void SendMapData (const cMap & a_Map, int a_DataStartX, int a_DataStartY) override;
|
||||
@ -129,6 +130,7 @@ public:
|
||||
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
|
||||
virtual void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) override;
|
||||
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle) override;
|
||||
virtual void SendUnleashEntity (const cEntity & a_Entity) override;
|
||||
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
|
||||
virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override;
|
||||
virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
|
||||
|
@ -2503,6 +2503,24 @@ void cWorld::BroadcastDetachEntity(const cEntity & a_Entity, const cEntity & a_P
|
||||
|
||||
|
||||
|
||||
void cWorld::BroadcastLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo)
|
||||
{
|
||||
m_ChunkMap->BroadcastLeashEntity(a_Entity, a_EntityLeashedTo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWorld::BroadcastUnleashEntity(const cEntity & a_Entity)
|
||||
{
|
||||
m_ChunkMap->BroadcastUnleashEntity(a_Entity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWorld::BroadcastEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude)
|
||||
{
|
||||
m_ChunkMap->BroadcastEntityEffect(a_Entity, a_EffectID, a_Amplifier, a_Duration, a_Exclude);
|
||||
|
@ -196,6 +196,7 @@ public:
|
||||
void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
|
||||
virtual void BroadcastEntityAnimation (const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude = nullptr) override; // tolua_export
|
||||
void BroadcastLeashEntity (const cEntity & a_Entity, const cEntity & a_EntityLeashedTo);
|
||||
void BroadcastPlayerListAddPlayer (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastPlayerListRemovePlayer (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastPlayerListUpdateGameMode (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
|
||||
@ -211,6 +212,7 @@ public:
|
||||
void BroadcastTeleportEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastTimeUpdate (const cClientHandle * a_Exclude = nullptr);
|
||||
void BroadcastUnleashEntity (const cEntity & a_Entity);
|
||||
virtual void BroadcastUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ) override;
|
||||
void BroadcastWeather (eWeather a_Weather, const cClientHandle * a_Exclude = nullptr);
|
||||
|
||||
@ -307,7 +309,7 @@ public:
|
||||
/** Calls the callback for each entity that has a nonempty intersection with the specified boundingbox.
|
||||
Returns true if all entities processed, false if the callback aborted by returning true.
|
||||
If any chunk in the box is missing, ignores the entities in that chunk silently. */
|
||||
bool ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp
|
||||
virtual bool ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback) override; // Exported in ManualBindings.cpp
|
||||
|
||||
/** Calls the callback if the entity with the specified ID is found, with the entity object as the callback param.
|
||||
Returns true if entity found and callback returned false. */
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "../Entities/ExpOrb.h"
|
||||
#include "../Entities/HangingEntity.h"
|
||||
#include "../Entities/ItemFrame.h"
|
||||
#include "../Entities/LeashKnot.h"
|
||||
#include "../Entities/Painting.h"
|
||||
|
||||
#include "../Mobs/IncludeAllMonsters.h"
|
||||
@ -574,6 +575,33 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
|
||||
m_Writer.AddByte("CanPickUpLoot", (a_Monster->CanPickUpLoot())? 1 : 0);
|
||||
m_Writer.AddString("CustomName", a_Monster->GetCustomName());
|
||||
m_Writer.AddByte("CustomNameVisible", static_cast<Byte>(a_Monster->IsCustomNameAlwaysVisible()));
|
||||
|
||||
// Mob was leashed
|
||||
if (a_Monster->IsLeashed() || (a_Monster->GetLeashToPos() != nullptr))
|
||||
{
|
||||
m_Writer.AddByte("Leashed", 1);
|
||||
|
||||
const Vector3d * LeashedToPos = nullptr;
|
||||
|
||||
if (a_Monster->GetLeashToPos() != nullptr)
|
||||
{
|
||||
LeashedToPos = a_Monster->GetLeashToPos();
|
||||
}
|
||||
else if (a_Monster->GetLeashedTo()->IsLeashKnot())
|
||||
{
|
||||
LeashedToPos = & a_Monster->GetLeashedTo()->GetPosition();
|
||||
}
|
||||
|
||||
if (LeashedToPos != nullptr)
|
||||
{
|
||||
m_Writer.BeginCompound("Leash");
|
||||
m_Writer.AddDouble("X", LeashedToPos->x);
|
||||
m_Writer.AddDouble("Y", LeashedToPos->y);
|
||||
m_Writer.AddDouble("Z", LeashedToPos->z);
|
||||
m_Writer.EndCompound();
|
||||
}
|
||||
}
|
||||
|
||||
switch (a_Monster->GetMobType())
|
||||
{
|
||||
case mtBat:
|
||||
@ -850,8 +878,13 @@ void cNBTChunkSerializer::AddItemFrameEntity(cItemFrame * a_ItemFrame)
|
||||
m_Writer.EndCompound();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void cNBTChunkSerializer::AddLeashKnotEntity(cLeashKnot * a_LeashKnot)
|
||||
{
|
||||
m_Writer.BeginCompound("");
|
||||
AddBasicEntity(a_LeashKnot, "LeashKnot");
|
||||
AddHangingEntity(a_LeashKnot);
|
||||
m_Writer.EndCompound();
|
||||
}
|
||||
|
||||
|
||||
void cNBTChunkSerializer::AddPaintingEntity(cPainting * a_Painting)
|
||||
@ -965,6 +998,7 @@ void cNBTChunkSerializer::Entity(cEntity * a_Entity)
|
||||
case cEntity::etTNT: AddTNTEntity (reinterpret_cast<cTNTEntity *> (a_Entity)); break;
|
||||
case cEntity::etExpOrb: AddExpOrbEntity (reinterpret_cast<cExpOrb *> (a_Entity)); break;
|
||||
case cEntity::etItemFrame: AddItemFrameEntity (reinterpret_cast<cItemFrame *> (a_Entity)); break;
|
||||
case cEntity::etLeashKnot: AddLeashKnotEntity (reinterpret_cast<cLeashKnot *> (a_Entity)); break;
|
||||
case cEntity::etPainting: AddPaintingEntity (reinterpret_cast<cPainting *> (a_Entity)); break;
|
||||
case cEntity::etPlayer: return; // Players aren't saved into the world
|
||||
case cEntity::etFloater: return; // Floaters aren't saved either
|
||||
|
@ -47,6 +47,7 @@ class cTNTEntity;
|
||||
class cExpOrb;
|
||||
class cHangingEntity;
|
||||
class cItemFrame;
|
||||
class cLeashKnot;
|
||||
class cPainting;
|
||||
|
||||
|
||||
@ -123,6 +124,7 @@ protected:
|
||||
void AddTNTEntity (cTNTEntity * a_TNT);
|
||||
void AddExpOrbEntity (cExpOrb * a_ExpOrb);
|
||||
void AddItemFrameEntity (cItemFrame * a_ItemFrame);
|
||||
void AddLeashKnotEntity (cLeashKnot * a_LeashKnot);
|
||||
void AddPaintingEntity (cPainting * a_Painting);
|
||||
|
||||
void AddMinecartChestContents(cMinecartWithChest * a_Minecart);
|
||||
|
@ -50,10 +50,12 @@
|
||||
#include "../Entities/ExpOrb.h"
|
||||
#include "../Entities/HangingEntity.h"
|
||||
#include "../Entities/ItemFrame.h"
|
||||
#include "../Entities/LeashKnot.h"
|
||||
#include "../Entities/Painting.h"
|
||||
|
||||
#include "../Protocol/MojangAPI.h"
|
||||
#include "Server.h"
|
||||
#include "BoundingBox.h"
|
||||
|
||||
|
||||
|
||||
@ -1540,6 +1542,8 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
|
||||
{ "minecraft:xp_orb", &cWSSAnvil::LoadExpOrbFromNBT },
|
||||
{ "ItemFrame", &cWSSAnvil::LoadItemFrameFromNBT },
|
||||
{ "minecraft:item_frame", &cWSSAnvil::LoadItemFrameFromNBT },
|
||||
{ "LeashKnot", &cWSSAnvil::LoadLeashKnotFromNBT },
|
||||
{ "minecraft:leash_knot", &cWSSAnvil::LoadLeashKnotFromNBT },
|
||||
{ "Arrow", &cWSSAnvil::LoadArrowFromNBT },
|
||||
{ "minecraft:arrow", &cWSSAnvil::LoadArrowFromNBT },
|
||||
{ "SplashPotion", &cWSSAnvil::LoadSplashPotionFromNBT },
|
||||
@ -1958,6 +1962,24 @@ void cWSSAnvil::LoadItemFrameFromNBT(cEntityList & a_Entities, const cParsedNBT
|
||||
|
||||
|
||||
|
||||
void cWSSAnvil::LoadLeashKnotFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
|
||||
{
|
||||
auto LeashKnot = cpp14::make_unique<cLeashKnot>(BLOCK_FACE_NONE, 0.0, 0.0, 0.0);
|
||||
|
||||
if (!LoadEntityBaseFromNBT(*LeashKnot.get(), a_NBT, a_TagIdx))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LoadHangingFromNBT(*LeashKnot.get(), a_NBT, a_TagIdx);
|
||||
|
||||
a_Entities.emplace_back(std::move(LeashKnot));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWSSAnvil::LoadPaintingFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
|
||||
{
|
||||
// Load painting name:
|
||||
@ -3182,6 +3204,13 @@ bool cWSSAnvil::LoadMonsterBaseFromNBT(cMonster & a_Monster, const cParsedNBT &
|
||||
a_Monster.SetCustomNameAlwaysVisible(CustomNameVisible);
|
||||
}
|
||||
|
||||
// Leashed to a knot
|
||||
int LeashedIdx = a_NBT.FindChildByName(a_TagIdx, "Leashed");
|
||||
if ((LeashedIdx >= 0) && a_NBT.GetByte(LeashedIdx))
|
||||
{
|
||||
LoadLeashToPosition(a_Monster, a_NBT, a_TagIdx);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3189,6 +3218,54 @@ bool cWSSAnvil::LoadMonsterBaseFromNBT(cMonster & a_Monster, const cParsedNBT &
|
||||
|
||||
|
||||
|
||||
void cWSSAnvil::LoadLeashToPosition(cMonster & a_Monster, const cParsedNBT & a_NBT, int a_TagIdx)
|
||||
{
|
||||
int LeashIdx = a_NBT.FindChildByName(a_TagIdx, "Leash");
|
||||
if (LeashIdx < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double PosX = 0.0, PosY = 0.0, PosZ = 0.0;
|
||||
bool KnotPosPresent = true;
|
||||
int LeashDataLine = a_NBT.FindChildByName(LeashIdx, "X");
|
||||
if (LeashDataLine >= 0)
|
||||
{
|
||||
PosX = a_NBT.GetDouble(LeashDataLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
KnotPosPresent = false;
|
||||
}
|
||||
LeashDataLine = a_NBT.FindChildByName(LeashIdx, "Y");
|
||||
if (LeashDataLine >= 0)
|
||||
{
|
||||
PosY = a_NBT.GetDouble(LeashDataLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
KnotPosPresent = false;
|
||||
}
|
||||
LeashDataLine = a_NBT.FindChildByName(LeashIdx, "Z");
|
||||
if (LeashDataLine >= 0)
|
||||
{
|
||||
PosZ = a_NBT.GetDouble(LeashDataLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
KnotPosPresent = false;
|
||||
}
|
||||
if (KnotPosPresent)
|
||||
{
|
||||
// Set leash pos for the mob
|
||||
a_Monster.SetLeashToPos(new Vector3d(PosX, PosY, PosZ));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cWSSAnvil::LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx)
|
||||
{
|
||||
if (!LoadEntityBaseFromNBT(a_Entity, a_NBT, a_TagIdx))
|
||||
|
@ -179,6 +179,7 @@ protected:
|
||||
void LoadExpOrbFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
void LoadHangingFromNBT (cHangingEntity & a_Hanging, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
void LoadItemFrameFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
void LoadLeashKnotFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
void LoadPaintingFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
|
||||
void LoadOldMinecartFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
@ -238,6 +239,9 @@ protected:
|
||||
/** Loads monster common data from the NBT compound; returns true if successful */
|
||||
bool LoadMonsterBaseFromNBT(cMonster & a_Monster, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
|
||||
/** Loads the position to where is leashed the monster */
|
||||
void LoadLeashToPosition(cMonster & a_Monster, const cParsedNBT & a_NBT, int a_TagIdx);
|
||||
|
||||
/** Loads projectile common data from the NBT compound; returns true if successful */
|
||||
bool LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIx);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user