implement waterlogged flow

This commit is contained in:
ShockedPlot7560 2024-08-10 22:16:16 +02:00
parent 3ec3be6b20
commit 7164dd9f49
No known key found for this signature in database
GPG Key ID: D7539B420F1FA86E
7 changed files with 84 additions and 14 deletions

View File

@ -623,6 +623,9 @@ class Block{
final public function position(World $world, int $x, int $y, int $z) : void{ final public function position(World $world, int $x, int $y, int $z) : void{
$this->position = new Position($x, $y, $z, $world); $this->position = new Position($x, $y, $z, $world);
$this->collisionBoxes = null; $this->collisionBoxes = null;
if($this instanceof Waterloggable){
$this->getWaterState()?->position($world, $x, $y, $z);
}
} }
/** /**

View File

@ -26,6 +26,7 @@ namespace pocketmine\block;
use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\BlockEventHelper;
use pocketmine\block\utils\MinimumCostFlowCalculator; use pocketmine\block\utils\MinimumCostFlowCalculator;
use pocketmine\block\utils\SupportType; use pocketmine\block\utils\SupportType;
use pocketmine\block\utils\Waterloggable;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\entity\Entity; use pocketmine\entity\Entity;
use pocketmine\event\block\BlockSpreadEvent; use pocketmine\event\block\BlockSpreadEvent;
@ -144,6 +145,9 @@ abstract class Liquid extends Transparent{
} }
protected function getEffectiveFlowDecay(Block $block) : int{ protected function getEffectiveFlowDecay(Block $block) : int{
if($block instanceof Waterloggable){
$block = $block->getWaterState() ?? $block;
}
if(!($block instanceof Liquid) || !$block->hasSameTypeId($this)){ if(!($block instanceof Liquid) || !$block->hasSameTypeId($this)){
return -1; return -1;
} }
@ -181,6 +185,9 @@ abstract class Liquid extends Transparent{
$sideZ = $z + $dz; $sideZ = $z + $dz;
$sideBlock = $world->getBlockAt($sideX, $sideY, $sideZ); $sideBlock = $world->getBlockAt($sideX, $sideY, $sideZ);
if($sideBlock instanceof Waterloggable){
$sideBlock = $sideBlock->getWaterState() ?? $sideBlock;
}
$blockDecay = $this->getEffectiveFlowDecay($sideBlock); $blockDecay = $this->getEffectiveFlowDecay($sideBlock);
if($blockDecay < 0){ if($blockDecay < 0){
@ -292,14 +299,25 @@ abstract class Liquid extends Transparent{
} }
if($falling !== $this->falling || (!$falling && $newDecay !== $this->decay)){ if($falling !== $this->falling || (!$falling && $newDecay !== $this->decay)){
$actualBlock = $world->getBlockAt($x, $y, $z);
if(!$falling && $newDecay < 0){ 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; return;
} }
$this->falling = $falling; $this->falling = $falling;
$this->decay = $falling ? 0 : $newDecay; $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 */ /** @phpstan-impure */
private function getSmallestFlowDecay(Block $block, int $decay) : int{ private function getSmallestFlowDecay(Block $block, int $decay) : int{
if($block instanceof Waterloggable){
$block = $block->getWaterState() ?? $block;
}
if(!($block instanceof Liquid) || !$block->hasSameTypeId($this)){ if(!($block instanceof Liquid) || !$block->hasSameTypeId($this)){
return $decay; return $decay;
} }
@ -374,6 +395,9 @@ abstract class Liquid extends Transparent{
} }
protected function canFlowInto(Block $block) : bool{ protected function canFlowInto(Block $block) : bool{
if($block instanceof Waterloggable){
$block = $block->getWaterState() ?? $block;
}
return return
$this->position->getWorld()->isInWorld($block->position->x, $block->position->y, $block->position->z) && $this->position->getWorld()->isInWorld($block->position->x, $block->position->y, $block->position->z) &&
$block->canBeFlowedInto() && $block->canBeFlowedInto() &&

View File

@ -30,7 +30,6 @@ use pocketmine\block\utils\Waterloggable;
use pocketmine\block\utils\WaterloggableTrait; use pocketmine\block\utils\WaterloggableTrait;
use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataDescriber;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\item\LiquidBucket;
use pocketmine\math\Axis; use pocketmine\math\Axis;
use pocketmine\math\AxisAlignedBB; use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Facing; use pocketmine\math\Facing;
@ -40,7 +39,9 @@ use pocketmine\world\BlockTransaction;
class Stair extends Transparent implements Waterloggable{ class Stair extends Transparent implements Waterloggable{
use HorizontalFacingTrait; use HorizontalFacingTrait;
use WaterloggableTrait; use WaterloggableTrait {
WaterloggableTrait::readStateFromWorld as private readWaterStateFromWorld;
}
protected bool $upsideDown = false; protected bool $upsideDown = false;
protected StairShape $shape = StairShape::STRAIGHT; protected StairShape $shape = StairShape::STRAIGHT;
@ -52,6 +53,7 @@ class Stair extends Transparent implements Waterloggable{
public function readStateFromWorld() : Block{ public function readStateFromWorld() : Block{
parent::readStateFromWorld(); parent::readStateFromWorld();
$this->readWaterStateFromWorld();
$this->collisionBoxes = null; $this->collisionBoxes = null;
@ -135,15 +137,19 @@ class Stair extends Transparent implements Waterloggable{
$this->facing = $player->getHorizontalFacing(); $this->facing = $player->getHorizontalFacing();
} }
$this->upsideDown = (($clickVector->y > 0.5 && $face !== Facing::UP) || $face === Facing::DOWN); $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); 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{ public function onBreak(Item $item, ?Player $player = null, array &$returnedItems = []) : bool{
if($item instanceof LiquidBucket && $item->getLiquid() instanceof Water){ $ret = parent::onBreak($item, $player, $returnedItems);
$this->position->getWorld()->setBlock($this->position, $this->setWaterState($item->getLiquid())); if($this->waterState !== null){
$this->position->getWorld()->setBlock($this->position, $this->waterState);
} }
return parent::onInteract($item, $face, $clickVector, $player, $returnedItems); return $ret;
} }
} }

View File

@ -37,4 +37,16 @@ trait WaterloggableTrait{
return $this; return $this;
} }
public function readStateFromWorld() : void{
$this->waterState?->readStateFromWorld();
}
public function onNearbyBlockChange() : void{
$this->waterState?->onNearbyBlockChange();
}
public function onScheduledUpdate() : void{
$this->waterState?->onScheduledUpdate();
}
} }

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\event\block; namespace pocketmine\event\block;
use pocketmine\block\Block; use pocketmine\block\Block;
use pocketmine\block\utils\Waterloggable;
use pocketmine\event\Cancellable; use pocketmine\event\Cancellable;
use pocketmine\event\CancellableTrait; use pocketmine\event\CancellableTrait;
use pocketmine\event\Event; use pocketmine\event\Event;

View File

@ -26,10 +26,12 @@ namespace pocketmine\item;
use pocketmine\block\Block; use pocketmine\block\Block;
use pocketmine\block\BlockTypeIds; use pocketmine\block\BlockTypeIds;
use pocketmine\block\Liquid; use pocketmine\block\Liquid;
use pocketmine\block\utils\Waterloggable;
use pocketmine\block\VanillaBlocks; use pocketmine\block\VanillaBlocks;
use pocketmine\event\player\PlayerBucketFillEvent; use pocketmine\event\player\PlayerBucketFillEvent;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\player\Player; use pocketmine\player\Player;
use function var_dump;
class Bucket extends Item{ 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{ public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{
//TODO: move this to generic placement logic //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 = clone $this;
$stack->pop(); $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::LAVA => VanillaItems::LAVA_BUCKET(),
BlockTypeIds::WATER => VanillaItems::WATER_BUCKET(), BlockTypeIds::WATER => VanillaItems::WATER_BUCKET(),
default => null default => null
@ -55,8 +61,16 @@ class Bucket extends Item{
$ev = new PlayerBucketFillEvent($player, $blockReplace, $face, $this, $resultItem); $ev = new PlayerBucketFillEvent($player, $blockReplace, $face, $this, $resultItem);
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ if(!$ev->isCancelled()){
$player->getWorld()->setBlock($blockClicked->getPosition(), VanillaBlocks::AIR()); if($blockClicked instanceof Waterloggable){
$player->getWorld()->addSound($blockClicked->getPosition()->add(0.5, 0.5, 0.5), $blockClicked->getBucketFillSound()); 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(); $this->pop();
$returnedItems[] = $ev->getItem(); $returnedItems[] = $ev->getItem();

View File

@ -26,9 +26,12 @@ namespace pocketmine\item;
use pocketmine\block\Block; use pocketmine\block\Block;
use pocketmine\block\Lava; use pocketmine\block\Lava;
use pocketmine\block\Liquid; use pocketmine\block\Liquid;
use pocketmine\block\utils\Waterloggable;
use pocketmine\block\Water;
use pocketmine\event\player\PlayerBucketEmptyEvent; use pocketmine\event\player\PlayerBucketEmptyEvent;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\player\Player; use pocketmine\player\Player;
use function var_dump;
class LiquidBucket extends Item{ class LiquidBucket extends Item{
private Liquid $liquid; 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{ 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; return ItemUseResult::NONE;
} }
@ -65,7 +68,14 @@ class LiquidBucket extends Item{
$ev = new PlayerBucketEmptyEvent($player, $blockReplace, $face, $this, VanillaItems::BUCKET()); $ev = new PlayerBucketEmptyEvent($player, $blockReplace, $face, $this, VanillaItems::BUCKET());
$ev->call(); $ev->call();
if(!$ev->isCancelled()){ 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()); $player->getWorld()->addSound($blockReplace->getPosition()->add(0.5, 0.5, 0.5), $resultBlock->getBucketEmptySound());
$this->pop(); $this->pop();