<?php
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\world\generator\object;
use pocketmine\block\Block;
use pocketmine\block\BlockTypeTags;
use pocketmine\block\NetherVines;
use pocketmine\block\VanillaBlocks;
use pocketmine\utils\Random;
use pocketmine\world\BlockTransaction;
use pocketmine\world\ChunkManager;
use function abs;
use function min;
class NetherTree extends Tree{
public function __construct(
Block $stemBlock,
Block $hatBlock,
private readonly Block $decorBlock,
int $treeHeight,
private readonly bool $hasVines,
private readonly bool $huge
){
parent::__construct($stemBlock, $hatBlock, $treeHeight);
}
public function canPlaceObject(ChunkManager $world, int $x, int $y, int $z, Random $random) : bool{
return true;
}
protected function placeTrunk(int $x, int $y, int $z, Random $random, int $trunkHeight, BlockTransaction $transaction) : void{
$i = $this->huge ? 1 : 0;
for($j = -$i; $j <= $i; ++$j){
for($k = -$i; $k <= $i; ++$k){
$isCorner = $this->huge && abs($j) === $i && abs($k) === $i;
for($l = 0; $l < $trunkHeight; ++$l){
$blockX = $x + $j;
$blockY = $y + $l;
$blockZ = $z + $k;
if((!$isCorner || $random->nextFloat() < 0.1) && $this->canOverride($transaction->fetchBlockAt($blockX, $blockY, $blockZ))){
$transaction->addBlockAt($blockX, $blockY, $blockZ, $this->trunkBlock);
}
}
}
}
}
protected function placeCanopy(int $x, int $y, int $z, Random $random, BlockTransaction $transaction) : void{
$isCrimson = $this->hasVines;
$i = min($random->nextBoundedInt(1 + (int) ($this->treeHeight / 3)) + 5, $this->treeHeight);
$j = $this->treeHeight - $i;
for($k = $j; $k <= $this->treeHeight; ++$k){
$l = $k < $this->treeHeight - $random->nextBoundedInt(3) ? 2 : 1;
if($i > 8 && $k < $j + 4){
$l = 3;
}
if($this->huge){
++$l;
}
for($i1 = -$l; $i1 <= $l; ++$i1){
for($j1 = -$l; $j1 <= $l; ++$j1){
$isEdgeX = $i1 === -$l || $i1 === $l;
$isEdgeZ = $j1 === -$l || $j1 === $l;
$isInner = !$isEdgeX && !$isEdgeZ && $k !== $this->treeHeight;
$isCorner = $isEdgeX && $isEdgeZ;
$isLowerSection = $k < $j + 3;
$blockX = $x + $i1;
$blockY = $y + $k;
$blockZ = $z + $j1;
if($this->canOverride($transaction->fetchBlockAt($blockX, $blockY, $blockZ))){
if($isLowerSection){
if(!$isInner){
$this->placeHatDropBlock($blockX, $blockY, $blockZ, $random, $transaction, $isCrimson);
}
}elseif($isInner){
$this->placeHatBlock($blockX, $blockY, $blockZ, $random, $transaction, 0.1, 0.2, $isCrimson ? 0.1 : 0.0);
}elseif($isCorner){
$this->placeHatBlock($blockX, $blockY, $blockZ, $random, $transaction, 0.01, 0.7, $isCrimson ? 0.083 : 0.0);
}else{
$this->placeHatBlock($blockX, $blockY, $blockZ, $random, $transaction, 0.0005, 0.98, $isCrimson ? 0.07 : 0.0);
}
}
}
}
}
}
protected function placeHatBlock(int $x, int $y, int $z, Random $random, BlockTransaction $transaction, float $decorChance, float $hatChance, float $vineChance) : void{
if($random->nextFloat() < $decorChance){
$transaction->addBlockAt($x, $y, $z, $this->decorBlock);
}elseif($random->nextFloat() < $hatChance){
$transaction->addBlockAt($x, $y, $z, $this->leafBlock);
if($random->nextFloat() < $vineChance){
$this->tryPlaceWeepingVines($x, $y, $z, $random, $transaction);
}
}
}
protected function placeHatDropBlock(int $x, int $y, int $z, Random $random, BlockTransaction $transaction, bool $isCrimson) : void{
$blockBelow = $transaction->fetchBlockAt($x, $y - 1, $z);
if($blockBelow->getTypeId() === $this->leafBlock->getTypeId()){
$transaction->addBlockAt($x, $y, $z, $this->leafBlock);
}elseif($random->nextFloat() < 0.15){
$transaction->addBlockAt($x, $y, $z, $this->leafBlock);
if($isCrimson && $random->nextBoundedInt(11) === 0){
$this->tryPlaceWeepingVines($x, $y, $z, $random, $transaction);
}
}
}
protected function tryPlaceWeepingVines(int $x, int $y, int $z, Random $random, BlockTransaction $transaction) : void{
$currentY = $y - 1;
if($this->canOverride($transaction->fetchBlockAt($x, $currentY, $z))){
$i = $random->nextBoundedInt(5) + 1;
if($random->nextBoundedInt(7) === 0){
$i *= 2;
}
$maxAge = NetherVines::MAX_AGE;
$startAge = 23;
for($v = 0; $v < $i; ++$v){
$vy = $currentY - $v;
if($vy < 0){
break;
}
if($this->canOverride($transaction->fetchBlockAt($x, $vy, $z))){
$vineAge = min($maxAge, $startAge + $v);
$transaction->addBlockAt($x, $vy, $z, VanillaBlocks::WEEPING_VINES()->setAge($vineAge));
}else{
break;
}
}
}
}
protected function canOverride(Block $block) : bool{
return $block->canBeReplaced() || $block->hasTypeTag(BlockTypeTags::HUGE_FUNGUS_REPLACEABLE);
}
protected function generateTrunkHeight(Random $random) : int{
return $this->treeHeight;
}
}