Cuberite
A lightweight, fast and extensible game server for Minecraft
Pawn.cpp
Go to the documentation of this file.
1 
2 #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
3 
4 #include "Pawn.h"
5 #include "Player.h"
6 #include "../World.h"
7 #include "../Bindings/PluginManager.h"
8 #include "../BoundingBox.h"
9 #include "../Blocks/BlockHandler.h"
10 #include "../EffectID.h"
11 #include "../Mobs/Monster.h"
12 
13 
14 
15 
16 cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) :
17  super(a_EntityType, Vector3d(), a_Width, a_Height),
18  m_EntityEffects(tEffectMap()),
19  m_LastGroundHeight(0),
20  m_bTouchGround(false)
21 {
22  SetGravity(-32.0f);
23  SetAirDrag(0.02f);
24 }
25 
26 
27 
28 
29 
31 {
32  ASSERT(m_TargetingMe.size() == 0);
33 }
34 
35 
36 
37 
38 
40 {
43 }
44 
45 
46 
47 
48 
49 void cPawn::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
50 {
51  std::vector<cEntityEffect *> EffectsToTick;
52 
53  // Iterate through this entity's applied effects
54  for (tEffectMap::iterator iter = m_EntityEffects.begin(); iter != m_EntityEffects.end();)
55  {
56  // Copies values to prevent pesky wrong accesses and erasures
57  cEntityEffect::eType EffectType = iter->first;
58  cEntityEffect * Effect = iter->second.get();
59 
60  // Iterates (must be called before any possible erasure)
61  ++iter;
62 
63  // Remove effect if duration has elapsed
64  if (Effect->GetDuration() - Effect->GetTicks() <= 0)
65  {
66  RemoveEntityEffect(EffectType);
67  }
68  // Call OnTick later to make sure the iterator won't be invalid
69  else
70  {
71  EffectsToTick.push_back(Effect);
72  }
73 
74  // TODO: Check for discrepancies between client and server effect values
75  }
76 
77 
78  for (auto * Effect : EffectsToTick)
79  {
80  Effect->OnTick(*this);
81  }
82 
83  // Spectators cannot push entities around
84  if ((!IsPlayer()) || (!static_cast<cPlayer *>(this)->IsGameModeSpectator()))
85  {
87  {
88  if (a_Entity.GetUniqueID() == GetUniqueID())
89  {
90  return false;
91  }
92 
93  // we only push other mobs, boats and minecarts
94  if ((a_Entity.GetEntityType() != etMonster) && (a_Entity.GetEntityType() != etMinecart) && (a_Entity.GetEntityType() != etBoat))
95  {
96  return false;
97  }
98 
99  // do not push a boat / minecart you're sitting in
100  if (IsAttachedTo(&a_Entity))
101  {
102  return false;
103  }
104 
105  Vector3d v3Delta = a_Entity.GetPosition() - GetPosition();
106  v3Delta.y = 0.0; // we only push sideways
107  v3Delta *= 1.0 / (v3Delta.Length() + 0.01); // we push harder if we're close
108  // QUESTION: is there an additional multiplier for this? current shoving seems a bit weak
109 
110  a_Entity.AddSpeed(v3Delta);
111  return false;
112  }
113  );
114  }
115 
116  super::Tick(a_Dt, a_Chunk);
117  if (!IsTicking())
118  {
119  // The base class tick destroyed us
120  return;
121  }
122  HandleFalling();
123 }
124 
125 
126 
127 
128 
130 {
132  super::KilledBy(a_TDI);
133 }
134 
135 
136 
137 
138 
139 bool cPawn::IsFireproof(void) const
140 {
142 }
143 
144 
145 
146 
147 
148 bool cPawn::IsInvisible() const
149 {
151 }
152 
153 
154 
155 
156 
158 {
160  {
161  // Prevent the oxygen from decreasing
162  return;
163  }
164 
166 }
167 
168 
169 
170 
171 
172 void cPawn::AddEntityEffect(cEntityEffect::eType a_EffectType, int a_Duration, short a_Intensity, double a_DistanceModifier)
173 {
174  // Check if the plugins allow the addition:
175  if (cPluginManager::Get()->CallHookEntityAddEffect(*this, a_EffectType, a_Duration, a_Intensity, a_DistanceModifier))
176  {
177  // A plugin disallows the addition, bail out.
178  return;
179  }
180 
181  // No need to add empty effects:
182  if (a_EffectType == cEntityEffect::effNoEffect)
183  {
184  return;
185  }
186  a_Duration = static_cast<int>(a_Duration * a_DistanceModifier);
187 
188  // If we already have the effect, we have to deactivate it or else it will act cumulatively
189  auto ExistingEffect = m_EntityEffects.find(a_EffectType);
190  if (ExistingEffect != m_EntityEffects.end())
191  {
192  ExistingEffect->second->OnDeactivate(*this);
193  }
194 
195  auto Res = m_EntityEffects.emplace(a_EffectType, cEntityEffect::CreateEntityEffect(a_EffectType, a_Duration, a_Intensity, a_DistanceModifier));
196  m_World->BroadcastEntityEffect(*this, a_EffectType, a_Intensity, a_Duration);
197  cEntityEffect * Effect = Res.first->second.get();
198  Effect->OnActivate(*this);
199 }
200 
201 
202 
203 
204 
206 {
207  m_World->BroadcastRemoveEntityEffect(*this, a_EffectType);
208  auto itr = m_EntityEffects.find(a_EffectType);
209  if (itr != m_EntityEffects.end())
210  {
211  // Erase from effect map before calling OnDeactivate to allow metadata broadcasts (e.g. for invisibility effect)
212  auto Effect = std::move(itr->second);
213  m_EntityEffects.erase(itr);
214  Effect->OnDeactivate(*this);
215  }
216 }
217 
218 
219 
220 
221 
223 {
224  return m_EntityEffects.find(a_EffectType) != m_EntityEffects.end();
225 }
226 
227 
228 
229 
230 
232 {
233  // Iterate through this entity's applied effects
234  for (tEffectMap::iterator iter = m_EntityEffects.begin(); iter != m_EntityEffects.end();)
235  {
236  // Copy values to prevent pesky wrong erasures
237  cEntityEffect::eType EffectType = iter->first;
238 
239  // Iterates (must be called before any possible erasure)
240  ++iter;
241 
242  // Remove effect
243  RemoveEntityEffect(EffectType);
244  }
245 }
246 
247 
248 
249 
250 
252 {
253  ASSERT(IsTicking()); // Our destroy override is supposed to clear all targets before we're destroyed.
254  for (auto i = m_TargetingMe.begin(); i != m_TargetingMe.end(); ++i)
255  {
256  cMonster * Monster = *i;
257  if (Monster == a_Monster)
258  {
259  ASSERT(Monster->GetTarget() != this); // The monster is notifying us it is no longer targeting us, assert if that's a lie
260  m_TargetingMe.erase(i);
261  return;
262  }
263  }
264  ASSERT(false); // If this happens, something is wrong. Perhaps the monster never called TargetingMe() or called NoLongerTargetingMe() twice.
265 }
266 
267 
268 
269 
270 
271 void cPawn::TargetingMe(cMonster * a_Monster)
272 {
273  ASSERT(IsTicking());
274  ASSERT(m_TargetingMe.size() < 10000);
275  ASSERT(a_Monster->GetTarget() == this);
276  m_TargetingMe.push_back(a_Monster);
277 }
278 
279 
280 
281 
282 
284 {
285  /* Not pretty looking, and is more suited to wherever server-sided collision detection is implemented.
286  The following condition sets on-ground-ness if
287  The player isn't swimming or flying (client hardcoded conditions) and
288  they're on a block (Y is exact) - ensure any they could be standing on (including on the edges) is solid or
289  they're on a slab (Y significand is 0.5) - ditto with slab check
290  they're on a snow layer (Y divisible by 0.125) - ditto with snow layer check
291  */
292 
293  static const auto HalfWidth = GetWidth() / 2;
294  static const auto EPS = 0.0001;
295 
296  /* Since swimming is decided in a tick and is asynchronous to this, we have to check for dampeners ourselves.
297  The behaviour as of 1.8.9 is the following:
298  - Landing in water alleviates all fall damage
299  - Passing through any liquid (water + lava) and cobwebs "slows" the player down,
300  i.e. resets the fall distance to that block, but only after checking for fall damage
301  (this means that plummeting into lava will still kill the player via fall damage, although cobwebs
302  will slow players down enough to have multiple updates that keep them alive)
303  - Slime blocks reverse falling velocity, unless it's a crouching player, in which case they act as standard blocks.
304  They also reset the topmost point of the damage calculation with each bounce,
305  so if the block is removed while the player is bouncing or crouches after a bounce, the last bounce's zenith is considered as fall damage.
306 
307  With this in mind, we first check the block at the player's feet, then the one below that (because fences),
308  and decide which behaviour we want to go with.
309  */
311 
312  /* We initialize these with what the foot is really IN, because for sampling we will move down with the epsilon above */
313  bool IsFootInWater = IsBlockWater(BlockAtFoot);
314  bool IsFootInLiquid = IsFootInWater || IsBlockLava(BlockAtFoot) || (BlockAtFoot == E_BLOCK_COBWEB); // okay so cobweb is not _technically_ a liquid...
315  bool IsFootOnSlimeBlock = false;
316 
317  /* The "cross" we sample around to account for the player width/girth */
318  static const struct
319  {
320  int x, z;
321  } CrossSampleCoords[] =
322  {
323  { 0, 0 },
324  { 1, 0 },
325  { -1, 0 },
326  { 0, 1 },
327  { 0, -1 },
328  };
329 
330  /* The blocks we're interested in relative to the player to account for larger than 1 blocks.
331  This can be extended to do additional checks in case there are blocks that are represented as one block
332  in memory but have a hitbox larger than 1 (like fences) */
333  static const struct
334  {
335  int x, y, z;
336  } BlockSampleOffsets[] =
337  {
338  { 0, 0, 0 }, // TODO: something went wrong here (offset 0?)
339  { 0, -1, 0 }, // Potentially causes mis-detection (IsFootInWater) when player stands on block diagonal to water (i.e. on side of pool)
340  };
341 
342  /* Here's the rough outline of how this mechanism works:
343  We take the player's pointlike position (sole of feet), and expand it into a crosslike shape.
344  If any of the five points hit a block, we consider the player to be "on" (or "in") the ground. */
345  bool OnGround = false;
346  for (size_t i = 0; i < ARRAYCOUNT(CrossSampleCoords); i++)
347  {
348  /* We calculate from the player's position, one of the cross-offsets above, and we move it down slightly so it's beyond inaccuracy.
349  The added advantage of this method is that if the player is simply standing on the floor,
350  the point will move into the next block, and the floor() will retrieve that instead of air. */
351  Vector3d CrossTestPosition = GetPosition() + Vector3d(CrossSampleCoords[i].x * HalfWidth, -EPS, CrossSampleCoords[i].z * HalfWidth);
352 
353  /* We go through the blocks that we consider "relevant" */
354  for (size_t j = 0; j < ARRAYCOUNT(BlockSampleOffsets); j++)
355  {
356  Vector3i BlockTestPosition = CrossTestPosition.Floor() + Vector3i(BlockSampleOffsets[j].x, BlockSampleOffsets[j].y, BlockSampleOffsets[j].z);
357 
358  if (!cChunkDef::IsValidHeight(BlockTestPosition.y))
359  {
360  continue;
361  }
362 
363  BLOCKTYPE Block = GetWorld()->GetBlock(BlockTestPosition);
364  NIBBLETYPE BlockMeta = GetWorld()->GetBlockMeta(BlockTestPosition);
365 
366  /* we do the cross-shaped sampling to check for water / liquids, but only on our level because water blocks are never bigger than unit voxels */
367  if (j == 0)
368  {
369  IsFootInWater |= IsBlockWater(Block);
370  IsFootInLiquid |= IsFootInWater || IsBlockLava(Block) || (Block == E_BLOCK_COBWEB); // okay so cobweb is not _technically_ a liquid...
371  IsFootOnSlimeBlock |= (Block == E_BLOCK_SLIME_BLOCK);
372  }
373 
374  /* If the block is solid, and the blockhandler confirms the block to be inside, we're officially on the ground. */
375  if ((cBlockInfo::IsSolid(Block)) && (cBlockInfo::GetHandler(Block)->IsInsideBlock(CrossTestPosition - BlockTestPosition, Block, BlockMeta)))
376  {
377  OnGround = true;
378  }
379  }
380  }
381 
382  /* So here's the use of the rules above: */
383  /* 1. Falling in water absorbs all fall damage */
384  bool FallDamageAbsorbed = IsFootInWater;
385 
386  /* 2. Falling in liquid (lava, water, cobweb) or hitting a slime block resets the "fall zenith".
387  Note: Even though the pawn bounces back with no damage after hitting the slime block,
388  the "fall zenith" will continue to increase back up when flying upwards - which is good */
389  bool ShouldBounceOnSlime = true;
390 
391  if (IsPlayer())
392  {
393  auto Player = static_cast<cPlayer *>(this);
394 
395  /* 3. If the player is flying or climbing, absorb fall damage */
396  FallDamageAbsorbed |= Player->IsFlying() || Player->IsClimbing();
397 
398  /* 4. If the player is about to bounce on a slime block and is not crouching, absorb all fall damage */
399  ShouldBounceOnSlime = !Player->IsCrouched();
400  FallDamageAbsorbed |= (IsFootOnSlimeBlock && ShouldBounceOnSlime);
401  }
402  else
403  {
404  /* 5. Bouncing on a slime block absorbs all fall damage */
405  FallDamageAbsorbed |= IsFootOnSlimeBlock;
406  }
407 
408  /* If the player is not crouching or is not a player, shoot them back up.
409  NOTE: this will only work in some cases; should be done in HandlePhysics() */
410  if (IsFootOnSlimeBlock && ShouldBounceOnSlime)
411  {
412  // TODO: doesn't work too well, causes dissatisfactory experience for players on slime blocks - SetSpeedY(-GetSpeedY());
413  }
414 
415  // TODO: put player speed into GetSpeedY, and use that.
416  // If flying, climbing, swimming, or going up...
417  if (FallDamageAbsorbed || ((GetPosition() - m_LastPosition).y > 0))
418  {
419  // ...update the ground height to have the highest position of the player (i.e. jumping up adds to the eventual fall damage)
421  }
422 
423  if (OnGround)
424  {
425  auto Damage = static_cast<int>(m_LastGroundHeight - GetPosY() - 3.0);
426  if ((Damage > 0) && !FallDamageAbsorbed)
427  {
428  TakeDamage(dtFalling, nullptr, Damage, static_cast<float>(Damage), 0);
429 
430  // Fall particles
432  "blockdust",
433  GetPosition(),
434  { 0, 0, 0 },
435  (Damage - 1.f) * ((0.3f - 0.1f) / (15.f - 1.f)) + 0.1f, // Map damage (1 - 15) to particle speed (0.1 - 0.3)
436  static_cast<int>((Damage - 1.f) * ((50.f - 20.f) / (15.f - 1.f)) + 20.f), // Map damage (1 - 15) to particle quantity (20 - 50)
437  { { GetWorld()->GetBlock(POS_TOINT - Vector3i(0, 1, 0)), 0 } }
438  );
439  }
440 
441  m_bTouchGround = true;
443  }
444  else
445  {
446  m_bTouchGround = false;
447  }
448 
449  /* Note: it is currently possible to fall through lava and still die from fall damage
450  because of the client skipping an update about the lava block. This can only be resolved by
451  somehow integrating these above checks into the tracer in HandlePhysics. */
452 }
453 
454 
455 
456 
457 
459 {
460  std::vector<cMonster*>::iterator i = m_TargetingMe.begin();
461  while (i != m_TargetingMe.end())
462  {
463  cMonster * Monster = *i;
464  ASSERT(Monster->GetTarget() == this);
465  Monster->UnsafeUnsetTarget();
466  i = m_TargetingMe.erase(i);
467  }
468  ASSERT(m_TargetingMe.size() == 0);
469 }
470 
471 
472 
473 
474 
475 std::map<cEntityEffect::eType, cEntityEffect *> cPawn::GetEntityEffects()
476 {
477  std::map<cEntityEffect::eType, cEntityEffect *> Effects;
478  for (auto & Effect : m_EntityEffects)
479  {
480  Effects.insert({ Effect.first, Effect.second.get() });
481  }
482  return Effects;
483 }
484 
485 
486 
487 
488 
490 {
491  auto itr = m_EntityEffects.find(a_EffectType);
492  return (itr != m_EntityEffects.end()) ? itr->second.get() : nullptr;
493 }
494 
495 
496 
497 
498 
499 void cPawn::ResetPosition(Vector3d a_NewPosition)
500 {
501  super::ResetPosition(a_NewPosition);
503 }
eType
All types of entity effects (numbers correspond to protocol / storage types)
Definition: EntityEffect.h:11
virtual bool IsHeadInWater(void) const
Returns true if any part of the entity is in a water block.
Definition: Entity.h:503
double GetPosY(void) const
Definition: Entity.h:207
virtual void BroadcastEntityEffect(const cEntity &a_Entity, int a_EffectID, int a_Amplifier, int a_Duration, const cClientHandle *a_Exclude=nullptr) override
virtual bool IsFireproof(void) const override
Definition: Pawn.cpp:139
virtual bool ForEachEntityInBox(const cBoundingBox &a_Box, cEntityCallback a_Callback) override
Calls the callback for each entity that has a nonempty intersection with the specified boundingbox...
Definition: World.cpp:2729
BLOCKTYPE GetBlock(Vector3i a_BlockPos)
Returns the block type at the specified position.
Definition: World.h:416
eEntityType GetEntityType(void) const
Definition: Entity.h:168
Vector3< int > Floor(void) const
Returns a new Vector3i with coords set to std::floor() of this vector&#39;s coords.
Definition: Vector3.h:172
bool IsBlockWater(BLOCKTYPE a_BlockType)
Definition: Defines.h:436
static bool IsSolid(BLOCKTYPE a_Type)
Definition: BlockInfo.h:48
virtual void OnActivate(cPawn &a_Target)
Called when the effect is first added to an entity.
Definition: EntityEffect.h:108
virtual void HandleAir(void)
Called in each tick to handle air-related processing i.e.
Definition: Entity.cpp:1709
int GetDuration(void) const
Returns the duration of the effect.
Definition: EntityEffect.h:90
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:42
double m_LastGroundHeight
Definition: Pawn.h:77
static cBlockHandler * GetHandler(BLOCKTYPE a_Type)
Definition: BlockInfo.h:57
virtual void KilledBy(TakeDamageInfo &a_TDI)
Called when the health drops below zero.
Definition: Entity.cpp:791
cWorld * m_World
Definition: Entity.h:620
static bool IsValidHeight(int a_Height)
Validates a height-coordinate.
Definition: ChunkDef.h:212
bool IsAttachedTo(const cEntity *a_Entity) const
Returns true if this entity is attached to the specified entity.
Definition: Entity.cpp:2014
Definition: Player.h:27
cPawn * GetTarget()
Returns the current target.
Definition: Monster.cpp:1120
virtual void BroadcastRemoveEntityEffect(const cEntity &a_Entity, int a_EffectID, const cClientHandle *a_Exclude=nullptr) override
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
Definition: ChunkDef.h:45
virtual void Destroyed(void)
Definition: Entity.h:674
Represents two sets of coords, minimum and maximum for each direction.
Definition: BoundingBox.h:23
void RemoveEntityEffect(cEntityEffect::eType a_EffectType)
Removes a currently applied entity effect.
Definition: Pawn.cpp:205
double GetWidth(void) const
Definition: Entity.h:216
static std::unique_ptr< cEntityEffect > CreateEntityEffect(cEntityEffect::eType a_EffectType, int a_Duration, short a_Intensity, double a_DistanceModifier)
Creates a pointer to the proper entity effect from the effect type.
static cPluginManager * Get(void)
Returns the instance of the Plugin Manager (there is only ever one)
Definition: Chunk.h:49
virtual void Destroyed() override
Definition: Pawn.cpp:39
T y
Definition: Vector3.h:17
NIBBLETYPE GetBlockMeta(Vector3i a_BlockPos)
Returns the block meta at the specified position.
Definition: World.h:431
void NoLongerTargetingMe(cMonster *a_Monster)
Remove the monster from the list of monsters targeting this pawn.
Definition: Pawn.cpp:251
double GetHeight(void) const
Definition: Entity.h:204
Vector3d m_LastPosition
Definition: Entity.h:616
void UnsafeUnsetTarget()
Unset the target without notifying the target entity.
Definition: Monster.cpp:1111
bool HasEntityEffect(cEntityEffect::eType a_EffectType) const
Returns true, if the entity effect is currently applied.
Definition: Pawn.cpp:222
void AddEntityEffect(cEntityEffect::eType a_EffectType, int a_EffectDurationTicks, short a_EffectIntensity, double a_DistanceModifier=1)
Applies an entity effect.
Definition: Pawn.cpp:172
bool IsTicking(void) const
Returns true if the entity is valid and ticking.
Definition: Entity.cpp:2201
bool IsFlying(void) const
Returns true if the player is currently flying.
Definition: Player.h:345
cEntityEffect * GetEntityEffect(cEntityEffect::eType a_EffectType)
Returns the entity effect, if it is currently applied or nullptr if not.
Definition: Pawn.cpp:489
double Length(void) const
Definition: Vector3.h:95
Vector3< double > Vector3d
Definition: Vector3.h:445
void AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ)
Definition: Entity.cpp:2136
virtual void HandleAir(void) override
Called in each tick to handle air-related processing i.e.
Definition: Pawn.cpp:157
virtual void HandleFalling(void)
Definition: Pawn.cpp:283
#define POSY_TOINT
Definition: Entity.h:30
#define ASSERT(x)
Definition: Globals.h:335
bool m_bTouchGround
Definition: Pawn.h:78
virtual void ResetPosition(Vector3d a_NewPosition) override
Set the entities position and last sent position.
Definition: Pawn.cpp:499
virtual bool IsInvisible() const override
Definition: Pawn.cpp:148
eEntityType
Definition: Entity.h:77
int GetTicks(void) const
Returns how many ticks this effect has been active for.
Definition: EntityEffect.h:87
void SetGravity(float a_Gravity)
Definition: Entity.h:288
virtual void BroadcastParticleEffect(const AString &a_ParticleName, Vector3f a_Src, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, const cClientHandle *a_Exclude=nullptr) override
virtual void ResetPosition(Vector3d a_NewPos)
Set the entities position and last sent position.
Definition: Entity.cpp:1768
cPawn(eEntityType a_EntityType, double a_Width, double a_Height)
Definition: Pawn.cpp:16
std::map< cEntityEffect::eType, std::unique_ptr< cEntityEffect > > tEffectMap
Definition: Pawn.h:74
void TakeDamage(cEntity &a_Attacker)
Makes this pawn take damage from an attack by a_Attacker.
Definition: Entity.cpp:269
bool IsPlayer(void) const
Definition: Entity.h:171
virtual ~cPawn() override
Definition: Pawn.cpp:30
tEffectMap m_EntityEffects
Definition: Pawn.h:75
std::vector< cMonster * > m_TargetingMe
A list of all monsters that are targeting this pawn.
Definition: Pawn.h:85
Definition: Entity.h:73
const Vector3d & GetPosition(void) const
Exported in ManualBindings.
Definition: Entity.h:307
void SetAirDrag(float a_AirDrag)
Definition: Entity.h:292
Vector3< int > Vector3i
Definition: Vector3.h:447
void TargetingMe(cMonster *a_Monster)
Add the monster to the list of monsters targeting this pawn.
Definition: Pawn.cpp:271
virtual bool IsFireproof(void) const
Definition: Entity.h:422
void StopEveryoneFromTargetingMe()
Tells all pawns which are targeting us to stop targeting us.
Definition: Pawn.cpp:458
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk &a_Chunk) override
Definition: Pawn.cpp:49
#define POS_TOINT
Definition: Entity.h:32
virtual void KilledBy(TakeDamageInfo &a_TDI) override
Called when the health drops below zero.
Definition: Pawn.cpp:129
virtual bool IsInsideBlock(Vector3d a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta)
Tests if a_Position is inside the block where a_Position is relative to the origin of the block Note ...
#define ARRAYCOUNT(X)
Evaluates to the number of elements in an array (compile-time!)
Definition: Globals.h:290
std::map< cEntityEffect::eType, cEntityEffect * > GetEntityEffects()
Returns all entity effects.
Definition: Pawn.cpp:475
void ClearEntityEffects(void)
Removes all currently applied entity effects (used when drinking milk)
Definition: Pawn.cpp:231
UInt32 GetUniqueID(void) const
Definition: Entity.h:261
cWorld * GetWorld(void) const
Definition: Entity.h:201
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk &a_Chunk)
Definition: Entity.cpp:841
bool IsBlockLava(BLOCKTYPE a_BlockType)
Definition: Defines.h:475