49 public function getNetworkTypeId() :
string{
return EntityIds::ARROW; }
51 public const PICKUP_NONE = 0;
52 public const PICKUP_ANY = 1;
53 public const PICKUP_CREATIVE = 2;
55 private const TAG_PICKUP =
"pickup";
56 public const TAG_CRIT =
"crit";
57 private const TAG_LIFE =
"life";
59 protected float $damage = 2.0;
60 protected int $pickupMode = self::PICKUP_ANY;
61 protected float $punchKnockback = 0.0;
62 protected int $collideTicks = 0;
63 protected bool $critical =
false;
66 parent::__construct($location, $shootingEntity, $nbt);
67 $this->setCritical($critical);
76 protected function initEntity(
CompoundTag $nbt) : void{
77 parent::initEntity($nbt);
79 $this->pickupMode = $nbt->getByte(self::TAG_PICKUP, self::PICKUP_ANY);
80 $this->critical = $nbt->getByte(self::TAG_CRIT, 0) === 1;
81 $this->collideTicks = $nbt->getShort(self::TAG_LIFE, $this->collideTicks);
85 $nbt = parent::saveNBT();
86 $nbt->
setByte(self::TAG_PICKUP, $this->pickupMode);
87 $nbt->
setByte(self::TAG_CRIT, $this->critical ? 1 : 0);
88 $nbt->
setShort(self::TAG_LIFE, $this->collideTicks);
92 public function isCritical() : bool{
93 return $this->critical;
96 public function setCritical(
bool $value =
true) : void{
97 $this->critical = $value;
98 $this->networkPropertiesDirty =
true;
102 $base = (int) ceil($this->motion->length() * parent::getResultDamage());
103 if($this->isCritical()){
104 return ($base + mt_rand(0, (
int) ($base / 2) + 1));
110 public function getPunchKnockback() : float{
111 return $this->punchKnockback;
114 public function setPunchKnockback(
float $punchKnockback) : void{
115 $this->punchKnockback = $punchKnockback;
118 protected function entityBaseTick(
int $tickDiff = 1) : bool{
123 $hasUpdate = parent::entityBaseTick($tickDiff);
125 if($this->blockHit !==
null){
126 $this->collideTicks += $tickDiff;
127 if($this->collideTicks > 1200){
128 $this->flagForDespawn();
132 $this->collideTicks = 0;
139 $this->setCritical(false);
144 parent::onHitBlock($blockHit, $hitResult);
149 parent::onHitEntity($entityHit, $hitResult);
150 if($this->punchKnockback > 0){
151 $horizontalSpeed = sqrt($this->motion->x ** 2 + $this->motion->z ** 2);
152 if($horizontalSpeed > 0){
153 $multiplier = $this->punchKnockback * 0.6 / $horizontalSpeed;
154 $entityHit->setMotion($entityHit->getMotion()->add($this->motion->x * $multiplier, 0.1, $this->motion->z * $multiplier));
159 public function getPickupMode() : int{
160 return $this->pickupMode;
163 public function setPickupMode(
int $pickupMode) : void{
164 $this->pickupMode = $pickupMode;
167 public function onCollideWithPlayer(Player $player) : void{
168 if($this->blockHit === null){
172 $item = VanillaItems::ARROW();
173 $playerInventory = match(
true){
174 !$player->hasFiniteResources() =>
null,
175 $player->getOffHandInventory()->getItem(0)->canStackWith($item) && $player->getOffHandInventory()->canAddItem($item) => $player->getOffHandInventory(),
176 $player->getInventory()->canAddItem($item) => $player->getInventory(),
180 $ev =
new EntityItemPickupEvent($player, $this, $item, $playerInventory);
181 if($player->hasFiniteResources() && $playerInventory ===
null){
184 if($this->pickupMode === self::PICKUP_NONE || ($this->pickupMode === self::PICKUP_CREATIVE && !$player->isCreative())){
189 if($ev->isCancelled()){
193 NetworkBroadcastUtils::broadcastEntityEvent(
195 fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onPickUpItem($recipients, $player, $this)
198 $ev->getInventory()?->addItem($ev->getItem());
199 $this->flagForDespawn();
202 protected function syncNetworkData(EntityMetadataCollection $properties) : void{
203 parent::syncNetworkData($properties);
205 $properties->setGenericFlag(EntityMetadataFlags::CRITICAL, $this->critical);