First step towards standardised opening logic for containers and menu blocks

we'll want to introduce interfaces for these, but getting the code deduplicated is enough to start with.
This commit is contained in:
Dylan K. Taylor
2025-08-30 21:35:42 +01:00
parent 174dfd1ecd
commit 496ab808a8
17 changed files with 270 additions and 162 deletions

View File

@ -29,6 +29,7 @@ use pocketmine\block\utils\FallableTrait;
use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\HorizontalFacingOption;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\block\utils\InventoryMenuTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\object\FallingBlock;
@ -39,6 +40,7 @@ use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\utils\Utils;
use pocketmine\world\BlockTransaction;
use pocketmine\world\Position;
use pocketmine\world\sound\AnvilFallSound;
use pocketmine\world\sound\Sound;
use function round;
@ -46,6 +48,7 @@ use function round;
class Anvil extends Transparent implements Fallable, HorizontalFacing{
use FallableTrait;
use HorizontalFacingTrait;
use InventoryMenuTrait;
public const UNDAMAGED = 0;
public const SLIGHTLY_DAMAGED = 1;
@ -80,12 +83,8 @@ class Anvil extends Transparent implements Fallable, HorizontalFacing{
return SupportType::NONE;
}
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player instanceof Player){
$player->setCurrentWindow(new AnvilInventoryWindow($player, $this->position));
}
return true;
protected function newWindow(Player $player, Position $position) : AnvilInventoryWindow{
return new AnvilInventoryWindow($player, $position);
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player = null) : bool{

View File

@ -23,12 +23,11 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\inventory\window\BlockInventoryWindow;
use pocketmine\block\tile\Barrel as TileBarrel;
use pocketmine\block\utils\AnimatedContainer;
use pocketmine\block\utils\AnimatedContainerTrait;
use pocketmine\block\utils\AnyFacing;
use pocketmine\block\utils\AnyFacingTrait;
use pocketmine\block\utils\ContainerTrait;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
use pocketmine\math\Facing;
@ -44,6 +43,7 @@ use function abs;
class Barrel extends Opaque implements AnimatedContainer, AnyFacing{
use AnimatedContainerTrait;
use AnyFacingTrait;
use ContainerTrait;
protected bool $open = false;
@ -82,21 +82,6 @@ class Barrel extends Opaque implements AnimatedContainer, AnyFacing{
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player instanceof Player){
$barrel = $this->position->getWorld()->getTile($this->position);
if($barrel instanceof TileBarrel){
if(!$barrel->canOpenWith($item->getCustomName())){
return true;
}
$player->setCurrentWindow(new BlockInventoryWindow($player, $barrel->getInventory(), $this->position));
}
}
return true;
}
public function getFuelTime() : int{
return 300;
}

View File

@ -26,18 +26,20 @@ namespace pocketmine\block;
use pocketmine\block\inventory\window\BrewingStandInventoryWindow;
use pocketmine\block\tile\BrewingStand as TileBrewingStand;
use pocketmine\block\utils\BrewingStandSlot;
use pocketmine\block\utils\ContainerTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
use pocketmine\inventory\Inventory;
use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\Position;
use function array_key_exists;
use function spl_object_id;
class BrewingStand extends Transparent{
use ContainerTrait;
/**
* @var BrewingStandSlot[]
@ -96,15 +98,8 @@ class BrewingStand extends Transparent{
return $this;
}
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player instanceof Player){
$stand = $this->position->getWorld()->getTile($this->position);
if($stand instanceof TileBrewingStand && $stand->canOpenWith($item->getCustomName())){
$player->setCurrentWindow(new BrewingStandInventoryWindow($player, $stand->getInventory(), $this->position));
}
}
return true;
protected function newWindow(Player $player, Inventory $inventory, Position $position) : BrewingStandInventoryWindow{
return new BrewingStandInventoryWindow($player, $inventory, $position);
}
public function onScheduledUpdate() : void{

View File

@ -24,19 +24,15 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\inventory\window\CartographyTableInventoryWindow;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\block\utils\InventoryMenuTrait;
use pocketmine\player\Player;
use pocketmine\world\Position;
final class CartographyTable extends Opaque{
use InventoryMenuTrait;
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player !== null){
$player->setCurrentWindow(new CartographyTableInventoryWindow($player, $this->position));
}
return true;
protected function newWindow(Player $player, Position $position) : CartographyTableInventoryWindow{
return new CartographyTableInventoryWindow($player, $position);
}
public function getFuelTime() : int{

View File

@ -28,16 +28,17 @@ use pocketmine\block\inventory\window\DoubleChestInventoryWindow;
use pocketmine\block\tile\Chest as TileChest;
use pocketmine\block\utils\AnimatedContainer;
use pocketmine\block\utils\AnimatedContainerTrait;
use pocketmine\block\utils\ContainerTrait;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\SupportType;
use pocketmine\event\block\ChestPairEvent;
use pocketmine\item\Item;
use pocketmine\inventory\Inventory;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\BlockEventPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\player\InventoryWindow;
use pocketmine\player\Player;
use pocketmine\world\Position;
use pocketmine\world\sound\ChestCloseSound;
@ -46,6 +47,7 @@ use pocketmine\world\sound\Sound;
class Chest extends Transparent implements AnimatedContainer, HorizontalFacing{
use AnimatedContainerTrait;
use ContainerTrait;
use FacesOppositePlacingPlayerTrait;
protected function recalculateCollisionBoxes() : array{
@ -100,34 +102,25 @@ class Chest extends Transparent implements AnimatedContainer, HorizontalFacing{
}
}
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player instanceof Player){
$world = $this->position->getWorld();
$chest = $world->getTile($this->position);
if($chest instanceof TileChest){
[$pairOnLeft, $pair] = $this->locatePair($this->position) ?? [false, null];
if(
!$this->getSide(Facing::UP)->isTransparent() ||
($pair !== null && !$pair->getBlock()->getSide(Facing::UP)->isTransparent()) ||
!$chest->canOpenWith($item->getCustomName())
){
return true;
}
if($pair !== null){
[$left, $right] = $pairOnLeft ? [$pair->getBlock(), $this] : [$this, $pair->getBlock()];
//TODO: we should probably construct DoubleChestInventory here directly too using the same logic
//right now it uses some weird logic in TileChest which produces incorrect results
//however I'm not sure if this is currently possible
$window = new DoubleChestInventoryWindow($player, $chest->getInventory(), $left->position, $right->position);
}
$player->setCurrentWindow($window ?? new BlockInventoryWindow($player, $chest->getInventory(), $this->position));
}
protected function isOpeningObstructed() : bool{
if(!$this->getSide(Facing::UP)->isTransparent()){
return true;
}
[, $pair] = $this->locatePair($this->position) ?? [false, null];
return $pair !== null && !$pair->getBlock()->getSide(Facing::UP)->isTransparent();
}
return true;
protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
[$pairOnLeft, $pair] = $this->locatePair($position) ?? [false, null];
if($pair === null){
return new BlockInventoryWindow($player, $inventory, $position);
}
[$left, $right] = $pairOnLeft ? [$pair->getPosition(), $position] : [$position, $pair->getPosition()];
//TODO: we should probably construct DoubleChestInventory here directly too using the same logic
//right now it uses some weird logic in TileChest which produces incorrect results
//however I'm not sure if this is currently possible
return new DoubleChestInventoryWindow($player, $inventory, $left, $right);
}
public function getFuelTime() : int{

View File

@ -24,19 +24,15 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\inventory\window\CraftingTableInventoryWindow;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\block\utils\InventoryMenuTrait;
use pocketmine\player\Player;
use pocketmine\world\Position;
class CraftingTable extends Opaque{
use InventoryMenuTrait;
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player instanceof Player){
$player->setCurrentWindow(new CraftingTableInventoryWindow($player, $this->position));
}
return true;
protected function newWindow(Player $player, Position $position) : CraftingTableInventoryWindow{
return new CraftingTableInventoryWindow($player, $position);
}
public function getFuelTime() : int{

View File

@ -24,14 +24,15 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\inventory\window\EnchantingTableInventoryWindow;
use pocketmine\block\utils\InventoryMenuTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\Position;
class EnchantingTable extends Transparent{
use InventoryMenuTrait;
protected function recalculateCollisionBoxes() : array{
return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 0.25)];
@ -41,13 +42,7 @@ class EnchantingTable extends Transparent{
return SupportType::NONE;
}
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player instanceof Player){
//TODO lock
$player->setCurrentWindow(new EnchantingTableInventoryWindow($player, $this->position));
}
return true;
protected function newWindow(Player $player, Position $position) : EnchantingTableInventoryWindow{
return new EnchantingTableInventoryWindow($player, $position);
}
}

View File

@ -29,11 +29,11 @@ use pocketmine\block\utils\AnimatedContainer;
use pocketmine\block\utils\AnimatedContainerTrait;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\InventoryMenuTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\BlockEventPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\player\Player;
@ -47,6 +47,7 @@ class EnderChest extends Transparent implements AnimatedContainer, HorizontalFac
onContainerOpen as private traitOnContainerOpen;
onContainerClose as private traitOnContainerClose;
}
use InventoryMenuTrait;
use FacesOppositePlacingPlayerTrait;
public function getLightLevel() : int{
@ -62,15 +63,12 @@ class EnderChest extends Transparent implements AnimatedContainer, HorizontalFac
return SupportType::NONE;
}
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player instanceof Player){
$enderChest = $this->position->getWorld()->getTile($this->position);
if($enderChest instanceof TileEnderChest && $this->getSide(Facing::UP)->isTransparent()){
$player->setCurrentWindow(new BlockInventoryWindow($player, $player->getEnderInventory(), $this->position));
}
}
protected function isOpeningObstructed() : bool{
return !$this->getSide(Facing::UP)->isTransparent();
}
return true;
protected function newWindow(Player $player, Position $position) : BlockInventoryWindow{
return new BlockInventoryWindow($player, $player->getEnderInventory(), $position);
}
public function getDropsForCompatibleTool(Item $item) : array{

View File

@ -25,19 +25,21 @@ namespace pocketmine\block;
use pocketmine\block\inventory\window\FurnaceInventoryWindow;
use pocketmine\block\tile\Furnace as TileFurnace;
use pocketmine\block\utils\ContainerTrait;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\Lightable;
use pocketmine\block\utils\LightableTrait;
use pocketmine\crafting\FurnaceType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\inventory\Inventory;
use pocketmine\player\InventoryWindow;
use pocketmine\player\Player;
use pocketmine\world\Position;
use function mt_rand;
class Furnace extends Opaque implements Lightable, HorizontalFacing{
use ContainerTrait;
use FacesOppositePlacingPlayerTrait;
use LightableTrait;
@ -61,15 +63,8 @@ class Furnace extends Opaque implements Lightable, HorizontalFacing{
return $this->lit ? 13 : 0;
}
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player instanceof Player){
$furnace = $this->position->getWorld()->getTile($this->position);
if($furnace instanceof TileFurnace && $furnace->canOpenWith($item->getCustomName())){
$player->setCurrentWindow(new FurnaceInventoryWindow($player, $furnace->getInventory(), $this->position, $this->furnaceType));
}
}
return true;
protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
return new FurnaceInventoryWindow($player, $inventory, $position, $this->furnaceType);
}
public function onScheduledUpdate() : void{

View File

@ -24,19 +24,23 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\inventory\window\HopperInventoryWindow;
use pocketmine\block\tile\Hopper as TileHopper;
use pocketmine\block\utils\ContainerTrait;
use pocketmine\block\utils\PoweredByRedstone;
use pocketmine\block\utils\PoweredByRedstoneTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\inventory\Inventory;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\InventoryWindow;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\Position;
class Hopper extends Transparent implements PoweredByRedstone{
use ContainerTrait;
use PoweredByRedstoneTrait;
private Facing $facing = Facing::DOWN;
@ -82,15 +86,8 @@ class Hopper extends Transparent implements PoweredByRedstone{
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player !== null){
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileHopper){ //TODO: find a way to have inventories open on click without this boilerplate in every block
$player->setCurrentWindow(new HopperInventoryWindow($player, $tile->getInventory(), $this->position));
}
return true;
}
return false;
protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
return new HopperInventoryWindow($player, $inventory, $position);
}
public function onScheduledUpdate() : void{

View File

@ -26,19 +26,15 @@ namespace pocketmine\block;
use pocketmine\block\inventory\window\LoomInventoryWindow;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\HorizontalFacing;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\block\utils\InventoryMenuTrait;
use pocketmine\player\Player;
use pocketmine\world\Position;
final class Loom extends Opaque implements HorizontalFacing{
use FacesOppositePlacingPlayerTrait;
use InventoryMenuTrait;
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player !== null){
$player->setCurrentWindow(new LoomInventoryWindow($player, $this->position));
return true;
}
return false;
protected function newWindow(Player $player, Position $position) : LoomInventoryWindow{
return new LoomInventoryWindow($player, $position);
}
}

View File

@ -29,13 +29,16 @@ use pocketmine\block\utils\AnimatedContainer;
use pocketmine\block\utils\AnimatedContainerTrait;
use pocketmine\block\utils\AnyFacing;
use pocketmine\block\utils\AnyFacingTrait;
use pocketmine\block\utils\ContainerTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\inventory\Inventory;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\BlockEventPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\player\InventoryWindow;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\Position;
@ -46,6 +49,7 @@ use pocketmine\world\sound\Sound;
class ShulkerBox extends Opaque implements AnimatedContainer, AnyFacing{
use AnimatedContainerTrait;
use AnyFacingTrait;
use ContainerTrait;
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
//NOOP - we don't read or write facing here, because the tile persists it
@ -105,23 +109,12 @@ class ShulkerBox extends Opaque implements AnimatedContainer, AnyFacing{
return $result;
}
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player instanceof Player){
protected function isOpeningObstructed() : bool{
return $this->getSide($this->facing)->isSolid();
}
$shulker = $this->position->getWorld()->getTile($this->position);
if($shulker instanceof TileShulkerBox){
if(
$this->getSide($this->facing)->isSolid() ||
!$shulker->canOpenWith($item->getCustomName())
){
return true;
}
$player->setCurrentWindow(new BlockInventoryWindow($player, $shulker->getInventory(), $this->position));
}
}
return true;
protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
return new BlockInventoryWindow($player, $inventory, $position);
}
public function getSupportType(Facing $facing) : SupportType{

View File

@ -24,19 +24,16 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\inventory\window\SmithingTableInventoryWindow;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\block\utils\InventoryMenuTrait;
use pocketmine\player\InventoryWindow;
use pocketmine\player\Player;
use pocketmine\world\Position;
final class SmithingTable extends Opaque{
use InventoryMenuTrait;
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player !== null){
$player->setCurrentWindow(new SmithingTableInventoryWindow($player, $this->position));
}
return true;
protected function newWindow(Player $player, Position $position) : InventoryWindow{
return new SmithingTableInventoryWindow($player, $position);
}
public function getFuelTime() : int{

View File

@ -26,21 +26,19 @@ namespace pocketmine\block;
use pocketmine\block\inventory\window\StonecutterInventoryWindow;
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\InventoryMenuTrait;
use pocketmine\block\utils\SupportType;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\Position;
class Stonecutter extends Transparent implements HorizontalFacing{
use FacesOppositePlacingPlayerTrait;
use InventoryMenuTrait;
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player !== null){
$player->setCurrentWindow(new StonecutterInventoryWindow($player, $this->position));
}
return true;
protected function newWindow(Player $player, Position $position) : StonecutterInventoryWindow{
return new StonecutterInventoryWindow($player, $position);
}
protected function recalculateCollisionBoxes() : array{

View File

@ -0,0 +1,43 @@
<?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 ContainerOpenResult{
/**
* Opening the container succeeded and the player is now viewing the contents
*/
case SUCCESS;
/**
* No container tile (and therefore no content) was found on the block's position
*/
case CONTAINER_NOT_FOUND;
/**
* The container's opening is obstructed (e.g a block on top of a chest's lid)
*/
case OBSTRUCTED;
/**
* The container is locked and the used item doesn't have the correct custom name to unlock it
*/
case INCORRECT_KEY;
}

View File

@ -0,0 +1,72 @@
<?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;
use pocketmine\block\Block;
use pocketmine\block\inventory\window\BlockInventoryWindow;
use pocketmine\block\tile\ContainerTile;
use pocketmine\inventory\Inventory;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\InventoryWindow;
use pocketmine\player\Player;
use pocketmine\world\Position;
trait ContainerTrait{
/**
* @see Block::onInteract()
*/
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player instanceof Player){
$this->openTo($player, ignoreObstruction: false, ignoreLock: false);
}
return true;
}
protected function isOpeningObstructed() : bool{
return false;
}
protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
return new BlockInventoryWindow($player, $inventory, $position);
}
public function openTo(Player $player, bool $ignoreObstruction, bool $ignoreLock) : ContainerOpenResult{
$tile = $this->position->getWorld()->getTile($this->position);
if(!$tile instanceof ContainerTile){
return ContainerOpenResult::CONTAINER_NOT_FOUND;
}
if(!$ignoreLock && !$tile->canOpenWith($player->getHotbar()->getHeldItem()->getCustomName())){
return ContainerOpenResult::INCORRECT_KEY;
}
if(!$ignoreObstruction && $this->isOpeningObstructed()){
return ContainerOpenResult::OBSTRUCTED;
}
$window = $this->newWindow($player, $tile->getInventory(), $this->position);
$player->setCurrentWindow($window);
return ContainerOpenResult::SUCCESS;
}
}

View File

@ -0,0 +1,60 @@
<?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;
use pocketmine\block\Block;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\InventoryWindow;
use pocketmine\player\Player;
use pocketmine\world\Position;
trait InventoryMenuTrait{
/**
* @see Block::onInteract()
*/
public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player instanceof Player){
$this->openTo($player, ignoreObstruction: false);
}
return true;
}
abstract protected function newWindow(Player $player, Position $position) : InventoryWindow;
protected function isOpeningObstructed() : bool{
return false;
}
public function openTo(Player $player, bool $ignoreObstruction) : bool{
if(!$ignoreObstruction && $this->isOpeningObstructed()){
return false;
}
$player->setCurrentWindow($this->newWindow($player, $this->position));
return true;
}
}