diff --git a/src/block/BlockFactory.php b/src/block/BlockFactory.php index be3cbc8ee..69f11dbb1 100644 --- a/src/block/BlockFactory.php +++ b/src/block/BlockFactory.php @@ -44,6 +44,7 @@ use pocketmine\block\tile\ItemFrame as TileItemFrame; use pocketmine\block\tile\Jukebox as TileJukebox; use pocketmine\block\tile\MonsterSpawner as TileMonsterSpawner; use pocketmine\block\tile\Note as TileNote; +use pocketmine\block\tile\ShulkerBox as TileShulkerBox; use pocketmine\block\tile\Skull as TileSkull; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\InvalidBlockStateException; @@ -316,6 +317,8 @@ class BlockFactory{ $this->register(new SnowLayer(new BID(Ids::SNOW_LAYER, 0), "Snow Layer", new BlockBreakInfo(0.1, BlockToolType::SHOVEL, ToolTier::WOOD()->getHarvestLevel()))); $this->register(new SoulSand(new BID(Ids::SOUL_SAND, 0), "Soul Sand", new BlockBreakInfo(0.5, BlockToolType::SHOVEL))); $this->register(new Sponge(new BID(Ids::SPONGE, 0), "Sponge", new BlockBreakInfo(0.6, BlockToolType::HOE))); + $shulkerBoxBreakInfo = new BlockBreakInfo(2, BlockToolType::PICKAXE); + $this->register(new ShulkerBox(new BID(Ids::UNDYED_SHULKER_BOX, 0, null, TileShulkerBox::class), "Shulker Box", $shulkerBoxBreakInfo)); $stoneBreakInfo = new BlockBreakInfo(1.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel(), 30.0); $this->register($stone = new class(new BID(Ids::STONE, Meta::STONE_NORMAL), "Stone", $stoneBreakInfo) extends Opaque{ @@ -473,6 +476,7 @@ class BlockFactory{ }; $this->register(new GlazedTerracotta(BlockLegacyIdHelper::getGlazedTerracottaIdentifier($color), $coloredName("Glazed Terracotta"), $glazedTerracottaBreakInfo)); } + $this->register(new DyedShulkerBox(new BID(Ids::SHULKER_BOX, 0, null, TileShulkerBox::class), "Dyed Shulker Box", $shulkerBoxBreakInfo)); $this->register(new StainedGlass(new BID(Ids::STAINED_GLASS, 0), "Stained Glass", $glassBreakInfo)); $this->register(new StainedGlassPane(new BID(Ids::STAINED_GLASS_PANE, 0), "Stained Glass Pane", $glassBreakInfo)); $this->register(new StainedHardenedClay(new BID(Ids::STAINED_CLAY, 0), "Stained Clay", $hardenedClayBreakInfo)); @@ -569,7 +573,6 @@ class BlockFactory{ //TODO: minecraft:repeating_command_block //TODO: minecraft:scaffolding //TODO: minecraft:seagrass - //TODO: minecraft:shulker_box //TODO: minecraft:slime //TODO: minecraft:smithing_table //TODO: minecraft:smoker @@ -578,7 +581,6 @@ class BlockFactory{ //TODO: minecraft:structure_block //TODO: minecraft:sweet_berry_bush //TODO: minecraft:turtle_egg - //TODO: minecraft:undyed_shulker_box //endregion //region --- auto-generated TODOs for bedrock-1.13.0 --- diff --git a/src/block/DyedShulkerBox.php b/src/block/DyedShulkerBox.php new file mode 100644 index 000000000..7c50cacd0 --- /dev/null +++ b/src/block/DyedShulkerBox.php @@ -0,0 +1,36 @@ +color = DyeColor::WHITE(); + parent::__construct($idInfo, $name, $breakInfo); + } +} diff --git a/src/block/ShulkerBox.php b/src/block/ShulkerBox.php new file mode 100644 index 000000000..5ad7f21fb --- /dev/null +++ b/src/block/ShulkerBox.php @@ -0,0 +1,95 @@ +pos->getWorld()->getTile($this->pos); + if($shulker instanceof TileShulkerBox){ + $shulker->setFacing($this->facing); + } + } + + public function readStateFromWorld() : void{ + parent::readStateFromWorld(); + $shulker = $this->pos->getWorld()->getTile($this->pos); + if($shulker instanceof TileShulkerBox){ + $this->facing = $shulker->getFacing(); + } + } + + public function getMaxStackSize() : int{ + return 1; + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + $this->facing = $face; + + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + + public function asItem() : Item{ + $item = parent::asItem(); + $shulker = $this->pos->getWorld()->getTile($this->pos); + if($shulker instanceof TileShulkerBox){ + $shulkerNBT = $shulker->getCleanedNBT(); + if($shulkerNBT !== null){ + $item->setNamedTag($shulkerNBT); + } + if($shulker->hasName()){ + $item->setCustomName($shulker->getName()); + } + } + return $item; + } + + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + if($player instanceof Player){ + + $shulker = $this->pos->getWorld()->getTile($this->pos); + if($shulker instanceof TileShulkerBox){ + if( + $this->getSide($this->facing)->getId() !== BlockLegacyIds::AIR or + !$shulker->canOpenWith($item->getCustomName()) + ){ + return true; + } + + $player->setCurrentWindow($shulker->getInventory()); + } + } + + return true; + } +} diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index eb8cf9731..1da1d079f 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -161,6 +161,7 @@ use function assert; * @method static DoubleTallGrass DOUBLE_TALLGRASS() * @method static DragonEgg DRAGON_EGG() * @method static DriedKelp DRIED_KELP() + * @method static DyedShulkerBox DYED_SHULKER_BOX() * @method static Element ELEMENT_ACTINIUM() * @method static Element ELEMENT_ALUMINUM() * @method static Element ELEMENT_AMERICIUM() @@ -484,6 +485,7 @@ use function assert; * @method static Wall SANDSTONE_WALL() * @method static SeaLantern SEA_LANTERN() * @method static SeaPickle SEA_PICKLE() + * @method static ShulkerBox SHULKER_BOX() * @method static Opaque SMOOTH_QUARTZ() * @method static Slab SMOOTH_QUARTZ_SLAB() * @method static Stair SMOOTH_QUARTZ_STAIRS() @@ -720,6 +722,7 @@ final class VanillaBlocks{ self::register("double_tallgrass", $factory->get(175, 2)); self::register("dragon_egg", $factory->get(122, 0)); self::register("dried_kelp", $factory->get(394, 0)); + self::register("dyed_shulker_box", $factory->get(218, 0)); self::register("element_actinium", $factory->get(355, 0)); self::register("element_aluminum", $factory->get(279, 0)); self::register("element_americium", $factory->get(361, 0)); @@ -1043,6 +1046,7 @@ final class VanillaBlocks{ self::register("sandstone_wall", $factory->get(139, 5)); self::register("sea_lantern", $factory->get(169, 0)); self::register("sea_pickle", $factory->get(411, 0)); + self::register("shulker_box", $factory->get(205, 0)); self::register("smooth_quartz", $factory->get(155, 3)); self::register("smooth_quartz_slab", $factory->get(421, 1)); self::register("smooth_quartz_stairs", $factory->get(440, 0)); diff --git a/src/block/inventory/ShulkerBoxInventory.php b/src/block/inventory/ShulkerBoxInventory.php new file mode 100644 index 000000000..7c5a00e6d --- /dev/null +++ b/src/block/inventory/ShulkerBoxInventory.php @@ -0,0 +1,64 @@ +holder = $holder; + parent::__construct(27); + } + + protected function getOpenSound() : Sound{ + return new ShulkerOpenSound(); + } + + protected function getCloseSound() : Sound{ + return new ShulkerCloseSound(); + } + + public function canAddItem(Item $item) : bool{ + if($item->getId() === BlockLegacyIds::UNDYED_SHULKER_BOX || $item->getId() === BlockLegacyIds::SHULKER_BOX){ + return false; + } + return parent::canAddItem($item); + } + + protected function animateBlock(bool $isOpen) : void{ + $holder = $this->getHolder(); + + //event ID is always 1 for a chest + $holder->getWorld()->broadcastPacketToViewers($holder, BlockEventPacket::create(1, $isOpen ? 1 : 0, $holder->asVector3())); + } +} diff --git a/src/block/tile/ShulkerBox.php b/src/block/tile/ShulkerBox.php new file mode 100644 index 000000000..a2e3bbb36 --- /dev/null +++ b/src/block/tile/ShulkerBox.php @@ -0,0 +1,120 @@ +inventory = new ShulkerBoxInventory($this->pos); + } + + public function readSaveData(CompoundTag $nbt) : void{ + $this->loadName($nbt); + $this->loadItems($nbt); + $this->facing = $nbt->getByte(self::TAG_FACING, $this->facing); + } + + protected function writeSaveData(CompoundTag $nbt) : void{ + $this->saveName($nbt); + $this->saveItems($nbt); + $nbt->setByte(self::TAG_FACING, $this->facing); + } + + public function copyDataFromItem(Item $item) : void{ + $this->readSaveData($item->getNamedTag()); + if($item->hasCustomName()){ + $this->setName($item->getCustomName()); + } + } + + public function close() : void{ + if(!$this->closed){ + $this->inventory->removeAllViewers(); + parent::close(); + } + } + + protected function onBlockDestroyedHook() : void{ + //NOOP override of ContainerTrait - shulker boxes retain their contents when destroyed + } + + public function getCleanedNBT() : ?CompoundTag{ + $nbt = parent::getCleanedNBT(); + if($nbt !== null){ + $nbt->removeTag(self::TAG_FACING); + } + return $nbt; + } + + public function getFacing() : int{ + return $this->facing; + } + + public function setFacing(int $facing) : void{ + $this->facing = $facing; + } + + /** + * @return ShulkerBoxInventory + */ + public function getInventory(){ + return $this->inventory; + } + + /** + * @return ShulkerBoxInventory + */ + public function getRealInventory(){ + return $this->inventory; + } + + public function getDefaultName() : string{ + return "Shulker Box"; + } + + protected function addAdditionalSpawnData(CompoundTag $nbt) : void{ + $nbt->setByte(self::TAG_FACING, $this->facing); + $this->addNameSpawnData($nbt); + } +} diff --git a/src/block/tile/TileFactory.php b/src/block/tile/TileFactory.php index 10d144648..0e37366d7 100644 --- a/src/block/tile/TileFactory.php +++ b/src/block/tile/TileFactory.php @@ -66,6 +66,7 @@ final class TileFactory{ $this->register(Jukebox::class, ["Jukebox", "RecordPlayer", "minecraft:jukebox"]); $this->register(MonsterSpawner::class, ["MobSpawner", "minecraft:mob_spawner"]); $this->register(Note::class, ["Music", "minecraft:noteblock"]); + $this->register(ShulkerBox::class, ["ShulkerBox", "minecraft:shulker_box"]); $this->register(Sign::class, ["Sign", "minecraft:sign"]); $this->register(Skull::class, ["Skull", "minecraft:skull"]); @@ -86,7 +87,6 @@ final class TileFactory{ //TODO: MovingBlock //TODO: NetherReactor //TODO: PistonArm - //TODO: ShulkerBox //TODO: Smoker //TODO: StructureBlock } diff --git a/src/world/sound/ShulkerCloseSound.php b/src/world/sound/ShulkerCloseSound.php new file mode 100644 index 000000000..fc8bbd930 --- /dev/null +++ b/src/world/sound/ShulkerCloseSound.php @@ -0,0 +1,34 @@ +