PocketMine-MP  1.4 - API 1.10.0
 All Classes Namespaces Functions Variables Pages
Liquid.php
1 <?php
2 
3 /*
4  *
5  * ____ _ _ __ __ _ __ __ ____
6  * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7  * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8  * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9  * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * @author PocketMine Team
17  * @link http://www.pocketmine.net/
18  *
19  *
20 */
21 
22 namespace pocketmine\block;
23 
24 
29 
30 abstract class Liquid extends Transparent{
31 
33  private $temporalVector = null;
34 
35  public function hasEntityCollision(){
36  return true;
37  }
38 
39  public function isBreakable(Item $item){
40  return false;
41  }
42 
43  public function canBeReplaced(){
44  return true;
45  }
46 
47  public function isSolid(){
48  return false;
49  }
50 
51  public $adjacentSources = 0;
52  public $isOptimalFlowDirection = [0, 0, 0, 0];
53  public $flowCost = [0, 0, 0, 0];
54 
55  public function getFluidHeightPercent(){
56  $d = $this->meta;
57  if($d >= 8){
58  $d = 0;
59  }
60 
61  return ($d + 1) / 9;
62  }
63 
64  protected function getFlowDecay(Vector3 $pos){
65  if(!($pos instanceof Block)){
66  $pos = $this->getLevel()->getBlock($pos);
67  }
68 
69  if($pos->getId() !== $this->getId()){
70  return -1;
71  }else{
72  return $pos->getDamage();
73  }
74  }
75 
76  protected function getEffectiveFlowDecay(Vector3 $pos){
77  if(!($pos instanceof Block)){
78  $pos = $this->getLevel()->getBlock($pos);
79  }
80 
81  if($pos->getId() !== $this->getId()){
82  return -1;
83  }
84 
85  $decay = $pos->getDamage();
86 
87  if($decay >= 8){
88  $decay = 0;
89  }
90 
91  return $decay;
92  }
93 
94  public function getFlowVector(){
95  $vector = new Vector3(0, 0, 0);
96 
97  if($this->temporalVector === null){
98  $this->temporalVector = new Vector3(0, 0, 0);
99  }
100 
101  $decay = $this->getEffectiveFlowDecay($this);
102 
103  for($j = 0; $j < 4; ++$j){
104 
105  $x = $this->x;
106  $y = $this->y;
107  $z = $this->z;
108 
109  if($j === 0){
110  --$x;
111  }elseif($j === 1){
112  ++$x;
113  }elseif($j === 2){
114  --$z;
115  }elseif($j === 3){
116  ++$z;
117  }
118  $sideBlock = $this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $z));
119  $blockDecay = $this->getEffectiveFlowDecay($sideBlock);
120 
121  if($blockDecay < 0){
122  if(!$sideBlock->canBeFlowedInto()){
123  continue;
124  }
125 
126  $blockDecay = $this->getEffectiveFlowDecay($this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y - 1, $z)));
127 
128  if($blockDecay >= 0){
129  $realDecay = $blockDecay - ($decay - 8);
130  $vector->x += ($sideBlock->x - $this->x) * $realDecay;
131  $vector->y += ($sideBlock->y - $this->y) * $realDecay;
132  $vector->z += ($sideBlock->z - $this->z) * $realDecay;
133  }
134 
135  continue;
136  }else{
137  $realDecay = $blockDecay - $decay;
138  $vector->x += ($sideBlock->x - $this->x) * $realDecay;
139  $vector->y += ($sideBlock->y - $this->y) * $realDecay;
140  $vector->z += ($sideBlock->z - $this->z) * $realDecay;
141  }
142  }
143 
144  if($this->getDamage() >= 8){
145  $falling = false;
146 
147  if(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x, $this->y, $this->z - 1))->canBeFlowedInto()){
148  $falling = true;
149  }elseif(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x, $this->y, $this->z + 1))->canBeFlowedInto()){
150  $falling = true;
151  }elseif(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x - 1, $this->y, $this->z))->canBeFlowedInto()){
152  $falling = true;
153  }elseif(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x + 1, $this->y, $this->z))->canBeFlowedInto()){
154  $falling = true;
155  }elseif(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x, $this->y + 1, $this->z - 1))->canBeFlowedInto()){
156  $falling = true;
157  }elseif(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x, $this->y + 1, $this->z + 1))->canBeFlowedInto()){
158  $falling = true;
159  }elseif(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x - 1, $this->y + 1, $this->z))->canBeFlowedInto()){
160  $falling = true;
161  }elseif(!$this->getLevel()->getBlock($this->temporalVector->setComponents($this->x + 1, $this->y + 1, $this->z))->canBeFlowedInto()){
162  $falling = true;
163  }
164 
165  if($falling){
166  $vector = $vector->normalize()->add(0, -6, 0);
167  }
168  }
169 
170  return $vector->normalize();
171  }
172 
173  public function addVelocityToEntity(Entity $entity, Vector3 $vector){
174  $flow = $this->getFlowVector();
175  $vector->x += $flow->x;
176  $vector->y += $flow->y;
177  $vector->z += $flow->z;
178  }
179 
180  public function tickRate(){
181  if($this instanceof Water){
182  return 5;
183  }elseif($this instanceof Lava){
184  return 30;
185  }
186 
187  return 0;
188  }
189 
190  public function onUpdate($type){
191  if($type === Level::BLOCK_UPDATE_NORMAL){
192  $this->checkForHarden();
193  $this->getLevel()->scheduleUpdate($this, $this->tickRate());
194  }elseif($type === Level::BLOCK_UPDATE_SCHEDULED){
195  if($this->temporalVector === null){
196  $this->temporalVector = new Vector3(0, 0, 0);
197  }
198 
199  $decay = $this->getFlowDecay($this);
200  $multiplier = $this instanceof Lava ? 2 : 1;
201 
202  $flag = true;
203 
204  if($decay > 0){
205  $smallestFlowDecay = -100;
206  $this->adjacentSources = 0;
207  $smallestFlowDecay = $this->getSmallestFlowDecay($this->level->getBlock($this->temporalVector->setComponents($this->x, $this->y, $this->z - 1)), $smallestFlowDecay);
208  $smallestFlowDecay = $this->getSmallestFlowDecay($this->level->getBlock($this->temporalVector->setComponents($this->x, $this->y, $this->z + 1)), $smallestFlowDecay);
209  $smallestFlowDecay = $this->getSmallestFlowDecay($this->level->getBlock($this->temporalVector->setComponents($this->x - 1, $this->y, $this->z)), $smallestFlowDecay);
210  $smallestFlowDecay = $this->getSmallestFlowDecay($this->level->getBlock($this->temporalVector->setComponents($this->x + 1, $this->y, $this->z)), $smallestFlowDecay);
211 
212  $k = $smallestFlowDecay + $multiplier;
213 
214  if($k >= 8 or $smallestFlowDecay < 0){
215  $k = -1;
216  }
217 
218  if(($topFlowDecay = $this->getFlowDecay($this->level->getBlock($this->level->getBlock($this->temporalVector->setComponents($this->x, $this->y + 1, $this->z))))) >= 0){
219  if($topFlowDecay >= 8){
220  $k = $topFlowDecay;
221  }else{
222  $k = $topFlowDecay | 0x08;
223  }
224  }
225 
226  if($this->adjacentSources >= 2 and $this instanceof Water){
227  $bottomBlock = $this->level->getBlock($this->level->getBlock($this->temporalVector->setComponents($this->x, $this->y - 1, $this->z)));
228  if($bottomBlock->isSolid()){
229  $k = 0;
230  }elseif($bottomBlock instanceof Water and $bottomBlock->getDamage() === 0){
231  $k = 0;
232  }
233  }
234 
235  if($this instanceof Lava and $decay < 8 and $k < 8 and $k > 1 and mt_rand(0, 4) !== 0){
236  $k = $decay;
237  $flag = false;
238  }
239 
240  if($k !== $decay){
241  $decay = $k;
242  if($decay < 0){
243  $this->getLevel()->setBlock($this, new Air(), true);
244  }else{
245  $this->getLevel()->setBlock($this, Block::get($this->id, $decay), true);
246  $this->getLevel()->scheduleUpdate($this, $this->tickRate());
247  }
248  }elseif($flag){
249  //$this->getLevel()->scheduleUpdate($this, $this->tickRate());
250  //$this->updateFlow();
251  }
252  }else{
253  //$this->updateFlow();
254  }
255 
256  $bottomBlock = $this->level->getBlock($this->temporalVector->setComponents($this->x, $this->y - 1, $this->z));
257 
258  if($bottomBlock->canBeFlowedInto() or $bottomBlock instanceof Liquid){
259  if($this instanceof Lava and $bottomBlock instanceof Water){
260  $this->getLevel()->setBlock($bottomBlock, Block::get(Item::STONE), true);
261  return;
262  }
263 
264  if($decay >= 8){
265  $this->getLevel()->setBlock($bottomBlock, Block::get($this->id, $decay), true);
266  $this->getLevel()->scheduleUpdate($bottomBlock, $this->tickRate());
267  }else{
268  $this->getLevel()->setBlock($bottomBlock, Block::get($this->id, $decay + 8), true);
269  $this->getLevel()->scheduleUpdate($bottomBlock, $this->tickRate());
270  }
271  }elseif($decay >= 0 and ($decay === 0 or !$bottomBlock->canBeFlowedInto())){
272  $flags = $this->getOptimalFlowDirections();
273 
274  $l = $decay + $multiplier;
275 
276  if($decay >= 8){
277  $l = 1;
278  }
279 
280  if($l >= 8){
281  $this->checkForHarden();
282  return;
283  }
284 
285  if($flags[0]){
286  $this->flowIntoBlock($this->level->getBlock($this->temporalVector->setComponents($this->x - 1, $this->y, $this->z)), $l);
287  }
288 
289  if($flags[1]){
290  $this->flowIntoBlock($this->level->getBlock($this->temporalVector->setComponents($this->x + 1, $this->y, $this->z)), $l);
291  }
292 
293  if($flags[2]){
294  $this->flowIntoBlock($this->level->getBlock($this->temporalVector->setComponents($this->x, $this->y, $this->z - 1)), $l);
295  }
296 
297  if($flags[3]){
298  $this->flowIntoBlock($this->level->getBlock($this->temporalVector->setComponents($this->x, $this->y, $this->z + 1)), $l);
299  }
300  }
301 
302  $this->checkForHarden();
303 
304  }
305  }
306 
307  private function flowIntoBlock(Block $block, $newFlowDecay){
308  if($block->canBeFlowedInto()){
309  if($block->getId() > 0){
310  $this->getLevel()->useBreakOn($block);
311  }
312 
313  $this->getLevel()->setBlock($block, Block::get($this->id, $newFlowDecay), true);
314  $this->getLevel()->scheduleUpdate($block, $this->tickRate());
315  }
316  }
317 
318  private function calculateFlowCost(Block $block, $accumulatedCost, $previousDirection){
319  $cost = 1000;
320 
321  for($j = 0; $j < 4; ++$j){
322  if(
323  ($j === 0 and $previousDirection === 1) or
324  ($j === 1 and $previousDirection === 0) or
325  ($j === 2 and $previousDirection === 3) or
326  ($j === 3 and $previousDirection === 2)
327  ){
328  $x = $block->x;
329  $y = $block->y;
330  $z = $block->z;
331 
332  if($j === 0){
333  --$x;
334  }elseif($j === 1){
335  ++$x;
336  }elseif($j === 2){
337  --$z;
338  }elseif($j === 3){
339  ++$z;
340  }
341  $blockSide = $this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $z));
342 
343  if(!$blockSide->canBeFlowedInto() and !($blockSide instanceof Liquid)){
344  continue;
345  }elseif($blockSide instanceof Liquid and $blockSide->getDamage() === 0){
346  continue;
347  }elseif($this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y - 1, $z))->canBeFlowedInto()){
348  return $accumulatedCost;
349  }
350 
351  if($accumulatedCost >= 4){
352  continue;
353  }
354 
355  $realCost = $this->calculateFlowCost($blockSide, $accumulatedCost + 1, $j);
356 
357  if($realCost < $cost){
358  $cost = $realCost;
359  }
360  }
361  }
362 
363  return $cost;
364  }
365 
366  private function getOptimalFlowDirections(){
367  if($this->temporalVector === null){
368  $this->temporalVector = new Vector3(0, 0, 0);
369  }
370 
371  for($j = 0; $j < 4; ++$j){
372  $this->flowCost[$j] = 1000;
373 
374  $x = $this->x;
375  $y = $this->y;
376  $z = $this->z;
377 
378  if($j === 0){
379  --$x;
380  }elseif($j === 1){
381  ++$x;
382  }elseif($j === 2){
383  --$z;
384  }elseif($j === 3){
385  ++$z;
386  }
387  $block = $this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $z));
388 
389  if(!$block->canBeFlowedInto() and !($block instanceof Liquid)){
390  continue;
391  }elseif($block instanceof Liquid and $block->getDamage() === 0){
392  continue;
393  }elseif($this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y - 1, $z))->canBeFlowedInto()){
394  $this->flowCost[$j] = 0;
395  }else{
396  $this->flowCost[$j] = $this->calculateFlowCost($block, 1, $j);
397  }
398  }
399 
400  $minCost = $this->flowCost[0];
401 
402  for($i = 1; $i < 4; ++$i){
403  if($this->flowCost[$i] < $minCost){
404  $minCost = $this->flowCost[$i];
405  }
406  }
407 
408  for($i = 0; $i < 4; ++$i){
409  $this->isOptimalFlowDirection[$i] = ($this->flowCost[$i] === $minCost);
410  }
411 
412  return $this->isOptimalFlowDirection;
413  }
414 
415  private function getSmallestFlowDecay(Vector3 $pos, $decay){
416  $blockDecay = $this->getFlowDecay($pos);
417 
418  if($blockDecay < 0){
419  return $decay;
420  }elseif($blockDecay === 0){
421  ++$this->adjacentSources;
422  }elseif($blockDecay >= 8){
423  $blockDecay = 0;
424  }
425 
426  return ($decay >= 0 && $blockDecay >= $decay) ? $decay : $blockDecay;
427  }
428 
429  private function checkForHarden(){
430  if($this instanceof Lava){
431  $colliding = false;
432  for($side = 0; $side <= 5 and !$colliding; ++$side){
433  $colliding = $this->getSide($side) instanceof Water;
434  }
435 
436  if($colliding){
437  if($this->getDamage() === 0){
438  $this->getLevel()->setBlock($this, Block::get(Item::OBSIDIAN), true);
439  }elseif($this->getDamage() <= 4){
440  $this->getLevel()->setBlock($this, Block::get(Item::COBBLESTONE), true);
441  }
442  }
443  }
444  }
445 
446  public function getBoundingBox(){
447  return null;
448  }
449 }
getSide($side, $step=1)
Definition: Block.php:1028
static get($id, $meta=0, Position $pos=null)
Definition: Block.php:782