22declare(strict_types=1);
24namespace pocketmine\block\utils;
29use
function array_fill_keys;
38 private const CAN_FLOW_DOWN = 1;
39 private const CAN_FLOW = 0;
40 private const BLOCKED = -1;
43 private array $flowCostVisited = [];
50 private int $flowDecayPerBlock,
51 private \Closure $canFlowInto
54 private function calculateFlowCost(
int $blockX,
int $blockY,
int $blockZ,
int $accumulatedCost,
int $maxCost,
int $originOpposite,
int $lastOpposite) : int{
57 foreach(Facing::HORIZONTAL as $j){
58 if($j === $originOpposite || $j === $lastOpposite){
61 [$dx, $dy, $dz] = Facing::OFFSET[$j];
67 if(!$this->world->isInWorld($x, $y, $z) || !$this->canFlowInto($this->world->getBlockAt($x, $y, $z))){
68 $this->flowCostVisited[$hash] = self::BLOCKED;
69 }elseif($this->world->getBlockAt($x, $y - 1, $z)->canBeFlowedInto()){
70 $this->flowCostVisited[$hash] = self::CAN_FLOW_DOWN;
72 $this->flowCostVisited[$hash] = self::CAN_FLOW;
76 $status = $this->flowCostVisited[$hash];
78 if($status === self::BLOCKED){
80 }elseif($status === self::CAN_FLOW_DOWN){
81 return $accumulatedCost;
84 if($accumulatedCost >= $maxCost){
88 $realCost = $this->calculateFlowCost($x, $y, $z, $accumulatedCost + 1, $maxCost, $originOpposite,
Facing::opposite($j));
90 if($realCost < $cost){
102 $flowCost = array_fill_keys(
Facing::HORIZONTAL, 1000);
103 $maxCost = intdiv(4, $this->flowDecayPerBlock);
104 foreach(Facing::HORIZONTAL as $j){
105 [$dx, $dy, $dz] = Facing::OFFSET[$j];
110 if(!$this->world->isInWorld($x, $y, $z) || !$this->canFlowInto($this->world->getBlockAt($x, $y, $z))){
112 }elseif($this->world->getBlockAt($x, $y - 1, $z)->canBeFlowedInto()){
114 $flowCost[$j] = $maxCost = 0;
115 }elseif($maxCost > 0){
118 $flowCost[$j] = $this->calculateFlowCost($x, $y, $z, 1, $maxCost, $opposite, $opposite);
119 $maxCost = min($maxCost, $flowCost[$j]);
123 $this->flowCostVisited = [];
125 $minCost = min($flowCost);
127 $isOptimalFlowDirection = [];
129 foreach($flowCost as $facing => $cost){
130 if($cost === $minCost){
131 $isOptimalFlowDirection[] = $facing;
135 return $isOptimalFlowDirection;
138 private function canFlowInto(Block $block) : bool{
139 return ($this->canFlowInto)($block);
getOptimalFlowDirections(int $originX, int $originY, int $originZ)
__construct(private World $world, private int $flowDecayPerBlock, private \Closure $canFlowInto)
static opposite(int $direction)
static blockHash(int $x, int $y, int $z)