40    public static function getNetworkTypeId() : 
string{ 
return EntityIds::XP_ORB; }
 
   42    public const TAG_VALUE_PC = 
"Value"; 
 
   43    public const TAG_VALUE_PE = 
"experience value"; 
 
   44    private const TAG_AGE = 
"Age"; 
 
   50    public const ORB_SPLIT_SIZES = [2477, 1237, 617, 307, 149, 73, 37, 17, 7, 3, 1]; 
 
   52    public const DEFAULT_DESPAWN_DELAY = 6000;
 
   53    public const NEVER_DESPAWN = -1;
 
   54    public const MAX_DESPAWN_DELAY = 32767 + self::DEFAULT_DESPAWN_DELAY; 
 
   62            if($amount >= $split){
 
 
   79            $size = self::getMaxOrbSize($amount);
 
 
   88    protected int $lookForTargetTime = 0;
 
   91    protected ?
int $targetPlayerRuntimeId = 
null;
 
   93    protected int $xpValue;
 
   95    private int $despawnDelay = self::DEFAULT_DESPAWN_DELAY;
 
   98        $this->xpValue = $xpValue;
 
   99        parent::__construct($location, $nbt);
 
  108    protected function initEntity(
CompoundTag $nbt) : void{
 
  109        parent::initEntity($nbt);
 
  111        $age = $nbt->getShort(self::TAG_AGE, 0);
 
  113            $this->despawnDelay = self::NEVER_DESPAWN;
 
  115            $this->despawnDelay = max(0, self::DEFAULT_DESPAWN_DELAY - $age);
 
  119    public function saveNBT() : CompoundTag{
 
  120        $nbt = parent::saveNBT();
 
  122        if($this->despawnDelay === self::NEVER_DESPAWN){
 
  125            $age = self::DEFAULT_DESPAWN_DELAY - $this->despawnDelay;
 
  127        $nbt->
setShort(self::TAG_AGE, $age);
 
  129        $nbt->
setShort(self::TAG_VALUE_PC, $this->getXpValue());
 
  130        $nbt->
setInt(self::TAG_VALUE_PE, $this->getXpValue());
 
  135    public function getDespawnDelay() : int{ return $this->despawnDelay; }
 
  137    public function setDespawnDelay(
int $despawnDelay) : void{
 
  138        if(($despawnDelay < 0 || $despawnDelay > self::MAX_DESPAWN_DELAY) && $despawnDelay !== self::NEVER_DESPAWN){
 
  139            throw new \InvalidArgumentException(
"Despawn ticker must be in range 0 ... " . self::MAX_DESPAWN_DELAY . 
" or " . self::NEVER_DESPAWN . 
", got $despawnDelay");
 
  141        $this->despawnDelay = $despawnDelay;
 
  144    public function getXpValue() : int{
 
  145        return $this->xpValue;
 
  148    public function setXpValue(
int $amount) : void{
 
  150            throw new \InvalidArgumentException(
"XP amount must be greater than 0, got $amount");
 
  152        $this->xpValue = $amount;
 
  153        $this->networkPropertiesDirty = 
true;
 
  156    public function hasTargetPlayer() : bool{
 
  157        return $this->targetPlayerRuntimeId !== null;
 
  160    public function getTargetPlayer() : ?Human{
 
  161        if($this->targetPlayerRuntimeId === null){
 
  165        $entity = $this->getWorld()->getEntity($this->targetPlayerRuntimeId);
 
  166        if($entity instanceof Human){
 
  173    public function setTargetPlayer(?Human $player) : void{
 
  174        $this->targetPlayerRuntimeId = $player !== null ? $player->getId() : null;
 
  177    protected function entityBaseTick(
int $tickDiff = 1) : bool{
 
  178        $hasUpdate = parent::entityBaseTick($tickDiff);
 
  180        $this->despawnDelay -= $tickDiff;
 
  181        if($this->despawnDelay <= 0){
 
  182            $this->flagForDespawn();
 
  186        $currentTarget = $this->getTargetPlayer();
 
  187        if($currentTarget !== 
null && (!$currentTarget->isAlive() || !$currentTarget->getXpManager()->canAttractXpOrbs() || $currentTarget->location->distanceSquared($this->location) > self::MAX_TARGET_DISTANCE ** 2)){
 
  188            $currentTarget = 
null;
 
  191        if($this->lookForTargetTime >= 20){
 
  192            if($currentTarget === 
null){
 
  193                $newTarget = $this->getWorld()->getNearestEntity($this->location, self::MAX_TARGET_DISTANCE, Human::class);
 
  195                if($newTarget instanceof Human && !($newTarget instanceof Player && $newTarget->isSpectator()) && $newTarget->getXpManager()->canAttractXpOrbs()){
 
  196                    $currentTarget = $newTarget;
 
  200            $this->lookForTargetTime = 0;
 
  202            $this->lookForTargetTime += $tickDiff;
 
  205        $this->setTargetPlayer($currentTarget);
 
  207        if($currentTarget !== 
null){
 
  208            $vector = $currentTarget->getPosition()->add(0, $currentTarget->getEyeHeight() / 2, 0)->subtractVector($this->location)->divide(self::MAX_TARGET_DISTANCE);
 
  210            $distance = $vector->lengthSquared();
 
  212                $this->motion = $this->motion->addVector($vector->normalize()->multiply(0.2 * (1 - sqrt($distance)) ** 2));
 
  215            if($currentTarget->getXpManager()->canPickupXp() && $this->boundingBox->intersectsWith($currentTarget->getBoundingBox())){
 
  216                $this->flagForDespawn();
 
  218                $currentTarget->getXpManager()->onPickupXp($this->getXpValue());
 
  225    protected function tryChangeMovement() : void{
 
  226        $this->checkObstruction($this->location->x, $this->location->y, $this->location->z);
 
  227        parent::tryChangeMovement();
 
  230    public function canBeCollidedWith() : bool{
 
  234    protected function syncNetworkData(EntityMetadataCollection $properties) : void{
 
  235        parent::syncNetworkData($properties);
 
  237        $properties->setInt(EntityMetadataProperties::EXPERIENCE_VALUE, $this->xpValue);