45 private int $waterHeight = 62;
47 private array $populators = [];
49 private array $generationPopulators = [];
58 parent::__construct($seed, $preset);
62 $this->noiseBase =
new Simplex($this->random, 4, 1 / 4, 1 / 32);
63 $this->random->setSeed($this->seed);
65 $this->selector =
new class($this->random) extends
BiomeSelector{
66 protected function lookup(
float $temperature,
float $rainfall) :
int{
68 if($temperature < 0.7){
69 return BiomeIds::OCEAN;
70 }elseif($temperature < 0.85){
71 return BiomeIds::RIVER;
73 return BiomeIds::SWAMPLAND;
75 }elseif($rainfall < 0.60){
76 if($temperature < 0.25){
77 return BiomeIds::ICE_PLAINS;
78 }elseif($temperature < 0.75){
79 return BiomeIds::PLAINS;
81 return BiomeIds::DESERT;
83 }elseif($rainfall < 0.80){
84 if($temperature < 0.25){
85 return BiomeIds::TAIGA;
86 }elseif($temperature < 0.75){
87 return BiomeIds::FOREST;
89 return BiomeIds::BIRCH_FOREST;
92 if($temperature < 0.20){
93 return BiomeIds::EXTREME_HILLS;
94 }elseif($temperature < 0.40){
95 return BiomeIds::EXTREME_HILLS_EDGE;
97 return BiomeIds::RIVER;
103 $this->selector->recalculate();
106 $this->generationPopulators[] = $cover;
109 $stone = VanillaBlocks::STONE();
111 new OreType(VanillaBlocks::COAL_ORE(), $stone, 20, 16, 0, 128),
112 new OreType(VanillaBlocks::IRON_ORE(), $stone, 20, 8, 0, 64),
113 new OreType(VanillaBlocks::REDSTONE_ORE(), $stone, 8, 7, 0, 16),
114 new OreType(VanillaBlocks::LAPIS_LAZULI_ORE(), $stone, 1, 6, 0, 32),
115 new OreType(VanillaBlocks::GOLD_ORE(), $stone, 2, 8, 0, 32),
116 new OreType(VanillaBlocks::DIAMOND_ORE(), $stone, 1, 7, 0, 16),
117 new OreType(VanillaBlocks::DIRT(), $stone, 20, 32, 0, 128),
118 new OreType(VanillaBlocks::GRAVEL(), $stone, 10, 16, 0, 128)
120 $this->populators[] = $ores;
123 private function pickBiome(
int $x,
int $z) :
Biome{
124 $hash = $x * 2345803 ^ $z * 9236449 ^ $this->seed;
125 $hash *= $hash + 223;
127 $xNoise = $hash >> 20 & 3;
128 $zNoise = $hash >> 22 & 3;
136 return $this->selector->pickBiome($x + $xNoise - 1, $z + $zNoise - 1);
139 public function generateChunk(ChunkManager $world,
int $chunkX,
int $chunkZ) : void{
140 $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->seed);
142 $noise = $this->noiseBase->getFastNoise3D(Chunk::EDGE_LENGTH, 128, Chunk::EDGE_LENGTH, 4, 8, 4, $chunkX * Chunk::EDGE_LENGTH, 0, $chunkZ * Chunk::EDGE_LENGTH);
145 $chunk = $world->getChunk($chunkX, $chunkZ) ??
throw new \InvalidArgumentException(
"Chunk $chunkX $chunkZ does not yet exist");
149 $bedrock = VanillaBlocks::BEDROCK()->getStateId();
150 $stillWater = VanillaBlocks::WATER()->getStateId();
151 $stone = VanillaBlocks::STONE()->getStateId();
153 $baseX = $chunkX * Chunk::EDGE_LENGTH;
154 $baseZ = $chunkZ * Chunk::EDGE_LENGTH;
155 for($x = 0; $x < Chunk::EDGE_LENGTH; ++$x){
156 $absoluteX = $baseX + $x;
157 for($z = 0; $z < Chunk::EDGE_LENGTH; ++$z){
158 $absoluteZ = $baseZ + $z;
163 $biome = $this->pickBiome($absoluteX, $absoluteZ);
164 for($y = World::Y_MIN; $y < World::Y_MAX; $y++){
165 $chunk->setBiomeId($x, $y, $z, $biome->getId());
168 for($sx = -$this->gaussian->smoothSize; $sx <= $this->gaussian->smoothSize; ++$sx){
169 for($sz = -$this->gaussian->smoothSize; $sz <= $this->gaussian->smoothSize; ++$sz){
171 $weight = $this->gaussian->kernel[$sx + $this->gaussian->smoothSize][$sz + $this->gaussian->smoothSize];
173 if($sx === 0 && $sz === 0){
177 if(isset($biomeCache[$index])){
178 $adjacent = $biomeCache[$index];
180 $biomeCache[$index] = $adjacent = $this->pickBiome($absoluteX + $sx, $absoluteZ + $sz);
184 $minSum += ($adjacent->getMinElevation() - 1) * $weight;
185 $maxSum += $adjacent->getMaxElevation() * $weight;
187 $weightSum += $weight;
191 $minSum /= $weightSum;
192 $maxSum /= $weightSum;
194 $smoothHeight = ($maxSum - $minSum) / 2;
196 for($y = 0; $y < 128; ++$y){
198 $chunk->setBlockStateId($x, $y, $z, $bedrock);
201 $noiseValue = $noise[$x][$z][$y] - 1 / $smoothHeight * ($y - $smoothHeight - $minSum);
204 $chunk->setBlockStateId($x, $y, $z, $stone);
205 }elseif($y <= $this->waterHeight){
206 $chunk->setBlockStateId($x, $y, $z, $stillWater);
212 foreach($this->generationPopulators as $populator){
213 $populator->populate($world, $chunkX, $chunkZ, $this->random);
217 public function populateChunk(ChunkManager $world,
int $chunkX,
int $chunkZ) : void{
218 $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->seed);
219 foreach($this->populators as $populator){
220 $populator->populate($world, $chunkX, $chunkZ, $this->random);
223 $chunk = $world->getChunk($chunkX, $chunkZ);
224 $biome = BiomeRegistry::getInstance()->getBiome($chunk->getBiomeId(7, 7, 7));
225 $biome->populateChunk($world, $chunkX, $chunkZ, $this->random);