Implement Chiseled Bookshelf (#5827)

This commit is contained in:
HimmelKreis4865 2023-09-28 16:56:46 +02:00 committed by GitHub
parent a6b030f2b3
commit d94391af57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 313 additions and 2 deletions

View File

@ -739,8 +739,9 @@ final class BlockTypeIds{
public const PINK_PETALS = 10709;
public const CRIMSON_ROOTS = 10710;
public const WARPED_ROOTS = 10711;
public const CHISELED_BOOKSHELF = 10712;
public const FIRST_UNUSED_BLOCK_ID = 10712;
public const FIRST_UNUSED_BLOCK_ID = 10713;
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;

View File

@ -0,0 +1,133 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\tile\ChiseledBookshelf as TileChiseledBookshelf;
use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Book;
use pocketmine\item\EnchantedBook;
use pocketmine\item\Item;
use pocketmine\item\WritableBookBase;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use function spl_object_id;
class ChiseledBookshelf extends Opaque{
use HorizontalFacingTrait;
use FacesOppositePlacingPlayerTrait;
/**
* @var ChiseledBookshelfSlot[]
* @phpstan-var array<int, ChiseledBookshelfSlot>
*/
private array $slots = [];
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
$w->horizontalFacing($this->facing);
$w->chiseledBookshelfSlots($this->slots);
}
/**
* Returns whether the given slot is displayed as occupied.
* This doesn't guarantee that there is or isn't a book in the bookshelf's inventory.
*/
public function hasSlot(ChiseledBookshelfSlot $slot) : bool{
return isset($this->slots[spl_object_id($slot)]);
}
/**
* Sets whether the given slot is displayed as occupied.
*
* This doesn't modify the bookshelf's inventory, so you can use this to make invisible
* books or display books that aren't actually in the bookshelf.
*
* To modify the contents of the bookshelf inventory, access the tile inventory.
*
* @return $this
*/
public function setSlot(ChiseledBookshelfSlot $slot, bool $occupied) : self{
if($occupied){
$this->slots[spl_object_id($slot)] = $slot;
}else{
unset($this->slots[spl_object_id($slot)]);
}
return $this;
}
/**
* Returns which slots of the bookshelf are displayed as occupied.
* As above, these values do not necessarily reflect the contents of the bookshelf inventory,
* although they usually will unless modified by plugins.
*
* @return ChiseledBookshelfSlot[]
* @phpstan-return array<int, ChiseledBookshelfSlot>
*/
public function getSlots() : array{
return $this->slots;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($face !== $this->facing){
return false;
}
$slot = ChiseledBookshelfSlot::fromBlockFaceCoordinates(
Facing::axis($face) === Axis::X ? $clickVector->getZ() : $clickVector->getX(),
$clickVector->y
);
$tile = $this->position->getWorld()->getTile($this->position);
if(!$tile instanceof TileChiseledBookshelf){
return false;
}
$inventory = $tile->getInventory();
if(!$inventory->isSlotEmpty($slot->value)){
$returnedItems[] = $inventory->getItem($slot->value);
$inventory->clear($slot->value);
$this->setSlot($slot, false);
}elseif($item instanceof WritableBookBase || $item instanceof Book || $item instanceof EnchantedBook){
//TODO: type tags like blocks would be better for this
$inventory->setItem($slot->value, $item);
$this->setSlot($slot, true);
}else{
return true;
}
$this->position->getWorld()->setBlock($this->position, $this);
return true;
}
public function getDropsForCompatibleTool(Item $item) : array{
return [];
}
public function isAffectedBySilkTouch() : bool{
return true;
}
}

View File

@ -38,6 +38,7 @@ use pocketmine\block\tile\BlastFurnace as TileBlastFurnace;
use pocketmine\block\tile\BrewingStand as TileBrewingStand;
use pocketmine\block\tile\Cauldron as TileCauldron;
use pocketmine\block\tile\Chest as TileChest;
use pocketmine\block\tile\ChiseledBookshelf as TileChiseledBookshelf;
use pocketmine\block\tile\Comparator as TileComparator;
use pocketmine\block\tile\DaylightSensor as TileDaylightSensor;
use pocketmine\block\tile\EnchantTable as TileEnchantingTable;
@ -177,6 +178,7 @@ use function strtolower;
* @method static WallSign CHERRY_WALL_SIGN()
* @method static Wood CHERRY_WOOD()
* @method static Chest CHEST()
* @method static ChiseledBookshelf CHISELED_BOOKSHELF()
* @method static Opaque CHISELED_DEEPSLATE()
* @method static Opaque CHISELED_NETHER_BRICKS()
* @method static Opaque CHISELED_POLISHED_BLACKSTONE()
@ -809,6 +811,7 @@ final class VanillaBlocks{
self::register("blue_ice", new BlueIce(new BID(Ids::BLUE_ICE), "Blue Ice", new Info(BreakInfo::pickaxe(2.8))));
self::register("bone_block", new BoneBlock(new BID(Ids::BONE_BLOCK), "Bone Block", new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD))));
self::register("bookshelf", new Bookshelf(new BID(Ids::BOOKSHELF), "Bookshelf", new Info(BreakInfo::axe(1.5))));
self::register("chiseled_bookshelf", new ChiseledBookshelf(new BID(Ids::CHISELED_BOOKSHELF, TileChiseledBookshelf::class), "Chiseled Bookshelf", new Info(BreakInfo::axe(1.5))));
self::register("brewing_stand", new BrewingStand(new BID(Ids::BREWING_STAND, TileBrewingStand::class), "Brewing Stand", new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD))));
$bricksBreakInfo = new Info(BreakInfo::pickaxe(2.0, ToolTier::WOOD, 30.0));

View File

@ -0,0 +1,58 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block\tile;
use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\inventory\SimpleInventory;
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\world\World;
use function count;
class ChiseledBookshelf extends Tile implements Container{
use ContainerTrait;
private SimpleInventory $inventory;
public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
$this->inventory = new SimpleInventory(count(ChiseledBookshelfSlot::cases()));
}
public function getInventory() : SimpleInventory{
return $this->inventory;
}
public function getRealInventory() : SimpleInventory{
return $this->inventory;
}
public function readSaveData(CompoundTag $nbt) : void{
$this->loadItems($nbt);
}
public function writeSaveData(CompoundTag $nbt) : void{
$this->saveItems($nbt);
}
}

View File

@ -59,6 +59,7 @@ final class TileFactory{
$this->register(BrewingStand::class, ["BrewingStand", "minecraft:brewing_stand"]);
$this->register(Cauldron::class, ["Cauldron", "minecraft:cauldron"]);
$this->register(Chest::class, ["Chest", "minecraft:chest"]);
$this->register(ChiseledBookshelf::class, ["ChiseledBookshelf", "minecraft:chiseled_bookshelf"]);
$this->register(Comparator::class, ["Comparator", "minecraft:comparator"]);
$this->register(DaylightSensor::class, ["DaylightDetector", "minecraft:daylight_detector"]);
$this->register(EnchantTable::class, ["EnchantTable", "minecraft:enchanting_table"]);

View File

@ -0,0 +1,53 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block\utils;
enum ChiseledBookshelfSlot : int{
case TOP_LEFT = 0;
case TOP_MIDDLE = 1;
case TOP_RIGHT = 2;
case BOTTOM_LEFT = 3;
case BOTTOM_MIDDLE = 4;
case BOTTOM_RIGHT = 5;
private const SLOTS_PER_SHELF = 3;
public static function fromBlockFaceCoordinates(float $x, float $y) : self{
if($x < 0 || $x > 1){
throw new \InvalidArgumentException("X must be between 0 and 1, got $x");
}
if($y < 0 || $y > 1){
throw new \InvalidArgumentException("Y must be between 0 and 1, got $y");
}
$slot = ($y < 0.5 ? self::SLOTS_PER_SHELF : 0) + match(true){
//we can't use simple maths here as the action is aligned to the 16x16 pixel grid :(
$x < 6 / 16 => 0,
$x < 11 / 16 => 1,
default => 2
};
return self::from($slot);
}
}

View File

@ -51,6 +51,7 @@ use pocketmine\block\CaveVines;
use pocketmine\block\Chain;
use pocketmine\block\ChemistryTable;
use pocketmine\block\Chest;
use pocketmine\block\ChiseledBookshelf;
use pocketmine\block\ChorusFlower;
use pocketmine\block\CocoaBlock;
use pocketmine\block\Concrete;
@ -1115,6 +1116,15 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{
return Writer::create(Ids::CHEST)
->writeHorizontalFacing($block->getFacing());
});
$this->map(Blocks::CHISELED_BOOKSHELF(), function(ChiseledBookshelf $block) : Writer{
$flags = 0;
foreach($block->getSlots() as $slot){
$flags |= 1 << $slot->value;
}
return Writer::create(Ids::CHISELED_BOOKSHELF)
->writeLegacyHorizontalFacing($block->getFacing())
->writeInt(StateNames::BOOKS_STORED, $flags);
});
$this->map(Blocks::CHISELED_QUARTZ(), fn(SimplePillar $block) => Helper::encodeQuartz(StringValues::CHISEL_TYPE_CHISELED, $block->getAxis()));
$this->map(Blocks::CHISELED_RED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::RED_SANDSTONE, StringValues::SAND_STONE_TYPE_HEIROGLYPHS));
$this->map(Blocks::CHISELED_SANDSTONE(), fn() => Helper::encodeSandstone(Ids::SANDSTONE, StringValues::SAND_STONE_TYPE_HEIROGLYPHS));

View File

@ -34,6 +34,7 @@ use pocketmine\block\Slab;
use pocketmine\block\Stair;
use pocketmine\block\SweetBerryBush;
use pocketmine\block\utils\BrewingStandSlot;
use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\block\utils\CopperOxidation;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\DirtType;
@ -992,6 +993,18 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
return Blocks::CHAIN()
->setAxis($in->readPillarAxis());
});
$this->map(Ids::CHISELED_BOOKSHELF, function(Reader $in) : Block{
$block = Blocks::CHISELED_BOOKSHELF()
->setFacing($in->readLegacyHorizontalFacing());
//we don't use API constant for bounds here as the data bounds might be different to what we support internally
$flags = $in->readBoundedInt(StateNames::BOOKS_STORED, 0, (1 << 6) - 1);
foreach(ChiseledBookshelfSlot::cases() as $slot){
$block->setSlot($slot, ($flags & (1 << $slot->value)) !== 0);
}
return $block;
});
$this->map(Ids::CHEMISTRY_TABLE, function(Reader $in) : Block{
return (match($type = $in->readString(StateNames::CHEMISTRY_TABLE_TYPE)){
StringValues::CHEMISTRY_TABLE_TYPE_COMPOUND_CREATOR => Blocks::COMPOUND_CREATOR(),

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\data\runtime;
use pocketmine\block\utils\BrewingStandSlot;
use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\block\utils\WallConnectionType;
use pocketmine\math\Facing;
@ -78,6 +79,12 @@ interface RuntimeDataDescriber extends RuntimeEnumDescriber{
public function straightOnlyRailShape(int &$railShape) : void;
/**
* @param ChiseledBookshelfSlot[] $slots
* @phpstan-param array<int, ChiseledBookshelfSlot> $slots
*/
public function chiseledBookshelfSlots(array &$slots) : void;
/**
* @phpstan-template T of \UnitEnum
* @phpstan-param T $case

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\data\runtime;
use pocketmine\block\utils\BrewingStandSlot;
use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\block\utils\RailConnectionInfo;
use pocketmine\block\utils\WallConnectionType;
use pocketmine\math\Axis;
@ -211,6 +212,20 @@ final class RuntimeDataReader implements RuntimeDataDescriber{
$railShape = $result;
}
/**
* @param ChiseledBookshelfSlot[] $slots
* @phpstan-param array<int, ChiseledBookshelfSlot> $slots
*/
public function chiseledBookshelfSlots(array &$slots) : void{
$result = [];
foreach(ChiseledBookshelfSlot::cases() as $member){
if($this->readBool()){
$result[spl_object_id($member)] = $member;
}
}
$slots = $result;
}
public function enum(\UnitEnum &$case) : void{
$metadata = RuntimeEnumMetadata::from($case);
$raw = $this->readInt($metadata->bits);

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\data\runtime;
use pocketmine\block\utils\BrewingStandSlot;
use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\math\Facing;
use function count;
@ -96,6 +97,10 @@ final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{
$this->addBits(3);
}
public function chiseledBookshelfSlots(array &$slots) : void{
$this->addBits(count(ChiseledBookshelfSlot::cases()));
}
public function enum(\UnitEnum &$case) : void{
$metadata = RuntimeEnumMetadata::from($case);
$this->addBits($metadata->bits);

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\data\runtime;
use pocketmine\block\utils\BrewingStandSlot;
use pocketmine\block\utils\ChiseledBookshelfSlot;
use pocketmine\block\utils\WallConnectionType;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
@ -174,6 +175,16 @@ final class RuntimeDataWriter implements RuntimeDataDescriber{
$this->int(3, $railShape);
}
/**
* @param ChiseledBookshelfSlot[] $slots
* @phpstan-param array<int, ChiseledBookshelfSlot> $slots
*/
public function chiseledBookshelfSlots(array &$slots) : void{
foreach(ChiseledBookshelfSlot::cases() as $member){
$this->writeBool(isset($slots[spl_object_id($member)]));
}
}
public function enum(\UnitEnum &$case) : void{
$metadata = RuntimeEnumMetadata::from($case);
$this->writeInt($metadata->bits, $metadata->enumToInt($case));

View File

@ -230,6 +230,7 @@ final class StringToItemParser extends StringToTParser{
$result->registerBlock("chemistry_table", fn() => Blocks::COMPOUND_CREATOR());
$result->registerBlock("chest", fn() => Blocks::CHEST());
$result->registerBlock("chipped_anvil", fn() => Blocks::ANVIL()->setDamage(1));
$result->registerBlock("chiseled_bookshelf", fn() => Blocks::CHISELED_BOOKSHELF());
$result->registerBlock("chiseled_deepslate", fn() => Blocks::CHISELED_DEEPSLATE());
$result->registerBlock("chiseled_nether_bricks", fn() => Blocks::CHISELED_NETHER_BRICKS());
$result->registerBlock("chiseled_polished_blackstone", fn() => Blocks::CHISELED_POLISHED_BLACKSTONE());

File diff suppressed because one or more lines are too long