From 7164dd9f49cf3685908bd9ea4e01a72adaa6c390 Mon Sep 17 00:00:00 2001 From: ShockedPlot7560 Date: Sat, 10 Aug 2024 22:16:16 +0200 Subject: [PATCH] implement waterlogged flow --- src/block/Block.php | 3 +++ src/block/Liquid.php | 28 ++++++++++++++++++++++++-- src/block/Stair.php | 18 +++++++++++------ src/block/utils/WaterloggableTrait.php | 12 +++++++++++ src/event/block/BlockPlaceEvent.php | 1 + src/item/Bucket.php | 22 ++++++++++++++++---- src/item/LiquidBucket.php | 14 +++++++++++-- 7 files changed, 84 insertions(+), 14 deletions(-) diff --git a/src/block/Block.php b/src/block/Block.php index 820aadb33..3dfa6ae86 100644 --- a/src/block/Block.php +++ b/src/block/Block.php @@ -623,6 +623,9 @@ class Block{ final public function position(World $world, int $x, int $y, int $z) : void{ $this->position = new Position($x, $y, $z, $world); $this->collisionBoxes = null; + if($this instanceof Waterloggable){ + $this->getWaterState()?->position($world, $x, $y, $z); + } } /** diff --git a/src/block/Liquid.php b/src/block/Liquid.php index 6404cf908..153f5f449 100644 --- a/src/block/Liquid.php +++ b/src/block/Liquid.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\MinimumCostFlowCalculator; use pocketmine\block\utils\SupportType; +use pocketmine\block\utils\Waterloggable; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Entity; use pocketmine\event\block\BlockSpreadEvent; @@ -144,6 +145,9 @@ abstract class Liquid extends Transparent{ } protected function getEffectiveFlowDecay(Block $block) : int{ + if($block instanceof Waterloggable){ + $block = $block->getWaterState() ?? $block; + } if(!($block instanceof Liquid) || !$block->hasSameTypeId($this)){ return -1; } @@ -181,6 +185,9 @@ abstract class Liquid extends Transparent{ $sideZ = $z + $dz; $sideBlock = $world->getBlockAt($sideX, $sideY, $sideZ); + if($sideBlock instanceof Waterloggable){ + $sideBlock = $sideBlock->getWaterState() ?? $sideBlock; + } $blockDecay = $this->getEffectiveFlowDecay($sideBlock); if($blockDecay < 0){ @@ -292,14 +299,25 @@ abstract class Liquid extends Transparent{ } if($falling !== $this->falling || (!$falling && $newDecay !== $this->decay)){ + $actualBlock = $world->getBlockAt($x, $y, $z); if(!$falling && $newDecay < 0){ - $world->setBlockAt($x, $y, $z, VanillaBlocks::AIR()); + if($actualBlock instanceof Waterloggable && $this instanceof Water){ + $actualBlock->setWaterState(null); + $world->setBlockAt($x, $y, $z, $actualBlock); + }else{ + $world->setBlockAt($x, $y, $z, VanillaBlocks::AIR()); + } return; } $this->falling = $falling; $this->decay = $falling ? 0 : $newDecay; - $world->setBlockAt($x, $y, $z, $this); //local block update will cause an update to be scheduled + if($actualBlock instanceof Waterloggable && $this instanceof Water){ + $actualBlock->setWaterState($this); + $world->setBlockAt($x, $y, $z, $actualBlock); + }else{ + $world->setBlockAt($x, $y, $z, $this); //local block update will cause an update to be scheduled + } } } @@ -347,6 +365,9 @@ abstract class Liquid extends Transparent{ /** @phpstan-impure */ private function getSmallestFlowDecay(Block $block, int $decay) : int{ + if($block instanceof Waterloggable){ + $block = $block->getWaterState() ?? $block; + } if(!($block instanceof Liquid) || !$block->hasSameTypeId($this)){ return $decay; } @@ -374,6 +395,9 @@ abstract class Liquid extends Transparent{ } protected function canFlowInto(Block $block) : bool{ + if($block instanceof Waterloggable){ + $block = $block->getWaterState() ?? $block; + } return $this->position->getWorld()->isInWorld($block->position->x, $block->position->y, $block->position->z) && $block->canBeFlowedInto() && diff --git a/src/block/Stair.php b/src/block/Stair.php index 49084f673..00f0fd929 100644 --- a/src/block/Stair.php +++ b/src/block/Stair.php @@ -30,7 +30,6 @@ use pocketmine\block\utils\Waterloggable; use pocketmine\block\utils\WaterloggableTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; -use pocketmine\item\LiquidBucket; use pocketmine\math\Axis; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; @@ -40,7 +39,9 @@ use pocketmine\world\BlockTransaction; class Stair extends Transparent implements Waterloggable{ use HorizontalFacingTrait; - use WaterloggableTrait; + use WaterloggableTrait { + WaterloggableTrait::readStateFromWorld as private readWaterStateFromWorld; + } protected bool $upsideDown = false; protected StairShape $shape = StairShape::STRAIGHT; @@ -52,6 +53,7 @@ class Stair extends Transparent implements Waterloggable{ public function readStateFromWorld() : Block{ parent::readStateFromWorld(); + $this->readWaterStateFromWorld(); $this->collisionBoxes = null; @@ -135,15 +137,19 @@ class Stair extends Transparent implements Waterloggable{ $this->facing = $player->getHorizontalFacing(); } $this->upsideDown = (($clickVector->y > 0.5 && $face !== Facing::UP) || $face === Facing::DOWN); + if($blockReplace instanceof Water && $blockReplace->isSource()){ + $this->waterState = $blockReplace; + } return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ - if($item instanceof LiquidBucket && $item->getLiquid() instanceof Water){ - $this->position->getWorld()->setBlock($this->position, $this->setWaterState($item->getLiquid())); + public function onBreak(Item $item, ?Player $player = null, array &$returnedItems = []) : bool{ + $ret = parent::onBreak($item, $player, $returnedItems); + if($this->waterState !== null){ + $this->position->getWorld()->setBlock($this->position, $this->waterState); } - return parent::onInteract($item, $face, $clickVector, $player, $returnedItems); + return $ret; } } diff --git a/src/block/utils/WaterloggableTrait.php b/src/block/utils/WaterloggableTrait.php index 3bfb84d4d..08f4415e5 100644 --- a/src/block/utils/WaterloggableTrait.php +++ b/src/block/utils/WaterloggableTrait.php @@ -37,4 +37,16 @@ trait WaterloggableTrait{ return $this; } + + public function readStateFromWorld() : void{ + $this->waterState?->readStateFromWorld(); + } + + public function onNearbyBlockChange() : void{ + $this->waterState?->onNearbyBlockChange(); + } + + public function onScheduledUpdate() : void{ + $this->waterState?->onScheduledUpdate(); + } } diff --git a/src/event/block/BlockPlaceEvent.php b/src/event/block/BlockPlaceEvent.php index 3572cb5e0..686db5b74 100644 --- a/src/event/block/BlockPlaceEvent.php +++ b/src/event/block/BlockPlaceEvent.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\event\block; use pocketmine\block\Block; +use pocketmine\block\utils\Waterloggable; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; use pocketmine\event\Event; diff --git a/src/item/Bucket.php b/src/item/Bucket.php index ee69a0a8a..afdc28e64 100644 --- a/src/item/Bucket.php +++ b/src/item/Bucket.php @@ -26,10 +26,12 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\BlockTypeIds; use pocketmine\block\Liquid; +use pocketmine\block\utils\Waterloggable; use pocketmine\block\VanillaBlocks; use pocketmine\event\player\PlayerBucketFillEvent; use pocketmine\math\Vector3; use pocketmine\player\Player; +use function var_dump; class Bucket extends Item{ @@ -39,11 +41,15 @@ class Bucket extends Item{ public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ //TODO: move this to generic placement logic - if($blockClicked instanceof Liquid && $blockClicked->isSource()){ + if($blockClicked instanceof Liquid && $blockClicked->isSource() || $blockClicked instanceof Waterloggable && $blockClicked->getWaterState() !== null){ $stack = clone $this; $stack->pop(); - $resultItem = match($blockClicked->getTypeId()){ + $id = $blockClicked->getTypeId(); + if($blockClicked instanceof Waterloggable){ + $id = $blockClicked->getWaterState()->getTypeId(); + } + $resultItem = match($id){ BlockTypeIds::LAVA => VanillaItems::LAVA_BUCKET(), BlockTypeIds::WATER => VanillaItems::WATER_BUCKET(), default => null @@ -55,8 +61,16 @@ class Bucket extends Item{ $ev = new PlayerBucketFillEvent($player, $blockReplace, $face, $this, $resultItem); $ev->call(); if(!$ev->isCancelled()){ - $player->getWorld()->setBlock($blockClicked->getPosition(), VanillaBlocks::AIR()); - $player->getWorld()->addSound($blockClicked->getPosition()->add(0.5, 0.5, 0.5), $blockClicked->getBucketFillSound()); + if($blockClicked instanceof Waterloggable){ + var_dump("Setting water state", $blockClicked->__toString()); + $sound = $blockClicked->getWaterState()->getBucketFillSound(); + $blockClicked->setWaterState(null); + $player->getWorld()->setBlock($blockClicked->getPosition(), $blockClicked); + }else{ + $sound = $blockClicked->getBucketFillSound(); + $player->getWorld()->setBlock($blockClicked->getPosition(), VanillaBlocks::AIR()); + } + $player->getWorld()->addSound($blockClicked->getPosition()->add(0.5, 0.5, 0.5), $sound); $this->pop(); $returnedItems[] = $ev->getItem(); diff --git a/src/item/LiquidBucket.php b/src/item/LiquidBucket.php index eb2cb18ed..b756a0cf9 100644 --- a/src/item/LiquidBucket.php +++ b/src/item/LiquidBucket.php @@ -26,9 +26,12 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\block\Lava; use pocketmine\block\Liquid; +use pocketmine\block\utils\Waterloggable; +use pocketmine\block\Water; use pocketmine\event\player\PlayerBucketEmptyEvent; use pocketmine\math\Vector3; use pocketmine\player\Player; +use function var_dump; class LiquidBucket extends Item{ private Liquid $liquid; @@ -55,7 +58,7 @@ class LiquidBucket extends Item{ } public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ - if(!$blockReplace->canBeReplaced()){ + if(!$blockReplace->canBeReplaced() && !($blockReplace instanceof Waterloggable)){ return ItemUseResult::NONE; } @@ -65,7 +68,14 @@ class LiquidBucket extends Item{ $ev = new PlayerBucketEmptyEvent($player, $blockReplace, $face, $this, VanillaItems::BUCKET()); $ev->call(); if(!$ev->isCancelled()){ - $player->getWorld()->setBlock($blockReplace->getPosition(), $resultBlock->getFlowingForm()); + if($blockClicked instanceof Waterloggable && $resultBlock instanceof Water){ + var_dump("Setting water state", $resultBlock->__toString()); + $blockClicked->setWaterState($resultBlock); + $player->getWorld()->setBlock($blockClicked->getPosition(), $blockClicked); + $blockClicked->onNearbyBlockChange(); + }else{ + $player->getWorld()->setBlock($blockReplace->getPosition(), $resultBlock->getFlowingForm()); + } $player->getWorld()->addSound($blockReplace->getPosition()->add(0.5, 0.5, 0.5), $resultBlock->getBucketEmptySound()); $this->pop();