diff --git a/src/pocketmine/block/Skull.php b/src/pocketmine/block/Skull.php index da62c2c26..c067fc3d8 100644 --- a/src/pocketmine/block/Skull.php +++ b/src/pocketmine/block/Skull.php @@ -24,24 +24,33 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\utils\BlockDataValidator; +use pocketmine\block\utils\SkullType; use pocketmine\item\Item; use pocketmine\item\ItemFactory; +use pocketmine\item\Skull as ItemSkull; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\Player; use pocketmine\tile\Skull as TileSkull; +use function assert; use function floor; class Skull extends Flowable{ + /** @var SkullType */ + protected $skullType; /** @var int */ protected $facing = Facing::NORTH; - protected $type = TileSkull::TYPE_SKELETON; /** @var int */ protected $rotation = 0; //TODO: split this into floor skull and wall skull handling + public function __construct(BlockIdentifier $idInfo, string $name){ + $this->skullType = SkullType::SKELETON(); //TODO: this should be a parameter + parent::__construct($idInfo, $name); + } + protected function writeStateToMeta() : int{ return $this->facing; } @@ -58,7 +67,7 @@ class Skull extends Flowable{ parent::readStateFromWorld(); $tile = $this->level->getTile($this); if($tile instanceof TileSkull){ - $this->type = $tile->getType(); + $this->skullType = $tile->getSkullType(); $this->rotation = $tile->getRotation(); } } @@ -67,16 +76,22 @@ class Skull extends Flowable{ parent::writeStateToWorld(); //extra block properties storage hack $tile = $this->level->getTile($this); - if($tile instanceof TileSkull){ - $tile->setRotation($this->rotation); - $tile->setType($this->type); - } + assert($tile instanceof TileSkull); + $tile->setRotation($this->rotation); + $tile->setSkullType($this->skullType); } public function getHardness() : float{ return 1; } + /** + * @return SkullType + */ + public function getSkullType() : SkullType{ + return $this->skullType; + } + protected function recalculateBoundingBox() : ?AxisAlignedBB{ //TODO: different bounds depending on attached face return AxisAlignedBB::one()->contract(0.25, 0, 0.25)->trim(Facing::UP, 0.5); @@ -88,7 +103,9 @@ class Skull extends Flowable{ } $this->facing = $face; - $this->type = $item->getDamage(); //TODO: replace this with a proper variant getter + if($item instanceof ItemSkull){ + $this->skullType = $item->getSkullType(); //TODO: the item should handle this, but this hack is currently needed because of tile mess + } if($player !== null and $face === Facing::UP){ $this->rotation = ((int) floor(($player->yaw * 16 / 360) + 0.5)) & 0xf; } @@ -96,7 +113,7 @@ class Skull extends Flowable{ } public function asItem() : Item{ - return ItemFactory::get(Item::SKULL, $this->type); + return ItemFactory::get(Item::SKULL, $this->skullType->getMagicNumber()); } public function isAffectedBySilkTouch() : bool{ diff --git a/src/pocketmine/block/utils/SkullType.php b/src/pocketmine/block/utils/SkullType.php new file mode 100644 index 000000000..ecbdd9302 --- /dev/null +++ b/src/pocketmine/block/utils/SkullType.php @@ -0,0 +1,131 @@ +getMagicNumber()] = $type; + self::$all[] = $type; + } + + /** + * @return SkullType[] + */ + public static function getAll() : array{ + return self::$all; + } + + /** + * @internal + * @param int $magicNumber + * + * @return SkullType + * @throws \InvalidArgumentException + */ + public static function fromMagicNumber(int $magicNumber) : SkullType{ + if(!isset(self::$numericIdMap[$magicNumber])){ + throw new \InvalidArgumentException("Unknown skull type magic number $magicNumber"); + } + return self::$numericIdMap[$magicNumber]; + } + + /** @var string */ + private $displayName; + /** @var int */ + private $magicNumber; + + public function __construct(string $displayName, int $magicNumber){ + $this->displayName = $displayName; + $this->magicNumber = $magicNumber; + } + + /** + * @return string + */ + public function getDisplayName() : string{ + return $this->displayName; + } + + /** + * @return int + */ + public function getMagicNumber() : int{ + return $this->magicNumber; + } +} +SkullType::_init(); diff --git a/src/pocketmine/item/ItemFactory.php b/src/pocketmine/item/ItemFactory.php index 7527a5d99..5f5a57844 100644 --- a/src/pocketmine/item/ItemFactory.php +++ b/src/pocketmine/item/ItemFactory.php @@ -26,10 +26,10 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\BlockFactory; use pocketmine\block\utils\DyeColor; +use pocketmine\block\utils\SkullType; use pocketmine\entity\EntityFactory; use pocketmine\entity\Living; use pocketmine\nbt\tag\CompoundTag; -use pocketmine\tile\Skull; use function constant; use function defined; use function explode; @@ -172,12 +172,6 @@ class ItemFactory{ self::register(new ItemBlock(Block::NETHER_WART_PLANT, 0, Item::NETHER_WART)); self::register(new ItemBlock(Block::OAK_DOOR_BLOCK, 0, Item::OAK_DOOR)); self::register(new ItemBlock(Block::REPEATER_BLOCK, 0, Item::REPEATER)); - self::register(new ItemBlock(Block::SKULL_BLOCK, Skull::TYPE_CREEPER, Item::SKULL)); - self::register(new ItemBlock(Block::SKULL_BLOCK, Skull::TYPE_DRAGON, Item::SKULL)); - self::register(new ItemBlock(Block::SKULL_BLOCK, Skull::TYPE_HUMAN, Item::SKULL)); - self::register(new ItemBlock(Block::SKULL_BLOCK, Skull::TYPE_SKELETON, Item::SKULL)); - self::register(new ItemBlock(Block::SKULL_BLOCK, Skull::TYPE_WITHER, Item::SKULL)); - self::register(new ItemBlock(Block::SKULL_BLOCK, Skull::TYPE_ZOMBIE, Item::SKULL)); self::register(new ItemBlock(Block::SPRUCE_DOOR_BLOCK, 0, Item::SPRUCE_DOOR)); self::register(new ItemBlock(Block::SUGARCANE_BLOCK, 0, Item::SUGARCANE)); self::register(new LeatherBoots()); @@ -236,6 +230,10 @@ class ItemFactory{ self::register(new WritableBook()); self::register(new WrittenBook()); + foreach(SkullType::getAll() as $skullType){ + self::register(new Skull(Item::SKULL, $skullType->getMagicNumber(), $skullType->getDisplayName(), $skullType)); + } + /** @var int[]|\SplObjectStorage $dyeMap */ $dyeMap = new \SplObjectStorage(); $dyeMap[DyeColor::BLACK()] = 16; diff --git a/src/pocketmine/item/Skull.php b/src/pocketmine/item/Skull.php new file mode 100644 index 000000000..8ad427243 --- /dev/null +++ b/src/pocketmine/item/Skull.php @@ -0,0 +1,47 @@ +skullType = $skullType; + } + + public function getBlock() : Block{ + return BlockFactory::get(Block::SKULL_BLOCK); + } + + public function getSkullType() : SkullType{ + return $this->skullType; + } +} diff --git a/src/pocketmine/tile/Skull.php b/src/pocketmine/tile/Skull.php index 87876a76c..42802db66 100644 --- a/src/pocketmine/tile/Skull.php +++ b/src/pocketmine/tile/Skull.php @@ -23,42 +23,58 @@ declare(strict_types=1); namespace pocketmine\tile; +use pocketmine\block\utils\SkullType; +use pocketmine\level\Level; +use pocketmine\math\Vector3; +use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; +/** + * @deprecated + * @see \pocketmine\block\Skull + */ class Skull extends Spawnable{ - public const TYPE_SKELETON = 0; - public const TYPE_WITHER = 1; - public const TYPE_ZOMBIE = 2; - public const TYPE_HUMAN = 3; - public const TYPE_CREEPER = 4; - public const TYPE_DRAGON = 5; - public const TAG_SKULL_TYPE = "SkullType"; //TAG_Byte - public const TAG_ROT = "Rot"; //TAG_Byte - public const TAG_MOUTH_MOVING = "MouthMoving"; //TAG_Byte - public const TAG_MOUTH_TICK_COUNT = "MouthTickCount"; //TAG_Int + private const TAG_SKULL_TYPE = "SkullType"; //TAG_Byte + private const TAG_ROT = "Rot"; //TAG_Byte + private const TAG_MOUTH_MOVING = "MouthMoving"; //TAG_Byte + private const TAG_MOUTH_TICK_COUNT = "MouthTickCount"; //TAG_Int - /** @var int */ - private $skullType = self::TYPE_SKELETON; + /** @var SkullType */ + private $skullType; /** @var int */ private $skullRotation = 0; + public function __construct(Level $level, Vector3 $pos){ + $this->skullType = SkullType::SKELETON(); + parent::__construct($level, $pos); + } + public function readSaveData(CompoundTag $nbt) : void{ - $this->skullType = $nbt->getByte(self::TAG_SKULL_TYPE, $this->skullType, true); - $this->skullRotation = $nbt->getByte(self::TAG_ROT, $this->skullRotation, true); + if($nbt->hasTag(self::TAG_SKULL_TYPE, ByteTag::class)){ + try{ + $this->skullType = SkullType::fromMagicNumber($nbt->getByte(self::TAG_SKULL_TYPE)); + }catch(\InvalidArgumentException $e){ + //bad data, drop it + } + } + $rotation = $nbt->getByte(self::TAG_ROT, 0, true); + if($rotation >= 0 and $rotation <= 15){ + $this->skullRotation = $rotation; + } } protected function writeSaveData(CompoundTag $nbt) : void{ - $nbt->setByte(self::TAG_SKULL_TYPE, $this->skullType); + $nbt->setByte(self::TAG_SKULL_TYPE, $this->skullType->getMagicNumber()); $nbt->setByte(self::TAG_ROT, $this->skullRotation); } - public function setType(int $type){ + public function setSkullType(SkullType $type){ $this->skullType = $type; $this->onChanged(); } - public function getType() : int{ + public function getSkullType() : SkullType{ return $this->skullType; } @@ -72,7 +88,7 @@ class Skull extends Spawnable{ } protected function addAdditionalSpawnData(CompoundTag $nbt) : void{ - $nbt->setByte(self::TAG_SKULL_TYPE, $this->skullType); + $nbt->setByte(self::TAG_SKULL_TYPE, $this->skullType->getMagicNumber()); $nbt->setByte(self::TAG_ROT, $this->skullRotation); } }