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{
$this->position = new Position($x, $y, $z, $world);
$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\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() &&

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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();

View File

@ -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();