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;
 
 
   92        $inset = 1 - (($this->thick ? 3 : 2) / 16);
 
   93        return [AxisAlignedBB::one()->trim(Facing::SOUTH, $inset)->trim(Facing::EAST, $inset)];
 
 
   97        return SupportType::NONE;
 
 
  100    private static function getOffsetSeed(
int $x, 
int $y, 
int $z) : int{
 
  101        $p1 = gmp_mul($z, 0x6ebfff5);
 
  102        $p2 = gmp_mul($x, 0x2fc20f);
 
  105        $xord = gmp_xor(gmp_xor($p1, $p2), $p3);
 
  107        $fullResult = gmp_mul(gmp_add(gmp_mul($xord, 0x285b825), 0xb), $xord);
 
  108        return gmp_intval(gmp_and($fullResult, 0xffffffff));
 
  111    private static function getMaxHeight(
int $x, 
int $z) : int{
 
  112        return 12 + (self::getOffsetSeed($x, 0, $z) % 5);
 
  116        $seed = self::getOffsetSeed($this->position->getFloorX(), 0, $this->position->getFloorZ());
 
  117        $retX = (($seed % 12) + 1) / 16;
 
  118        $retZ = ((($seed >> 8) % 12) + 1) / 16;
 
  119        return new Vector3($retX, 0, $retZ);
 
 
  122    private function canBeSupportedAt(
Block $block) : bool{
 
  123        $supportBlock = $block->getSide(
Facing::DOWN);
 
  126            $supportBlock->getTypeId() === BlockTypeIds::GRAVEL ||
 
  127            $supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
 
  128            $supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
 
  129            $supportBlock->hasTypeTag(BlockTypeTags::SAND);
 
  132    private function seekToTop() : Bamboo{
 
  133        $world = $this->position->getWorld();
 
  135        while(($next = $world->getBlock($top->position->up())) instanceof Bamboo && $next->hasSameTypeId($this)){
 
  143            $top = $this->seekToTop();
 
  144            if($top->grow(self::getMaxHeight($top->position->getFloorX(), $top->position->getFloorZ()), mt_rand(1, 2), $player)){
 
  148        }elseif($item instanceof ItemBamboo){
 
  149            if($this->seekToTop()->grow(PHP_INT_MAX, 1, $player)){
 
 
  157    private function grow(
int $maxHeight, 
int $growAmount, ?Player $player) : bool{
 
  158        $world = $this->position->getWorld();
 
  159        if(!$world->getBlock($this->position->up())->canBeReplaced()){
 
  164        while($world->getBlock($this->position->subtract(0, $height, 0))->hasSameTypeId($this)){
 
  165            if(++$height >= $maxHeight){
 
  170        $newHeight = $height + $growAmount;
 
  172        $stemBlock = (clone $this)->setReady(
false)->setLeafSize(self::NO_LEAVES);
 
  173        if($newHeight >= 4 && !$stemBlock->thick){ 
 
  174            $stemBlock = $stemBlock->setThick(true);
 
  176        $smallLeavesBlock = (clone $stemBlock)->setLeafSize(self::SMALL_LEAVES);
 
  177        $bigLeavesBlock = (clone $stemBlock)->setLeafSize(self::LARGE_LEAVES);
 
  180        if($newHeight === 2){
 
  181            $newBlocks[] = $smallLeavesBlock;
 
  182        }elseif($newHeight === 3){
 
  183            $newBlocks[] = $smallLeavesBlock;
 
  184            $newBlocks[] = $smallLeavesBlock;
 
  185        }elseif($newHeight === 4){
 
  186            $newBlocks[] = $bigLeavesBlock;
 
  187            $newBlocks[] = $smallLeavesBlock;
 
  188            $newBlocks[] = $stemBlock;
 
  189            $newBlocks[] = $stemBlock;
 
  190        }elseif($newHeight > 4){
 
  191            $newBlocks[] = $bigLeavesBlock;
 
  192            $newBlocks[] = $bigLeavesBlock;
 
  193            $newBlocks[] = $smallLeavesBlock;
 
  194            for($i = 0, $max = min($growAmount, $newHeight - count($newBlocks)); $i < $max; ++$i){
 
  195                $newBlocks[] = $stemBlock; 
 
  199        $tx = 
new BlockTransaction($world);
 
  200        foreach($newBlocks as $idx => $newBlock){
 
  201            $tx->addBlock($this->position->subtract(0, $idx - $growAmount, 0), $newBlock);
 
  204        $ev = 
new StructureGrowEvent($this, $tx, $player);
 
  206        if($ev->isCancelled()){
 
  218        $world = $this->position->getWorld();
 
  220            $this->ready = 
false;
 
  221            if($world->getFullLight($this->position) < 9 || !$this->grow(self::getMaxHeight($this->position->getFloorX(), $this->position->getFloorZ()), 1, 
null)){
 
  222                $world->setBlock($this->position, $this);
 
  224        }elseif($world->getBlock($this->position->up())->canBeReplaced()){
 
  226            $world->setBlock($this->position, $this);