PocketMine-MP 5.18.1 git-9381fc4172e5dce4cada1cb356050c8a2ab57b94
Normal.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\world\generator\normal;
25
42
43class Normal extends Generator{
44
45 private int $waterHeight = 62;
47 private array $populators = [];
49 private array $generationPopulators = [];
50 private Simplex $noiseBase;
51 private BiomeSelector $selector;
52 private Gaussian $gaussian;
53
57 public function __construct(int $seed, string $preset){
58 parent::__construct($seed, $preset);
59
60 $this->gaussian = new Gaussian(2);
61
62 $this->noiseBase = new Simplex($this->random, 4, 1 / 4, 1 / 32);
63 $this->random->setSeed($this->seed);
64
65 $this->selector = new class($this->random) extends BiomeSelector{
66 protected function lookup(float $temperature, float $rainfall) : int{
67 if($rainfall < 0.25){
68 if($temperature < 0.7){
69 return BiomeIds::OCEAN;
70 }elseif($temperature < 0.85){
71 return BiomeIds::RIVER;
72 }else{
73 return BiomeIds::SWAMPLAND;
74 }
75 }elseif($rainfall < 0.60){
76 if($temperature < 0.25){
77 return BiomeIds::ICE_PLAINS;
78 }elseif($temperature < 0.75){
79 return BiomeIds::PLAINS;
80 }else{
81 return BiomeIds::DESERT;
82 }
83 }elseif($rainfall < 0.80){
84 if($temperature < 0.25){
85 return BiomeIds::TAIGA;
86 }elseif($temperature < 0.75){
87 return BiomeIds::FOREST;
88 }else{
89 return BiomeIds::BIRCH_FOREST;
90 }
91 }else{
92 if($temperature < 0.20){
93 return BiomeIds::EXTREME_HILLS;
94 }elseif($temperature < 0.40){
95 return BiomeIds::EXTREME_HILLS_EDGE;
96 }else{
97 return BiomeIds::RIVER;
98 }
99 }
100 }
101 };
102
103 $this->selector->recalculate();
104
105 $cover = new GroundCover();
106 $this->generationPopulators[] = $cover;
107
108 $ores = new Ore();
109 $stone = VanillaBlocks::STONE();
110 $ores->setOreTypes([
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)
119 ]);
120 $this->populators[] = $ores;
121 }
122
123 private function pickBiome(int $x, int $z) : Biome{
124 $hash = $x * 2345803 ^ $z * 9236449 ^ $this->seed;
125 $hash *= $hash + 223;
126 $hash = (int) $hash;
127 $xNoise = $hash >> 20 & 3;
128 $zNoise = $hash >> 22 & 3;
129 if($xNoise == 3){
130 $xNoise = 1;
131 }
132 if($zNoise == 3){
133 $zNoise = 1;
134 }
135
136 return $this->selector->pickBiome($x + $xNoise - 1, $z + $zNoise - 1);
137 }
138
139 public function generateChunk(ChunkManager $world, int $chunkX, int $chunkZ) : void{
140 $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->seed);
141
142 $noise = $this->noiseBase->getFastNoise3D(Chunk::EDGE_LENGTH, 128, Chunk::EDGE_LENGTH, 4, 8, 4, $chunkX * Chunk::EDGE_LENGTH, 0, $chunkZ * Chunk::EDGE_LENGTH);
143
144 //TODO: why don't we just create and set the chunk here directly?
145 $chunk = $world->getChunk($chunkX, $chunkZ) ?? throw new \InvalidArgumentException("Chunk $chunkX $chunkZ does not yet exist");
146
147 $biomeCache = [];
148
149 $bedrock = VanillaBlocks::BEDROCK()->getStateId();
150 $stillWater = VanillaBlocks::WATER()->getStateId();
151 $stone = VanillaBlocks::STONE()->getStateId();
152
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;
159 $minSum = 0;
160 $maxSum = 0;
161 $weightSum = 0;
162
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());
166 }
167
168 for($sx = -$this->gaussian->smoothSize; $sx <= $this->gaussian->smoothSize; ++$sx){
169 for($sz = -$this->gaussian->smoothSize; $sz <= $this->gaussian->smoothSize; ++$sz){
170
171 $weight = $this->gaussian->kernel[$sx + $this->gaussian->smoothSize][$sz + $this->gaussian->smoothSize];
172
173 if($sx === 0 && $sz === 0){
174 $adjacent = $biome;
175 }else{
176 $index = World::chunkHash($absoluteX + $sx, $absoluteZ + $sz);
177 if(isset($biomeCache[$index])){
178 $adjacent = $biomeCache[$index];
179 }else{
180 $biomeCache[$index] = $adjacent = $this->pickBiome($absoluteX + $sx, $absoluteZ + $sz);
181 }
182 }
183
184 $minSum += ($adjacent->getMinElevation() - 1) * $weight;
185 $maxSum += $adjacent->getMaxElevation() * $weight;
186
187 $weightSum += $weight;
188 }
189 }
190
191 $minSum /= $weightSum;
192 $maxSum /= $weightSum;
193
194 $smoothHeight = ($maxSum - $minSum) / 2;
195
196 for($y = 0; $y < 128; ++$y){
197 if($y === 0){
198 $chunk->setBlockStateId($x, $y, $z, $bedrock);
199 continue;
200 }
201 $noiseValue = $noise[$x][$z][$y] - 1 / $smoothHeight * ($y - $smoothHeight - $minSum);
202
203 if($noiseValue > 0){
204 $chunk->setBlockStateId($x, $y, $z, $stone);
205 }elseif($y <= $this->waterHeight){
206 $chunk->setBlockStateId($x, $y, $z, $stillWater);
207 }
208 }
209 }
210 }
211
212 foreach($this->generationPopulators as $populator){
213 $populator->populate($world, $chunkX, $chunkZ, $this->random);
214 }
215 }
216
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);
221 }
222
223 $chunk = $world->getChunk($chunkX, $chunkZ);
224 $biome = BiomeRegistry::getInstance()->getBiome($chunk->getBiomeId(7, 7, 7));
225 $biome->populateChunk($world, $chunkX, $chunkZ, $this->random);
226 }
227}
static chunkHash(int $x, int $z)
Definition: World.php:376
__construct(int $seed, string $preset)
Definition: Normal.php:57