= $split){ return $split; } } return 1; } /** * Splits the specified amount of XP into an array of acceptable XP orb sizes. * * @return int[] */ public static function splitIntoOrbSizes(int $amount) : array{ $result = []; while($amount > 0){ $size = self::getMaxOrbSize($amount); $result[] = $size; $amount -= $size; } return $result; } protected int $age = 0; /** Ticker used for determining interval in which to look for new target players. */ protected int $lookForTargetTime = 0; /** Runtime entity ID of the player this XP orb is targeting. */ protected ?int $targetPlayerRuntimeId = null; protected int $xpValue; public function __construct(Location $location, int $xpValue, ?CompoundTag $nbt = null){ $this->xpValue = $xpValue; parent::__construct($location, $nbt); } protected function getInitialSizeInfo() : EntitySizeInfo{ return new EntitySizeInfo(0.25, 0.25); } protected function getInitialDragMultiplier() : float{ return 0.02; } protected function getInitialGravity() : float{ return 0.04; } protected function initEntity(CompoundTag $nbt) : void{ parent::initEntity($nbt); $this->age = $nbt->getShort("Age", 0); } public function saveNBT() : CompoundTag{ $nbt = parent::saveNBT(); $nbt->setShort("Age", $this->age); $nbt->setShort(self::TAG_VALUE_PC, $this->getXpValue()); $nbt->setInt(self::TAG_VALUE_PE, $this->getXpValue()); return $nbt; } public function getXpValue() : int{ return $this->xpValue; } public function setXpValue(int $amount) : void{ if($amount <= 0){ throw new \InvalidArgumentException("XP amount must be greater than 0, got $amount"); } $this->xpValue = $amount; $this->networkPropertiesDirty = true; } public function hasTargetPlayer() : bool{ return $this->targetPlayerRuntimeId !== null; } public function getTargetPlayer() : ?Human{ if($this->targetPlayerRuntimeId === null){ return null; } $entity = $this->getWorld()->getEntity($this->targetPlayerRuntimeId); if($entity instanceof Human){ return $entity; } return null; } public function setTargetPlayer(?Human $player) : void{ $this->targetPlayerRuntimeId = $player !== null ? $player->getId() : null; } protected function entityBaseTick(int $tickDiff = 1) : bool{ $hasUpdate = parent::entityBaseTick($tickDiff); $this->age += $tickDiff; if($this->age > 6000){ $this->flagForDespawn(); return true; } $currentTarget = $this->getTargetPlayer(); if($currentTarget !== null && (!$currentTarget->isAlive() || !$currentTarget->getXpManager()->canAttractXpOrbs() || $currentTarget->location->distanceSquared($this->location) > self::MAX_TARGET_DISTANCE ** 2)){ $currentTarget = null; } if($this->lookForTargetTime >= 20){ if($currentTarget === null){ $newTarget = $this->getWorld()->getNearestEntity($this->location, self::MAX_TARGET_DISTANCE, Human::class); if($newTarget instanceof Human && !($newTarget instanceof Player && $newTarget->isSpectator()) && $newTarget->getXpManager()->canAttractXpOrbs()){ $currentTarget = $newTarget; } } $this->lookForTargetTime = 0; }else{ $this->lookForTargetTime += $tickDiff; } $this->setTargetPlayer($currentTarget); if($currentTarget !== null){ $vector = $currentTarget->getPosition()->add(0, $currentTarget->getEyeHeight() / 2, 0)->subtractVector($this->location)->divide(self::MAX_TARGET_DISTANCE); $distance = $vector->lengthSquared(); if($distance < 1){ $this->motion = $this->motion->addVector($vector->normalize()->multiply(0.2 * (1 - sqrt($distance)) ** 2)); } if($currentTarget->getXpManager()->canPickupXp() && $this->boundingBox->intersectsWith($currentTarget->getBoundingBox())){ $this->flagForDespawn(); $currentTarget->getXpManager()->onPickupXp($this->getXpValue()); } } return $hasUpdate; } protected function tryChangeMovement() : void{ $this->checkObstruction($this->location->x, $this->location->y, $this->location->z); parent::tryChangeMovement(); } public function canBeCollidedWith() : bool{ return false; } protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); $properties->setInt(EntityMetadataProperties::EXPERIENCE_VALUE, $this->xpValue); } }