PocketMine-MP 5.18.1 git-9381fc4172e5dce4cada1cb356050c8a2ab57b94
Farmland.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
22declare(strict_types=1);
23
24namespace pocketmine\block;
25
34use function intdiv;
35use function lcg_value;
36
37class Farmland extends Transparent{
38 public const MAX_WETNESS = 7;
39
40 private const WATER_SEARCH_HORIZONTAL_LENGTH = 9;
41
42 private const WATER_SEARCH_VERTICAL_LENGTH = 2;
43
44 private const WATER_POSITION_INDEX_UNKNOWN = -1;
46 private const WATER_POSITION_INDICES_TOTAL = (self::WATER_SEARCH_HORIZONTAL_LENGTH ** 2) * 2;
47
48 protected int $wetness = 0; //"moisture" blockstate property in PC
49
63 private int $waterPositionIndex = self::WATER_POSITION_INDEX_UNKNOWN;
64
65 protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
66 $w->boundedIntAuto(0, self::MAX_WETNESS, $this->wetness);
67 $w->boundedIntAuto(-1, self::WATER_POSITION_INDICES_TOTAL - 1, $this->waterPositionIndex);
68 }
69
70 public function getWetness() : int{ return $this->wetness; }
71
73 public function setWetness(int $wetness) : self{
74 if($wetness < 0 || $wetness > self::MAX_WETNESS){
75 throw new \InvalidArgumentException("Wetness must be in range 0 ... " . self::MAX_WETNESS);
76 }
77 $this->wetness = $wetness;
78 return $this;
79 }
80
84 public function getWaterPositionIndex() : int{ return $this->waterPositionIndex; }
85
89 public function setWaterPositionIndex(int $waterPositionIndex) : self{
90 if($waterPositionIndex < -1 || $waterPositionIndex >= self::WATER_POSITION_INDICES_TOTAL){
91 throw new \InvalidArgumentException("Water XZ index must be in range -1 ... " . (self::WATER_POSITION_INDICES_TOTAL - 1));
92 }
93 $this->waterPositionIndex = $waterPositionIndex;
94 return $this;
95 }
96
100 protected function recalculateCollisionBoxes() : array{
101 return [AxisAlignedBB::one()->trim(Facing::UP, 1 / 16)];
102 }
103
104 public function onNearbyBlockChange() : void{
105 if($this->getSide(Facing::UP)->isSolid()){
106 $this->position->getWorld()->setBlock($this->position, VanillaBlocks::DIRT());
107 }
108 }
109
110 public function ticksRandomly() : bool{
111 return true;
112 }
113
114 public function onRandomTick() : void{
115 $world = $this->position->getWorld();
116
117 //this property may be updated by canHydrate() - track this so we know if we need to set the block again
118 $oldWaterPositionIndex = $this->waterPositionIndex;
119 $changed = false;
120
121 if(!$this->canHydrate()){
122 if($this->wetness > 0){
123 $event = new FarmlandHydrationChangeEvent($this, $this->wetness, $this->wetness - 1);
124 $event->call();
125 if(!$event->isCancelled()){
126 $this->wetness = $event->getNewHydration();
127 $world->setBlock($this->position, $this, false);
128 $changed = true;
129 }
130 }else{
131 $world->setBlock($this->position, VanillaBlocks::DIRT());
132 $changed = true;
133 }
134 }elseif($this->wetness < self::MAX_WETNESS){
135 $event = new FarmlandHydrationChangeEvent($this, $this->wetness, self::MAX_WETNESS);
136 $event->call();
137 if(!$event->isCancelled()){
138 $this->wetness = $event->getNewHydration();
139 $world->setBlock($this->position, $this, false);
140 $changed = true;
141 }
142 }
143
144 if(!$changed && $oldWaterPositionIndex !== $this->waterPositionIndex){
145 //ensure the water square index is saved regardless of whether anything else happened
146 $world->setBlock($this->position, $this, false);
147 }
148 }
149
150 public function onEntityLand(Entity $entity) : ?float{
151 if($entity instanceof Living && lcg_value() < $entity->getFallDistance() - 0.5){
152 $ev = new EntityTrampleFarmlandEvent($entity, $this);
153 $ev->call();
154 if(!$ev->isCancelled()){
155 $this->position->getWorld()->setBlock($this->position, VanillaBlocks::DIRT());
156 }
157 }
158 return null;
159 }
160
161 protected function canHydrate() : bool{
162 $world = $this->position->getWorld();
163
164 $startX = $this->position->getFloorX() - (int) (self::WATER_SEARCH_HORIZONTAL_LENGTH / 2);
165 $startY = $this->position->getFloorY();
166 $startZ = $this->position->getFloorZ() - (int) (self::WATER_SEARCH_HORIZONTAL_LENGTH / 2);
167
168 if($this->waterPositionIndex !== self::WATER_POSITION_INDEX_UNKNOWN){
169 $raw = $this->waterPositionIndex;
170 $x = $raw % self::WATER_SEARCH_HORIZONTAL_LENGTH;
171 $raw = intdiv($raw, self::WATER_SEARCH_HORIZONTAL_LENGTH);
172 $z = $raw % self::WATER_SEARCH_HORIZONTAL_LENGTH;
173 $raw = intdiv($raw, self::WATER_SEARCH_HORIZONTAL_LENGTH);
174 $y = $raw % self::WATER_SEARCH_VERTICAL_LENGTH;
175
176 if($world->getBlockAt($startX + $x, $startY + $y, $startZ + $z) instanceof Water){
177 return true;
178 }
179 }
180
181 //no water found at cached position - search the whole area
182 //y will increment after x/z have been exhausted, as usually water will be at the same Y as the farmland
183 for($y = 0; $y < self::WATER_SEARCH_VERTICAL_LENGTH; $y++){
184 for($x = 0; $x < self::WATER_SEARCH_HORIZONTAL_LENGTH; $x++){
185 for($z = 0; $z < self::WATER_SEARCH_HORIZONTAL_LENGTH; $z++){
186 if($world->getBlockAt($startX + $x, $startY + $y, $startZ + $z) instanceof Water){
187 $this->waterPositionIndex = $x + ($z * self::WATER_SEARCH_HORIZONTAL_LENGTH) + ($y * self::WATER_SEARCH_HORIZONTAL_LENGTH ** 2);
188 return true;
189 }
190 }
191 }
192 }
193
194 $this->waterPositionIndex = self::WATER_POSITION_INDEX_UNKNOWN;
195 return false;
196 }
197
198 public function getDropsForCompatibleTool(Item $item) : array{
199 return [
200 VanillaBlocks::DIRT()->asItem()
201 ];
202 }
203
204 public function getPickedItem(bool $addUserData = false) : Item{
205 return VanillaBlocks::DIRT()->asItem();
206 }
207}
setWetness(int $wetness)
Definition: Farmland.php:73
getPickedItem(bool $addUserData=false)
Definition: Farmland.php:204
onEntityLand(Entity $entity)
Definition: Farmland.php:150
describeBlockOnlyState(RuntimeDataDescriber $w)
Definition: Farmland.php:65
getDropsForCompatibleTool(Item $item)
Definition: Farmland.php:198
boundedIntAuto(int $min, int $max, int &$value)