Cuberite
A lightweight, fast and extensible game server for Minecraft
Entity.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 "Entity.h"
5 #include "Player.h"
6 #include "../BlockInfo.h"
7 #include "../World.h"
8 #include "../Root.h"
9 #include "../Matrix4.h"
10 #include "../ClientHandle.h"
11 #include "../Chunk.h"
12 #include "../Simulator/FluidSimulator.h"
13 #include "../Bindings/PluginManager.h"
14 #include "../LineBlockTracer.h"
15 #include "../Items/ItemHandler.h"
16 #include "../FastRandom.h"
17 #include "../NetherPortalScanner.h"
18 #include "../BoundingBox.h"
19 #include "../WorldStorage/NamespaceSerializer.h"
20 
21 
22 
23 
24 static UInt32 GetNextUniqueID(void)
25 {
26  static std::atomic<UInt32> counter(1);
27  return counter.fetch_add(1);
28 }
29 
30 
31 
32 
33 
35 // cEntity:
36 
37 cEntity::cEntity(eEntityType a_EntityType, Vector3d a_Pos, float a_Width, float a_Height):
38  m_UniqueID(GetNextUniqueID()),
39  m_Health(1),
40  m_MaxHealth(1),
41  m_AttachedTo(nullptr),
42  m_Attachee(nullptr),
43  m_bDirtyHead(false),
44  m_bDirtyOrientation(false),
45  m_bHasSentNoSpeed(true),
46  m_bOnGround(false),
47  m_Gravity(-9.81f),
48  m_AirDrag(0.02f),
49  m_LastSentPosition(a_Pos),
50  m_LastPosition(a_Pos),
51  m_EntityType(a_EntityType),
52  m_World(nullptr),
53  m_IsFireproof(false),
54  m_TicksSinceLastBurnDamage(0),
55  m_TicksSinceLastLavaDamage(0),
56  m_TicksSinceLastFireDamage(0),
57  m_TicksLeftBurning(0),
58  m_TicksSinceLastVoidDamage(0),
59  m_IsInFire(false),
60  m_IsInLava(false),
61  m_IsInWater(false),
62  m_IsHeadInWater(false),
63  m_AirLevel(MAX_AIR_LEVEL),
64  m_AirTickTimer(DROWNING_TICKS),
65  m_TicksAlive(0),
66  m_IsTicking(false),
67  m_ParentChunk(nullptr),
68  m_HeadYaw(0.0),
69  m_Rot(0.0, 0.0, 0.0),
70  m_Position(a_Pos),
71  m_WaterSpeed(0, 0, 0),
72  m_Mass (0.001), // Default 1g
73  m_Width(a_Width),
74  m_Height(a_Height),
75  m_InvulnerableTicks(0)
76 {
77  m_WorldChangeInfo.m_NewWorld = nullptr;
78 }
79 
80 
81 
82 
83 
84 const char * cEntity::GetClass(void) const
85 {
86  return "cEntity";
87 }
88 
89 
90 
91 
92 
93 const char * cEntity::GetClassStatic(void)
94 {
95  return "cEntity";
96 }
97 
98 
99 
100 
101 
102 const char * cEntity::GetParentClass(void) const
103 {
104  return "";
105 }
106 
107 
108 
109 
110 
111 bool cEntity::Initialize(OwnedEntity a_Self, cWorld & a_EntityWorld)
112 {
113  if (cPluginManager::Get()->CallHookSpawningEntity(a_EntityWorld, *this))
114  {
115  return false;
116  }
117 
118  /*
119  // DEBUG:
120  FLOGD("Initializing entity #{0} ({1}) at {2:.02f}",
121  m_UniqueID, GetClass(), m_Pos
122  );
123  */
124 
125 
126  ASSERT(a_Self->IsPlayer() || (m_World == nullptr)); // Players' worlds are loaded from disk.
127  ASSERT(GetParentChunk() == nullptr);
128  SetWorld(&a_EntityWorld);
129  a_EntityWorld.AddEntity(std::move(a_Self));
130 
131  return true;
132 }
133 
134 
135 
136 
137 
139 {
140  m_Spectators.push_back(&a_Player);
141 }
142 
143 
144 
145 
146 
148 {
149  // Spawn the entity on the clients:
151  a_World.BroadcastSpawnEntity(*this);
153 }
154 
155 
156 
157 
158 
160 {
161  const auto Spectator = std::find(m_Spectators.begin(), m_Spectators.end(), &a_Player);
162 
163  ASSERT(Spectator != m_Spectators.end());
164  std::swap(*Spectator, m_Spectators.back());
165  m_Spectators.pop_back();
166 }
167 
168 
169 
170 
171 
173 {
174  // Remove all mobs from the leashed list of mobs:
175  while (!m_LeashedMobs.empty())
176  {
177  m_LeashedMobs.front()->Unleash(false, true);
178  }
179 
180  for (const auto Player : m_Spectators)
181  {
182  Player->OnLoseSpectated();
183  }
184 
185  m_Spectators.clear();
186 
187  if (m_AttachedTo != nullptr)
188  {
189  Detach();
190  }
191 
192  if (m_Attachee != nullptr)
193  {
194  m_Attachee->Detach();
195  }
196 
197  a_World.BroadcastDestroyEntity(*this);
198 }
199 
200 
201 
202 
203 
205 {
207 }
208 
209 
210 
211 
212 
214 {
217 }
218 
219 
220 
221 
222 
224 {
225  m_Speed.x = Clamp(m_Speed.x, -78.0, 78.0);
226  m_Speed.y = Clamp(m_Speed.y, -78.0, 78.0);
227  m_Speed.z = Clamp(m_Speed.z, -78.0, 78.0);
228 }
229 
230 
231 
232 
233 
235 {
236  m_ParentChunk = a_Chunk;
237 }
238 
239 
240 
241 
242 
244 {
245  SetIsTicking(false);
246 
247  // Unleash leashed mobs
248  while (!m_LeashedMobs.empty())
249  {
250  m_LeashedMobs.front()->Unleash(true, true);
251  }
252 
253  auto ParentChunkCoords = cChunkDef::BlockToChunk(GetPosition());
254  m_World->QueueTask([this, ParentChunkCoords](cWorld & a_World)
255  {
256  LOGD("Destroying entity #%i (%s) from chunk (%d, %d)",
257  this->GetUniqueID(), this->GetClass(),
258  ParentChunkCoords.m_ChunkX, ParentChunkCoords.m_ChunkZ
259  );
260  UNUSED(ParentChunkCoords); // Non Debug mode only
261 
262  // Make sure that RemoveEntity returned a valid smart pointer
263  // Also, not storing the returned pointer means automatic destruction
264  VERIFY(a_World.RemoveEntity(*this));
265  });
266 }
267 
268 
269 
270 
271 
272 void cEntity::TakeDamage(cEntity & a_Attacker)
273 {
274  int RawDamage = a_Attacker.GetRawDamageAgainst(*this);
275  TakeDamage(dtAttack, &a_Attacker, RawDamage, a_Attacker.GetKnockbackAmountAgainst(*this));
276 }
277 
278 
279 
280 
281 
282 void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, double a_KnockbackAmount)
283 {
284  float FinalDamage = static_cast<float>(a_RawDamage);
285  float ArmorCover = GetArmorCoverAgainst(a_Attacker, a_DamageType, a_RawDamage);
286 
287  ApplyArmorDamage(static_cast<int>(ArmorCover));
288 
289  cEntity::TakeDamage(a_DamageType, a_Attacker, a_RawDamage, FinalDamage, a_KnockbackAmount);
290 }
291 
292 
293 
294 
295 
296 void cEntity::TakeDamage(eDamageType a_DamageType, UInt32 a_AttackerID, int a_RawDamage, double a_KnockbackAmount)
297 {
298  m_World->DoWithEntityByID(a_AttackerID, [=](cEntity & a_Attacker)
299  {
300  cPawn * Attacker;
301  if (a_Attacker.IsPawn())
302  {
303  Attacker = static_cast<cPawn*>(&a_Attacker);
304  }
305  else
306  {
307  Attacker = nullptr;
308  }
309 
310  TakeDamage(a_DamageType, Attacker, a_RawDamage, a_KnockbackAmount);
311  return true;
312  }
313  );
314 }
315 
316 
317 
318 
319 
320 void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, float a_FinalDamage, double a_KnockbackAmount)
321 {
322  TakeDamageInfo TDI;
323  TDI.DamageType = a_DamageType;
324  if ((a_Attacker != nullptr) && a_Attacker->IsPawn())
325  {
326  TDI.Attacker = a_Attacker;
327  }
328  else
329  {
330  TDI.Attacker = nullptr;
331  }
332 
333  if (a_RawDamage <= 0)
334  {
335  a_RawDamage = 0;
336  }
337 
338  TDI.RawDamage = a_RawDamage;
339 
340  if (a_FinalDamage <= 0)
341  {
342  a_FinalDamage = 0;
343  }
344 
345  TDI.FinalDamage = a_FinalDamage;
346 
347  Vector3d Heading(0, 0, 0);
348  if (a_Attacker != nullptr)
349  {
350  Heading = a_Attacker->GetLookVector();
351  }
352 
353  int KnockbackHeight = 3;
354 
355  if (IsPlayer())
356  {
357  KnockbackHeight = 8;
358  }
359 
360  // Apply slight height to knockback
361  Vector3d FinalKnockback = Vector3d(Heading.x * a_KnockbackAmount, Heading.y + KnockbackHeight, Heading.z * a_KnockbackAmount);
362 
363  TDI.Knockback = FinalKnockback;
364  DoTakeDamage(TDI);
365 }
366 
367 
368 
369 
370 
372 {
373  const double EPS = 0.0000001;
374  if ((std::abs(m_Speed.x) < EPS) && (std::abs(m_Speed.z) < EPS))
375  {
376  // atan2() may overflow or is undefined, pick any number
377  SetYaw(0);
378  return;
379  }
380  SetYaw(atan2(m_Speed.x, m_Speed.z) * 180 / M_PI);
381 }
382 
383 
384 
385 
386 
388 {
389  const double EPS = 0.0000001;
390  double xz = sqrt(m_Speed.x * m_Speed.x + m_Speed.z * m_Speed.z); // Speed XZ-plane component
391  if ((std::abs(xz) < EPS) && (std::abs(m_Speed.y) < EPS))
392  {
393  // atan2() may overflow or is undefined, pick any number
394  SetPitch(0);
395  return;
396  }
397  SetPitch(atan2(m_Speed.y, xz) * 180 / M_PI);
398 }
399 
400 
401 
402 
403 
405 {
406  if (m_Health <= 0)
407  {
408  // Can't take damage if already dead
409  return false;
410  }
411 
412  if (m_InvulnerableTicks > 0)
413  {
414  // Entity is invulnerable
415  return false;
416  }
417 
418  if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI))
419  {
420  return false;
421  }
422 
423  if (IsPainting())
424  {
425  KilledBy(a_TDI);
426 
427  if (a_TDI.Attacker != nullptr)
428  {
429  a_TDI.Attacker->Killed(*this, a_TDI.DamageType);
430  }
431 
432  return true;
433  }
434 
435  if ((a_TDI.Attacker != nullptr) && (a_TDI.Attacker->IsPlayer()))
436  {
437  cPlayer * Player = static_cast<cPlayer *>(a_TDI.Attacker);
438 
439  Player->GetEquippedItem().GetHandler().OnEntityAttack(Player, this);
440 
441  // Whether an enchantment boosted this attack's damage.
442  bool MagicalCriticalHit = false;
443 
444  // IsOnGround() only is false if the player is moving downwards
445  // Ref: https://minecraft.wiki/w/Damage#Critical_Hits
446  if (!Player->IsOnGround())
447  {
448  if ((a_TDI.DamageType == dtAttack) || (a_TDI.DamageType == dtArrowAttack))
449  {
450  a_TDI.FinalDamage *= 1.5f; // 150% damage
452  }
453  }
454 
455  const cEnchantments & Enchantments = Player->GetEquippedItem().m_Enchantments;
456 
457  int SharpnessLevel = static_cast<int>(Enchantments.GetLevel(cEnchantments::enchSharpness));
458  int SmiteLevel = static_cast<int>(Enchantments.GetLevel(cEnchantments::enchSmite));
459  int BaneOfArthropodsLevel = static_cast<int>(Enchantments.GetLevel(cEnchantments::enchBaneOfArthropods));
460 
461  if (SharpnessLevel > 0)
462  {
463  MagicalCriticalHit = true;
464  a_TDI.FinalDamage += 1.25f * SharpnessLevel;
465  }
466  else if (SmiteLevel > 0)
467  {
468  if (IsMob())
469  {
470  cMonster * Monster = static_cast<cMonster *>(this);
471  switch (Monster->GetMobType())
472  {
473  case mtSkeleton:
474  case mtWither:
475  case mtZombie:
476  case mtZombiePigman:
477  case mtZombieVillager:
478  {
479  MagicalCriticalHit = true;
480  a_TDI.FinalDamage += 2.5f * SmiteLevel;
481  break;
482  }
483  default: break;
484  }
485  }
486  }
487  else if (BaneOfArthropodsLevel > 0)
488  {
489  if (IsMob())
490  {
491  cMonster * Monster = static_cast<cMonster *>(this);
492  switch (Monster->GetMobType())
493  {
494  case mtSpider:
495  case mtCaveSpider:
496  case mtSilverfish:
497  case mtEndermite:
498  {
499  MagicalCriticalHit = true;
500  a_TDI.FinalDamage += 2.5f * BaneOfArthropodsLevel;
501 
502  // The duration of the effect is a random value between 1 and 1.5 seconds at level I,
503  // increasing the max duration by 0.5 seconds each level.
504  // Ref: https://minecraft.wiki/w/Enchanting#Bane_of_Arthropods
505  int Duration = 20 + GetRandomProvider().RandInt(BaneOfArthropodsLevel * 10); // Duration in ticks.
506  Monster->AddEntityEffect(cEntityEffect::effSlowness, Duration, 4);
507 
508  break;
509  }
510  default: break;
511  }
512  }
513  }
514 
515  int FireAspectLevel = static_cast<int>(Enchantments.GetLevel(cEnchantments::enchFireAspect));
516  if (FireAspectLevel > 0)
517  {
518  int BurnTicks = 3;
519 
520  if (FireAspectLevel > 1)
521  {
522  BurnTicks += 4 * (FireAspectLevel - 1);
523  }
524 
525  if (!IsMob() && !IsInWater())
526  {
527  StartBurning(BurnTicks * 20);
528  }
529  else if (IsMob() && !IsInWater())
530  {
531  cMonster * Monster = static_cast<cMonster *>(this);
532  switch (Monster->GetMobType())
533  {
534  case mtGhast:
535  case mtZombiePigman:
536  case mtMagmaCube:
537  {
538  break;
539  }
540  default:
541  {
542  MagicalCriticalHit = true;
543  StartBurning(BurnTicks * 20);
544  }
545  }
546  }
547  }
548 
549  if (MagicalCriticalHit)
550  {
552  }
553 
554  unsigned int ThornsLevel = 0;
556  for (size_t i = 0; i < ARRAYCOUNT(ArmorItems); i++)
557  {
558  const cItem & Item = ArmorItems[i];
559  ThornsLevel = std::max(ThornsLevel, Item.m_Enchantments.GetLevel(cEnchantments::enchThorns));
560  }
561 
562  if (ThornsLevel > 0)
563  {
564  int Chance = static_cast<int>(ThornsLevel * 15);
565 
566  auto & Random = GetRandomProvider();
567 
568  if (Random.RandBool(Chance / 100.0))
569  {
570  a_TDI.Attacker->TakeDamage(dtAttack, this, 0, Random.RandReal(1.0f, 4.0f), 0);
571  }
572  }
573 
574  Player->GetStatistics().Custom[CustomStatistic::DamageDealt] += FloorC<StatisticsManager::StatValue>(a_TDI.FinalDamage * 10 + 0.5);
575  }
576 
577  m_Health -= a_TDI.FinalDamage;
578  m_Health = std::max(m_Health, 0.0f);
579 
580  // Add knockback:
581  if ((IsMob() || IsPlayer()) && (a_TDI.Attacker != nullptr))
582  {
583  SetSpeed(a_TDI.Knockback);
584  }
585 
586  m_World->BroadcastEntityAnimation(*this, [&a_TDI]
587  {
588  switch (a_TDI.DamageType)
589  {
590  case eDamageType::dtBurning: return EntityAnimation::PawnBurns;
591  case eDamageType::dtDrowning: return EntityAnimation::PawnDrowns;
592  default: return EntityAnimation::PawnHurts;
593  }
594  }());
595 
596  m_InvulnerableTicks = 10;
597 
598  if (m_Health <= 0)
599  {
600  KilledBy(a_TDI);
601 
602  if (a_TDI.Attacker != nullptr)
603  {
604  a_TDI.Attacker->Killed(*this, a_TDI.DamageType);
605  }
606  }
607  return true;
608 }
609 
610 
611 
612 
613 
614 int cEntity::GetRawDamageAgainst(const cEntity & a_Receiver)
615 {
616  // Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items
617  // Ref: https://minecraft.wiki/w/Damage#Dealing_damage as of 2012_12_20
618  switch (this->GetEquippedWeapon().m_ItemType)
619  {
620  case E_ITEM_WOODEN_SWORD: return 4;
621  case E_ITEM_GOLD_SWORD: return 4;
622  case E_ITEM_STONE_SWORD: return 5;
623  case E_ITEM_IRON_SWORD: return 6;
624  case E_ITEM_DIAMOND_SWORD: return 7;
625 
626  case E_ITEM_WOODEN_AXE: return 3;
627  case E_ITEM_GOLD_AXE: return 3;
628  case E_ITEM_STONE_AXE: return 4;
629  case E_ITEM_IRON_AXE: return 5;
630  case E_ITEM_DIAMOND_AXE: return 6;
631 
632  case E_ITEM_WOODEN_PICKAXE: return 2;
633  case E_ITEM_GOLD_PICKAXE: return 2;
634  case E_ITEM_STONE_PICKAXE: return 3;
635  case E_ITEM_IRON_PICKAXE: return 4;
636  case E_ITEM_DIAMOND_PICKAXE: return 5;
637 
638  case E_ITEM_WOODEN_SHOVEL: return 1;
639  case E_ITEM_GOLD_SHOVEL: return 1;
640  case E_ITEM_STONE_SHOVEL: return 2;
641  case E_ITEM_IRON_SHOVEL: return 3;
642  case E_ITEM_DIAMOND_SHOVEL: return 4;
643  }
644  // All other equipped items give a damage of 1:
645  return 1;
646 }
647 
648 
649 
650 
651 
652 void cEntity::ApplyArmorDamage(int DamageBlocked)
653 {
654  // cEntities don't necessarily have armor to damage.
655 }
656 
657 
658 
659 
660 
662 {
663  // Ref.: https://minecraft.wiki/w/Armor#Effects as of 2012_12_20
664  switch (a_DamageType)
665  {
666  case dtOnFire:
667  case dtSuffocating:
668  case dtDrowning: // TODO: This one could be a special case - in various MC versions (PC vs XBox) it is and isn't armor-protected
669  case dtEnderPearl:
670  case dtStarving:
671  case dtInVoid:
672  case dtPoisoning:
673  case dtWithering:
674  case dtPotionOfHarming:
675  case dtFalling:
676  case dtLightning:
677  case dtPlugin:
678  case dtEnvironment:
679  {
680  return false;
681  }
682 
683  case dtAttack:
684  case dtArrowAttack:
685  case dtCactusContact:
686  case dtMagmaContact:
687  case dtLavaContact:
688  case dtFireContact:
689  case dtExplosion:
690  {
691  return true;
692  }
693  }
694  UNREACHABLE("Unsupported damage type");
695 }
696 
697 
698 
699 
700 
701 float cEntity::GetEnchantmentCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage)
702 {
703  int TotalEPF = 0;
704 
706  for (size_t i = 0; i < ARRAYCOUNT(ArmorItems); i++)
707  {
708  const cItem & Item = ArmorItems[i];
709 
710  if ((a_DamageType != dtInVoid) && (a_DamageType != dtAdmin) && (a_DamageType != dtStarving))
711  {
712  TotalEPF += static_cast<int>(Item.m_Enchantments.GetLevel(cEnchantments::enchProtection)) * 1;
713  }
714 
715  if ((a_DamageType == dtBurning) || (a_DamageType == dtFireContact) || (a_DamageType == dtLavaContact) || (a_DamageType == dtMagmaContact))
716  {
717  TotalEPF += static_cast<int>(Item.m_Enchantments.GetLevel(cEnchantments::enchFireProtection)) * 2;
718  }
719 
720  if ((a_DamageType == dtFalling) || (a_DamageType == dtEnderPearl))
721  {
722  TotalEPF += static_cast<int>(Item.m_Enchantments.GetLevel(cEnchantments::enchFeatherFalling)) * 3;
723  }
724 
725  if (a_DamageType == dtExplosion)
726  {
727  TotalEPF += static_cast<int>(Item.m_Enchantments.GetLevel(cEnchantments::enchBlastProtection)) * 2;
728  }
729 
730  // Note: Also blocks against fire charges, etc.
731  if (a_DamageType == dtProjectile)
732  {
733  TotalEPF += static_cast<int>(Item.m_Enchantments.GetLevel(cEnchantments::enchProjectileProtection)) * 2;
734  }
735  }
736  int CappedEPF = std::min(20, TotalEPF);
737  return (a_Damage * CappedEPF / 25.0f);
738 }
739 
740 
741 
742 
743 
745 {
746  UInt32 MaxLevel = 0;
747 
749 
750  for (auto & Item : ArmorItems)
751  {
752  UInt32 Level = Item.m_Enchantments.GetLevel(cEnchantments::enchBlastProtection);
753  if (Level > MaxLevel)
754  {
755  // Get max blast protection
756  MaxLevel = Level;
757  }
758  }
759 
760  // Max blast protect level is 4, each level provide 15% knock back reduction
761  MaxLevel = std::min<UInt32>(MaxLevel, 4);
762  return MaxLevel * 0.15f;
763 }
764 
765 
766 
767 
768 
769 float cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage)
770 {
771  // Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover
772 
773  // Filter out damage types that are not protected by armor:
774  if (!ArmorCoversAgainst(a_DamageType))
775  {
776  return 0;
777  }
778 
779  // Add up all armor points:
780  // Ref.: https://minecraft.wiki/w/Armor#Defense_points
781  int ArmorValue = 0;
782  int Toughness = 0;
783  switch (GetEquippedHelmet().m_ItemType)
784  {
785  case E_ITEM_LEATHER_CAP: ArmorValue += 1; break;
786  case E_ITEM_GOLD_HELMET: ArmorValue += 2; break;
787  case E_ITEM_CHAIN_HELMET: ArmorValue += 2; break;
788  case E_ITEM_IRON_HELMET: ArmorValue += 2; break;
789  case E_ITEM_DIAMOND_HELMET: ArmorValue += 3; Toughness += 2; break;
790  }
791  switch (GetEquippedChestplate().m_ItemType)
792  {
793  case E_ITEM_LEATHER_TUNIC: ArmorValue += 3; break;
794  case E_ITEM_GOLD_CHESTPLATE: ArmorValue += 5; break;
795  case E_ITEM_CHAIN_CHESTPLATE: ArmorValue += 5; break;
796  case E_ITEM_IRON_CHESTPLATE: ArmorValue += 6; break;
797  case E_ITEM_DIAMOND_CHESTPLATE: ArmorValue += 8; Toughness += 2; break;
798  }
799  switch (GetEquippedLeggings().m_ItemType)
800  {
801  case E_ITEM_LEATHER_PANTS: ArmorValue += 2; break;
802  case E_ITEM_GOLD_LEGGINGS: ArmorValue += 3; break;
803  case E_ITEM_CHAIN_LEGGINGS: ArmorValue += 4; break;
804  case E_ITEM_IRON_LEGGINGS: ArmorValue += 5; break;
805  case E_ITEM_DIAMOND_LEGGINGS: ArmorValue += 6; Toughness += 2; break;
806  }
807  switch (GetEquippedBoots().m_ItemType)
808  {
809  case E_ITEM_LEATHER_BOOTS: ArmorValue += 1; break;
810  case E_ITEM_GOLD_BOOTS: ArmorValue += 1; break;
811  case E_ITEM_CHAIN_BOOTS: ArmorValue += 1; break;
812  case E_ITEM_IRON_BOOTS: ArmorValue += 2; break;
813  case E_ITEM_DIAMOND_BOOTS: ArmorValue += 3; Toughness += 2; break;
814  }
815 
816  // TODO: Special armor cases, such as wool, saddles, dog's collar
817  // Ref.: https://minecraft.wiki/w/Armor#Mob_armor as of 2012_12_20
818 
819  float Reduction = std::max(ArmorValue / 5.0f, ArmorValue - a_Damage / (2.0f + Toughness / 4.0f));
820  return (a_Damage * std::min(20.0f, Reduction) / 25.0f);
821 }
822 
823 
824 
825 
826 
827 double cEntity::GetKnockbackAmountAgainst(const cEntity & a_Receiver)
828 {
829  // Default knockback for entities
830  double Knockback = 10;
831 
832  // If we're sprinting, bump up the knockback
833  if (IsSprinting())
834  {
835  Knockback = 15;
836  }
837 
838  // Check for knockback enchantments (punch only applies to shot arrows)
840  unsigned int KnockbackLevelMultiplier = 8;
841 
842  Knockback += KnockbackLevelMultiplier * KnockbackLevel;
843 
844  return Knockback;
845 }
846 
847 
848 
849 
850 
852 {
853  m_Health = 0;
854 
855  cRoot::Get()->GetPluginManager()->CallHookKilling(*this, a_TDI.Attacker, a_TDI);
856 
857  if (m_Health > 0)
858  {
859  // Plugin wants to 'unkill' the pawn. Abort
860  return;
861  }
862 
863  // If the victim is a player the hook is handled by the cPlayer class
864  if (!IsPlayer())
865  {
866  AString emptystring;
867  cRoot::Get()->GetPluginManager()->CallHookKilled(*this, a_TDI, emptystring);
868  }
869 
870  // Drop loot, unless the attacker was a creative mode player:
871  if (
872  (a_TDI.Attacker == nullptr) ||
873  !a_TDI.Attacker->IsPlayer() ||
874  !static_cast<cPlayer *>(a_TDI.Attacker)->IsGameModeCreative()
875  )
876  {
877  cItems Drops;
878  GetDrops(Drops, a_TDI.Attacker);
880  }
881 
882  m_TicksAlive = 0;
884 }
885 
886 
887 
888 
889 
890 void cEntity::Heal(int a_HitPoints)
891 {
892  m_Health += a_HitPoints;
893  m_Health = std::min(m_Health, m_MaxHealth);
894 }
895 
896 
897 
898 
899 
900 void cEntity::SetHealth(float a_Health)
901 {
902  m_Health = Clamp(a_Health, 0.0f, m_MaxHealth);
903 }
904 
905 
906 
907 
908 
909 void cEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
910 {
911  ASSERT(IsTicking());
912  ASSERT(GetWorld() != nullptr);
913  m_TicksAlive++;
914 
915  if (m_InvulnerableTicks > 0)
916  {
918  }
919 
920  // Non-players are destroyed as soon as they fall out of the world:
921  if ((GetPosY() < 0) && (!IsPlayer()))
922  {
923  Destroy();
924  return;
925  }
926 
927  if (m_AttachedTo != nullptr)
928  {
930  }
931  else
932  {
933  if (!a_Chunk.IsValid())
934  {
935  return;
936  }
937 
938  // Position changed -> Super::Tick() called:
940 
941  // Set swim states (water, lava, and fire):
942  SetSwimState(*NextChunk);
943 
944  // Handle catching on fire and burning:
945  TickBurning(*NextChunk);
946 
947  // Damage players if they are in the void
948  if (GetPosY() < VOID_BOUNDARY)
949  {
950  TickInVoid(*NextChunk);
951  }
952  else
953  {
955  }
956 
957  // Handle cactus damage or destruction:
958  if (
959  IsMob() || IsPickup() ||
960  (IsPlayer() && !((static_cast<cPlayer *>(this))->IsGameModeCreative() || (static_cast<cPlayer *>(this))->IsGameModeSpectator()))
961  )
962  {
963  DetectCacti();
964  }
965 
966  // Handle magma block damage
967  if
968  (
969  IsOnGround() &&
970  (
971  (IsMob() && !static_cast<cPawn *>(this)->IsFireproof()) ||
972  (
973  IsPlayer() && !((static_cast<cPlayer *>(this))->IsGameModeCreative() || (static_cast<cPlayer *>(this))->IsGameModeSpectator())
974  && !static_cast<cPlayer *>(this)->IsFireproof()
975  && !static_cast<cPlayer *>(this)->HasEntityEffect(cEntityEffect::effFireResistance)
976  )
977  )
978  )
979  {
980  DetectMagma();
981  }
982 
983  // Handle drowning:
984  if (IsMob() || IsPlayer())
985  {
986  HandleAir();
987  }
988 
989  if (!DetectPortal()) // Our chunk is invalid if we have moved to another world
990  {
991  // None of the above functions changed position, we remain in the chunk of NextChunk
992  HandlePhysics(a_Dt, *NextChunk);
993  }
994  }
995 }
996 
997 
998 
999 
1000 
1001 void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
1002 {
1003  int BlockX = POSX_TOINT;
1004  int BlockY = POSY_TOINT;
1005  int BlockZ = POSZ_TOINT;
1006 
1007  // Position changed -> Super::HandlePhysics() called
1008  GET_AND_VERIFY_CURRENT_CHUNK(NextChunk, BlockX, BlockZ);
1009 
1010  // TODO Add collision detection with entities.
1011  auto DtSec = std::chrono::duration_cast<std::chrono::duration<double>>(a_Dt);
1012  Vector3d NextPos = Vector3d(GetPosX(), GetPosY(), GetPosZ());
1013  Vector3d NextSpeed = Vector3d(GetSpeedX(), GetSpeedY(), GetSpeedZ());
1014 
1015  if ((BlockY >= cChunkDef::Height) || (BlockY < 0))
1016  {
1017  // Outside of the world
1018  AddSpeedY(m_Gravity * DtSec.count());
1019  AddPosition(GetSpeed() * DtSec.count());
1020  return;
1021  }
1022 
1023  int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width);
1024  int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width);
1025  BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ);
1026  BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
1027  if (!cBlockInfo::IsSolid(BlockIn)) // Making sure we are not inside a solid block
1028  {
1029  if (m_bOnGround) // check if it's still on the ground
1030  {
1031  if (!cBlockInfo::IsSolid(BlockBelow)) // Check if block below is air or water.
1032  {
1033  m_bOnGround = false;
1034  }
1035  }
1036  }
1037  else if (!(IsMinecart() || IsTNT() || (IsPickup() && (m_TicksAlive < 15))))
1038  {
1039  // Push out entity.
1040  BLOCKTYPE GotBlock;
1041 
1042  static const struct
1043  {
1044  int x, y, z;
1045  } gCrossCoords[] =
1046  {
1047  { 1, 0, 0},
1048  {-1, 0, 0},
1049  { 0, 0, 1},
1050  { 0, 0, -1},
1051  } ;
1052 
1053  bool IsNoAirSurrounding = true;
1054  for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
1055  {
1056  if (!NextChunk->UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock))
1057  {
1058  // The pickup is too close to an unloaded chunk, bail out of any physics handling
1059  return;
1060  }
1061  if (!cBlockInfo::IsSolid(GotBlock))
1062  {
1063  NextPos.x += gCrossCoords[i].x;
1064  NextPos.z += gCrossCoords[i].z;
1065  IsNoAirSurrounding = false;
1066  break;
1067  }
1068  } // for i - gCrossCoords[]
1069 
1070  if (IsNoAirSurrounding)
1071  {
1072  NextPos.y += 0.5;
1073  }
1074 
1075  m_bHasSentNoSpeed = false; // this unlocks movement sending to client in BroadcastMovementUpdate function
1076  m_bOnGround = true;
1077 
1078  /*
1079  // DEBUG:
1080  FLOGD("Entity #{0} ({1}) is inside a block at {{2}}",
1081  m_UniqueID, GetClass(), Vector3i{BlockX, BlockY, BlockZ}
1082  );
1083  */
1084  }
1085 
1086  if (!m_bOnGround)
1087  {
1088  double fallspeed;
1089  if (IsBlockWater(BlockIn))
1090  {
1091  fallspeed = m_Gravity * DtSec.count() / 3; // Fall 3x slower in water
1092  ApplyFriction(NextSpeed, 0.7, static_cast<float>(DtSec.count()));
1093  }
1094  else if (BlockIn == E_BLOCK_COBWEB)
1095  {
1096  NextSpeed.y *= 0.05; // Reduce overall falling speed
1097  fallspeed = 0; // No falling
1098  }
1099  else
1100  {
1101  // Normal gravity
1102  fallspeed = m_Gravity * DtSec.count();
1103  NextSpeed -= NextSpeed * (m_AirDrag * 20.0f) * DtSec.count();
1104  }
1105  NextSpeed.y += static_cast<float>(fallspeed);
1106 
1107  // A real boat floats
1108  if (IsBoat())
1109  {
1110  // Find top water block and sit there
1111  int NextBlockY = BlockY;
1112  BLOCKTYPE NextBlock = NextChunk->GetBlock(RelBlockX, NextBlockY, RelBlockZ);
1113  while (IsBlockWater(NextBlock))
1114  {
1115  NextBlock = NextChunk->GetBlock(RelBlockX, ++NextBlockY, RelBlockZ);
1116  }
1117  NextPos.y = NextBlockY - 0.5;
1118  NextSpeed.y = 0;
1119  }
1120  }
1121  else
1122  {
1123  ApplyFriction(NextSpeed, 0.7, static_cast<float>(DtSec.count()));
1124  }
1125 
1126  // Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we
1127  // might have different speed modifiers according to terrain.
1128  if (BlockIn == E_BLOCK_COBWEB)
1129  {
1130  NextSpeed.x *= 0.25;
1131  NextSpeed.z *= 0.25;
1132  }
1133 
1134  // Get water direction
1135  Vector3f WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection({BlockX, BlockY, BlockZ});
1136 
1137  m_WaterSpeed *= 0.9; // Reduce speed each tick
1138 
1139  auto AdjustSpeed = [](double & a_WaterSpeed, float a_WaterDir)
1140  {
1141  if (std::abs(a_WaterDir) > (0.05f / 0.4f))
1142  {
1143  a_WaterSpeed = 0.4 * a_WaterDir;
1144  }
1145  else if (std::abs(a_WaterSpeed) < 0.05)
1146  {
1147  a_WaterSpeed = 0.0;
1148  }
1149  };
1150  AdjustSpeed(m_WaterSpeed.x, WaterDir.x);
1151  AdjustSpeed(m_WaterSpeed.z, WaterDir.z);
1152 
1153  NextSpeed += m_WaterSpeed;
1154 
1155  if (NextSpeed.SqrLength() > 0.0f)
1156  {
1157  Vector3d HitCoords;
1158  Vector3i HitBlockCoords;
1159  eBlockFace HitBlockFace;
1160  Vector3d wantNextPos = NextPos + NextSpeed * DtSec.count();
1161  auto isHit = cLineBlockTracer::FirstSolidHitTrace(*GetWorld(), NextPos, wantNextPos, HitCoords, HitBlockCoords, HitBlockFace);
1162  if (isHit)
1163  {
1164  // Set our position to where the block was hit:
1165  NextPos = HitCoords;
1166 
1167  // Avoid movement in the direction of the blockface that has been hit and correct for collision box:
1168  const auto HalfWidth = GetWidth() / 2;
1169  switch (HitBlockFace)
1170  {
1171  case BLOCK_FACE_XM:
1172  {
1173  NextSpeed.x = 0;
1174  NextPos.x -= HalfWidth;
1175  break;
1176  }
1177  case BLOCK_FACE_XP:
1178  {
1179  NextSpeed.x = 0;
1180  NextPos.x += HalfWidth;
1181  break;
1182  }
1183  case BLOCK_FACE_YM:
1184  {
1185  NextSpeed.y = 0;
1186  NextPos.y -= GetHeight();
1187  break;
1188  }
1189  case BLOCK_FACE_YP:
1190  {
1191  NextSpeed.y = 0;
1192  // We hit the ground, adjust the position to the top of the block:
1193  m_bOnGround = true;
1194  NextPos.y = HitBlockCoords.y + 1;
1195  break;
1196  }
1197  case BLOCK_FACE_ZM:
1198  {
1199  NextSpeed.z = 0;
1200  NextPos.z -= HalfWidth;
1201  break;
1202  }
1203  case BLOCK_FACE_ZP:
1204  {
1205  NextSpeed.z = 0;
1206  NextPos.z += HalfWidth;
1207  break;
1208  }
1209  default:
1210  {
1211  break;
1212  }
1213  }
1214  }
1215  else
1216  {
1217  // We didn't hit anything, so move:
1218  NextPos += (NextSpeed * DtSec.count());
1219  }
1220  }
1221 
1222  SetPosition(NextPos);
1223  SetSpeed(NextSpeed);
1224 }
1225 
1226 
1227 
1228 
1229 
1230 void cEntity::ApplyFriction(Vector3d & a_Speed, double a_SlowdownMultiplier, float a_Dt)
1231 {
1232  if (a_Speed.SqrLength() > 0.0004f)
1233  {
1234  a_Speed.x *= a_SlowdownMultiplier / (1 + a_Dt);
1235  if (fabs(a_Speed.x) < 0.05)
1236  {
1237  a_Speed.x = 0;
1238  }
1239  a_Speed.z *= a_SlowdownMultiplier / (1 + a_Dt);
1240  if (fabs(a_Speed.z) < 0.05)
1241  {
1242  a_Speed.z = 0;
1243  }
1244  }
1245 }
1246 
1247 
1248 
1249 
1250 
1252 {
1253  // Remember the current burning state:
1254  bool HasBeenBurning = (m_TicksLeftBurning > 0);
1255 
1256  // Fireproof entities burn out on the next tick
1257  if (IsFireproof())
1258  {
1259  m_TicksLeftBurning = 0;
1260  }
1261 
1262  // Fire is extinguished by rain:
1263  if (a_Chunk.IsWeatherWetAt(cChunkDef::AbsoluteToRelative(GetPosition().Floor(), a_Chunk.GetPos())))
1264  {
1265  m_TicksLeftBurning = 0;
1266  }
1267 
1268  // Do the burning damage:
1269  if (m_TicksLeftBurning > 0)
1270  {
1273  {
1274  if (!IsFireproof())
1275  {
1276  TakeDamage(dtOnFire, nullptr, BURN_DAMAGE, 0);
1277  }
1279  }
1281  }
1282 
1283  if (IsInWater())
1284  {
1285  // Extinguish the fire
1286  m_TicksLeftBurning = 0;
1287  }
1288 
1289  if (IsInLava())
1290  {
1291  // Burn:
1293 
1294  // Periodically damage:
1297  {
1298  if (!IsFireproof())
1299  {
1300  TakeDamage(dtLavaContact, nullptr, LAVA_DAMAGE, 0);
1301  }
1303  }
1304  }
1305  else
1306  {
1308  }
1309 
1310  if (IsInFire())
1311  {
1312  // Burn:
1314 
1315  // Periodically damage:
1318  {
1319  if (!IsFireproof() && !IsInLava())
1320  {
1321  TakeDamage(dtFireContact, nullptr, FIRE_DAMAGE, 0);
1322  }
1324  }
1325  }
1326  else
1327  {
1329  }
1330 
1331  // If just started / finished burning, notify descendants:
1332  if ((m_TicksLeftBurning > 0) && !HasBeenBurning)
1333  {
1334  OnStartedBurning();
1335  }
1336  else if ((m_TicksLeftBurning <= 0) && HasBeenBurning)
1337  {
1339  }
1340 }
1341 
1342 
1343 
1344 
1345 
1347 {
1348  if (m_TicksSinceLastVoidDamage == 20)
1349  {
1350  TakeDamage(dtInVoid, nullptr, 2, 0);
1352  }
1353  else
1354  {
1356  }
1357 }
1358 
1359 
1360 
1361 
1362 
1364 {
1365  int MinX = FloorC(GetPosX() - m_Width / 2);
1366  int MaxX = FloorC(GetPosX() + m_Width / 2);
1367  int MinZ = FloorC(GetPosZ() - m_Width / 2);
1368  int MaxZ = FloorC(GetPosZ() + m_Width / 2);
1369  int MinY = Clamp(POSY_TOINT, 0, cChunkDef::Height - 1);
1370  int MaxY = Clamp(FloorC(GetPosY() + m_Height), 0, cChunkDef::Height - 1);
1371 
1372  for (int x = MinX; x <= MaxX; x++)
1373  {
1374  for (int z = MinZ; z <= MaxZ; z++)
1375  {
1376  for (int y = MinY; y <= MaxY; y++)
1377  {
1378  if (GetWorld()->GetBlock({ x, y, z }) == E_BLOCK_CACTUS)
1379  {
1380  TakeDamage(dtCactusContact, nullptr, 1, 0);
1381  return;
1382  }
1383  } // for y
1384  } // for z
1385  } // for x
1386 }
1387 
1388 
1389 
1390 
1391 
1393 {
1394  int MinX = FloorC(GetPosX() - m_Width / 2);
1395  int MaxX = FloorC(GetPosX() + m_Width / 2);
1396  int MinZ = FloorC(GetPosZ() - m_Width / 2);
1397  int MaxZ = FloorC(GetPosZ() + m_Width / 2);
1398  int MinY = Clamp(POSY_TOINT - 1, 0, cChunkDef::Height - 1);
1399  int MaxY = Clamp(FloorC(GetPosY() + m_Height), 0, cChunkDef::Height - 1);
1400 
1401  for (int x = MinX; x <= MaxX; x++)
1402  {
1403  for (int z = MinZ; z <= MaxZ; z++)
1404  {
1405  for (int y = MinY; y <= MaxY; y++)
1406  {
1407  if (GetWorld()->GetBlock({ x, y, z }) == E_BLOCK_MAGMA)
1408  {
1409  TakeDamage(dtMagmaContact, nullptr, 1, 0);
1410  return;
1411  }
1412  } // for y
1413  } // for z
1414  } // for x
1415 }
1416 
1417 
1418 
1419 
1420 
1422 {
1423  if (GetWorld()->GetDimension() == dimOverworld)
1424  {
1425  if (GetWorld()->GetLinkedNetherWorldName().empty() && GetWorld()->GetLinkedEndWorldName().empty())
1426  {
1427  // Teleportation to either dimension not enabled, don't bother proceeding
1428  return false;
1429  }
1430  }
1431  else if (GetWorld()->GetLinkedOverworldName().empty())
1432  {
1433  // Overworld teleportation disabled, abort
1434  return false;
1435  }
1436 
1437  if (const auto Position = m_Position.Floor(); cChunkDef::IsValidHeight(Position))
1438  {
1439  switch (GetWorld()->GetBlock(Position))
1440  {
1441  case E_BLOCK_NETHER_PORTAL:
1442  {
1444  {
1445  // Just exited a portal, don't teleport again
1446  return false;
1447  }
1448 
1449  if ((m_AttachedTo != nullptr) || (m_Attachee != nullptr))
1450  {
1451  // Don't let attached entities change worlds, like players riding a minecart
1452  return false;
1453  }
1454 
1455  if (IsPlayer() && !(static_cast<cPlayer *>(this))->IsGameModeCreative() && (m_PortalCooldownData.m_TicksDelayed != 80))
1456  {
1457  // Delay teleportation for four seconds if the entity is a non-creative player
1459  return false;
1460  }
1462 
1463  // Nether portal in the nether
1464  if (GetWorld()->GetDimension() == dimNether)
1465  {
1466  if (GetWorld()->GetLinkedOverworldName().empty())
1467  {
1468  return false;
1469  }
1470 
1471  m_PortalCooldownData.m_ShouldPreventTeleportation = true; // Stop portals from working on respawn
1472 
1473  Vector3d TargetPos = GetPosition();
1474  TargetPos.x *= 8.0;
1475  TargetPos.z *= 8.0;
1476 
1477  cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName());
1478  ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
1479  LOGD("Jumping %s -> %s", DimensionToString(dimNether).c_str(), DimensionToString(TargetWorld->GetDimension()).c_str());
1480  new cNetherPortalScanner(*this, *TargetWorld, TargetPos, cChunkDef::Height);
1481  return true;
1482  }
1483  // Nether portal in the overworld
1484  else
1485  {
1486  if (GetWorld()->GetLinkedNetherWorldName().empty())
1487  {
1488  return false;
1489  }
1490 
1492 
1493  Vector3d TargetPos = GetPosition();
1494  TargetPos.x /= 8.0;
1495  TargetPos.z /= 8.0;
1496 
1497  cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedNetherWorldName());
1498  ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
1499  LOGD("Jumping %s -> %s", DimensionToString(dimOverworld).c_str(), DimensionToString(TargetWorld->GetDimension()).c_str());
1500  new cNetherPortalScanner(*this, *TargetWorld, TargetPos, (cChunkDef::Height / 2));
1501  return true;
1502  }
1503  }
1504  case E_BLOCK_END_PORTAL:
1505  {
1507  {
1508  return false;
1509  }
1510 
1511  if ((m_AttachedTo != nullptr) || (m_Attachee != nullptr))
1512  {
1513  // Don't let attached entities change worlds, like players riding a minecart
1514  return false;
1515  }
1516 
1517  // End portal in the end
1518  if (GetWorld()->GetDimension() == dimEnd)
1519  {
1520  if (GetWorld()->GetLinkedOverworldName().empty())
1521  {
1522  return false;
1523  }
1524 
1526 
1527  cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName());
1528  ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
1529  LOGD("Jumping %s -> %s", DimensionToString(dimEnd).c_str(), DimensionToString(TargetWorld->GetDimension()).c_str());
1530 
1531  if (IsPlayer())
1532  {
1533  cPlayer * Player = static_cast<cPlayer *>(this);
1534  if (Player->GetRespawnWorld() == TargetWorld)
1535  {
1536  return MoveToWorld(*TargetWorld, Player->GetLastBedPos());
1537  }
1538  }
1539 
1540  return MoveToWorld(*TargetWorld, false);
1541  }
1542  // End portal in the overworld
1543  else
1544  {
1545  if (GetWorld()->GetLinkedEndWorldName().empty())
1546  {
1547  return false;
1548  }
1549 
1551 
1552  cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedEndWorldName());
1553  ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
1554  LOGD("Jumping %s -> %s", DimensionToString(dimOverworld).c_str(), DimensionToString(TargetWorld->GetDimension()).c_str());
1555  return MoveToWorld(*TargetWorld, false);
1556  }
1557 
1558  }
1559  default: break;
1560  }
1561  }
1562 
1563  // Allow portals to work again
1566  return false;
1567 }
1568 
1569 
1570 
1571 
1572 
1573 void cEntity::DoMoveToWorld(const sWorldChangeInfo & a_WorldChangeInfo)
1574 {
1575  ASSERT(a_WorldChangeInfo.m_NewWorld != nullptr);
1576 
1577  // Reset portal cooldown:
1578  if (a_WorldChangeInfo.m_SetPortalCooldown)
1579  {
1582  }
1583 
1584  if (m_World == a_WorldChangeInfo.m_NewWorld)
1585  {
1586  // Moving to same world, don't need to remove from world
1587  SetPosition(a_WorldChangeInfo.m_NewPosition);
1588  return;
1589  }
1590 
1591  LOGD("Warping entity #%i (%s) from world \"%s\" to \"%s\". Source chunk: (%d, %d) ",
1592  GetUniqueID(), GetClass(),
1593  m_World->GetName(), a_WorldChangeInfo.m_NewWorld->GetName(),
1594  GetChunkX(), GetChunkZ()
1595  );
1596 
1597  // Stop ticking, in preperation for detaching from this world.
1598  SetIsTicking(false);
1599 
1600  // Remove from the old world
1601  const auto OldWorld = m_World;
1602  auto Self = m_World->RemoveEntity(*this);
1603 
1604  // Update entity:
1605  ResetPosition(a_WorldChangeInfo.m_NewPosition);
1606  SetWorld(a_WorldChangeInfo.m_NewWorld);
1607 
1608  // Don't do anything after adding as the old world's CS no longer protects us
1609  a_WorldChangeInfo.m_NewWorld->AddEntity(std::move(Self), OldWorld);
1610 }
1611 
1612 
1613 
1614 
1615 
1616 bool cEntity::MoveToWorld(cWorld & a_World, Vector3d a_NewPosition, bool a_SetPortalCooldown, bool a_ShouldSendRespawn)
1617 {
1618  // Ask the plugins if the entity is allowed to change world
1619  if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, a_World))
1620  {
1621  // A Plugin isn't allowing the entity to change world
1622  return false;
1623  }
1624 
1625  const auto OldWorld = m_WorldChangeInfo.m_NewWorld;
1626 
1627  // Create new world change info
1628  // (The last warp command always takes precedence)
1629  m_WorldChangeInfo = { &a_World, a_NewPosition, a_SetPortalCooldown };
1630 
1631  if (OldWorld != nullptr)
1632  {
1633  // Avoid scheduling multiple warp tasks
1634  // Only move ahead if we came from a "not warping" state
1635  return true;
1636  }
1637 
1638  // TODO: move to capture when C++14
1639  const auto EntityID = GetUniqueID();
1640 
1641  /* Requirements:
1642  Only one world change in-flight at any time
1643  No ticking during world changes
1644  The last invocation takes effect
1645 
1646  As of writing, cWorld ticks entities, clients, and then processes tasks
1647  We may call MoveToWorld (any number of times - consider multiple /portal commands within a tick) in the first and second stages
1648  Queue a task onto the third stage to invoke DoMoveToWorld ONCE with the last given destination world
1649  Store entity IDs in case client tick found the player disconnected and immediately destroys the object
1650 
1651  After the move begins, no further calls to MoveToWorld is possible since neither the client nor entity is ticked
1652  This remains until the warp is complete and the destination world resumes ticking.
1653  */
1654  GetWorld()->QueueTask(
1655  [EntityID](cWorld & a_CurWorld)
1656  {
1657  a_CurWorld.DoWithEntityByID(
1658  EntityID,
1659  [](cEntity & a_Entity)
1660  {
1661  auto & WCI = a_Entity.m_WorldChangeInfo;
1662  a_Entity.DoMoveToWorld(WCI);
1663  WCI.m_NewWorld = nullptr;
1664  return true;
1665  }
1666  );
1667  }
1668  );
1669 
1670  return true;
1671 }
1672 
1673 
1674 
1675 
1676 
1677 bool cEntity::MoveToWorld(cWorld & a_World, bool a_ShouldSendRespawn)
1678 {
1679  return MoveToWorld(a_World, a_ShouldSendRespawn, Vector3i(a_World.GetSpawnX(), a_World.GetSpawnY(), a_World.GetSpawnZ()));
1680 }
1681 
1682 
1683 
1684 
1685 
1686 bool cEntity::MoveToWorld(const AString & a_WorldName, bool a_ShouldSendRespawn)
1687 {
1688  cWorld * World = cRoot::Get()->GetWorld(a_WorldName);
1689  if (World == nullptr)
1690  {
1691  LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName.c_str());
1692  return false;
1693  }
1694 
1695  return MoveToWorld(*World, Vector3i(World->GetSpawnX(), World->GetSpawnY(), World->GetSpawnZ()), false, a_ShouldSendRespawn);
1696 }
1697 
1698 
1699 
1700 
1701 
1703 {
1704  m_IsInFire = false;
1705  m_IsInLava = false;
1706  m_IsInWater = false;
1707  m_IsHeadInWater = false;
1708 
1709  int RelY = FloorC(GetPosY() + 0.1);
1710  int HeadRelY = CeilC(GetPosY() + GetHeight()) - 1;
1711  ASSERT(RelY <= HeadRelY);
1712  if ((RelY < 0) || (HeadRelY >= cChunkDef::Height))
1713  {
1714  return;
1715  }
1716 
1717  int MinRelX = FloorC(GetPosX() - m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width;
1718  int MaxRelX = FloorC(GetPosX() + m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width;
1719  int MinRelZ = FloorC(GetPosZ() - m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width;
1720  int MaxRelZ = FloorC(GetPosZ() + m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width;
1721  int MinY = Clamp(POSY_TOINT, 0, cChunkDef::Height - 1);
1722  int MaxY = Clamp(FloorC(GetPosY() + m_Height), 0, cChunkDef::Height - 1);
1723 
1724  for (int x = MinRelX; x <= MaxRelX; x++)
1725  {
1726  for (int z = MinRelZ; z <= MaxRelZ; z++)
1727  {
1728  for (int y = MinY; y <= MaxY; y++)
1729  {
1730  BLOCKTYPE Block;
1731  if (!a_Chunk.UnboundedRelGetBlockType(x, y, z, Block))
1732  {
1733  /*
1734  LOGD("SetSwimState failure: RelX = %d, RelY = %d, RelZ = %d, Pos = %.02f, %.02f}",
1735  x, y, z, GetPosX(), GetPosZ()
1736  );
1737  */
1738  continue;
1739  }
1740 
1741  if (Block == E_BLOCK_FIRE)
1742  {
1743  m_IsInFire = true;
1744  }
1745  else if (IsBlockLava(Block))
1746  {
1747  m_IsInLava = true;
1748  }
1749  else if (IsBlockWater(Block))
1750  {
1751  m_IsInWater = true;
1752  }
1753  } // for y
1754  } // for z
1755  } // for x
1756 
1757  // Check if the entity's head is in water.
1758  int RelX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width;
1759  int RelZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width;
1760  int HeadHeight = CeilC(GetPosY() + GetHeight()) - 1;
1761  BLOCKTYPE BlockIn;
1762  if (!a_Chunk.UnboundedRelGetBlockType(RelX, HeadHeight, RelZ, BlockIn))
1763  {
1764  /*
1765  LOGD("SetSwimState failure: RelX = %d, RelY = %d, RelZ = %d, Pos = %.02f, %.02f}",
1766  RelX, HeadHeight, RelZ, GetPosX(), GetPosZ()
1767  );
1768  */
1769  return;
1770  }
1771  m_IsHeadInWater = IsBlockWater(BlockIn);
1772 }
1773 
1774 
1775 
1776 
1777 
1778 void cEntity::SetIsTicking(bool a_IsTicking)
1779 {
1780  m_IsTicking = a_IsTicking;
1781  ASSERT(!(m_IsTicking && (m_ParentChunk == nullptr))); // We shouldn't be ticking if we have no parent chunk
1782 }
1783 
1784 
1785 
1786 
1787 
1788 void cEntity::SetSize(const float a_Width, const float a_Height)
1789 {
1790  m_Width = a_Width;
1791  m_Height = a_Height;
1792 }
1793 
1794 
1795 
1796 
1797 
1799 {
1800  // Ref.: https://minecraft.wiki/w/Chunk_format
1801  // See if the entity is /submerged/ water (head is in water)
1802  // Get the type of block the entity is standing in:
1803 
1804  int RespirationLevel = static_cast<int>(GetEquippedHelmet().m_Enchantments.GetLevel(cEnchantments::enchRespiration));
1805 
1806  if (IsHeadInWater())
1807  {
1808  if (!IsPlayer()) // Players control themselves
1809  {
1810  SetSpeedY(1); // Float in the water
1811  }
1812 
1813  if (RespirationLevel > 0)
1814  {
1815  static_cast<cPawn *>(this)->AddEntityEffect(cEntityEffect::effNightVision, 200, 5, 0);
1816  }
1817 
1818  if (m_AirLevel <= 0)
1819  {
1820  // Runs the air tick timer to check whether the player should be damaged
1821  if (m_AirTickTimer <= 0)
1822  {
1823  // Damage player
1824  TakeDamage(dtDrowning, nullptr, 1, 1, 0);
1825  // Reset timer
1827  }
1828  else
1829  {
1830  m_AirTickTimer--;
1831  }
1832  }
1833  else
1834  {
1835  // Reduce air supply
1836  m_AirLevel--;
1837  }
1838  }
1839  else
1840  {
1841  // Set the air back to maximum
1844 
1845  if (RespirationLevel > 0)
1846  {
1847  m_AirTickTimer = DROWNING_TICKS + (RespirationLevel * 15 * 20);
1848  }
1849 
1850  }
1851 }
1852 
1853 
1854 
1855 
1856 
1858 {
1859  SetPosition(a_NewPos);
1861 }
1862 
1863 
1864 
1865 
1866 
1868 {
1869  // Broadcast the change:
1871 }
1872 
1873 
1874 
1875 
1876 
1878 {
1879  // Broadcast the change:
1881 }
1882 
1883 
1884 
1885 
1886 
1887 void cEntity::SetMaxHealth(float a_MaxHealth)
1888 {
1889  m_MaxHealth = a_MaxHealth;
1890 
1891  // Reset health, if too high:
1892  m_Health = std::min(m_Health, a_MaxHealth);
1893 }
1894 
1895 
1896 
1897 
1898 
1899 void cEntity::SetIsFireproof(bool a_IsFireproof)
1900 {
1901  m_IsFireproof = a_IsFireproof;
1902 }
1903 
1904 
1905 
1906 
1907 
1908 void cEntity::StartBurning(int a_TicksLeftBurning)
1909 {
1910  if (m_TicksLeftBurning > 0)
1911  {
1912  // Already burning, top up the ticks left burning and bail out:
1913  m_TicksLeftBurning = std::max(m_TicksLeftBurning, a_TicksLeftBurning);
1914  return;
1915  }
1916 
1917  m_TicksLeftBurning = a_TicksLeftBurning;
1918  OnStartedBurning();
1919 }
1920 
1921 
1922 
1923 
1924 
1926 {
1927  bool HasBeenBurning = (m_TicksLeftBurning > 0);
1928  m_TicksLeftBurning = 0;
1932 
1933  // Notify if the entity has stopped burning
1934  if (HasBeenBurning)
1935  {
1937  }
1938 }
1939 
1940 
1941 
1942 
1943 
1945 {
1946  TeleportToCoords(a_Entity.GetPosX(), a_Entity.GetPosY(), a_Entity.GetPosZ());
1947 }
1948 
1949 
1950 
1951 
1952 
1953 void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
1954 {
1955  // ask the plugins to allow teleport to the new position.
1956  if (!cRoot::Get()->GetPluginManager()->CallHookEntityTeleport(*this, m_LastPosition, Vector3d(a_PosX, a_PosY, a_PosZ)))
1957  {
1958  SetPosition({a_PosX, a_PosY, a_PosZ});
1959  }
1960 }
1961 
1962 
1963 
1964 
1965 
1967 {
1968  // Process packet sending every two ticks:
1969  if ((GetWorld()->GetWorldTickAge() % 2_tick) != 0_tick)
1970  {
1971  return;
1972  }
1973 
1974  if (m_Speed.HasNonZeroLength())
1975  {
1976  // Movin'
1977  m_World->BroadcastEntityVelocity(*this, a_Exclude);
1978  m_bHasSentNoSpeed = false;
1979  }
1980  else if (!m_bHasSentNoSpeed)
1981  {
1982  // Speed is zero, send this to clients once only as well as an absolute position
1983  m_World->BroadcastEntityVelocity(*this, a_Exclude);
1984  m_World->BroadcastEntityPosition(*this, a_Exclude);
1986  m_bDirtyOrientation = false;
1987  m_bHasSentNoSpeed = true;
1988  }
1989 
1990  if ((m_Position - m_LastSentPosition).HasNonZeroLength()) // Have we moved?
1991  {
1992  m_World->BroadcastEntityPosition(*this, a_Exclude);
1993 
1994  // Clients seem to store two positions, one for the velocity packet and one for the teleport / relmove packet
1995  // The latter is only changed with a relmove / teleport, and m_LastSentPosition stores this position
1997  m_bDirtyOrientation = false;
1998  }
1999 
2000  if (m_bDirtyHead)
2001  {
2002  m_World->BroadcastEntityHeadLook(*this, a_Exclude);
2003  m_bDirtyHead = false;
2004  }
2005 
2006  if (m_bDirtyOrientation)
2007  {
2008  // Send individual update in case above (sending with rel-move packet) wasn't done
2009  GetWorld()->BroadcastEntityLook(*this, a_Exclude);
2010  m_bDirtyOrientation = false;
2011  }
2012 }
2013 
2014 
2015 
2016 
2017 
2019 {
2020  return m_AttachedTo;
2021 }
2022 
2023 
2024 
2025 
2026 
2027 void cEntity::AttachTo(cEntity & a_AttachTo)
2028 {
2029  if (m_AttachedTo == &a_AttachTo)
2030  {
2031  // Already attached to that entity, nothing to do here:
2032  return;
2033  }
2034 
2035  if (m_AttachedTo != nullptr)
2036  {
2037  // Detach from any previous entity:
2038  Detach();
2039  }
2040 
2041  // Update state information:
2042  m_AttachedTo = &a_AttachTo;
2043  a_AttachTo.m_Attachee = this;
2044 
2045  m_World->BroadcastAttachEntity(*this, a_AttachTo);
2046 }
2047 
2048 
2049 
2050 
2051 
2053 {
2054  if (m_AttachedTo == nullptr)
2055  {
2056  // Already not attached to any entity, our work is done:
2057  return;
2058  }
2059 
2061 
2062  m_AttachedTo->m_Attachee = nullptr;
2063  m_AttachedTo = nullptr;
2064 
2065  OnDetach();
2066 }
2067 
2068 
2069 
2070 
2071 
2072 bool cEntity::IsA(const char * a_ClassName) const
2073 {
2074  return ((a_ClassName != nullptr) && (strcmp(a_ClassName, "cEntity") == 0));
2075 }
2076 
2077 
2078 
2079 
2080 
2081 bool cEntity::IsAttachedTo(const cEntity * a_Entity) const
2082 {
2083  return (
2084  (m_AttachedTo != nullptr) &&
2085  (a_Entity->GetUniqueID() == m_AttachedTo->GetUniqueID())
2086  );
2087 }
2088 
2089 
2090 
2091 
2092 
2094 {
2095  return m_bDirtyOrientation;
2096 }
2097 
2098 
2099 
2100 
2101 
2102 void cEntity::SetHeadYaw(double a_HeadYaw)
2103 {
2104  m_HeadYaw = a_HeadYaw;
2105  m_bDirtyHead = true;
2106  WrapHeadYaw();
2107 }
2108 
2109 
2110 
2111 
2112 
2113 void cEntity::SetMass(double a_Mass)
2114 {
2115  // Make sure that mass is not zero. 1g is the default because we
2116  // have to choose a number. It's perfectly legal to have a mass
2117  // less than 1g as long as is NOT equal or less than zero.
2118  m_Mass = std::max(a_Mass, 0.001);
2119 }
2120 
2121 
2122 
2123 
2124 
2125 void cEntity::SetYaw(double a_Yaw)
2126 {
2127  m_Rot.x = a_Yaw;
2128  m_bDirtyOrientation = true;
2129  WrapRotation();
2130 }
2131 
2132 
2133 
2134 
2135 
2136 void cEntity::SetPitch(double a_Pitch)
2137 {
2138  m_Rot.y = a_Pitch;
2139  m_bDirtyOrientation = true;
2140  WrapRotation();
2141 }
2142 
2143 
2144 
2145 
2146 
2147 void cEntity::SetRoll(double a_Roll)
2148 {
2149  m_Rot.z = a_Roll;
2150  m_bDirtyOrientation = true;
2151 }
2152 
2153 
2154 
2155 
2156 
2157 void cEntity::SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ)
2158 {
2159  m_Speed.Set(a_SpeedX, a_SpeedY, a_SpeedZ);
2160  WrapSpeed();
2161 }
2162 
2163 
2164 
2165 
2166 
2167 void cEntity::SetSpeedX(double a_SpeedX)
2168 {
2169  SetSpeed(a_SpeedX, m_Speed.y, m_Speed.z);
2170 }
2171 
2172 
2173 
2174 
2175 
2176 void cEntity::SetSpeedY(double a_SpeedY)
2177 {
2178  SetSpeed(m_Speed.x, a_SpeedY, m_Speed.z);
2179 }
2180 
2181 
2182 
2183 
2184 
2185 void cEntity::SetSpeedZ(double a_SpeedZ)
2186 {
2187  SetSpeed(m_Speed.x, m_Speed.y, a_SpeedZ);
2188 }
2189 
2190 
2191 
2192 
2193 
2194 void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ)
2195 {
2196  SetSpeed(m_Speed.x + a_AddSpeedX, m_Speed.y + a_AddSpeedY, m_Speed.z + a_AddSpeedZ);
2197 }
2198 
2199 
2200 
2201 
2202 
2203 void cEntity::AddSpeedX(double a_AddSpeedX)
2204 {
2205  AddSpeed(a_AddSpeedX, 0, 0);
2206 }
2207 
2208 
2209 
2210 
2211 
2212 void cEntity::AddSpeedY(double a_AddSpeedY)
2213 {
2214  AddSpeed(0, a_AddSpeedY, 0);
2215 }
2216 
2217 
2218 
2219 
2220 
2221 void cEntity::AddSpeedZ(double a_AddSpeedZ)
2222 {
2223  AddSpeed(0, 0, a_AddSpeedZ);
2224 }
2225 
2226 
2227 
2228 
2229 
2230 void cEntity::HandleSpeedFromAttachee(float a_Forward, float a_Sideways)
2231 {
2232  Vector3d LookVector = m_Attachee->GetLookVector();
2233  double AddSpeedX = LookVector.x * a_Forward + LookVector.z * a_Sideways;
2234  double AddSpeedZ = LookVector.z * a_Forward - LookVector.x * a_Sideways;
2237 }
2238 
2239 
2240 
2241 
2242 
2243 void cEntity::SteerVehicle(float a_Forward, float a_Sideways)
2244 {
2245  if (m_AttachedTo == nullptr)
2246  {
2247  return;
2248  }
2249  if ((a_Forward != 0.0f) || (a_Sideways != 0.0f))
2250  {
2251  m_AttachedTo->HandleSpeedFromAttachee(a_Forward, a_Sideways);
2252  }
2253 }
2254 
2255 
2256 
2257 
2258 
2259 bool cEntity::IsTicking(void) const
2260 {
2261  ASSERT(!(m_IsTicking && (m_ParentChunk == nullptr))); // We shouldn't be ticking if we have no parent chunk
2262  return m_IsTicking;
2263 }
2264 
2266 // Get look vector (this is NOT a rotation!)
2268 {
2269  Matrix4d m;
2270  m.Init(Vector3d(), 0, m_Rot.x, -m_Rot.y);
2271  Vector3d Look = m.Transform(Vector3d(0, 0, 1));
2272  return Look;
2273 }
2274 
2275 
2276 
2277 
2278 
2280 // Set position
2281 void cEntity::SetPosition(const Vector3d & a_Position)
2282 {
2283  // Clamp the positions to exactly representable single-precision floating point values
2284  // This is necessary to avoid rounding errors in the noise generator and overflows in the chunk loader
2285  const double MaxFloat = std::pow(2, std::numeric_limits<float>().digits);
2286 
2287  const double ClampedPosX = Clamp(a_Position.x, -MaxFloat, MaxFloat);
2288  const double ClampedPosY = Clamp(a_Position.y, -MaxFloat, MaxFloat);
2289  const double ClampedPosZ = Clamp(a_Position.z, -MaxFloat, MaxFloat);
2290 
2292  m_Position = {ClampedPosX, ClampedPosY, ClampedPosZ};
2293 }
2294 
2295 
2296 
2297 
2298 
2300 {
2301  // Not there already
2302  ASSERT(std::find(m_LeashedMobs.begin(), m_LeashedMobs.end(), a_Monster) == m_LeashedMobs.end());
2303 
2304  m_LeashedMobs.push_back(a_Monster);
2305 }
2306 
2307 
2308 
2309 
2310 
2312 {
2313  ASSERT(a_Monster->GetLeashedTo() == this);
2314 
2315  // Must exists
2316  ASSERT(std::find(m_LeashedMobs.begin(), m_LeashedMobs.end(), a_Monster) != m_LeashedMobs.end());
2317 
2318  m_LeashedMobs.remove(a_Monster);
2319 }
2320 
2321 
2322 
2323 
2324 
2326 {
2327  // If has any mob leashed broadcast every leashed entity to this
2328  if (HasAnyMobLeashed())
2329  {
2330  for (auto LeashedMob : m_LeashedMobs)
2331  {
2332  m_World->BroadcastLeashEntity(*LeashedMob, *this);
2333  }
2334  }
2335 }
2336 
2337 
2338 
2339 
2340 
2342 {
2343 }
2344 
2345 
2346 
2347 
2348 
2350 {
2351  cPluginManager * PluginManager = cRoot::Get()->GetPluginManager();
2352 
2353  AString Name;
2354  if (IsPlayer())
2355  {
2356  cPlayer * Player = static_cast<cPlayer *>(this);
2357  Name = Player->GetName();
2358  }
2359  else if (IsMob())
2360  {
2361  cMonster * Monster = static_cast<cMonster *>(this);
2362  if (Monster->HasCustomName())
2363  {
2364  Name = Monster->GetCustomName();
2365  }
2366  else
2367  {
2369  }
2370  }
2371  else
2372  {
2373  // If the entity is neither a player nor a mob, we should quit.
2374  return;
2375  }
2376 
2377  if (a_TDI.Attacker == nullptr)
2378  {
2379  const AString DamageText = [&]
2380  {
2381  switch (a_TDI.DamageType)
2382  {
2383  case dtRangedAttack: return "was shot";
2384  case dtLightning: return "was plasmified by lightining";
2385  case dtFalling: return GetRandomProvider().RandBool() ? "fell to death" : "hit the ground too hard";
2386  case dtDrowning: return "drowned";
2387  case dtSuffocating: return GetRandomProvider().RandBool() ? "git merge'd into a block" : "fused with a block";
2388  case dtStarving: return "forgot the importance of food";
2389  case dtCactusContact: return "was impaled on a cactus";
2390  case dtMagmaContact: return "discovered the floor was lava";
2391  case dtLavaContact: return "was melted by lava";
2392  case dtPoisoning: return "died from septicaemia";
2393  case dtWithering: return "is a husk of their former selves";
2394  case dtOnFire: return "forgot to stop, drop, and roll";
2395  case dtFireContact: return "burnt themselves to death";
2396  case dtInVoid: return "somehow fell out of the world";
2397  case dtPotionOfHarming: return "was magicked to death";
2398  case dtEnderPearl: return "misused an ender pearl";
2399  case dtAdmin: return "was administrator'd";
2400  case dtExplosion: return "blew up";
2401  case dtAttack: return "was attacked by thin air";
2402  case dtEnvironment: return "played too much dress up"; // This is not vanilla - added a own pun
2403  }
2404  UNREACHABLE("Unsupported damage type");
2405  }();
2406  auto DeathMessage = fmt::format(FMT_STRING("{} {}"), Name, DamageText);
2407  PluginManager->CallHookKilled(*this, a_TDI, DeathMessage);
2408  if (!DeathMessage.empty())
2409  {
2410  GetWorld()->BroadcastChatDeath(DeathMessage);
2411  }
2412  }
2413  else if (a_TDI.Attacker->IsPlayer())
2414  {
2415  cPlayer * Killer = static_cast<cPlayer *>(a_TDI.Attacker);
2416  auto DeathMessage = fmt::format(FMT_STRING("{0} was killed by {1}"), Name, Killer->GetName());
2417  PluginManager->CallHookKilled(*this, a_TDI, DeathMessage);
2418  if (!DeathMessage.empty())
2419  {
2420  GetWorld()->BroadcastChatDeath(DeathMessage);
2421  }
2422  } // This will trigger if a player / tamed pet has been killed by another mob / tamed pet.
2423  else if (a_TDI.Attacker->IsMob())
2424  {
2425  cMonster * Monster = static_cast<cMonster *>(a_TDI.Attacker);
2426 
2427  AString DeathMessage;
2428  if (Monster->HasCustomName())
2429  {
2430  DeathMessage = fmt::format(FMT_STRING("{0} was killed by {1}"), Name, Monster->GetCustomName());
2431  }
2432  else
2433  {
2435  DeathMessage = fmt::format(FMT_STRING("{0} was killed by a {1}"), Name, KillerName);
2436  }
2437 
2438  PluginManager->CallHookKilled(*this, a_TDI, DeathMessage);
2439  if (!DeathMessage.empty())
2440  {
2441  GetWorld()->BroadcastChatDeath(DeathMessage);
2442  }
2443  }
2444 }
static int GetBlock(lua_State *a_LuaState)
Templated bindings for the GetBlock___() functions.
bool IsBlockWater(BLOCKTYPE a_BlockType)
Definition: BlockInfo.cpp:10
bool IsBlockLava(BLOCKTYPE a_BlockType)
Definition: BlockInfo.cpp:49
@ E_BLOCK_NETHER_PORTAL
Definition: BlockType.h:105
@ E_BLOCK_AIR
Definition: BlockType.h:10
@ E_BLOCK_FIRE
Definition: BlockType.h:61
@ E_BLOCK_COBWEB
Definition: BlockType.h:40
@ E_BLOCK_CACTUS
Definition: BlockType.h:95
@ E_BLOCK_END_PORTAL
Definition: BlockType.h:134
@ E_BLOCK_MAGMA
Definition: BlockType.h:232
@ E_ITEM_WOODEN_PICKAXE
Definition: BlockType.h:314
@ E_ITEM_GOLD_BOOTS
Definition: BlockType.h:361
@ E_ITEM_STONE_PICKAXE
Definition: BlockType.h:318
@ E_ITEM_DIAMOND_AXE
Definition: BlockType.h:323
@ E_ITEM_LEATHER_PANTS
Definition: BlockType.h:344
@ E_ITEM_CHAIN_BOOTS
Definition: BlockType.h:349
@ E_ITEM_IRON_LEGGINGS
Definition: BlockType.h:352
@ E_ITEM_DIAMOND_BOOTS
Definition: BlockType.h:357
@ E_ITEM_STONE_AXE
Definition: BlockType.h:319
@ E_ITEM_CHAIN_HELMET
Definition: BlockType.h:346
@ E_ITEM_GOLD_HELMET
Definition: BlockType.h:358
@ E_ITEM_IRON_SWORD
Definition: BlockType.h:311
@ E_ITEM_GOLD_CHESTPLATE
Definition: BlockType.h:359
@ E_ITEM_IRON_HELMET
Definition: BlockType.h:350
@ E_ITEM_DIAMOND_PICKAXE
Definition: BlockType.h:322
@ E_ITEM_IRON_CHESTPLATE
Definition: BlockType.h:351
@ E_ITEM_GOLD_LEGGINGS
Definition: BlockType.h:360
@ E_ITEM_DIAMOND_CHESTPLATE
Definition: BlockType.h:355
@ E_ITEM_DIAMOND_HELMET
Definition: BlockType.h:354
@ E_ITEM_IRON_BOOTS
Definition: BlockType.h:353
@ E_ITEM_GOLD_SHOVEL
Definition: BlockType.h:328
@ E_ITEM_IRON_PICKAXE
Definition: BlockType.h:301
@ E_ITEM_GOLD_PICKAXE
Definition: BlockType.h:329
@ E_ITEM_WOODEN_SHOVEL
Definition: BlockType.h:313
@ E_ITEM_LEATHER_TUNIC
Definition: BlockType.h:343
@ E_ITEM_DIAMOND_SWORD
Definition: BlockType.h:320
@ E_ITEM_IRON_SHOVEL
Definition: BlockType.h:300
@ E_ITEM_STONE_SWORD
Definition: BlockType.h:316
@ E_ITEM_STONE_SHOVEL
Definition: BlockType.h:317
@ E_ITEM_DIAMOND_LEGGINGS
Definition: BlockType.h:356
@ E_ITEM_DIAMOND_SHOVEL
Definition: BlockType.h:321
@ E_ITEM_WOODEN_SWORD
Definition: BlockType.h:312
@ E_ITEM_LEATHER_CAP
Definition: BlockType.h:342
@ E_ITEM_WOODEN_AXE
Definition: BlockType.h:315
@ E_ITEM_GOLD_AXE
Definition: BlockType.h:330
@ E_ITEM_GOLD_SWORD
Definition: BlockType.h:327
@ E_ITEM_CHAIN_CHESTPLATE
Definition: BlockType.h:347
@ E_ITEM_LEATHER_BOOTS
Definition: BlockType.h:345
@ E_ITEM_CHAIN_LEGGINGS
Definition: BlockType.h:348
@ E_ITEM_IRON_AXE
Definition: BlockType.h:302
std::unique_ptr< cEntity > OwnedEntity
Definition: ChunkDef.h:32
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:41
AString DimensionToString(eDimension a_Dimension)
Translates a dimension enum to dimension string.
Definition: Defines.cpp:236
@ EntityGetsMagicalCriticalHit
@ dimEnd
Definition: Defines.h:234
@ dimNether
Definition: Defines.h:232
@ dimOverworld
Definition: Defines.h:233
double NormalizeAngleDegrees(const double a_Degrees)
Normalizes an angle in degrees to the [-180, +180) range:
Definition: Defines.h:629
eDamageType
Damage type, used in the TakeDamageInfo structure and related functions.
Definition: Defines.h:244
@ dtAdmin
Definition: Defines.h:263
@ dtPoisoning
Definition: Defines.h:256
@ dtSuffocating
Definition: Defines.h:251
@ dtMagmaContact
Definition: Defines.h:254
@ dtAttack
Definition: Defines.h:246
@ dtStarving
Definition: Defines.h:252
@ dtExplosion
Definition: Defines.h:264
@ dtInVoid
Definition: Defines.h:260
@ dtFireContact
Definition: Defines.h:259
@ dtEnderPearl
Definition: Defines.h:262
@ dtDrowning
Definition: Defines.h:250
@ dtEnvironment
Definition: Defines.h:265
@ dtBurning
Definition: Defines.h:287
@ dtWithering
Definition: Defines.h:257
@ dtRangedAttack
Definition: Defines.h:247
@ dtArrowAttack
Definition: Defines.h:272
@ dtFalling
Definition: Defines.h:249
@ dtOnFire
Definition: Defines.h:258
@ dtCactusContact
Definition: Defines.h:253
@ dtProjectile
Definition: Defines.h:274
@ dtLavaContact
Definition: Defines.h:255
@ dtLightning
Definition: Defines.h:248
@ dtPotionOfHarming
Definition: Defines.h:261
@ dtPlugin
Definition: Defines.h:289
eBlockFace
Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc.
Definition: Defines.h:38
@ BLOCK_FACE_XP
Definition: Defines.h:41
@ BLOCK_FACE_YP
Definition: Defines.h:43
@ BLOCK_FACE_YM
Definition: Defines.h:42
@ BLOCK_FACE_ZM
Definition: Defines.h:44
@ BLOCK_FACE_ZP
Definition: Defines.h:45
@ BLOCK_FACE_XM
Definition: Defines.h:40
static UInt32 GetNextUniqueID(void)
Definition: Entity.cpp:24
#define POSX_TOINT
Definition: Entity.h:31
#define POSZ_TOINT
Definition: Entity.h:33
#define GET_AND_VERIFY_CURRENT_CHUNK(ChunkVarName, X, Z)
Definition: Entity.h:36
#define POSY_TOINT
Definition: Entity.h:32
MTRand & GetRandomProvider()
Returns the current thread's random number source.
Definition: FastRandom.cpp:12
#define UNREACHABLE(x)
Definition: Globals.h:288
#define ARRAYCOUNT(X)
Evaluates to the number of elements in an array (compile-time!)
Definition: Globals.h:231
#define VERIFY(x)
Definition: Globals.h:280
unsigned int UInt32
Definition: Globals.h:157
T Clamp(T a_Value, T a_Min, T a_Max)
Clamp X to the specified range.
Definition: Globals.h:336
std::enable_if< std::is_arithmetic< T >::value, C >::type CeilC(T a_Value)
Ceils a value, then casts it to C (an int by default).
Definition: Globals.h:354
#define ASSERT(x)
Definition: Globals.h:276
#define UNUSED
Definition: Globals.h:72
std::enable_if< std::is_arithmetic< T >::value, C >::type FloorC(T a_Value)
Floors a value, then casts it to C (an int by default).
Definition: Globals.h:347
void LOG(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:55
#define LOGD
Definition: LoggerSimple.h:83
@ mtZombieVillager
Definition: MonsterTypes.h:82
@ mtSkeleton
Definition: MonsterTypes.h:59
@ mtEndermite
Definition: MonsterTypes.h:28
@ mtMagmaCube
Definition: MonsterTypes.h:40
@ mtZombie
Definition: MonsterTypes.h:79
@ mtCaveSpider
Definition: MonsterTypes.h:17
@ mtWither
Definition: MonsterTypes.h:75
@ mtSpider
Definition: MonsterTypes.h:63
@ mtGhast
Definition: MonsterTypes.h:31
@ mtSilverfish
Definition: MonsterTypes.h:58
@ mtZombiePigman
Definition: MonsterTypes.h:85
Item
Definition: Items.h:4
static constexpr Vector3i gCrossCoords[]
std::string AString
Definition: StringUtils.h:11
Vector3< double > Vector3d
Definition: Vector3.h:485
Vector3< int > Vector3i
Definition: Vector3.h:487
Utilities to allow casting a cWorld to one of its interfaces without including World....
Definition: OpaqueWorld.h:13
unsigned char Level(const BlockState Block)
AString PrettifyEntityName(const AString &a_ID, const bool a_IsTamed=false)
std::string_view From(CustomStatistic a_ID)
static cPluginManager * Get(void)
Returns the instance of the Plugin Manager (there is only ever one)
bool CallHookKilled(cEntity &a_Victim, TakeDamageInfo &a_TDI, AString &a_DeathMessage)
bool CallHookKilling(cEntity &a_Victim, cEntity *a_Killer, TakeDamageInfo &a_TDI)
static bool IsSolid(BLOCKTYPE Block)
Is this block solid (player cannot walk through)?
Definition: BlockInfo.cpp:892
Definition: Chunk.h:36
int GetPosX(void) const
Definition: Chunk.h:131
cChunkCoords GetPos() const
Definition: Chunk.h:133
bool IsWeatherWetAt(int a_RelX, int a_RelZ) const
Returns true if it is raining or storming at the specified location, taking into account biomes.
Definition: Chunk.cpp:1196
bool IsValid(void) const
Returns true iff the chunk block data is valid (loaded / generated)
Definition: Chunk.h:58
int GetPosZ(void) const
Definition: Chunk.h:132
bool UnboundedRelGetBlockType(Vector3i a_RelCoords, BLOCKTYPE &a_BlockType) const
Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap...
Definition: Chunk.cpp:1029
static bool IsValidHeight(Vector3i a_BlockPosition)
Validates a height-coordinate.
Definition: ChunkDef.h:185
static void BlockToChunk(int a_X, int a_Z, int &a_ChunkX, int &a_ChunkZ)
Converts absolute block coords to chunk coords:
Definition: ChunkDef.h:210
static void AbsoluteToRelative(int &a_X, int &a_Y, int &a_Z, int &a_ChunkX, int &a_ChunkZ)
Converts absolute block coords into relative (chunk + block) coords:
Definition: ChunkDef.h:147
static const int Width
Definition: ChunkDef.h:124
static const int Height
Definition: ChunkDef.h:125
Class that stores item enchantments or stored-enchantments The enchantments may be serialized to a st...
Definition: Enchantments.h:42
@ enchProjectileProtection
Definition: Enchantments.h:54
unsigned int GetLevel(int a_EnchantmentID) const
Returns the level for the specified enchantment; 0 if not stored.
cEntity * Attacker
Definition: Entity.h:62
Vector3d Knockback
Definition: Entity.h:65
int RawDamage
Definition: Entity.h:63
eDamageType DamageType
Definition: Entity.h:61
float FinalDamage
Definition: Entity.h:64
Definition: Entity.h:76
virtual const char * GetParentClass(void) const
Returns the topmost class's parent class name for the object.
Definition: Entity.cpp:102
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
Teleports to the coordinates specified.
Definition: Entity.cpp:1953
virtual void DetectMagma(void)
Detects the time for application of magma block damage.
Definition: Entity.cpp:1392
Vector3d m_LastSentPosition
Last position sent to client via the Relative Move or Teleport packets (not Velocity) Only updated if...
Definition: Entity.h:618
void DoMoveToWorld(const sWorldChangeInfo &a_WorldChangeInfo)
Handles the moving of this entity between worlds.
Definition: Entity.cpp:1573
const Vector3d & GetSpeed(void) const
Exported in ManualBindings.
Definition: Entity.h:300
static const int FIRE_TICKS_PER_DAMAGE
Ticks to wait between damaging an entity when it stands in fire.
Definition: Entity.h:112
float m_Health
Definition: Entity.h:584
bool m_IsFireproof
Whether the entity is capable of taking fire or lava damage.
Definition: Entity.h:630
void SetPitch(double a_Pitch)
Definition: Entity.cpp:2136
void SetIsTicking(bool a_IsTicking)
Set the entity's status to either ticking or not ticking.
Definition: Entity.cpp:1778
int GetChunkZ(void) const
Definition: Entity.h:208
void AddSpeedX(double a_AddSpeedX)
Definition: Entity.cpp:2203
virtual void OnAddToWorld(cWorld &a_World)
Called when the entity is added to a world.
Definition: Entity.cpp:147
bool IsPlayer(void) const
Definition: Entity.h:160
Vector3d m_LastPosition
Definition: Entity.h:620
void SteerVehicle(float a_Forward, float a_Sideways)
Definition: Entity.cpp:2243
bool IsPainting(void) const
Definition: Entity.h:173
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk &a_Chunk)
Definition: Entity.cpp:909
void SetHeadYaw(double a_HeadYaw)
Definition: Entity.cpp:2102
bool m_IsInWater
If any part of the entity is in a water block.
Definition: Entity.h:654
cEntity * m_Attachee
The entity which is attached to this entity (rider), nullptr if none.
Definition: Entity.h:591
void BroadcastDeathMessage(TakeDamageInfo &a_TDI)
Announces a death message on chat about killing the entity.
Definition: Entity.cpp:2349
bool IsPickup(void) const
Definition: Entity.h:161
bool IsAttachedTo(const cEntity *a_Entity) const
Returns true if this entity is attached to the specified entity.
Definition: Entity.cpp:2081
void SetRoll(double a_Roll)
Definition: Entity.cpp:2147
virtual float GetEnchantmentBlastKnockbackReduction()
Returns explosion knock back reduction percent from blast protection level.
Definition: Entity.cpp:744
void SetSpeedX(double a_SpeedX)
Sets the speed in the X axis, leaving the other speed components intact.
Definition: Entity.cpp:2167
Vector3d m_WaterSpeed
Measured in meter / second.
Definition: Entity.h:713
float m_AirDrag
Stores the air drag that is applied to the entity every tick, measured in speed ratio per tick Acts a...
Definition: Entity.h:614
void Detach(void)
Detaches from the currently attached entity, if any.
Definition: Entity.cpp:2052
int m_InvulnerableTicks
If a player hit a entity, the entity receive a invulnerable of 10 ticks.
Definition: Entity.h:726
virtual void OnStartedBurning(void)
Called when the entity starts burning.
Definition: Entity.cpp:1867
virtual bool IsInWater(void) const
Returns true if any part of the entity is in a water block.
Definition: Entity.h:501
bool IsTNT(void) const
Definition: Entity.h:167
int m_TicksSinceLastBurnDamage
Time, in ticks, since the last damage dealt by being on fire.
Definition: Entity.h:633
virtual void TickInVoid(cChunk &a_Chunk)
Handles when the entity is in the void.
Definition: Entity.cpp:1346
static const int FIRE_DAMAGE
Damage to deal when standing in fire.
Definition: Entity.h:113
virtual double GetKnockbackAmountAgainst(const cEntity &a_Receiver)
Returns the knockback amount that the currently equipped items would cause to a_Receiver on a hit.
Definition: Entity.cpp:827
void AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ)
Definition: Entity.cpp:2194
void WrapHeadYaw()
Makes sure head yaw is not over the specified range.
Definition: Entity.cpp:204
double GetSpeedZ(void) const
Definition: Entity.h:204
float m_Width
Width of the entity, in the XZ plane.
Definition: Entity.h:719
bool m_IsTicking
Whether the entity is ticking or not.
Definition: Entity.h:698
bool MoveToWorld(cWorld &a_World, Vector3d a_NewPosition, bool a_ShouldSetPortalCooldown=false, bool a_ShouldSendRespawn=true)
Definition: Entity.cpp:1616
void SetYaw(double a_Yaw)
Definition: Entity.cpp:2125
cChunk * GetParentChunk()
Returns the chunk responsible for ticking this entity.
Definition: Entity.h:541
bool m_bDirtyOrientation
Stores whether our yaw / pitch / roll (body orientation) has been set manually.
Definition: Entity.h:597
static const int VOID_BOUNDARY
Y position to begin applying void damage.
Definition: Entity.h:124
virtual bool IsInFire(void) const
Returns true if any part of the entity is in a fire block.
Definition: Entity.h:495
virtual cItem GetEquippedLeggings(void) const
Returns the currently equipped leggings; empty item if none.
Definition: Entity.h:342
void SetPitchFromSpeed(void)
Sets the pitch to match the speed vector (entity gies "face-forward")
Definition: Entity.cpp:387
cMonsterList m_LeashedMobs
List of leashed mobs to this entity.
Definition: Entity.h:731
Vector3d m_Rot
Measured in degrees, [-180, +180)
Definition: Entity.h:707
virtual void HandleAir(void)
Called in each tick to handle air-related processing i.e.
Definition: Entity.cpp:1798
int GetChunkX(void) const
Definition: Entity.h:207
virtual const char * GetClass(void) const
Returns the topmost class name for the object.
Definition: Entity.cpp:84
void SetHealth(float a_Health)
Sets the health of this entity; doesn't broadcast any hurt animation.
Definition: Entity.cpp:900
double GetSpeedY(void) const
Definition: Entity.h:203
int m_AirLevel
Air level of a mobile.
Definition: Entity.h:660
sPortalCooldownData m_PortalCooldownData
Portal delay timer and cooldown boolean data.
Definition: Entity.h:664
virtual void TickBurning(cChunk &a_Chunk)
Updates the state related to this entity being on fire.
Definition: Entity.cpp:1251
static const char * GetClassStatic(void)
Returns the class name of this class.
Definition: Entity.cpp:93
void SetSpeedY(double a_SpeedY)
Sets the speed in the Y axis, leaving the other speed components intact.
Definition: Entity.cpp:2176
int m_TicksLeftBurning
Time, in ticks, until the entity extinguishes its fire.
Definition: Entity.h:642
virtual void TeleportToEntity(cEntity &a_Entity)
Teleports to the entity specified.
Definition: Entity.cpp:1944
bool IsTicking(void) const
Returns true if the entity is valid and ticking.
Definition: Entity.cpp:2259
Vector3d m_Position
Position of the entity's XZ center and Y bottom.
Definition: Entity.h:710
std::vector< cPlayer * > m_Spectators
List of players who are spectating this entity.
Definition: Entity.h:734
static const int BURN_DAMAGE
Damage to deal when the entity is burning.
Definition: Entity.h:117
float m_Gravity
Stores gravity that is applied to an entity every tick For realistic effects, this should be negative...
Definition: Entity.h:608
void SetSpeedZ(double a_SpeedZ)
Sets the speed in the Z axis, leaving the other speed components intact.
Definition: Entity.cpp:2185
float m_MaxHealth
Definition: Entity.h:585
void SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ)
Sets the speed of the entity, measured in m / sec.
Definition: Entity.cpp:2157
double GetPosX(void) const
Definition: Entity.h:195
virtual bool IsA(const char *a_ClassName) const
Returns true if the entity is of the specified class or a subclass (cPawn's IsA("cEntity") returns tr...
Definition: Entity.cpp:2072
float m_Height
Height of the entity (Y axis).
Definition: Entity.h:722
virtual cItem GetEquippedBoots(void) const
Returns the currently equipped boots; empty item if none.
Definition: Entity.h:345
sWorldChangeInfo m_WorldChangeInfo
If field m_NewWorld not nullptr, a world change is scheduled and a task is queued in the current worl...
Definition: Entity.h:627
void SetPosition(double a_PosX, double a_PosY, double a_PosZ)
Definition: Entity.h:218
virtual void DetectCacti(void)
Detects the time for application of cacti damage.
Definition: Entity.cpp:1363
int m_TicksSinceLastVoidDamage
Time, in ticks, since the last damage dealt by the void.
Definition: Entity.h:645
virtual bool IsFireproof(void) const
Definition: Entity.h:412
virtual void Killed(const cEntity &a_Victim, eDamageType a_DamageType)
Called when the entity kills another entity.
Definition: Entity.h:361
float GetWidth(void) const
Definition: Entity.h:205
cWorld * m_World
Definition: Entity.h:624
double GetPosZ(void) const
Definition: Entity.h:197
virtual void HandleSpeedFromAttachee(float a_Forward, float a_Sideways)
Definition: Entity.cpp:2230
virtual bool IsHeadInWater(void) const
Returns true if any part of the entity is in a water block.
Definition: Entity.h:504
bool m_IsInLava
If any part of the entity is in a lava block.
Definition: Entity.h:651
bool IsMinecart(void) const
Definition: Entity.h:165
virtual void OnFinishedBurning(void)
Called when the entity finishes burning.
Definition: Entity.cpp:1877
bool Initialize(OwnedEntity a_Self, cWorld &a_EntityWorld)
Spawns the entity in the world; returns true if spawned, false if not (plugin disallowed).
Definition: Entity.cpp:111
virtual float GetEnchantmentCoverAgainst(const cEntity *a_Attacker, eDamageType a_DamageType, int a_Damage)
Returns the hitpoints that the currently equipped armor's enchantments would cover.
Definition: Entity.cpp:701
UInt32 GetUniqueID(void) const
Definition: Entity.h:253
void OnAcquireSpectator(cPlayer &a_Player)
Called when a player begins spectating this entity.
Definition: Entity.cpp:138
void Destroy()
Destroys the entity, schedules it for memory freeing and broadcasts the DestroyEntity packet.
Definition: Entity.cpp:243
void WrapSpeed()
Makes speed is not over 20.
Definition: Entity.cpp:223
virtual void GetDrops(cItems &a_Drops, cEntity *a_Killer=nullptr)
Returns the list of drops for this pawn when it is killed.
Definition: Entity.h:527
double m_Mass
Measured in Kilograms (Kg)
Definition: Entity.h:716
bool IsOrientationDirty() const
Returns whether the entity's orientation has been set manually.
Definition: Entity.cpp:2093
int m_AirTickTimer
Definition: Entity.h:661
static const int LAVA_TICKS_PER_DAMAGE
Ticks to wait between damaging an entity when it stands in lava.
Definition: Entity.h:114
static const int BURN_TICKS
Ticks to keep an entity burning after it has stood in lava / fire.
Definition: Entity.h:119
void AddSpeedY(double a_AddSpeedY)
Definition: Entity.cpp:2212
void AddLeashedMob(cMonster *a_Monster)
Adds a mob to the leashed list of mobs.
Definition: Entity.cpp:2299
int m_TicksSinceLastLavaDamage
Time, in ticks, since the last damage dealt by standing in lava.
Definition: Entity.h:636
double GetPosY(void) const
Definition: Entity.h:196
Vector3d m_Speed
Measured in meters / second (m / s)
Definition: Entity.h:577
void SetParentChunk(cChunk *a_Chunk)
Sets the parent chunk, which is the chunk responsible for ticking this entity.
Definition: Entity.cpp:234
bool m_IsHeadInWater
If the entity's head is in a water block.
Definition: Entity.h:657
void TakeDamage(cEntity &a_Attacker)
Makes this pawn take damage from an attack by a_Attacker.
Definition: Entity.cpp:272
virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk &a_Chunk)
Handles the physics of the entity - updates position based on speed, updates speed based on environme...
Definition: Entity.cpp:1001
virtual bool DetectPortal(void)
Detects whether we are in a portal block and begins teleportation procedures if so Returns true if Mo...
Definition: Entity.cpp:1421
void AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ)
Definition: Entity.h:242
void AddSpeedZ(double a_AddSpeedZ)
Definition: Entity.cpp:2221
bool IsPawn(void) const
Definition: Entity.h:163
virtual bool IsOnGround(void) const
Returns whether the entity is on ground or not.
Definition: Entity.h:519
bool m_IsInFire
If any part of the entity is in a fire block.
Definition: Entity.h:648
cEntity(eEntityType a_EntityType, Vector3d a_Pos, float a_Width, float a_Height)
Definition: Entity.cpp:37
void SetMass(double a_Mass)
Definition: Entity.cpp:2113
bool IsBoat(void) const
Definition: Entity.h:166
float GetHeight(void) const
Definition: Entity.h:193
virtual cItem GetEquippedWeapon(void) const
Returns the curently equipped weapon; empty item if none.
Definition: Entity.h:333
void BroadcastLeashedMobs()
If has any mobs are leashed, broadcasts every leashed entity to this.
Definition: Entity.cpp:2325
static const int DROWNING_TICKS
Number of ticks per heart of damage.
Definition: Entity.h:122
virtual void KilledBy(TakeDamageInfo &a_TDI)
Called when the health drops below zero.
Definition: Entity.cpp:851
virtual bool DoTakeDamage(TakeDamageInfo &a_TDI)
Makes this entity take damage specified in the a_TDI.
Definition: Entity.cpp:404
virtual void OnRemoveFromWorld(cWorld &a_World)
Called when the entity is removed from a world.
Definition: Entity.cpp:172
bool m_bOnGround
Stores if the entity is on the ground.
Definition: Entity.h:604
virtual cItem GetEquippedHelmet(void) const
Returns the currently equipped helmet; empty item if none.
Definition: Entity.h:336
virtual bool ArmorCoversAgainst(eDamageType a_DamageType)
Returns whether armor will protect against the specified damage type.
Definition: Entity.cpp:661
double m_HeadYaw
Measured in degrees, [-180, +180)
Definition: Entity.h:704
cEntity * GetAttached()
Gets entity (vehicle) attached to this entity.
Definition: Entity.cpp:2018
void StopBurning(void)
Stops the entity from burning, resets all burning timers.
Definition: Entity.cpp:1925
virtual void OnDetach()
Called when this entity dismounts from m_AttachedTo.
Definition: Entity.cpp:2341
static void ApplyFriction(Vector3d &a_Speed, double a_SlowdownMultiplier, float a_Dt)
Applies friction to an entity.
Definition: Entity.cpp:1230
virtual bool IsInLava(void) const
Returns true if any part of the entity is in a lava block.
Definition: Entity.h:498
void SetIsFireproof(bool a_IsFireproof)
Sets whether the entity is fireproof.
Definition: Entity.cpp:1899
void SetMaxHealth(float a_MaxHealth)
Sets the maximum value for the health.
Definition: Entity.cpp:1887
void WrapRotation()
Makes sure rotation is not over the specified range.
Definition: Entity.cpp:213
int m_TicksSinceLastFireDamage
Time, in ticks, since the last damage dealt by standing in fire.
Definition: Entity.h:639
virtual void ApplyArmorDamage(int DamageBlocked)
Applies damage to the armor after the armor blocked the given amount.
Definition: Entity.cpp:652
cChunk * m_ParentChunk
The chunk which is responsible for ticking this entity.
Definition: Entity.h:701
const Vector3d & GetPosition(void) const
Exported in ManualBindings.
Definition: Entity.h:297
virtual void Heal(int a_HitPoints)
Heals the specified amount of HPs.
Definition: Entity.cpp:890
virtual float GetArmorCoverAgainst(const cEntity *a_Attacker, eDamageType a_DamageType, int a_RawDamage)
Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover.
Definition: Entity.cpp:769
static const int LAVA_DAMAGE
Damage to deal when standing in lava.
Definition: Entity.h:115
virtual int GetRawDamageAgainst(const cEntity &a_Receiver)
Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items.
Definition: Entity.cpp:614
void SetSize(float a_Width, float a_Height)
Update an entity's size, for example, on body stance changes.
Definition: Entity.cpp:1788
virtual void SetSwimState(cChunk &a_Chunk)
Called once per tick to set m_IsInFire, m_IsInLava, m_IsInWater and m_IsHeadInWater.
Definition: Entity.cpp:1702
long int m_TicksAlive
The number of ticks this entity has been alive for.
Definition: Entity.h:667
void SetYawFromSpeed(void)
Sets the rotation to match the speed vector (entity goes "face-forward")
Definition: Entity.cpp:371
Vector3d GetLookVector(void) const
Definition: Entity.cpp:2267
eEntityType
Definition: Entity.h:89
void OnLoseSpectator(cPlayer &a_Player)
Called when a player stops spectating this entity.
Definition: Entity.cpp:159
bool HasAnyMobLeashed() const
Returs whether the entity has any mob leashed to it.
Definition: Entity.h:557
void RemoveLeashedMob(cMonster *a_Monster)
Removes a mob from the leashed list of mobs.
Definition: Entity.cpp:2311
bool m_bDirtyHead
Stores whether head yaw has been set manually.
Definition: Entity.h:594
virtual bool IsSprinting(void) const
Definition: Entity.h:492
void AttachTo(cEntity &a_AttachTo)
Attaches to the specified entity; detaches from any previous one first.
Definition: Entity.cpp:2027
virtual void BroadcastMovementUpdate(const cClientHandle *a_Exclude=nullptr)
Updates clients of changes in the entity.
Definition: Entity.cpp:1966
bool m_bHasSentNoSpeed
Stores whether we have sent a Velocity packet with a speed of zero (no speed) to the client Ensures t...
Definition: Entity.h:601
void StartBurning(int a_TicksLeftBurning)
Puts the entity on fire for the specified amount of ticks.
Definition: Entity.cpp:1908
double GetSpeedX(void) const
Definition: Entity.h:202
cWorld * GetWorld(void) const
Definition: Entity.h:190
static const int MAX_AIR_LEVEL
Maximum air an entity can have.
Definition: Entity.h:121
virtual void ResetPosition(Vector3d a_NewPos)
Set the entities position and last sent position.
Definition: Entity.cpp:1857
virtual cItem GetEquippedChestplate(void) const
Returns the currently equipped chestplate; empty item if none.
Definition: Entity.h:339
static const int BURN_TICKS_PER_DAMAGE
Ticks to wait between damaging an entity when it is burning.
Definition: Entity.h:116
void SetWorld(cWorld *a_World)
Sets the internal world pointer to a new cWorld, doesn't update anything else.
Definition: Entity.h:534
cEntity * m_AttachedTo
The entity to which this entity is attached (vehicle), nullptr if none.
Definition: Entity.h:588
bool IsMob(void) const
Definition: Entity.h:162
State variables for MoveToWorld.
Definition: Entity.h:80
Vector3d m_NewPosition
Definition: Entity.h:82
unsigned short m_TicksDelayed
Ticks since entry of portal, used to delay teleportation.
Definition: Entity.h:568
bool m_ShouldPreventTeleportation
Whether the entity has just exited the portal, and should therefore not be teleported again.
Definition: Entity.h:572
Definition: Pawn.h:17
void AddEntityEffect(cEntityEffect::eType a_EffectType, int a_EffectDurationTicks, short a_EffectIntensity, double a_DistanceModifier=1)
Applies an entity effect.
Definition: Pawn.cpp:186
Definition: Player.h:29
const AString & GetName(void) const
Definition: Player.cpp:1299
Vector3i GetLastBedPos(void) const
Gets the player's potential respawn position (named LastBedPos for compatibility reasons).
Definition: Player.h:515
StatisticsManager & GetStatistics()
Return the associated statistic and achievement manager.
Definition: Player.h:237
cWorld * GetRespawnWorld()
Definition: Player.cpp:817
const cItem & GetEquippedItem(void) const
Definition: Player.h:162
bool IsGameModeCreative(void) const
Returns true if the player is in Creative mode, either explicitly, or by inheriting from current worl...
Definition: Player.cpp:1025
virtual bool IsOnGround(void) const override
Returns whether the entity is on ground or not.
Definition: Player.h:604
IntType RandInt(IntType a_Min, IntType a_Max)
Return a random IntType in the range [a_Min, a_Max].
Definition: FastRandom.h:78
bool RandBool(double a_TrueProbability=0.5)
Return a random bool with the given probability of being true.
Definition: FastRandom.h:158
Definition: Item.h:37
const cItemHandler & GetHandler(void) const
Returns the cItemHandler responsible for this item type.
Definition: Item.cpp:216
cEnchantments m_Enchantments
Definition: Item.h:166
This class bridges a vector of cItem for safe access via Lua.
Definition: Item.h:215
virtual void OnEntityAttack(cPlayer *a_Attacker, cEntity *a_AttackedEntity) const
Called when a player attacks an entity with this item in hand.
static bool FirstSolidHitTrace(cWorld &a_World, const Vector3d &a_Start, const Vector3d &a_End, Vector3d &a_HitCoords, Vector3i &a_HitBlockCoords, eBlockFace &a_HitBlockFace)
Traces until the first solid block is hit (or until end, whichever comes first.
Vector3< T > Transform(const Vector3< T > &v) const
Definition: Matrix4.h:144
void Init(const Vector3< T > &a_Pos, T a_RX, T a_RY, T a_RZ)
Definition: Matrix4.h:66
bool HasCustomName(void) const
Returns true if the monster has a custom name.
Definition: Monster.h:165
virtual bool IsTame(void) const
Definition: Monster.h:152
const AString & GetCustomName(void) const
Gets the custom name of the monster.
Definition: Monster.h:168
eMonsterType GetMobType(void) const
Definition: Monster.h:70
cEntity * GetLeashedTo() const
Returns the entity to where this mob is leashed, returns nullptr if it's not leashed.
Definition: Monster.h:95
static cRoot * Get()
Definition: Root.h:52
cPluginManager * GetPluginManager(void)
Definition: Root.h:111
cWorld * GetWorld(const AString &a_WorldName)
Returns a pointer to the world specified.
Definition: Root.cpp:465
virtual Vector3f GetFlowingDirection(Vector3i a_Pos)
Returns a unit vector in the direction the fluid is flowing or a zero-vector if not flowing.
std::unordered_map< CustomStatistic, StatValue > Custom
bool HasNonZeroLength(void) const
Definition: Vector3.h:86
T x
Definition: Vector3.h:17
Vector3< int > Floor(void) const
Returns a new Vector3i with coords set to std::floor() of this vector's coords.
Definition: Vector3.h:177
T y
Definition: Vector3.h:17
void Set(T a_x, T a_y, T a_z)
Definition: Vector3.h:42
T z
Definition: Vector3.h:17
double SqrLength(void) const
Definition: Vector3.h:105
Definition: World.h:53
virtual void BroadcastChatDeath(const AString &a_Message, const cClientHandle *a_Exclude=nullptr) override
Definition: World.h:166
cFluidSimulator * GetWaterSimulator(void)
Definition: World.h:599
void AddEntity(OwnedEntity a_Entity, cWorld *a_OldWorld=nullptr)
Adds the entity into its appropriate chunk; takes ownership of the entity ptr.
Definition: World.cpp:2759
int GetSpawnX(void) const
Definition: World.h:585
virtual void BroadcastDetachEntity(const cEntity &a_Entity, const cEntity &a_PreviousVehicle) override
virtual void BroadcastAttachEntity(const cEntity &a_Entity, const cEntity &a_Vehicle) override
virtual void BroadcastEntityMetadata(const cEntity &a_Entity, const cClientHandle *a_Exclude=nullptr) override
virtual void BroadcastEntityAnimation(const cEntity &a_Entity, EntityAnimation a_Animation, const cClientHandle *a_Exclude=nullptr) override
bool DoWithEntityByID(UInt32 a_UniqueID, cEntityCallback a_Callback)
Calls the callback if the entity with the specified ID is found, with the entity object as the callba...
Definition: World.cpp:2464
const AString & GetName(void) const
Returns the name of the world.
Definition: World.h:691
virtual void BroadcastEntityHeadLook(const cEntity &a_Entity, const cClientHandle *a_Exclude=nullptr) override
void QueueTask(std::function< void(cWorld &)> a_Task)
Queues a task onto the tick thread.
Definition: World.cpp:2734
int GetSpawnZ(void) const
Definition: World.h:587
virtual eDimension GetDimension(void) const override
Definition: World.h:133
virtual void BroadcastSpawnEntity(cEntity &a_Entity, const cClientHandle *a_Exclude=nullptr) override
virtual void BroadcastEntityLook(const cEntity &a_Entity, const cClientHandle *a_Exclude=nullptr) override
virtual void BroadcastEntityVelocity(const cEntity &a_Entity, const cClientHandle *a_Exclude=nullptr) override
virtual void BroadcastLeashEntity(const cEntity &a_Entity, const cEntity &a_EntityLeashedTo) override
void SpawnItemPickups(const cItems &a_Pickups, Vector3i a_BlockPos, double a_FlyAwaySpeed=1.0, bool a_IsPlayerCreated=false)
Spawns item pickups for each item in the list.
Definition: World.cpp:1806
virtual void BroadcastEntityPosition(const cEntity &a_Entity, const cClientHandle *a_Exclude=nullptr) override
virtual void BroadcastDestroyEntity(const cEntity &a_Entity, const cClientHandle *a_Exclude=nullptr) override
int GetSpawnY(void) const
Definition: World.h:586
OwnedEntity RemoveEntity(cEntity &a_Entity)
Removes the entity from the world.
Definition: World.cpp:2769