1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "LeashKnot.h"
#include "Player.h"
#include "../Mobs/Monster.h"
#include "../BoundingBox.h"
#include "../ClientHandle.h"
// Ticks to wait in Tick function to optimize calculations
#define TICK_STEP 10
cLeashKnot::cLeashKnot(eBlockFace a_BlockFace, Vector3d a_Pos) :
Super(etLeashKnot, a_BlockFace, a_Pos),
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
// 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), [&](cEntity & a_Entity)
{
// If the entity is not a monster skip it
if (a_Entity.GetEntityType() != cEntity::eEntityType::etMonster)
{
return false;
}
auto & 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() != a_Player.GetUniqueID())
)
{
return false;
}
// All conditions met, unleash from player and leash to fence
PotentialLeashed.Unleash(false, false);
PotentialLeashed.LeashTo(*this, a_ShouldBroadcast);
return false;
}
);
}
void cLeashKnot::KilledBy(TakeDamageInfo & a_TDI)
{
Super::KilledBy(a_TDI);
m_World->BroadcastSoundEffect("entity.leashknot.break", GetPosition(), 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());
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", GetPosition(), 1, 1);
}
}
}
cLeashKnot * cLeashKnot::FindKnotAtPos(cWorldInterface & a_WorldInterface, Vector3i a_BlockPos)
{
cLeashKnot * LeashKnot = nullptr;
a_WorldInterface.ForEachEntityInBox(cBoundingBox(a_BlockPos, 0.5, 1), [&](cEntity & a_Entity)
{
if (a_Entity.IsLeashKnot())
{
LeashKnot = static_cast<cLeashKnot *>(&a_Entity);
return true;
}
return false;
}
);
return LeashKnot;
}
|