50 use StaticSupportTrait;
52 public const NO_LEAVES = 0;
53 public const SMALL_LEAVES = 1;
54 public const LARGE_LEAVES = 2;
56 protected bool $thick =
false;
57 protected bool $ready =
false;
58 protected int $leafSize = self::NO_LEAVES;
61 $w->boundedIntAuto(self::NO_LEAVES, self::LARGE_LEAVES, $this->leafSize);
62 $w->bool($this->thick);
63 $w->bool($this->ready);
66 public function isThick() : bool{ return $this->thick; }
70 $this->thick = $thick;
74 public function isReady() : bool{ return $this->ready; }
78 $this->ready = $ready;
82 public function getLeafSize() : int{ return $this->leafSize; }
86 $this->leafSize = $leafSize;
95 $inset = 1 - (($this->thick ? 3 : 2) / 16);
96 return [AxisAlignedBB::one()->trim(Facing::SOUTH, $inset)->trim(Facing::EAST, $inset)];
100 return SupportType::NONE;
103 private static function getOffsetSeed(
int $x,
int $y,
int $z) : int{
104 $p1 = gmp_mul($z, 0x6ebfff5);
105 $p2 = gmp_mul($x, 0x2fc20f);
108 $xord = gmp_xor(gmp_xor($p1, $p2), $p3);
110 $fullResult = gmp_mul(gmp_add(gmp_mul($xord, 0x285b825), 0xb), $xord);
111 return gmp_intval(gmp_and($fullResult, 0xffffffff));
114 private static function getMaxHeight(
int $x,
int $z) : int{
115 return 12 + (self::getOffsetSeed($x, 0, $z) % 5);
119 $seed = self::getOffsetSeed($this->position->getFloorX(), 0, $this->position->getFloorZ());
120 $retX = (($seed % 12) + 1) / 16;
121 $retZ = ((($seed >> 8) % 12) + 1) / 16;
122 return new Vector3($retX, 0, $retZ);
125 private function canBeSupportedAt(
Block $block) : bool{
126 $supportBlock = $block->getSide(
Facing::DOWN);
129 $supportBlock->getTypeId() === BlockTypeIds::GRAVEL ||
130 $supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
131 $supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
132 $supportBlock->hasTypeTag(BlockTypeTags::SAND);
135 private function seekToTop() : Bamboo{
136 $world = $this->position->getWorld();
138 while(($next = $world->getBlock($top->position->up())) instanceof Bamboo && $next->hasSameTypeId($this)){
146 $top = $this->seekToTop();
147 if($top->grow(self::getMaxHeight($top->position->getFloorX(), $top->position->getFloorZ()), mt_rand(1, 2), $player)){
151 }elseif($item instanceof ItemBamboo){
152 if($this->seekToTop()->grow(PHP_INT_MAX, 1, $player)){
160 private function grow(
int $maxHeight,
int $growAmount, ?Player $player) : bool{
161 $world = $this->position->getWorld();
162 if(!$world->getBlock($this->position->up())->canBeReplaced()){
167 while($world->getBlock($this->position->subtract(0, $height, 0))->hasSameTypeId($this)){
168 if(++$height >= $maxHeight){
173 $newHeight = $height + $growAmount;
175 $stemBlock = (clone $this)->setReady(
false)->setLeafSize(self::NO_LEAVES);
176 if($newHeight >= 4 && !$stemBlock->thick){
177 $stemBlock = $stemBlock->setThick(true);
179 $smallLeavesBlock = (clone $stemBlock)->setLeafSize(self::SMALL_LEAVES);
180 $bigLeavesBlock = (clone $stemBlock)->setLeafSize(self::LARGE_LEAVES);
183 if($newHeight === 2){
184 $newBlocks[] = $smallLeavesBlock;
185 }elseif($newHeight === 3){
186 $newBlocks[] = $smallLeavesBlock;
187 $newBlocks[] = $smallLeavesBlock;
188 }elseif($newHeight === 4){
189 $newBlocks[] = $bigLeavesBlock;
190 $newBlocks[] = $smallLeavesBlock;
191 $newBlocks[] = $stemBlock;
192 $newBlocks[] = $stemBlock;
193 }elseif($newHeight > 4){
194 $newBlocks[] = $bigLeavesBlock;
195 $newBlocks[] = $bigLeavesBlock;
196 $newBlocks[] = $smallLeavesBlock;
197 for($i = 0, $max = min($growAmount, $newHeight - count($newBlocks)); $i < $max; ++$i){
198 $newBlocks[] = $stemBlock;
202 $tx =
new BlockTransaction($world);
203 foreach($newBlocks as $idx => $newBlock){
204 $tx->addBlock($this->position->subtract(0, $idx - $growAmount, 0), $newBlock);
207 $ev =
new StructureGrowEvent($this, $tx, $player);
209 if($ev->isCancelled()){
221 $world = $this->position->getWorld();
223 $this->ready =
false;
224 if($world->getFullLight($this->position) < 9 || !$this->grow(self::getMaxHeight($this->position->getFloorX(), $this->position->getFloorZ()), 1,
null)){
225 $world->setBlock($this->position, $this);
227 }elseif($world->getBlock($this->position->up())->canBeReplaced()){
229 $world->setBlock($this->position, $this);