mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-20 07:54:19 +00:00
Implement Chiseled Bookshelf (#5827)
This commit is contained in:
parent
a6b030f2b3
commit
d94391af57
@ -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;
|
||||
|
||||
|
133
src/block/ChiseledBookshelf.php
Normal file
133
src/block/ChiseledBookshelf.php
Normal 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;
|
||||
}
|
||||
}
|
@ -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));
|
||||
|
58
src/block/tile/ChiseledBookshelf.php
Normal file
58
src/block/tile/ChiseledBookshelf.php
Normal 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);
|
||||
}
|
||||
}
|
@ -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"]);
|
||||
|
53
src/block/utils/ChiseledBookshelfSlot.php
Normal file
53
src/block/utils/ChiseledBookshelfSlot.php
Normal 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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user