setCritical($critical); } protected function getInitialSizeInfo() : EntitySizeInfo{ return new EntitySizeInfo(0.25, 0.25); } protected function initEntity(CompoundTag $nbt) : void{ parent::initEntity($nbt); $this->pickupMode = $nbt->getByte(self::TAG_PICKUP, self::PICKUP_ANY); $this->critical = $nbt->getByte(self::TAG_CRIT, 0) === 1; $this->collideTicks = $nbt->getShort("life", $this->collideTicks); } public function saveNBT() : CompoundTag{ $nbt = parent::saveNBT(); $nbt->setByte(self::TAG_PICKUP, $this->pickupMode); $nbt->setByte(self::TAG_CRIT, $this->critical ? 1 : 0); $nbt->setShort("life", $this->collideTicks); return $nbt; } public function isCritical() : bool{ return $this->critical; } public function setCritical(bool $value = true) : void{ $this->critical = $value; $this->networkPropertiesDirty = true; } public function getResultDamage() : int{ $base = (int) ceil($this->motion->length() * parent::getResultDamage()); if($this->isCritical()){ return ($base + mt_rand(0, (int) ($base / 2) + 1)); }else{ return $base; } } public function getPunchKnockback() : float{ return $this->punchKnockback; } public function setPunchKnockback(float $punchKnockback) : void{ $this->punchKnockback = $punchKnockback; } protected function entityBaseTick(int $tickDiff = 1) : bool{ if($this->closed){ return false; } $hasUpdate = parent::entityBaseTick($tickDiff); if($this->blockHit !== null){ $this->collideTicks += $tickDiff; if($this->collideTicks > 1200){ $this->flagForDespawn(); $hasUpdate = true; } }else{ $this->collideTicks = 0; } return $hasUpdate; } protected function onHit(ProjectileHitEvent $event) : void{ $this->setCritical(false); $this->broadcastSound(new ArrowHitSound()); } protected function onHitBlock(Block $blockHit, RayTraceResult $hitResult) : void{ parent::onHitBlock($blockHit, $hitResult); $this->broadcastAnimation(new ArrowShakeAnimation($this, 7)); } protected function onHitEntity(Entity $entityHit, RayTraceResult $hitResult) : void{ parent::onHitEntity($entityHit, $hitResult); if($this->punchKnockback > 0){ $horizontalSpeed = sqrt($this->motion->x ** 2 + $this->motion->z ** 2); if($horizontalSpeed > 0){ $multiplier = $this->punchKnockback * 0.6 / $horizontalSpeed; $entityHit->setMotion($entityHit->getMotion()->add($this->motion->x * $multiplier, 0.1, $this->motion->z * $multiplier)); } } } public function getPickupMode() : int{ return $this->pickupMode; } public function setPickupMode(int $pickupMode) : void{ $this->pickupMode = $pickupMode; } public function onCollideWithPlayer(Player $player) : void{ if($this->blockHit === null){ return; } $item = VanillaItems::ARROW(); $playerInventory = match(true){ !$player->hasFiniteResources() => null, //arrows are not picked up in creative $player->getOffHandInventory()->getItem(0)->canStackWith($item) && $player->getOffHandInventory()->canAddItem($item) => $player->getOffHandInventory(), $player->getInventory()->canAddItem($item) => $player->getInventory(), default => null }; $ev = new EntityItemPickupEvent($player, $this, $item, $playerInventory); if($player->hasFiniteResources() && $playerInventory === null){ $ev->cancel(); } if($this->pickupMode === self::PICKUP_NONE || ($this->pickupMode === self::PICKUP_CREATIVE && !$player->isCreative())){ $ev->cancel(); } $ev->call(); if($ev->isCancelled()){ return; } foreach($this->getViewers() as $viewer){ $viewer->getNetworkSession()->onPlayerPickUpItem($player, $this); } $ev->getInventory()?->addItem($ev->getItem()); $this->flagForDespawn(); } protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); $properties->setGenericFlag(EntityMetadataFlags::CRITICAL, $this->critical); } }