mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-07 18:32:55 +00:00
Finally deal with APIs on container and menu blocks
Two main interfaces are introduced: - Container (chest, furnace, etc) - MenuAccessor (includes Containers but also anvils, crafting tables etc. which are not containers) Containers have inventories, locks, and everything from MenuAccessor MenuAccessors have obstruction checks, and openToUnchecked() for plugins to use I opted not to include precondition checks in openTo() because it's possible to replicate those using the provided APIs if desired.
This commit is contained in:
@ -29,7 +29,8 @@ 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\MenuAccessor;
|
||||
use pocketmine\block\utils\MenuAccessorTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\entity\object\FallingBlock;
|
||||
@ -45,10 +46,10 @@ use pocketmine\world\sound\AnvilFallSound;
|
||||
use pocketmine\world\sound\Sound;
|
||||
use function round;
|
||||
|
||||
class Anvil extends Transparent implements Fallable, HorizontalFacing{
|
||||
class Anvil extends Transparent implements Fallable, HorizontalFacing, MenuAccessor{
|
||||
use FallableTrait;
|
||||
use HorizontalFacingTrait;
|
||||
use InventoryMenuTrait;
|
||||
use MenuAccessorTrait;
|
||||
|
||||
public const UNDAMAGED = 0;
|
||||
public const SLIGHTLY_DAMAGED = 1;
|
||||
@ -83,7 +84,7 @@ class Anvil extends Transparent implements Fallable, HorizontalFacing{
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
protected function newWindow(Player $player, Position $position) : AnvilInventoryWindow{
|
||||
protected function newMenu(Player $player, Position $position) : AnvilInventoryWindow{
|
||||
return new AnvilInventoryWindow($player, $position);
|
||||
}
|
||||
|
||||
|
@ -23,10 +23,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AnimatedContainer;
|
||||
use pocketmine\block\utils\AnimatedContainerTrait;
|
||||
use pocketmine\block\utils\AnimatedContainerLike;
|
||||
use pocketmine\block\utils\AnimatedContainerLikeTrait;
|
||||
use pocketmine\block\utils\AnyFacing;
|
||||
use pocketmine\block\utils\AnyFacingTrait;
|
||||
use pocketmine\block\utils\Container;
|
||||
use pocketmine\block\utils\ContainerTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
@ -40,8 +41,8 @@ use pocketmine\world\sound\BarrelOpenSound;
|
||||
use pocketmine\world\sound\Sound;
|
||||
use function abs;
|
||||
|
||||
class Barrel extends Opaque implements AnimatedContainer, AnyFacing{
|
||||
use AnimatedContainerTrait;
|
||||
class Barrel extends Opaque implements AnimatedContainerLike, AnyFacing, Container{
|
||||
use AnimatedContainerLikeTrait;
|
||||
use AnyFacingTrait;
|
||||
use ContainerTrait;
|
||||
|
||||
@ -86,15 +87,15 @@ class Barrel extends Opaque implements AnimatedContainer, AnyFacing{
|
||||
return 300;
|
||||
}
|
||||
|
||||
protected function getContainerOpenSound() : Sound{
|
||||
protected function getOpenSound() : Sound{
|
||||
return new BarrelOpenSound();
|
||||
}
|
||||
|
||||
protected function getContainerCloseSound() : Sound{
|
||||
protected function getCloseSound() : Sound{
|
||||
return new BarrelCloseSound();
|
||||
}
|
||||
|
||||
protected function doContainerAnimation(Position $position, bool $isOpen) : void{
|
||||
protected function playAnimationVisual(Position $position, bool $isOpen) : void{
|
||||
$world = $position->getWorld();
|
||||
$block = $world->getBlock($position);
|
||||
if($block instanceof Barrel){
|
||||
|
@ -26,6 +26,7 @@ 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\Container;
|
||||
use pocketmine\block\utils\ContainerTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
@ -38,7 +39,7 @@ use pocketmine\world\Position;
|
||||
use function array_key_exists;
|
||||
use function spl_object_id;
|
||||
|
||||
class BrewingStand extends Transparent{
|
||||
class BrewingStand extends Transparent implements Container{
|
||||
use ContainerTrait;
|
||||
|
||||
/**
|
||||
@ -98,7 +99,7 @@ class BrewingStand extends Transparent{
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function newWindow(Player $player, Inventory $inventory, Position $position) : BrewingStandInventoryWindow{
|
||||
protected function newMenu(Player $player, Inventory $inventory, Position $position) : BrewingStandInventoryWindow{
|
||||
return new BrewingStandInventoryWindow($player, $inventory, $position);
|
||||
}
|
||||
|
||||
|
@ -24,14 +24,15 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\window\CartographyTableInventoryWindow;
|
||||
use pocketmine\block\utils\InventoryMenuTrait;
|
||||
use pocketmine\block\utils\MenuAccessor;
|
||||
use pocketmine\block\utils\MenuAccessorTrait;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\Position;
|
||||
|
||||
final class CartographyTable extends Opaque{
|
||||
use InventoryMenuTrait;
|
||||
final class CartographyTable extends Opaque implements MenuAccessor{
|
||||
use MenuAccessorTrait;
|
||||
|
||||
protected function newWindow(Player $player, Position $position) : CartographyTableInventoryWindow{
|
||||
protected function newMenu(Player $player, Position $position) : CartographyTableInventoryWindow{
|
||||
return new CartographyTableInventoryWindow($player, $position);
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,9 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\inventory\window\BlockInventoryWindow;
|
||||
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\AnimatedContainerLike;
|
||||
use pocketmine\block\utils\AnimatedContainerLikeTrait;
|
||||
use pocketmine\block\utils\Container;
|
||||
use pocketmine\block\utils\ContainerTrait;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
@ -45,8 +46,8 @@ use pocketmine\world\sound\ChestCloseSound;
|
||||
use pocketmine\world\sound\ChestOpenSound;
|
||||
use pocketmine\world\sound\Sound;
|
||||
|
||||
class Chest extends Transparent implements AnimatedContainer, HorizontalFacing{
|
||||
use AnimatedContainerTrait;
|
||||
class Chest extends Transparent implements AnimatedContainerLike, Container, HorizontalFacing{
|
||||
use AnimatedContainerLikeTrait;
|
||||
use ContainerTrait;
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
|
||||
@ -102,7 +103,7 @@ class Chest extends Transparent implements AnimatedContainer, HorizontalFacing{
|
||||
}
|
||||
}
|
||||
|
||||
protected function isOpeningObstructed() : bool{
|
||||
public function isOpeningObstructed() : bool{
|
||||
if(!$this->getSide(Facing::UP)->isTransparent()){
|
||||
return true;
|
||||
}
|
||||
@ -110,7 +111,7 @@ class Chest extends Transparent implements AnimatedContainer, HorizontalFacing{
|
||||
return $pair !== null && !$pair->getBlock()->getSide(Facing::UP)->isTransparent();
|
||||
}
|
||||
|
||||
protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
|
||||
protected function newMenu(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
|
||||
[$pairOnLeft, $pair] = $this->locatePair($position) ?? [false, null];
|
||||
if($pair === null){
|
||||
return new BlockInventoryWindow($player, $inventory, $position);
|
||||
@ -127,29 +128,29 @@ class Chest extends Transparent implements AnimatedContainer, HorizontalFacing{
|
||||
return 300;
|
||||
}
|
||||
|
||||
protected function getContainerOpenSound() : Sound{
|
||||
protected function getOpenSound() : Sound{
|
||||
return new ChestOpenSound();
|
||||
}
|
||||
|
||||
protected function getContainerCloseSound() : Sound{
|
||||
protected function getCloseSound() : Sound{
|
||||
return new ChestCloseSound();
|
||||
}
|
||||
|
||||
protected function doContainerAnimation(Position $position, bool $isOpen) : void{
|
||||
protected function playAnimationVisual(Position $position, bool $isOpen) : void{
|
||||
//event ID is always 1 for a chest
|
||||
//TODO: we probably shouldn't be sending a packet directly here, but it doesn't fit anywhere into existing systems
|
||||
$position->getWorld()->broadcastPacketToViewers($position, BlockEventPacket::create(BlockPosition::fromVector3($position), 1, $isOpen ? 1 : 0));
|
||||
}
|
||||
|
||||
protected function doContainerEffects(bool $isOpen) : void{
|
||||
$this->doContainerAnimation($this->position, $isOpen);
|
||||
$this->playContainerSound($this->position, $isOpen);
|
||||
protected function doAnimationEffects(bool $isOpen) : void{
|
||||
$this->playAnimationVisual($this->position, $isOpen);
|
||||
$this->playAnimationSound($this->position, $isOpen);
|
||||
|
||||
$pairInfo = $this->locatePair($this->position);
|
||||
if($pairInfo !== null){
|
||||
[, $pair] = $pairInfo;
|
||||
$this->doContainerAnimation($pair->getPosition(), $isOpen);
|
||||
$this->playContainerSound($pair->getPosition(), $isOpen);
|
||||
$this->playAnimationVisual($pair->getPosition(), $isOpen);
|
||||
$this->playAnimationSound($pair->getPosition(), $isOpen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,14 +24,15 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\window\CraftingTableInventoryWindow;
|
||||
use pocketmine\block\utils\InventoryMenuTrait;
|
||||
use pocketmine\block\utils\MenuAccessor;
|
||||
use pocketmine\block\utils\MenuAccessorTrait;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\Position;
|
||||
|
||||
class CraftingTable extends Opaque{
|
||||
use InventoryMenuTrait;
|
||||
class CraftingTable extends Opaque implements MenuAccessor{
|
||||
use MenuAccessorTrait;
|
||||
|
||||
protected function newWindow(Player $player, Position $position) : CraftingTableInventoryWindow{
|
||||
protected function newMenu(Player $player, Position $position) : CraftingTableInventoryWindow{
|
||||
return new CraftingTableInventoryWindow($player, $position);
|
||||
}
|
||||
|
||||
|
@ -24,15 +24,16 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\window\EnchantingTableInventoryWindow;
|
||||
use pocketmine\block\utils\InventoryMenuTrait;
|
||||
use pocketmine\block\utils\MenuAccessor;
|
||||
use pocketmine\block\utils\MenuAccessorTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\Position;
|
||||
|
||||
class EnchantingTable extends Transparent{
|
||||
use InventoryMenuTrait;
|
||||
class EnchantingTable extends Transparent implements MenuAccessor{
|
||||
use MenuAccessorTrait;
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 0.25)];
|
||||
@ -42,7 +43,7 @@ class EnchantingTable extends Transparent{
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
protected function newWindow(Player $player, Position $position) : EnchantingTableInventoryWindow{
|
||||
protected function newMenu(Player $player, Position $position) : EnchantingTableInventoryWindow{
|
||||
return new EnchantingTableInventoryWindow($player, $position);
|
||||
}
|
||||
}
|
||||
|
@ -25,11 +25,11 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\window\BlockInventoryWindow;
|
||||
use pocketmine\block\tile\EnderChest as TileEnderChest;
|
||||
use pocketmine\block\utils\AnimatedContainer;
|
||||
use pocketmine\block\utils\AnimatedContainerTrait;
|
||||
use pocketmine\block\utils\AnimatedContainerLike;
|
||||
use pocketmine\block\utils\AnimatedContainerLikeTrait;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
use pocketmine\block\utils\InventoryMenuTrait;
|
||||
use pocketmine\block\utils\MenuAccessorTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
@ -42,12 +42,12 @@ use pocketmine\world\sound\EnderChestCloseSound;
|
||||
use pocketmine\world\sound\EnderChestOpenSound;
|
||||
use pocketmine\world\sound\Sound;
|
||||
|
||||
class EnderChest extends Transparent implements AnimatedContainer, HorizontalFacing{
|
||||
use AnimatedContainerTrait {
|
||||
onContainerOpen as private traitOnContainerOpen;
|
||||
onContainerClose as private traitOnContainerClose;
|
||||
class EnderChest extends Transparent implements AnimatedContainerLike, HorizontalFacing{
|
||||
use AnimatedContainerLikeTrait {
|
||||
onViewerAdded as private traitOnViewerAdded;
|
||||
onViewerRemoved as private traitOnViewerRemoved;
|
||||
}
|
||||
use InventoryMenuTrait;
|
||||
use MenuAccessorTrait;
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
|
||||
public function getLightLevel() : int{
|
||||
@ -67,7 +67,7 @@ class EnderChest extends Transparent implements AnimatedContainer, HorizontalFac
|
||||
return !$this->getSide(Facing::UP)->isTransparent();
|
||||
}
|
||||
|
||||
protected function newWindow(Player $player, Position $position) : BlockInventoryWindow{
|
||||
protected function newMenu(Player $player, Position $position) : BlockInventoryWindow{
|
||||
return new BlockInventoryWindow($player, $player->getEnderInventory(), $position);
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@ class EnderChest extends Transparent implements AnimatedContainer, HorizontalFac
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getContainerViewerCount() : int{
|
||||
protected function getViewerCount() : int{
|
||||
$enderChest = $this->position->getWorld()->getTile($this->position);
|
||||
if(!$enderChest instanceof TileEnderChest){
|
||||
return 0;
|
||||
@ -89,34 +89,34 @@ class EnderChest extends Transparent implements AnimatedContainer, HorizontalFac
|
||||
return $enderChest->getViewerCount();
|
||||
}
|
||||
|
||||
private function updateContainerViewerCount(int $amount) : void{
|
||||
private function updateViewerCount(int $amount) : void{
|
||||
$enderChest = $this->position->getWorld()->getTile($this->position);
|
||||
if($enderChest instanceof TileEnderChest){
|
||||
$enderChest->setViewerCount($enderChest->getViewerCount() + $amount);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getContainerOpenSound() : Sound{
|
||||
protected function getOpenSound() : Sound{
|
||||
return new EnderChestOpenSound();
|
||||
}
|
||||
|
||||
protected function getContainerCloseSound() : Sound{
|
||||
protected function getCloseSound() : Sound{
|
||||
return new EnderChestCloseSound();
|
||||
}
|
||||
|
||||
protected function doContainerAnimation(Position $position, bool $isOpen) : void{
|
||||
protected function playAnimationVisual(Position $position, bool $isOpen) : void{
|
||||
//event ID is always 1 for a chest
|
||||
//TODO: we probably shouldn't be sending a packet directly here, but it doesn't fit anywhere into existing systems
|
||||
$position->getWorld()->broadcastPacketToViewers($position, BlockEventPacket::create(BlockPosition::fromVector3($position), 1, $isOpen ? 1 : 0));
|
||||
}
|
||||
|
||||
public function onContainerOpen() : void{
|
||||
$this->updateContainerViewerCount(1);
|
||||
$this->traitOnContainerOpen();
|
||||
public function onViewerAdded() : void{
|
||||
$this->updateViewerCount(1);
|
||||
$this->traitOnViewerAdded();
|
||||
}
|
||||
|
||||
public function onContainerClose() : void{
|
||||
$this->traitOnContainerClose();
|
||||
$this->updateContainerViewerCount(-1);
|
||||
public function onViewerRemoved() : void{
|
||||
$this->traitOnViewerRemoved();
|
||||
$this->updateViewerCount(-1);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\window\FurnaceInventoryWindow;
|
||||
use pocketmine\block\tile\Furnace as TileFurnace;
|
||||
use pocketmine\block\utils\Container;
|
||||
use pocketmine\block\utils\ContainerTrait;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
@ -38,7 +39,7 @@ use pocketmine\player\Player;
|
||||
use pocketmine\world\Position;
|
||||
use function mt_rand;
|
||||
|
||||
class Furnace extends Opaque implements Lightable, HorizontalFacing{
|
||||
class Furnace extends Opaque implements Container, Lightable, HorizontalFacing{
|
||||
use ContainerTrait;
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use LightableTrait;
|
||||
@ -63,7 +64,7 @@ class Furnace extends Opaque implements Lightable, HorizontalFacing{
|
||||
return $this->lit ? 13 : 0;
|
||||
}
|
||||
|
||||
protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
|
||||
protected function newMenu(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
|
||||
return new FurnaceInventoryWindow($player, $inventory, $position, $this->furnaceType);
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\window\HopperInventoryWindow;
|
||||
use pocketmine\block\utils\Container;
|
||||
use pocketmine\block\utils\ContainerTrait;
|
||||
use pocketmine\block\utils\PoweredByRedstone;
|
||||
use pocketmine\block\utils\PoweredByRedstoneTrait;
|
||||
@ -39,7 +40,7 @@ use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\Position;
|
||||
|
||||
class Hopper extends Transparent implements PoweredByRedstone{
|
||||
class Hopper extends Transparent implements Container, PoweredByRedstone{
|
||||
use ContainerTrait;
|
||||
use PoweredByRedstoneTrait;
|
||||
|
||||
@ -86,7 +87,7 @@ class Hopper extends Transparent implements PoweredByRedstone{
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
|
||||
protected function newMenu(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
|
||||
return new HopperInventoryWindow($player, $inventory, $position);
|
||||
}
|
||||
|
||||
|
@ -26,15 +26,16 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\inventory\window\LoomInventoryWindow;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacing;
|
||||
use pocketmine\block\utils\InventoryMenuTrait;
|
||||
use pocketmine\block\utils\MenuAccessor;
|
||||
use pocketmine\block\utils\MenuAccessorTrait;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\Position;
|
||||
|
||||
final class Loom extends Opaque implements HorizontalFacing{
|
||||
final class Loom extends Opaque implements HorizontalFacing, MenuAccessor{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use InventoryMenuTrait;
|
||||
use MenuAccessorTrait;
|
||||
|
||||
protected function newWindow(Player $player, Position $position) : LoomInventoryWindow{
|
||||
protected function newMenu(Player $player, Position $position) : LoomInventoryWindow{
|
||||
return new LoomInventoryWindow($player, $position);
|
||||
}
|
||||
}
|
||||
|
@ -25,10 +25,11 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\window\BlockInventoryWindow;
|
||||
use pocketmine\block\tile\ShulkerBox as TileShulkerBox;
|
||||
use pocketmine\block\utils\AnimatedContainer;
|
||||
use pocketmine\block\utils\AnimatedContainerTrait;
|
||||
use pocketmine\block\utils\AnimatedContainerLike;
|
||||
use pocketmine\block\utils\AnimatedContainerLikeTrait;
|
||||
use pocketmine\block\utils\AnyFacing;
|
||||
use pocketmine\block\utils\AnyFacingTrait;
|
||||
use pocketmine\block\utils\Container;
|
||||
use pocketmine\block\utils\ContainerTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
@ -46,8 +47,8 @@ use pocketmine\world\sound\ShulkerBoxCloseSound;
|
||||
use pocketmine\world\sound\ShulkerBoxOpenSound;
|
||||
use pocketmine\world\sound\Sound;
|
||||
|
||||
class ShulkerBox extends Opaque implements AnimatedContainer, AnyFacing{
|
||||
use AnimatedContainerTrait;
|
||||
class ShulkerBox extends Opaque implements AnimatedContainerLike, AnyFacing, Container{
|
||||
use AnimatedContainerLikeTrait;
|
||||
use AnyFacingTrait;
|
||||
use ContainerTrait;
|
||||
|
||||
@ -109,11 +110,11 @@ class ShulkerBox extends Opaque implements AnimatedContainer, AnyFacing{
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function isOpeningObstructed() : bool{
|
||||
public function isOpeningObstructed() : bool{
|
||||
return $this->getSide($this->facing)->isSolid();
|
||||
}
|
||||
|
||||
protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
|
||||
protected function newMenu(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
|
||||
return new BlockInventoryWindow($player, $inventory, $position);
|
||||
}
|
||||
|
||||
@ -121,15 +122,15 @@ class ShulkerBox extends Opaque implements AnimatedContainer, AnyFacing{
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
protected function getContainerOpenSound() : Sound{
|
||||
protected function getOpenSound() : Sound{
|
||||
return new ShulkerBoxOpenSound();
|
||||
}
|
||||
|
||||
protected function getContainerCloseSound() : Sound{
|
||||
protected function getCloseSound() : Sound{
|
||||
return new ShulkerBoxCloseSound();
|
||||
}
|
||||
|
||||
protected function doContainerAnimation(Position $position, bool $isOpen) : void{
|
||||
protected function playAnimationVisual(Position $position, bool $isOpen) : void{
|
||||
//event ID is always 1 for a chest
|
||||
//TODO: we probably shouldn't be sending a packet directly here, but it doesn't fit anywhere into existing systems
|
||||
$position->getWorld()->broadcastPacketToViewers($position, BlockEventPacket::create(BlockPosition::fromVector3($position), 1, $isOpen ? 1 : 0));
|
||||
|
@ -24,15 +24,16 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\window\SmithingTableInventoryWindow;
|
||||
use pocketmine\block\utils\InventoryMenuTrait;
|
||||
use pocketmine\block\utils\MenuAccessor;
|
||||
use pocketmine\block\utils\MenuAccessorTrait;
|
||||
use pocketmine\player\InventoryWindow;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\Position;
|
||||
|
||||
final class SmithingTable extends Opaque{
|
||||
use InventoryMenuTrait;
|
||||
final class SmithingTable extends Opaque implements MenuAccessor{
|
||||
use MenuAccessorTrait;
|
||||
|
||||
protected function newWindow(Player $player, Position $position) : InventoryWindow{
|
||||
protected function newMenu(Player $player, Position $position) : InventoryWindow{
|
||||
return new SmithingTableInventoryWindow($player, $position);
|
||||
}
|
||||
|
||||
|
@ -26,18 +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\MenuAccessor;
|
||||
use pocketmine\block\utils\MenuAccessorTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\Position;
|
||||
|
||||
class Stonecutter extends Transparent implements HorizontalFacing{
|
||||
class Stonecutter extends Transparent implements HorizontalFacing, MenuAccessor{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use InventoryMenuTrait;
|
||||
use MenuAccessorTrait;
|
||||
|
||||
protected function newWindow(Player $player, Position $position) : StonecutterInventoryWindow{
|
||||
protected function newMenu(Player $player, Position $position) : StonecutterInventoryWindow{
|
||||
return new StonecutterInventoryWindow($player, $position);
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block\inventory\window;
|
||||
|
||||
use pocketmine\block\utils\AnimatedContainer;
|
||||
use pocketmine\block\utils\AnimatedContainerLike;
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\player\InventoryWindow;
|
||||
use pocketmine\player\Player;
|
||||
@ -44,15 +44,15 @@ class BlockInventoryWindow extends InventoryWindow{
|
||||
public function onOpen() : void{
|
||||
parent::onOpen();
|
||||
$block = $this->holder->getWorld()->getBlock($this->holder);
|
||||
if($block instanceof AnimatedContainer){
|
||||
$block->onContainerOpen();
|
||||
if($block instanceof AnimatedContainerLike){
|
||||
$block->onViewerAdded();
|
||||
}
|
||||
}
|
||||
|
||||
public function onClose() : void{
|
||||
$block = $this->holder->getWorld()->getBlock($this->holder);
|
||||
if($block instanceof AnimatedContainer){
|
||||
$block->onContainerClose();
|
||||
if($block instanceof AnimatedContainerLike){
|
||||
$block->onViewerRemoved();
|
||||
}
|
||||
parent::onClose();
|
||||
}
|
||||
|
@ -23,16 +23,20 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block\utils;
|
||||
|
||||
interface AnimatedContainer{
|
||||
/**
|
||||
* Blocks which have audiovisual behaviour (like chests) and remain in their "open" state for as long as at least 1
|
||||
* viewer is viewing the menu they provide access to
|
||||
*/
|
||||
interface AnimatedContainerLike extends MenuAccessor{
|
||||
/**
|
||||
* Do actions when the container block is opened by a player.
|
||||
* If you have a custom viewer counter (like ender chests), you should increment it here.
|
||||
*/
|
||||
public function onContainerOpen() : void;
|
||||
public function onViewerAdded() : void;
|
||||
|
||||
/**
|
||||
* Do actions when the container block is closed by a player.
|
||||
* As above, you should decrement your custom viewer counter here, if you have one.
|
||||
*/
|
||||
public function onContainerClose() : void;
|
||||
public function onViewerRemoved() : void;
|
||||
}
|
@ -28,9 +28,9 @@ use pocketmine\world\Position;
|
||||
use pocketmine\world\sound\Sound;
|
||||
use function count;
|
||||
|
||||
trait AnimatedContainerTrait{
|
||||
trait AnimatedContainerLikeTrait{
|
||||
|
||||
protected function getContainerViewerCount() : int{
|
||||
protected function getViewerCount() : int{
|
||||
$position = $this->getPosition();
|
||||
$tile = $position->getWorld()->getTile($position);
|
||||
if($tile instanceof InventoryHolder){
|
||||
@ -39,33 +39,33 @@ trait AnimatedContainerTrait{
|
||||
return 0;
|
||||
}
|
||||
|
||||
abstract protected function getContainerOpenSound() : Sound;
|
||||
abstract protected function getOpenSound() : Sound;
|
||||
|
||||
abstract protected function getContainerCloseSound() : Sound;
|
||||
abstract protected function getCloseSound() : Sound;
|
||||
|
||||
abstract protected function doContainerAnimation(Position $position, bool $isOpen) : void;
|
||||
abstract protected function playAnimationVisual(Position $position, bool $isOpen) : void;
|
||||
|
||||
protected function playContainerSound(Position $position, bool $isOpen) : void{
|
||||
$position->getWorld()->addSound($position->add(0.5, 0.5, 0.5), $isOpen ? $this->getContainerOpenSound() : $this->getContainerCloseSound());
|
||||
protected function playAnimationSound(Position $position, bool $isOpen) : void{
|
||||
$position->getWorld()->addSound($position->add(0.5, 0.5, 0.5), $isOpen ? $this->getOpenSound() : $this->getCloseSound());
|
||||
}
|
||||
|
||||
abstract protected function getPosition() : Position;
|
||||
|
||||
protected function doContainerEffects(bool $isOpen) : void{
|
||||
protected function doAnimationEffects(bool $isOpen) : void{
|
||||
$position = $this->getPosition();
|
||||
$this->doContainerAnimation($position, $isOpen);
|
||||
$this->playContainerSound($position, $isOpen);
|
||||
$this->playAnimationVisual($position, $isOpen);
|
||||
$this->playAnimationSound($position, $isOpen);
|
||||
}
|
||||
|
||||
public function onContainerOpen() : void{
|
||||
if($this->getContainerViewerCount() === 1){
|
||||
$this->doContainerEffects(true);
|
||||
public function onViewerAdded() : void{
|
||||
if($this->getViewerCount() === 1){
|
||||
$this->doAnimationEffects(true);
|
||||
}
|
||||
}
|
||||
|
||||
public function onContainerClose() : void{
|
||||
if($this->getContainerViewerCount() === 1){
|
||||
$this->doContainerEffects(false);
|
||||
public function onViewerRemoved() : void{
|
||||
if($this->getViewerCount() === 1){
|
||||
$this->doAnimationEffects(false);
|
||||
}
|
||||
}
|
||||
}
|
44
src/block/utils/Container.php
Normal file
44
src/block/utils/Container.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?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\inventory\Inventory;
|
||||
|
||||
/**
|
||||
* Blocks which have an associated inventory of contents
|
||||
* Default implementation provided by {@see ContainerTrait}
|
||||
*/
|
||||
interface Container extends MenuAccessor{
|
||||
/**
|
||||
* Returns whether an item with the given key as its custom name can be used to access the container's contents.
|
||||
*/
|
||||
public function canOpenWith(string $key) : bool;
|
||||
|
||||
/**
|
||||
* Returns the inventory of this container.
|
||||
* Note: This may return NULL if the container's tile was missing or incorrect. This is rare, but may occur as a
|
||||
* result of plugins incorrectly creating blocks, or legacy world data.
|
||||
*/
|
||||
public function getInventory() : ?Inventory;
|
||||
}
|
@ -39,34 +39,34 @@ 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);
|
||||
if($player instanceof Player && !$this->isOpeningObstructed() && $this->canOpenWith($item->getCustomName())){
|
||||
$this->openToUnchecked($player);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function isOpeningObstructed() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
|
||||
protected function newMenu(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
|
||||
return new BlockInventoryWindow($player, $inventory, $position);
|
||||
}
|
||||
|
||||
public function openTo(Player $player, bool $ignoreObstruction, bool $ignoreLock) : ContainerOpenResult{
|
||||
public function isOpeningObstructed() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function canOpenWith(string $key) : bool{
|
||||
//TODO: maybe we can bring the key to the block in readStateFromWorld()?
|
||||
$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;
|
||||
return $tile instanceof ContainerTile && $tile->canOpenWith($key);
|
||||
}
|
||||
|
||||
public function openToUnchecked(Player $player) : bool{
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
return $tile instanceof ContainerTile && $player->setCurrentWindow($this->newMenu($player, $tile->getInventory(), $this->position));
|
||||
}
|
||||
|
||||
public function getInventory() : ?Inventory{
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
return $tile instanceof ContainerTile ? $tile->getInventory() : null;
|
||||
}
|
||||
}
|
||||
|
@ -23,21 +23,23 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block\utils;
|
||||
|
||||
enum ContainerOpenResult{
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
* Blocks which open a menu when interacted with
|
||||
* This could be a container menu, or a menu that otherwise deals with items, such as a crafting menu
|
||||
*/
|
||||
interface MenuAccessor{
|
||||
/**
|
||||
* Opening the container succeeded and the player is now viewing the contents
|
||||
* Returns whether the block's ability to open the menu is currently obstructed (e.g. by nearby blocks).
|
||||
*/
|
||||
case SUCCESS;
|
||||
public function isOpeningObstructed() : bool;
|
||||
|
||||
/**
|
||||
* No container tile (and therefore no content) was found on the block's position
|
||||
* Opens the menu to the player.
|
||||
* Note: No preconditions are checked. Do not check for obstruction or locks here.
|
||||
*
|
||||
* Returns true if successful, false otherwise (e.g. event cancelled, container missing)
|
||||
*/
|
||||
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;
|
||||
public function openToUnchecked(Player $player) : bool;
|
||||
}
|
@ -31,30 +31,26 @@ use pocketmine\player\InventoryWindow;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\Position;
|
||||
|
||||
trait InventoryMenuTrait{
|
||||
trait MenuAccessorTrait{
|
||||
|
||||
/**
|
||||
* @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);
|
||||
if($player instanceof Player && !$this->isOpeningObstructed()){
|
||||
$this->openToUnchecked($player);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract protected function newWindow(Player $player, Position $position) : InventoryWindow;
|
||||
abstract protected function newMenu(Player $player, Position $position) : InventoryWindow;
|
||||
|
||||
protected function isOpeningObstructed() : bool{
|
||||
public 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;
|
||||
public function openToUnchecked(Player $player) : bool{
|
||||
return $player->setCurrentWindow($this->newMenu($player, $this->position));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user