From 4dcc14e0a1c7610815ee3824854f0009821480af Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 25 Nov 2024 10:55:35 +0000 Subject: [PATCH] Integrate block container animations and SFX into Block classes by way of AnimatedContainer interface this allows getting rid of several container window classes. we should probably look into adding more info to the BlockInventoryWindow to make the type easier to identify, though. now that holder is tracked by an ephemeral window, we can put whatever we like in there. --- src/block/Barrel.php | 29 +++++- src/block/Chest.php | 96 +++++++++++++++---- src/block/EnderChest.php | 57 ++++++++++- src/block/ShulkerBox.php | 29 +++++- .../window/AnimatedBlockInventoryWindow.php | 65 ------------- .../window/BarrelInventoryWindow.php | 49 ---------- .../inventory/window/BlockInventoryWindow.php | 17 ++++ .../inventory/window/ChestInventoryWindow.php | 48 ---------- .../window/DoubleChestInventoryWindow.php | 10 +- .../window/EnderChestInventoryWindow.php | 73 -------------- .../window/ShulkerBoxInventoryWindow.php | 46 --------- src/player/Player.php | 3 + 12 files changed, 205 insertions(+), 317 deletions(-) delete mode 100644 src/block/inventory/window/AnimatedBlockInventoryWindow.php delete mode 100644 src/block/inventory/window/BarrelInventoryWindow.php delete mode 100644 src/block/inventory/window/ChestInventoryWindow.php delete mode 100644 src/block/inventory/window/EnderChestInventoryWindow.php delete mode 100644 src/block/inventory/window/ShulkerBoxInventoryWindow.php diff --git a/src/block/Barrel.php b/src/block/Barrel.php index 2b781a17d..1b64e814c 100644 --- a/src/block/Barrel.php +++ b/src/block/Barrel.php @@ -23,8 +23,10 @@ declare(strict_types=1); namespace pocketmine\block; -use pocketmine\block\inventory\window\BarrelInventoryWindow; +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\AnyFacingTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -32,9 +34,14 @@ use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; +use pocketmine\world\Position; +use pocketmine\world\sound\BarrelCloseSound; +use pocketmine\world\sound\BarrelOpenSound; +use pocketmine\world\sound\Sound; use function abs; -class Barrel extends Opaque{ +class Barrel extends Opaque implements AnimatedContainer{ + use AnimatedContainerTrait; use AnyFacingTrait; protected bool $open = false; @@ -82,7 +89,7 @@ class Barrel extends Opaque{ return true; } - $player->setCurrentWindow(new BarrelInventoryWindow($player, $barrel->getInventory(), $this->position)); + $player->setCurrentWindow(new BlockInventoryWindow($player, $barrel->getInventory(), $this->position)); } } @@ -92,4 +99,20 @@ class Barrel extends Opaque{ public function getFuelTime() : int{ return 300; } + + protected function getContainerOpenSound() : Sound{ + return new BarrelOpenSound(); + } + + protected function getContainerCloseSound() : Sound{ + return new BarrelCloseSound(); + } + + protected function doContainerAnimation(Position $position, bool $isOpen) : void{ + $world = $position->getWorld(); + $block = $world->getBlock($position); + if($block instanceof Barrel){ + $world->setBlock($position, $block->setOpen($isOpen)); + } + } } diff --git a/src/block/Chest.php b/src/block/Chest.php index 539626535..a688df0bd 100644 --- a/src/block/Chest.php +++ b/src/block/Chest.php @@ -24,9 +24,11 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\inventory\DoubleChestInventory; -use pocketmine\block\inventory\window\ChestInventoryWindow; +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\FacesOppositePlacingPlayerTrait; use pocketmine\block\utils\SupportType; use pocketmine\event\block\ChestPairEvent; @@ -34,9 +36,17 @@ 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; +use pocketmine\world\Position; +use pocketmine\world\sound\ChestCloseSound; +use pocketmine\world\sound\ChestOpenSound; +use pocketmine\world\sound\Sound; +use function count; -class Chest extends Transparent{ +class Chest extends Transparent implements AnimatedContainer{ + use AnimatedContainerTrait; use FacesOppositePlacingPlayerTrait; /** @@ -51,6 +61,25 @@ class Chest extends Transparent{ return SupportType::NONE; } + /** + * @phpstan-return array{bool, TileChest}|null + */ + private function locatePair(Position $position) : ?array{ + $world = $position->getWorld(); + $tile = $world->getTile($position); + if($tile instanceof TileChest){ + foreach([false, true] as $clockwise){ + $side = Facing::rotateY($this->facing, $clockwise); + $c = $position->getSide($side); + $pair = $world->getTile($c); + if($pair instanceof TileChest && $pair->isPaired() && $pair->getPair() === $tile){ + return [$clockwise, $pair]; + } + } + } + return null; + } + public function onPostPlace() : void{ $world = $this->position->getWorld(); $tile = $world->getTile($this->position); @@ -90,27 +119,23 @@ class Chest extends Transparent{ $window = null; if($chest->isPaired()){ - foreach([false, true] as $clockwise){ - $sideFacing = Facing::rotateY($this->facing, $clockwise); - $side = $this->position->getSide($sideFacing); - $pair = $world->getTile($side); - if($pair instanceof TileChest && $pair->getPair() === $chest){ - [$left, $right] = $clockwise ? [$pair, $chest] : [$chest, $pair]; + $info = $this->locatePair($this->position); + if($info !== null){ + [$clockwise, $pair] = $info; + [$left, $right] = $clockwise ? [$pair, $chest] : [$chest, $pair]; - $doubleInventory = $left->getDoubleInventory() ?? $right->getDoubleInventory(); - if($doubleInventory === null){ - $doubleInventory = new DoubleChestInventory($left->getInventory(), $right->getInventory()); - $left->setDoubleInventory($doubleInventory); - $right->setDoubleInventory($doubleInventory); - } - - $window = new DoubleChestInventoryWindow($player, $doubleInventory, $left->getPosition(), $right->getPosition()); - break; + $doubleInventory = $left->getDoubleInventory() ?? $right->getDoubleInventory(); + if($doubleInventory === null){ + $doubleInventory = new DoubleChestInventory($left->getInventory(), $right->getInventory()); + $left->setDoubleInventory($doubleInventory); + $right->setDoubleInventory($doubleInventory); } + + $window = new DoubleChestInventoryWindow($player, $doubleInventory, $left->getPosition(), $right->getPosition()); } } - $player->setCurrentWindow($window ?? new ChestInventoryWindow($player, $chest->getInventory(), $this->position)); + $player->setCurrentWindow($window ?? new BlockInventoryWindow($player, $chest->getInventory(), $this->position)); } } @@ -120,4 +145,39 @@ class Chest extends Transparent{ public function getFuelTime() : int{ return 300; } + + protected function getContainerViewerCount() : int{ + $tile = $this->position->getWorld()->getTile($this->position); + if($tile instanceof TileChest){ + $inventory = $tile->getDoubleInventory() ?? $tile->getInventory(); + return count($inventory->getViewers()); + } + return 0; + } + + protected function getContainerOpenSound() : Sound{ + return new ChestOpenSound(); + } + + protected function getContainerCloseSound() : Sound{ + return new ChestCloseSound(); + } + + protected function doContainerAnimation(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); + + $pairInfo = $this->locatePair($this->position); + if($pairInfo !== null){ + [, $pair] = $pairInfo; + $this->doContainerAnimation($pair->getPosition(), $isOpen); + $this->playContainerSound($pair->getPosition(), $isOpen); + } + } } diff --git a/src/block/EnderChest.php b/src/block/EnderChest.php index 675bdef58..c5901d3f4 100644 --- a/src/block/EnderChest.php +++ b/src/block/EnderChest.php @@ -23,17 +23,29 @@ declare(strict_types=1); namespace pocketmine\block; -use pocketmine\block\inventory\window\EnderChestInventoryWindow; +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\FacesOppositePlacingPlayerTrait; 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; +use pocketmine\world\Position; +use pocketmine\world\sound\EnderChestCloseSound; +use pocketmine\world\sound\EnderChestOpenSound; +use pocketmine\world\sound\Sound; -class EnderChest extends Transparent{ +class EnderChest extends Transparent implements AnimatedContainer{ + use AnimatedContainerTrait { + onContainerOpen as private traitOnContainerOpen; + onContainerClose as private traitOnContainerClose; + } use FacesOppositePlacingPlayerTrait; public function getLightLevel() : int{ @@ -56,7 +68,7 @@ class EnderChest extends Transparent{ if($player instanceof Player){ $enderChest = $this->position->getWorld()->getTile($this->position); if($enderChest instanceof TileEnderChest && $this->getSide(Facing::UP)->isTransparent()){ - $player->setCurrentWindow(new EnderChestInventoryWindow($player, $player->getEnderInventory(), $this->position)); + $player->setCurrentWindow(new BlockInventoryWindow($player, $player->getEnderInventory(), $this->position)); } } @@ -72,4 +84,43 @@ class EnderChest extends Transparent{ public function isAffectedBySilkTouch() : bool{ return true; } + + protected function getContainerViewerCount() : int{ + $enderChest = $this->position->getWorld()->getTile($this->position); + if(!$enderChest instanceof TileEnderChest){ + return 0; + } + return $enderChest->getViewerCount(); + } + + private function updateContainerViewerCount(int $amount) : void{ + $enderChest = $this->position->getWorld()->getTile($this->position); + if($enderChest instanceof TileEnderChest){ + $enderChest->setViewerCount($enderChest->getViewerCount() + $amount); + } + } + + protected function getContainerOpenSound() : Sound{ + return new EnderChestOpenSound(); + } + + protected function getContainerCloseSound() : Sound{ + return new EnderChestCloseSound(); + } + + protected function doContainerAnimation(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 onContainerClose() : void{ + $this->traitOnContainerClose(); + $this->updateContainerViewerCount(-1); + } } diff --git a/src/block/ShulkerBox.php b/src/block/ShulkerBox.php index a21a373b6..170033963 100644 --- a/src/block/ShulkerBox.php +++ b/src/block/ShulkerBox.php @@ -23,17 +23,26 @@ declare(strict_types=1); namespace pocketmine\block; -use pocketmine\block\inventory\window\ShulkerBoxInventoryWindow; +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\AnyFacingTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; use pocketmine\math\Vector3; +use pocketmine\network\mcpe\protocol\BlockEventPacket; +use pocketmine\network\mcpe\protocol\types\BlockPosition; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; +use pocketmine\world\Position; +use pocketmine\world\sound\ShulkerBoxCloseSound; +use pocketmine\world\sound\ShulkerBoxOpenSound; +use pocketmine\world\sound\Sound; -class ShulkerBox extends Opaque{ +class ShulkerBox extends Opaque implements AnimatedContainer{ + use AnimatedContainerTrait; use AnyFacingTrait; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ @@ -106,7 +115,7 @@ class ShulkerBox extends Opaque{ return true; } - $player->setCurrentWindow(new ShulkerBoxInventoryWindow($player, $shulker->getInventory(), $this->position)); + $player->setCurrentWindow(new BlockInventoryWindow($player, $shulker->getInventory(), $this->position)); } } @@ -116,4 +125,18 @@ class ShulkerBox extends Opaque{ public function getSupportType(int $facing) : SupportType{ return SupportType::NONE; } + + protected function getContainerOpenSound() : Sound{ + return new ShulkerBoxOpenSound(); + } + + protected function getContainerCloseSound() : Sound{ + return new ShulkerBoxCloseSound(); + } + + protected function doContainerAnimation(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)); + } } diff --git a/src/block/inventory/window/AnimatedBlockInventoryWindow.php b/src/block/inventory/window/AnimatedBlockInventoryWindow.php deleted file mode 100644 index 29c700298..000000000 --- a/src/block/inventory/window/AnimatedBlockInventoryWindow.php +++ /dev/null @@ -1,65 +0,0 @@ -inventory->getViewers()); - } - - abstract protected function getOpenSound() : Sound; - - abstract protected function getCloseSound() : Sound; - - abstract protected function animateBlock(Position $position, bool $isOpen) : void; - - protected function playSound(Position $position, bool $isOpen) : void{ - $position->getWorld()->addSound($position->add(0.5, 0.5, 0.5), $isOpen ? $this->getOpenSound() : $this->getCloseSound()); - } - - protected function doBlockEffects(bool $isOpen) : void{ - $position = $this->holder; - $this->animateBlock($position, $isOpen); - $this->playSound($position, $isOpen); - } - - public function onOpen() : void{ - parent::onOpen(); - if($this->getViewerCount() === 1){ - $this->doBlockEffects(true); - } - } - - public function onClose() : void{ - if($this->getViewerCount() === 1){ - $this->doBlockEffects(false); - } - parent::onClose(); - } -} diff --git a/src/block/inventory/window/BarrelInventoryWindow.php b/src/block/inventory/window/BarrelInventoryWindow.php deleted file mode 100644 index 7dd4d0dde..000000000 --- a/src/block/inventory/window/BarrelInventoryWindow.php +++ /dev/null @@ -1,49 +0,0 @@ -getWorld(); - $block = $world->getBlock($position); - if($block instanceof Barrel){ - $world->setBlock($position, $block->setOpen($isOpen)); - } - } -} diff --git a/src/block/inventory/window/BlockInventoryWindow.php b/src/block/inventory/window/BlockInventoryWindow.php index b2a84a851..149c2e814 100644 --- a/src/block/inventory/window/BlockInventoryWindow.php +++ b/src/block/inventory/window/BlockInventoryWindow.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block\inventory\window; +use pocketmine\block\utils\AnimatedContainer; use pocketmine\inventory\Inventory; use pocketmine\player\InventoryWindow; use pocketmine\player\Player; @@ -39,4 +40,20 @@ class BlockInventoryWindow extends InventoryWindow{ } public function getHolder() : Position{ return $this->holder; } + + public function onOpen() : void{ + parent::onOpen(); + $block = $this->holder->getWorld()->getBlock($this->holder); + if($block instanceof AnimatedContainer){ + $block->onContainerOpen(); + } + } + + public function onClose() : void{ + $block = $this->holder->getWorld()->getBlock($this->holder); + if($block instanceof AnimatedContainer){ + $block->onContainerClose(); + } + parent::onClose(); + } } diff --git a/src/block/inventory/window/ChestInventoryWindow.php b/src/block/inventory/window/ChestInventoryWindow.php deleted file mode 100644 index 6b3dda978..000000000 --- a/src/block/inventory/window/ChestInventoryWindow.php +++ /dev/null @@ -1,48 +0,0 @@ -getWorld()->broadcastPacketToViewers($position, BlockEventPacket::create(BlockPosition::fromVector3($position), 1, $isOpen ? 1 : 0)); - } -} diff --git a/src/block/inventory/window/DoubleChestInventoryWindow.php b/src/block/inventory/window/DoubleChestInventoryWindow.php index 5455b1f51..801a6ff9d 100644 --- a/src/block/inventory/window/DoubleChestInventoryWindow.php +++ b/src/block/inventory/window/DoubleChestInventoryWindow.php @@ -27,7 +27,7 @@ use pocketmine\inventory\Inventory; use pocketmine\player\Player; use pocketmine\world\Position; -final class DoubleChestInventoryWindow extends ChestInventoryWindow{ +final class DoubleChestInventoryWindow extends BlockInventoryWindow{ public function __construct( Player $viewer, @@ -41,12 +41,4 @@ final class DoubleChestInventoryWindow extends ChestInventoryWindow{ public function getLeft() : Position{ return $this->left; } public function getRight() : Position{ return $this->right; } - - protected function doBlockEffects(bool $isOpen) : void{ - $this->animateBlock($this->left, $isOpen); - $this->animateBlock($this->right, $isOpen); - - $this->playSound($this->left, $isOpen); - $this->playSound($this->right, $isOpen); - } } diff --git a/src/block/inventory/window/EnderChestInventoryWindow.php b/src/block/inventory/window/EnderChestInventoryWindow.php deleted file mode 100644 index 97d951c93..000000000 --- a/src/block/inventory/window/EnderChestInventoryWindow.php +++ /dev/null @@ -1,73 +0,0 @@ -holder->getWorld()->getTile($this->getHolder()); - if(!$enderChest instanceof EnderChest){ - return 0; - } - return $enderChest->getViewerCount(); - } - - private function updateViewerCount(int $amount) : void{ - $enderChest = $this->holder->getWorld()->getTile($this->getHolder()); - if($enderChest instanceof EnderChest){ - $enderChest->setViewerCount($enderChest->getViewerCount() + $amount); - } - } - - protected function getOpenSound() : Sound{ - return new EnderChestOpenSound(); - } - - protected function getCloseSound() : Sound{ - return new EnderChestCloseSound(); - } - - protected function animateBlock(Position $position, bool $isOpen) : void{ - //event ID is always 1 for a chest - $position->getWorld()->broadcastPacketToViewers($position, BlockEventPacket::create(BlockPosition::fromVector3($position), 1, $isOpen ? 1 : 0)); - } - - public function onOpen() : void{ - parent::onOpen(); - $this->updateViewerCount(1); - } - - public function onClose() : void{ - parent::onClose(); - $this->updateViewerCount(-1); - } -} diff --git a/src/block/inventory/window/ShulkerBoxInventoryWindow.php b/src/block/inventory/window/ShulkerBoxInventoryWindow.php deleted file mode 100644 index 19d1cba18..000000000 --- a/src/block/inventory/window/ShulkerBoxInventoryWindow.php +++ /dev/null @@ -1,46 +0,0 @@ -getWorld()->broadcastPacketToViewers($position, BlockEventPacket::create(BlockPosition::fromVector3($position), 1, $isOpen ? 1 : 0)); - } -} diff --git a/src/player/Player.php b/src/player/Player.php index 3b54a2b86..e87fb662d 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -2693,6 +2693,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ if($window === $this->currentWindow){ return true; } + if($window->getViewer() !== $this){ + throw new \InvalidArgumentException("Cannot reuse InventoryWindow instances, please create a new one for each player"); + } $ev = new InventoryOpenEvent($window, $this); $ev->call(); if($ev->isCancelled()){