meta; if($d >= 8){ $d = 0; } return ($d + 1) / 9; } protected function getFlowDecay(Vector3 $pos){ if(!($pos instanceof Block)){ $pos = $this->level->getBlock($pos); } if($pos->getId() !== $this->getId()){ return -1; }else{ return $pos->getDamage(); } } protected function getEffectiveFlowDecay(Vector3 $pos){ if(!($pos instanceof Block)){ $pos = $this->level->getBlock($pos); } if($pos->getId() !== $this->getId()){ return -1; } $decay = $pos->getDamage(); if($decay >= 8){ $decay = 0; } return $decay; } public function getFlowVector(){ $vector = new Vector3(0, 0, 0); if($this->temporalVector === null){ $this->temporalVector = new Vector3(0, 0, 0); } $decay = $this->getEffectiveFlowDecay($this); for($j = 0; $j < 4; ++$j){ $x = $this->x; $y = $this->y; $z = $this->z; if($j === 0){ --$x; }elseif($j === 1){ ++$x; }elseif($j === 2){ --$z; }elseif($j === 3){ ++$z; } $sideBlock = $this->level->getBlockAt($x, $y, $z); $blockDecay = $this->getEffectiveFlowDecay($sideBlock); if($blockDecay < 0){ if(!$sideBlock->canBeFlowedInto()){ continue; } $blockDecay = $this->getEffectiveFlowDecay($this->level->getBlockAt($x, $y - 1, $z)); if($blockDecay >= 0){ $realDecay = $blockDecay - ($decay - 8); $vector->x += ($sideBlock->x - $this->x) * $realDecay; $vector->y += ($sideBlock->y - $this->y) * $realDecay; $vector->z += ($sideBlock->z - $this->z) * $realDecay; } continue; }else{ $realDecay = $blockDecay - $decay; $vector->x += ($sideBlock->x - $this->x) * $realDecay; $vector->y += ($sideBlock->y - $this->y) * $realDecay; $vector->z += ($sideBlock->z - $this->z) * $realDecay; } } if($this->getDamage() >= 8){ $falling = false; if(!$this->level->getBlockAt($this->x, $this->y, $this->z - 1)->canBeFlowedInto()){ $falling = true; }elseif(!$this->level->getBlockAt($this->x, $this->y, $this->z + 1)->canBeFlowedInto()){ $falling = true; }elseif(!$this->level->getBlockAt($this->x - 1, $this->y, $this->z)->canBeFlowedInto()){ $falling = true; }elseif(!$this->level->getBlockAt($this->x + 1, $this->y, $this->z)->canBeFlowedInto()){ $falling = true; }elseif(!$this->level->getBlockAt($this->x, $this->y + 1, $this->z - 1)->canBeFlowedInto()){ $falling = true; }elseif(!$this->level->getBlockAt($this->x, $this->y + 1, $this->z + 1)->canBeFlowedInto()){ $falling = true; }elseif(!$this->level->getBlockAt($this->x - 1, $this->y + 1, $this->z)->canBeFlowedInto()){ $falling = true; }elseif(!$this->level->getBlockAt($this->x + 1, $this->y + 1, $this->z)->canBeFlowedInto()){ $falling = true; } if($falling){ $vector = $vector->normalize()->add(0, -6, 0); } } return $vector->normalize(); } public function addVelocityToEntity(Entity $entity, Vector3 $vector) : void{ $flow = $this->getFlowVector(); $vector->x += $flow->x; $vector->y += $flow->y; $vector->z += $flow->z; } public function tickRate(){ if($this instanceof Water){ return 5; }elseif($this instanceof Lava){ return 30; } return 0; } public function onUpdate(int $type){ if($type === Level::BLOCK_UPDATE_NORMAL){ $this->checkForHarden(); $this->level->scheduleDelayedBlockUpdate($this, $this->tickRate()); }elseif($type === Level::BLOCK_UPDATE_SCHEDULED){ if($this->temporalVector === null){ $this->temporalVector = new Vector3(0, 0, 0); } $decay = $this->getFlowDecay($this); $multiplier = $this instanceof Lava ? 2 : 1; $flag = true; if($decay > 0){ $smallestFlowDecay = -100; $this->adjacentSources = 0; $smallestFlowDecay = $this->getSmallestFlowDecay($this->level->getBlockAt($this->x, $this->y, $this->z - 1), $smallestFlowDecay); $smallestFlowDecay = $this->getSmallestFlowDecay($this->level->getBlockAt($this->x, $this->y, $this->z + 1), $smallestFlowDecay); $smallestFlowDecay = $this->getSmallestFlowDecay($this->level->getBlockAt($this->x - 1, $this->y, $this->z), $smallestFlowDecay); $smallestFlowDecay = $this->getSmallestFlowDecay($this->level->getBlockAt($this->x + 1, $this->y, $this->z), $smallestFlowDecay); $k = $smallestFlowDecay + $multiplier; if($k >= 8 or $smallestFlowDecay < 0){ $k = -1; } if(($topFlowDecay = $this->getFlowDecay($this->level->getBlockAt($this->x, $this->y + 1, $this->z))) >= 0){ if($topFlowDecay >= 8){ $k = $topFlowDecay; }else{ $k = $topFlowDecay | 0x08; } } if($this->adjacentSources >= 2 and $this instanceof Water){ $bottomBlock = $this->level->getBlockAt($this->x, $this->y - 1, $this->z); if($bottomBlock->isSolid()){ $k = 0; }elseif($bottomBlock instanceof Water and $bottomBlock->getDamage() === 0){ $k = 0; } } if($this instanceof Lava and $decay < 8 and $k < 8 and $k > 1 and mt_rand(0, 4) !== 0){ $k = $decay; $flag = false; } if($k !== $decay){ $decay = $k; if($decay < 0){ $this->level->setBlock($this, BlockFactory::get(Block::AIR), true, true); }else{ $this->level->setBlock($this, BlockFactory::get($this->id, $decay), true, true); $this->level->scheduleDelayedBlockUpdate($this, $this->tickRate()); } }elseif($flag){ //$this->getLevel()->scheduleUpdate($this, $this->tickRate()); //$this->updateFlow(); } }else{ //$this->updateFlow(); } if($decay >= 0){ $bottomBlock = $this->level->getBlockAt($this->x, $this->y - 1, $this->z); if($this instanceof Lava and $bottomBlock instanceof Water){ $this->level->setBlock($bottomBlock, BlockFactory::get(Block::STONE), true, true); }elseif($bottomBlock->canBeFlowedInto() or ($bottomBlock instanceof Liquid and ($bottomBlock->getDamage() & 0x07) !== 0)){ $this->level->setBlock($bottomBlock, BlockFactory::get($this->id, $decay | 0x08), true, false); $this->level->scheduleDelayedBlockUpdate($bottomBlock, $this->tickRate()); }elseif($decay === 0 or !$bottomBlock->canBeFlowedInto()){ $flags = $this->getOptimalFlowDirections(); $l = $decay + $multiplier; if($decay >= 8){ $l = 1; } if($l >= 8){ $this->checkForHarden(); return; } if($flags[0]){ $this->flowIntoBlock($this->level->getBlockAt($this->x - 1, $this->y, $this->z), $l); } if($flags[1]){ $this->flowIntoBlock($this->level->getBlockAt($this->x + 1, $this->y, $this->z), $l); } if($flags[2]){ $this->flowIntoBlock($this->level->getBlockAt($this->x, $this->y, $this->z - 1), $l); } if($flags[3]){ $this->flowIntoBlock($this->level->getBlockAt($this->x, $this->y, $this->z + 1), $l); } } $this->checkForHarden(); } } } private function flowIntoBlock(Block $block, $newFlowDecay){ if($block->canBeFlowedInto()){ if($block->getId() > 0){ $this->level->useBreakOn($block); } $this->level->setBlock($block, BlockFactory::get($this->getId(), $newFlowDecay), true, true); $this->level->scheduleDelayedBlockUpdate($block, $this->tickRate()); } } private function calculateFlowCost(Block $block, $accumulatedCost, $previousDirection){ $cost = 1000; for($j = 0; $j < 4; ++$j){ if( ($j === 0 and $previousDirection === 1) or ($j === 1 and $previousDirection === 0) or ($j === 2 and $previousDirection === 3) or ($j === 3 and $previousDirection === 2) ){ $x = $block->x; $y = $block->y; $z = $block->z; if($j === 0){ --$x; }elseif($j === 1){ ++$x; }elseif($j === 2){ --$z; }elseif($j === 3){ ++$z; } $blockSide = $this->level->getBlockAt($x, $y, $z); if(!$blockSide->canBeFlowedInto() and !($blockSide instanceof Liquid)){ continue; }elseif($blockSide instanceof Liquid and $blockSide->getDamage() === 0){ continue; }elseif($this->level->getBlockAt($x, $y - 1, $z)->canBeFlowedInto()){ return $accumulatedCost; } if($accumulatedCost >= 4){ continue; } $realCost = $this->calculateFlowCost($blockSide, $accumulatedCost + 1, $j); if($realCost < $cost){ $cost = $realCost; } } } return $cost; } public function getHardness() : float{ return 100; } private function getOptimalFlowDirections(){ if($this->temporalVector === null){ $this->temporalVector = new Vector3(0, 0, 0); } for($j = 0; $j < 4; ++$j){ $this->flowCost[$j] = 1000; $x = $this->x; $y = $this->y; $z = $this->z; if($j === 0){ --$x; }elseif($j === 1){ ++$x; }elseif($j === 2){ --$z; }elseif($j === 3){ ++$z; } $block = $this->level->getBlockAt($x, $y, $z); if(!$block->canBeFlowedInto() and !($block instanceof Liquid)){ continue; }elseif($block instanceof Liquid and $block->getDamage() === 0){ continue; }elseif($this->level->getBlockAt($x, $y - 1, $z)->canBeFlowedInto()){ $this->flowCost[$j] = 0; }else{ $this->flowCost[$j] = $this->calculateFlowCost($block, 1, $j); } } $minCost = $this->flowCost[0]; for($i = 1; $i < 4; ++$i){ if($this->flowCost[$i] < $minCost){ $minCost = $this->flowCost[$i]; } } for($i = 0; $i < 4; ++$i){ $this->isOptimalFlowDirection[$i] = ($this->flowCost[$i] === $minCost); } return $this->isOptimalFlowDirection; } private function getSmallestFlowDecay(Vector3 $pos, $decay){ $blockDecay = $this->getFlowDecay($pos); if($blockDecay < 0){ return $decay; }elseif($blockDecay === 0){ ++$this->adjacentSources; }elseif($blockDecay >= 8){ $blockDecay = 0; } return ($decay >= 0 && $blockDecay >= $decay) ? $decay : $blockDecay; } private function checkForHarden(){ if($this instanceof Lava){ $colliding = false; for($side = 0; $side <= 5 and !$colliding; ++$side){ $colliding = $this->getSide($side) instanceof Water; } if($colliding){ if($this->getDamage() === 0){ $this->level->setBlock($this, BlockFactory::get(Block::OBSIDIAN), true, true); }elseif($this->getDamage() <= 4){ $this->level->setBlock($this, BlockFactory::get(Block::COBBLESTONE), true, true); } } } } protected function recalculateBoundingBox() : ?AxisAlignedBB{ return null; } public function getDrops(Item $item) : array{ return []; } }