88 protected const DEFAULT_BREATH_TICKS = 300;
101 private const TAG_LEGACY_HEALTH =
"HealF";
102 private const TAG_HEALTH =
"Health";
103 private const TAG_BREATH_TICKS =
"Air";
104 private const TAG_ACTIVE_EFFECTS =
"ActiveEffects";
105 private const TAG_EFFECT_ID =
"Id";
106 private const TAG_EFFECT_DURATION =
"Duration";
107 private const TAG_EFFECT_AMPLIFIER =
"Amplifier";
108 private const TAG_EFFECT_SHOW_PARTICLES =
"ShowParticles";
109 private const TAG_EFFECT_AMBIENT =
"Ambient";
111 protected int $attackTime = 0;
113 public int $deadTicks = 0;
114 protected int $maxDeadTicks = 25;
116 protected float $jumpVelocity = 0.42;
122 protected bool $breathing =
true;
123 protected int $breathTicks = self::DEFAULT_BREATH_TICKS;
124 protected int $maxBreathTicks = self::DEFAULT_BREATH_TICKS;
128 protected Attribute $knockbackResistanceAttr;
131 protected bool $sprinting =
false;
132 protected bool $sneaking =
false;
133 protected bool $gliding =
false;
134 protected bool $swimming =
false;
136 private ?
int $frostWalkerLevel =
null;
142 abstract public function getName() : string;
148 protected function initEntity(
CompoundTag $nbt) : void{
149 parent::initEntity($nbt);
152 $this->effectManager->getEffectAddHooks()->add(
function() :
void{ $this->networkPropertiesDirty =
true; });
153 $this->effectManager->getEffectRemoveHooks()->add(
function() :
void{ $this->networkPropertiesDirty =
true; });
155 $this->armorInventory =
new ArmorInventory($this);
157 $this->armorInventory->getListeners()->add(CallbackInventoryListener::onAnyChange(fn() => NetworkBroadcastUtils::broadcastEntityEvent(
159 fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobArmorChange($recipients, $this)
161 $this->armorInventory->getListeners()->add(
new CallbackInventoryListener(
162 onSlotChange:
function(Inventory $inventory,
int $slot) :
void{
163 if($slot === ArmorInventory::SLOT_FEET){
164 $this->frostWalkerLevel =
null;
167 onContentChange:
function() :
void{ $this->frostWalkerLevel =
null; }
170 $health = $this->getMaxHealth();
172 if(($healFTag = $nbt->
getTag(self::TAG_LEGACY_HEALTH)) instanceof FloatTag){
173 $health = $healFTag->getValue();
174 }elseif(($healthTag = $nbt->
getTag(self::TAG_HEALTH)) instanceof ShortTag){
175 $health = $healthTag->getValue();
176 }elseif($healthTag instanceof FloatTag){
177 $health = $healthTag->getValue();
180 $this->setHealth($health);
182 $this->setAirSupplyTicks($nbt->getShort(self::TAG_BREATH_TICKS, self::DEFAULT_BREATH_TICKS));
185 $activeEffectsTag = $nbt->
getListTag(self::TAG_ACTIVE_EFFECTS);
186 if($activeEffectsTag !==
null){
187 foreach($activeEffectsTag as $e){
188 $effect = EffectIdMap::getInstance()->fromId($e->getByte(self::TAG_EFFECT_ID));
189 if($effect ===
null){
193 $this->effectManager->add(
new EffectInstance(
195 $e->getInt(self::TAG_EFFECT_DURATION),
196 Binary::unsignByte($e->getByte(self::TAG_EFFECT_AMPLIFIER)),
197 $e->getByte(self::TAG_EFFECT_SHOW_PARTICLES, 1) !== 0,
198 $e->getByte(self::TAG_EFFECT_AMBIENT, 0) !== 0
204 protected function addAttributes() : void{
205 $this->attributeMap->add($this->healthAttr = AttributeFactory::getInstance()->mustGet(Attribute::HEALTH));
206 $this->attributeMap->add(AttributeFactory::getInstance()->mustGet(Attribute::FOLLOW_RANGE));
207 $this->attributeMap->add($this->knockbackResistanceAttr = AttributeFactory::getInstance()->mustGet(Attribute::KNOCKBACK_RESISTANCE));
208 $this->attributeMap->add($this->moveSpeedAttr = AttributeFactory::getInstance()->mustGet(Attribute::MOVEMENT_SPEED));
209 $this->attributeMap->add(AttributeFactory::getInstance()->mustGet(Attribute::ATTACK_DAMAGE));
210 $this->attributeMap->add($this->absorptionAttr = AttributeFactory::getInstance()->mustGet(Attribute::ABSORPTION));
217 return $this->nameTag !==
"" ? $this->nameTag : $this->getName();
221 $wasAlive = $this->isAlive();
222 parent::setHealth($amount);
223 $this->healthAttr->setValue(ceil($this->getHealth()),
true);
224 if($this->isAlive() && !$wasAlive){
229 public function getMaxHealth() : int{
230 return (int) $this->healthAttr->getMaxValue();
233 public function setMaxHealth(
int $amount) : void{
234 $this->healthAttr->setMaxValue($amount)->setDefaultValue($amount);
237 public function getAbsorption() : float{
238 return $this->absorptionAttr->getValue();
241 public function setAbsorption(
float $absorption) : void{
242 $this->absorptionAttr->setValue($absorption);
245 public function isSneaking() : bool{
246 return $this->sneaking;
249 public function setSneaking(
bool $value =
true) : void{
250 $this->sneaking = $value;
251 $this->networkPropertiesDirty =
true;
252 $this->recalculateSize();
255 public function isSprinting() : bool{
256 return $this->sprinting;
259 public function setSprinting(
bool $value =
true) : void{
260 if($value !== $this->isSprinting()){
261 $this->sprinting = $value;
262 $this->networkPropertiesDirty =
true;
263 $moveSpeed = $this->getMovementSpeed();
264 $this->setMovementSpeed($value ? ($moveSpeed * 1.3) : ($moveSpeed / 1.3));
265 $this->moveSpeedAttr->markSynchronized(
false);
269 public function isGliding() : bool{
270 return $this->gliding;
273 public function setGliding(
bool $value =
true) : void{
274 $this->gliding = $value;
275 $this->networkPropertiesDirty =
true;
276 $this->recalculateSize();
279 public function isSwimming() : bool{
280 return $this->swimming;
283 public function setSwimming(
bool $value =
true) : void{
284 $this->swimming = $value;
285 $this->networkPropertiesDirty =
true;
286 $this->recalculateSize();
289 private function recalculateSize() : void{
290 $size = $this->getInitialSizeInfo();
291 if($this->isSwimming() || $this->isGliding()){
292 $width = $size->getWidth();
293 $this->setSize((
new EntitySizeInfo($width, $width, $width * 0.9))->scale($this->getScale()));
294 }elseif($this->isSneaking()){
295 $this->setSize((
new EntitySizeInfo(3 / 4 * $size->getHeight(), $size->getWidth(), 3 / 4 * $size->getEyeHeight()))->scale($this->getScale()));
297 $this->setSize($size->scale($this->getScale()));
301 public function getMovementSpeed() : float{
302 return $this->moveSpeedAttr->getValue();
305 public function setMovementSpeed(
float $v,
bool $fit =
false) : void{
306 $this->moveSpeedAttr->setValue($v, $fit);
309 public function saveNBT() : CompoundTag{
310 $nbt = parent::saveNBT();
311 $nbt->
setFloat(self::TAG_HEALTH, $this->getHealth());
313 $nbt->
setShort(self::TAG_BREATH_TICKS, $this->getAirSupplyTicks());
315 if(count($this->effectManager->all()) > 0){
317 foreach($this->effectManager->all() as $effect){
318 $effects[] = CompoundTag::create()
319 ->setByte(self::TAG_EFFECT_ID, EffectIdMap::getInstance()->toId($effect->getType()))
320 ->setByte(self::TAG_EFFECT_AMPLIFIER, Binary::signByte($effect->getAmplifier()))
321 ->setInt(self::TAG_EFFECT_DURATION, $effect->getDuration())
322 ->setByte(self::TAG_EFFECT_AMBIENT, $effect->isAmbient() ? 1 : 0)
323 ->setByte(self::TAG_EFFECT_SHOW_PARTICLES, $effect->isVisible() ? 1 : 0);
326 $nbt->
setTag(self::TAG_ACTIVE_EFFECTS,
new ListTag($effects));
333 return $this->effectManager;
341 $this->applyConsumptionResults($consumable);
350 foreach($consumable->getAdditionalEffects() as $effect){
351 $this->effectManager->add($effect);
357 $consumable->onConsume($this);
364 return $this->jumpVelocity + ((($jumpBoost = $this->effectManager->get(
VanillaEffects::JUMP_BOOST())) !== null ? $jumpBoost->getEffectLevel() : 0) / 10);
372 $this->motion = $this->motion->withComponents(
null, $this->getJumpVelocity(),
null);
376 protected function calculateFallDamage(
float $fallDistance) : float{
377 return ceil($fallDistance - 3 - (($jumpBoost = $this->effectManager->get(VanillaEffects::JUMP_BOOST())) !== null ? $jumpBoost->getEffectLevel() : 0));
381 $fallBlockPos = $this->location->floor();
382 $fallBlock = $this->getWorld()->getBlock($fallBlockPos);
383 if(count($fallBlock->getCollisionBoxes()) === 0){
384 $fallBlockPos = $fallBlockPos->down();
385 $fallBlock = $this->getWorld()->getBlock($fallBlockPos);
387 $newVerticalVelocity = $fallBlock->onEntityLand($this);
389 $damage = $this->calculateFallDamage($this->fallDistance);
394 $this->broadcastSound($damage > 4 ?
398 }elseif($fallBlock->getTypeId() !== BlockTypeIds::AIR){
399 $this->broadcastSound(
new EntityLandSound($this, $fallBlock));
401 return $newVerticalVelocity;
411 foreach($this->armorInventory->getContents() as $item){
412 $total += $item->getDefensePoints();
423 foreach($this->armorInventory->getContents() as $item){
424 $result = max($result, $item->getEnchantmentLevel($enchantment));
430 public function getArmorInventory() : ArmorInventory{
431 return $this->armorInventory;
434 public function setOnFire(
int $seconds) : void{
435 parent::setOnFire($seconds - (int) min($seconds, $seconds * $this->getHighestArmorEnchantmentLevel(VanillaEnchantments::FIRE_PROTECTION()) * 0.15));
443 if($this->lastDamageCause !== null && $this->attackTime > 0){
444 if($this->lastDamageCause->getBaseDamage() >= $source->
getBaseDamage()){
447 $source->setModifier(-$this->lastDamageCause->getBaseDamage(), EntityDamageEvent::MODIFIER_PREVIOUS_DAMAGE_COOLDOWN);
449 if($source->canBeReducedByArmor()){
451 $source->setModifier(-$source->getFinalDamage() * $this->getArmorPoints() * 0.04, EntityDamageEvent::MODIFIER_ARMOR);
454 $cause = $source->getCause();
455 if(($resistance = $this->effectManager->get(VanillaEffects::RESISTANCE())) !==
null && $cause !== EntityDamageEvent::CAUSE_VOID && $cause !== EntityDamageEvent::CAUSE_SUICIDE){
456 $source->setModifier(-$source->getFinalDamage() * min(1, 0.2 * $resistance->getEffectLevel()), EntityDamageEvent::MODIFIER_RESISTANCE);
460 foreach($this->armorInventory->getContents() as $item){
461 if($item instanceof Armor){
462 $totalEpf += $item->getEnchantmentProtectionFactor($source);
465 $source->setModifier(-$source->getFinalDamage() * min(ceil(min($totalEpf, 25) * (mt_rand(50, 100) / 100)), 20) * 0.04, EntityDamageEvent::MODIFIER_ARMOR_ENCHANTMENTS);
467 $source->setModifier(-min($this->getAbsorption(), $source->getFinalDamage()), EntityDamageEvent::MODIFIER_ABSORPTION);
469 if($cause === EntityDamageEvent::CAUSE_FALLING_BLOCK && $this->armorInventory->getHelmet() instanceof Armor){
470 $source->setModifier(-($source->getFinalDamage() / 4), EntityDamageEvent::MODIFIER_ARMOR_HELMET);
480 $this->setAbsorption(max(0, $this->getAbsorption() + $source->getModifier(
EntityDamageEvent::MODIFIER_ABSORPTION)));
482 $this->damageArmor($source->getBaseDamage());
487 foreach($this->armorInventory->getContents() as $k => $item){
488 if($item instanceof Armor && ($thornsLevel = $item->getEnchantmentLevel(VanillaEnchantments::THORNS())) > 0){
489 if(mt_rand(0, 99) < $thornsLevel * 15){
490 $this->damageItem($item, 3);
491 $damage += ($thornsLevel > 10 ? $thornsLevel - 10 : 1 + mt_rand(0, 3));
493 $this->damageItem($item, 1);
496 $this->armorInventory->setItem($k, $item);
501 $attacker->attack(new EntityDamageByEntityEvent($this, $attacker, EntityDamageEvent::CAUSE_MAGIC, $damage));
504 if($source->getModifier(EntityDamageEvent::MODIFIER_ARMOR_HELMET) < 0){
505 $helmet = $this->armorInventory->getHelmet();
506 if($helmet instanceof Armor){
507 $finalDamage = $source->getFinalDamage();
508 $this->damageItem($helmet, (int) round($finalDamage * 4 + Utils::getRandomFloat() * $finalDamage * 2));
509 $this->armorInventory->setHelmet($helmet);
520 $durabilityRemoved = (int) max(floor($damage / 4), 1);
522 $armor = $this->armorInventory->getContents();
523 foreach($armor as $slotId => $item){
524 if($item instanceof
Armor){
525 $oldItem = clone $item;
526 $this->damageItem($item, $durabilityRemoved);
527 if(!$item->equalsExact($oldItem)){
528 $this->armorInventory->setItem($slotId, $item);
534 private function damageItem(Durable $item,
int $durabilityRemoved) : void{
535 $item->applyDamage($durabilityRemoved);
536 if($item->isBroken()){
537 $this->broadcastSound(new ItemBreakSound());
541 public function attack(EntityDamageEvent $source) : void{
542 if($this->noDamageTicks > 0 && $source->getCause() !== EntityDamageEvent::CAUSE_SUICIDE){
546 if($this->effectManager->has(VanillaEffects::FIRE_RESISTANCE()) && (
547 $source->getCause() === EntityDamageEvent::CAUSE_FIRE
548 || $source->getCause() === EntityDamageEvent::CAUSE_FIRE_TICK
549 || $source->getCause() === EntityDamageEvent::CAUSE_LAVA
555 if($source->getCause() !== EntityDamageEvent::CAUSE_SUICIDE){
556 $this->applyDamageModifiers($source);
559 if($source instanceof EntityDamageByEntityEvent && (
560 $source->getCause() === EntityDamageEvent::CAUSE_BLOCK_EXPLOSION ||
561 $source->getCause() === EntityDamageEvent::CAUSE_ENTITY_EXPLOSION)
565 $base = $source->getKnockBack();
566 $source->setKnockBack($base - min($base, $base * $this->getHighestArmorEnchantmentLevel(VanillaEnchantments::BLAST_PROTECTION()) * 0.15));
569 parent::attack($source);
571 if($source->isCancelled()){
575 if($this->attackTime <= 0){
578 $this->attackTime = $source->getAttackCooldown();
580 if($source instanceof EntityDamageByChildEntityEvent){
581 $e = $source->getChild();
583 $motion = $e->getMotion();
584 $this->knockBack($motion->x, $motion->z, $source->getKnockBack(), $source->getVerticalKnockBackLimit());
586 }elseif($source instanceof EntityDamageByEntityEvent){
587 $e = $source->getDamager();
589 $deltaX = $this->location->x - $e->location->x;
590 $deltaZ = $this->location->z - $e->location->z;
591 $this->knockBack($deltaX, $deltaZ, $source->getKnockBack(), $source->getVerticalKnockBackLimit());
595 if($this->isAlive()){
596 $this->doHitAnimation();
600 if($this->isAlive()){
601 $this->applyPostDamageEffects($source);
605 protected function doHitAnimation() : void{
606 $this->broadcastAnimation(new HurtAnimation($this));
609 public function knockBack(
float $x,
float $z,
float $force = self::DEFAULT_KNOCKBACK_FORCE, ?
float $verticalLimit = self::DEFAULT_KNOCKBACK_VERTICAL_LIMIT) : void{
610 $f = sqrt($x * $x + $z * $z);
614 if(mt_rand() / mt_getrandmax() > $this->knockbackResistanceAttr->getValue()){
617 $motionX = $this->motion->x / 2;
618 $motionY = $this->motion->y / 2;
619 $motionZ = $this->motion->z / 2;
620 $motionX += $x * $f * $force;
622 $motionZ += $z * $f * $force;
624 $verticalLimit ??= $force;
625 if($motionY > $verticalLimit){
626 $motionY = $verticalLimit;
629 $this->setMotion(
new Vector3($motionX, $motionY, $motionZ));
634 $ev = new
EntityDeathEvent($this, $this->getDrops(), $this->getXpDropAmount());
636 foreach($ev->getDrops() as $item){
637 $this->getWorld()->dropItem($this->location, $item);
641 $this->getWorld()->dropExperience($this->location, $ev->getXpDropAmount());
643 $this->startDeathAnimation();
647 if($this->deadTicks < $this->maxDeadTicks){
648 $this->deadTicks += $tickDiff;
649 if($this->deadTicks >= $this->maxDeadTicks){
650 $this->endDeathAnimation();
654 return $this->deadTicks >= $this->maxDeadTicks;
657 protected function startDeathAnimation() : void{
658 $this->broadcastAnimation(new DeathAnimation($this));
661 protected function endDeathAnimation() : void{
662 $this->despawnFromAll();
665 protected function entityBaseTick(
int $tickDiff = 1) : bool{
666 Timings::$livingEntityBaseTick->startTiming();
668 $hasUpdate = parent::entityBaseTick($tickDiff);
670 if($this->isAlive()){
671 if($this->effectManager->tick($tickDiff)){
675 if($this->isInsideOfSolid()){
677 $ev =
new EntityDamageEvent($this, EntityDamageEvent::CAUSE_SUFFOCATION, 1);
681 if($this->doAirSupplyTick($tickDiff)){
685 foreach($this->armorInventory->getContents() as $index => $item){
686 $oldItem = clone $item;
687 if($item->onTickWorn($this)){
689 if(!$item->equalsExact($oldItem)){
690 $this->armorInventory->setItem($index, $item);
696 if($this->attackTime > 0){
697 $this->attackTime -= $tickDiff;
700 Timings::$livingEntityBaseTick->stopTiming();
705 protected function move(
float $dx,
float $dy,
float $dz) : void{
706 $oldX = $this->location->x;
707 $oldZ = $this->location->z;
709 parent::move($dx, $dy, $dz);
711 $frostWalkerLevel = $this->getFrostWalkerLevel();
712 if($frostWalkerLevel > 0 && (abs($this->location->x - $oldX) > self::MOTION_THRESHOLD || abs($this->location->z - $oldZ) > self::MOTION_THRESHOLD)){
713 $this->applyFrostWalker($frostWalkerLevel);
717 protected function applyFrostWalker(
int $level) : void{
718 $radius = $level + 2;
719 $world = $this->getWorld();
721 $baseX = $this->location->getFloorX();
722 $y = $this->location->getFloorY() - 1;
723 $baseZ = $this->location->getFloorZ();
725 $liquid = VanillaBlocks::WATER();
726 $targetBlock = VanillaBlocks::FROSTED_ICE();
727 if(EntityFrostWalkerEvent::hasHandlers()){
728 $ev =
new EntityFrostWalkerEvent($this, $radius, $liquid, $targetBlock);
730 if($ev->isCancelled()){
733 $radius = $ev->getRadius();
734 $liquid = $ev->getLiquid();
735 $targetBlock = $ev->getTargetBlock();
738 for($x = $baseX - $radius; $x <= $baseX + $radius; $x++){
739 for($z = $baseZ - $radius; $z <= $baseZ + $radius; $z++){
740 $block = $world->getBlockAt($x, $y, $z);
742 !$block->isSameState($liquid) ||
743 $world->getBlockAt($x, $y + 1, $z)->getTypeId() !== BlockTypeIds::AIR ||
744 count($world->getNearbyEntities(AxisAlignedBB::one()->offset($x, $y, $z))) !== 0
748 $world->setBlockAt($x, $y, $z, $targetBlock);
753 public function getFrostWalkerLevel() : int{
754 return $this->frostWalkerLevel ??= $this->armorInventory->getBoots()->
getEnchantmentLevel(VanillaEnchantments::FROST_WALKER());
761 $ticks = $this->getAirSupplyTicks();
763 if(!$this->canBreathe()){
764 $this->setBreathing(
false);
766 if(($respirationLevel = $this->armorInventory->getHelmet()->getEnchantmentLevel(VanillaEnchantments::RESPIRATION())) <= 0 ||
767 Utils::getRandomFloat() <= (1 / ($respirationLevel + 1))
772 $this->onAirExpired();
775 }elseif(!$this->isBreathing()){
776 if($ticks < ($max = $this->getMaxAirSupplyTicks())){
777 $ticks += $tickDiff * 5;
781 $this->setBreathing(
true);
785 if($ticks !== $oldTicks){
786 $this->setAirSupplyTicks($ticks);
789 return $ticks !== $oldTicks;
796 return $this->effectManager->has(
VanillaEffects::WATER_BREATHING()) || $this->effectManager->has(
VanillaEffects::CONDUIT_POWER()) || !$this->isUnderwater();
803 return $this->breathing;
811 $this->breathing = $value;
812 $this->networkPropertiesDirty =
true;
820 return $this->breathTicks;
827 $this->breathTicks = $ticks;
828 $this->networkPropertiesDirty =
true;
835 return $this->maxBreathTicks;
842 $this->maxBreathTicks = $ticks;
843 $this->networkPropertiesDirty =
true;
875 public function getLineOfSight(
int $maxDistance,
int $maxLength = 0, array $transparent = []) : array{
876 if($maxDistance > 120){
880 if(count($transparent) === 0){
887 foreach(VoxelRayTrace::inDirection($this->location->add(0, $this->size->getEyeHeight(), 0), $this->getDirectionVector(), $maxDistance) as $vector3){
888 $block = $this->getWorld()->getBlockAt($vector3->x, $vector3->y, $vector3->z);
889 $blocks[$nextIndex++] = $block;
891 if($maxLength !== 0 && count($blocks) > $maxLength){
892 array_shift($blocks);
896 $id = $block->getTypeId();
898 if($transparent ===
null){
899 if($id !== BlockTypeIds::AIR){
903 if(!isset($transparent[$id])){
917 $line = $this->getLineOfSight($maxDistance, 1, $transparent);
918 if(count($line) > 0){
919 return array_shift($line);
930 $horizontal = sqrt(($target->x - $this->location->x) ** 2 + ($target->z - $this->location->z) ** 2);
931 $vertical = $target->y - ($this->location->y + $this->getEyeHeight());
932 $pitch = -atan2($vertical, $horizontal) / M_PI * 180;
934 $xDist = $target->x - $this->location->x;
935 $zDist = $target->z - $this->location->z;
937 $yaw = atan2($zDist, $xDist) / M_PI * 180 - 90;
942 $this->setRotation($yaw, $pitch);
946 parent::sendSpawnPacket($player);
948 $networkSession = $player->getNetworkSession();
949 $networkSession->getEntityEventBroadcaster()->onMobArmorChange([$networkSession], $this);
953 parent::syncNetworkData($properties);
955 $visibleEffects = [];
956 foreach ($this->effectManager->all() as $effect) {
957 if (!$effect->isVisible() || !$effect->getType()->hasBubbles()) {
960 $visibleEffects[EffectIdMap::getInstance()->toId($effect->getType())] = $effect->isAmbient();
964 ksort($visibleEffects, SORT_NUMERIC);
967 $packedEffectsCount = 0;
968 foreach ($visibleEffects as $effectId => $isAmbient) {
969 $effectsData = ($effectsData << 7) |
970 (($effectId & 0x3f) << 1) |
971 ($isAmbient ? 1 : 0);
973 if (++$packedEffectsCount >= 8) {
977 $properties->setLong(EntityMetadataProperties::VISIBLE_MOB_EFFECTS, $effectsData);
979 $properties->setShort(EntityMetadataProperties::AIR, $this->breathTicks);
980 $properties->setShort(EntityMetadataProperties::MAX_AIR, $this->maxBreathTicks);
982 $properties->setGenericFlag(EntityMetadataFlags::BREATHING, $this->breathing);
983 $properties->setGenericFlag(EntityMetadataFlags::SNEAKING, $this->sneaking);
984 $properties->setGenericFlag(EntityMetadataFlags::SPRINTING, $this->sprinting);
985 $properties->setGenericFlag(EntityMetadataFlags::GLIDING, $this->gliding);
986 $properties->setGenericFlag(EntityMetadataFlags::SWIMMING, $this->swimming);
990 $this->armorInventory->removeAllViewers();
991 $this->effectManager->getEffectAddHooks()->clear();
992 $this->effectManager->getEffectRemoveHooks()->clear();
998 $this->armorInventory,
1001 parent::destroyCycles();