skullType = SkullType::SKELETON(); //TODO: this should be a parameter parent::__construct($idInfo, $name, $breakInfo); } protected function writeStateToMeta() : int{ return ($this->facing === Facing::UP ? 1 : BlockDataSerializer::writeHorizontalFacing($this->facing)) | ($this->noDrops ? BlockLegacyMetadata::SKULL_FLAG_NO_DROPS : 0); } public function readStateFromData(int $id, int $stateMeta) : void{ $this->facing = ($stateMeta & 0x07) === 1 ? Facing::UP : BlockDataSerializer::readHorizontalFacing($stateMeta); $this->noDrops = ($stateMeta & BlockLegacyMetadata::SKULL_FLAG_NO_DROPS) !== 0; } public function getStateBitmask() : int{ return 0b1111; } public function readStateFromWorld() : void{ parent::readStateFromWorld(); $tile = $this->position->getWorld()->getTile($this->position); if($tile instanceof TileSkull){ $this->skullType = $tile->getSkullType(); $this->rotation = $tile->getRotation(); } } public function writeStateToWorld() : void{ parent::writeStateToWorld(); //extra block properties storage hack $tile = $this->position->getWorld()->getTile($this->position); assert($tile instanceof TileSkull); $tile->setRotation($this->rotation); $tile->setSkullType($this->skullType); } public function getSkullType() : SkullType{ return $this->skullType; } /** @return $this */ public function setSkullType(SkullType $skullType) : self{ $this->skullType = $skullType; return $this; } public function getFacing() : int{ return $this->facing; } /** @return $this */ public function setFacing(int $facing) : self{ if($facing === Facing::DOWN){ throw new \InvalidArgumentException("Skull may not face DOWN"); } $this->facing = $facing; return $this; } public function getRotation() : int{ return $this->rotation; } /** @return $this */ public function setRotation(int $rotation) : self{ if($rotation < self::MIN_ROTATION || $rotation > self::MAX_ROTATION){ throw new \InvalidArgumentException("Rotation must be in range " . self::MIN_ROTATION . " ... " . self::MAX_ROTATION); } $this->rotation = $rotation; return $this; } public function isNoDrops() : bool{ return $this->noDrops; } /** @return $this */ public function setNoDrops(bool $noDrops) : self{ $this->noDrops = $noDrops; return $this; } /** * @return AxisAlignedBB[] */ protected function recalculateCollisionBoxes() : array{ $collisionBox = AxisAlignedBB::one()->contract(0.25, 0, 0.25)->trim(Facing::UP, 0.5); return match($this->facing){ Facing::NORTH => [$collisionBox->offset(0, 0.25, 0.25)], Facing::SOUTH => [$collisionBox->offset(0, 0.25, -0.25)], Facing::WEST => [$collisionBox->offset(0.25, 0.25, 0)], Facing::EAST => [$collisionBox->offset(-0.25, 0.25, 0)], default => [$collisionBox] }; } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($face === Facing::DOWN){ return false; } $this->facing = $face; if($player !== null && $face === Facing::UP){ $this->rotation = ((int) floor(($player->getLocation()->getYaw() * 16 / 360) + 0.5)) & 0xf; } return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } protected function writeStateToItemMeta() : int{ return $this->skullType->getMagicNumber(); } }