Code from my server core. PHP: <?phpnamespace sc\Entity;use sc\Utils\SupremeTextFormat as Text;use sc\Particle\Hologram;use sbh\Match\BlockHuntMatch;use pocketmine\entity\Entity;use pocketmine\entity\Human;use pocketmine\Player;use pocketmine\network\protocol\MovePlayerPacket;class NPC extends Human{ const RETURN_SPEED = 10; const TYPE_USELESS = 0; const TYPE_KIT = 1; const TYPE_INFO = 2; const TYPE_GAME = 3; /** @var string $type */ protected $type; /** @var array $npcData */ protected $npcData = []; /** @var Hologram $hologram */ protected $hologram; /** @var Player $target */ protected $target; /** @var int */ private $dYaw; private $dPitch; public function initEntity(){ parent::initEntity(); //$this->hologram = new Hologram($this, "", ""); $this->dYaw = $this->yaw; $this->dPitch = $this->pitch; echo $this->getId(); } public function updateMove() { if($this->x !== $this->lastX or $this->y !== $this->lastY or $this->z !== $this->lastZ or $this->yaw !== $this->lastYaw or $this->pitch !== $this->lastPitch){ $this->lastX = $this->x; $this->lastY = $this->y; $this->lastZ = $this->z; $this->lastYaw = $this->yaw; $this->lastPitch = $this->pitch; $pk = new \pocketmine\network\protocol\MovePlayerPacket(); $pk->eid = $this->id; $pk->x = $this->x; $pk->y = $this->y + $this->getEyeHeight();; $pk->z = $this->z; $pk->yaw = $this->yaw; $pk->pitch = $this->pitch; $pk->bodyYaw = $this->yaw; foreach($this->hasSpawned as $player){ $player->dataPacket($pk); } } if(($this->lastMotionX != $this->motionX or $this->lastMotionY != $this->motionY or $this->lastMotionZ != $this->motionZ)){ $this->lastMotionX = $this->motionX; $this->lastMotionY = $this->motionY; $this->lastMotionZ = $this->motionZ; foreach($this->hasSpawned as $player){ $player->addEntityMotion($this->id, $this->motionX, $this->motionY, $this->motionZ); } } } public function onUpdate($currentTick) { if(!$this->target){ $this->getChaseTarget(); if($this->yaw > $this->dYaw){ $this->yaw -= self::RETURN_SPEED; if($this->yaw < $this->dYaw) $this->yaw = $this->dYaw; } elseif ($this->yaw < $this->dYaw){ $this->yaw += self::RETURN_SPEED; if($this->yaw > $this->dYaw) $this->yaw = $this->dYaw; } if($this->pitch > $this->dPitch){ $this->pitch -= self::RETURN_SPEED; if($this->pitch < $this->dPitch) $this->pitch = $this->dPitch; } elseif ($this->pitch < $this->dPitch) { $this->pitch += self::RETURN_SPEED; if($this->pitch > $this->dPitch) $this->pitch = $this->dPitch; } $this->updateMove(); } else { if($this->distance($this->target) >= 4 or !$this->target->isConnected()){ $this->target = null; } else { $dist = $this->distance($this->target); $dir = $this->target->subtract($this); $dir = $dir->divide($dist); $this->yaw = rad2deg(atan2(-$dir->getX(),$dir->getZ())); $this->pitch = rad2deg(atan(-$dir->getY())); $this->updateMove(); } } switch($this->type){ case self::TYPE_USELESS: break; case self::TYPE_GAME: $match = $this->npcData['match']; if($currentTick % 20 === 1){ $mm = $match->getStages()[$match->getStage()]['title']; $status = ($match->canJoin() ? Text::GREEN : Text::RED); $title = "\n> $status$mm".Text::WHITE."<".Text::WHITE; $nametag = "Game: ".$this->npcData['game']."\n".Text::WHITE; if($this->npcData['game'] === 'BlockHunt'){ switch($match->getStage()){ case BlockHuntMatch::STAGE_HIDE: case BlockHuntMatch::STAGE_SEEK: $nametag .= "\nBlocks: ".count($match->getBlocks()); $nametag .= "\nHunters: ".count($match->getHunters()); } } else { $nametag .= "Players: ".count($this->npcData['match']->getParticipants())."/".$this->npcData['match']->getMaxPlayers()."\n".Text::WHITE; } $nametag .= "Map: ".$this->npcData['match']->getArena()->getName()."\n".Text::WHITE; // Map: Death Alley $join = ($status === Text::GREEN ? "JOIN" : "RUNNING"); $nametag .= "------ [ ".$status.$join.Text::WHITE." ]------"; $this->setNameTag($title.str_repeat("\n", 10).$nametag); } break; case self::TYPE_KIT: $nametag = " [ ".$this->npcData['kit']->getName()." ] "; $this->setNameTag($nametag); case self::TYPE_INFO: $nametag = "".$this->npcData['kit']->getName(); $this->setNameTag($nametag); break; default: break; } return true; } public function getName() { return parent::getName(); } /** * @deprecated */ public function getChaseTarget() { foreach ($this->getViewers() as $e) { if (!$e instanceof Player) continue; if ($this->distance($e) > 4) continue; $this->target = $e; } } /** * @param $type */ public function setType($type){ $this->type = $type; } /** * @return int */ public function getType(){ return $this->type; } /** * @param array $data */ public function setNPCData(array $data){ $this->npcData = $data; } /** * @param $key * @param $data */ public function addNPCData($key, $data){ $this->npcData[$key] = $data; } /** * @return array */ public function getNPCData() : array { return $this->npcData; } public function close(){ parent::close(); if($this->hologram != null){ $this->hologram->close(); } } /** -------[><>>-X-<<><]------- Map: Death Alley Players: 8/12 -------[>--<X^X>--<]------- */}