mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-06 11:57:10 +00:00
BiomeSelector: drastically simplified implementation and made more robust
- Doesn't need to be pre-populated with biomes prior to calculating the heatmap - now population of biomes is entirely dependent on the lookup function, improving consistency - Uses an abstract class method for lookup instead of callback (use anonymous class instead) - Faster because the heatmap is directly populated with biomes instead of biome IDs, removing an unnecessary lookup.
This commit is contained in:
parent
4e3e807741
commit
78ec3937bf
@ -24,49 +24,48 @@ declare(strict_types=1);
|
||||
namespace pocketmine\level\generator\biome;
|
||||
|
||||
use pocketmine\level\biome\Biome;
|
||||
use pocketmine\level\biome\UnknownBiome;
|
||||
use pocketmine\level\generator\noise\Simplex;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class BiomeSelector{
|
||||
|
||||
/** @var Biome */
|
||||
private $fallback;
|
||||
|
||||
abstract class BiomeSelector{
|
||||
/** @var Simplex */
|
||||
private $temperature;
|
||||
/** @var Simplex */
|
||||
private $rainfall;
|
||||
|
||||
/** @var Biome[] */
|
||||
private $biomes = [];
|
||||
|
||||
/** @var \SplFixedArray */
|
||||
/** @var Biome[]|\SplFixedArray */
|
||||
private $map = null;
|
||||
|
||||
/** @var callable */
|
||||
private $lookup;
|
||||
|
||||
public function __construct(Random $random, callable $lookup, Biome $fallback){
|
||||
$this->fallback = $fallback;
|
||||
$this->lookup = $lookup;
|
||||
public function __construct(Random $random){
|
||||
$this->temperature = new Simplex($random, 2, 1 / 16, 1 / 512);
|
||||
$this->rainfall = new Simplex($random, 2, 1 / 16, 1 / 512);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup function called by recalculate() to determine the biome to use for this temperature and rainfall.
|
||||
*
|
||||
* @param float $temperature
|
||||
* @param float $rainfall
|
||||
*
|
||||
* @return int biome ID 0-255
|
||||
*/
|
||||
abstract protected function lookup(float $temperature, float $rainfall) : int;
|
||||
|
||||
public function recalculate(){
|
||||
$this->map = new \SplFixedArray(64 * 64);
|
||||
|
||||
for($i = 0; $i < 64; ++$i){
|
||||
for($j = 0; $j < 64; ++$j){
|
||||
$this->map[$i + ($j << 6)] = call_user_func($this->lookup, $i / 63, $j / 63);
|
||||
$biome = Biome::getBiome($this->lookup($i / 63, $j / 63));
|
||||
if($biome instanceof UnknownBiome){
|
||||
throw new \RuntimeException("Unknown biome returned by selector with ID " . $biome->getId());
|
||||
}
|
||||
$this->map[$i + ($j << 6)] = $biome;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addBiome(Biome $biome){
|
||||
$this->biomes[$biome->getId()] = $biome;
|
||||
}
|
||||
|
||||
public function getTemperature($x, $z){
|
||||
return ($this->temperature->noise2D($x, $z, true) + 1) / 2;
|
||||
}
|
||||
@ -85,7 +84,6 @@ class BiomeSelector{
|
||||
$temperature = (int) ($this->getTemperature($x, $z) * 63);
|
||||
$rainfall = (int) ($this->getRainfall($x, $z) * 63);
|
||||
|
||||
$biomeId = $this->map[$temperature + ($rainfall << 6)];
|
||||
return $this->biomes[$biomeId] ?? $this->fallback;
|
||||
return $this->map[$temperature + ($rainfall << 6)];
|
||||
}
|
||||
}
|
||||
|
@ -107,56 +107,46 @@ class Normal extends Generator{
|
||||
$this->random->setSeed($this->level->getSeed());
|
||||
$this->noiseBase = new Simplex($this->random, 4, 1 / 4, 1 / 32);
|
||||
$this->random->setSeed($this->level->getSeed());
|
||||
$this->selector = new BiomeSelector($this->random, function($temperature, $rainfall){
|
||||
if($rainfall < 0.25){
|
||||
if($temperature < 0.7){
|
||||
return Biome::OCEAN;
|
||||
}elseif($temperature < 0.85){
|
||||
return Biome::RIVER;
|
||||
}else{
|
||||
return Biome::SWAMP;
|
||||
}
|
||||
}elseif($rainfall < 0.60){
|
||||
if($temperature < 0.25){
|
||||
return Biome::ICE_PLAINS;
|
||||
}elseif($temperature < 0.75){
|
||||
return Biome::PLAINS;
|
||||
}else{
|
||||
return Biome::DESERT;
|
||||
}
|
||||
}elseif($rainfall < 0.80){
|
||||
if($temperature < 0.25){
|
||||
return Biome::TAIGA;
|
||||
}elseif($temperature < 0.75){
|
||||
return Biome::FOREST;
|
||||
}else{
|
||||
return Biome::BIRCH_FOREST;
|
||||
}
|
||||
}else{
|
||||
//FIXME: This will always cause River to be used since the rainfall is always greater than 0.8 if we
|
||||
//reached this branch. However I don't think that substituting temperature for rainfall is correct given
|
||||
//that mountain biomes are supposed to be pretty cold.
|
||||
$this->selector = new class($this->random) extends BiomeSelector{
|
||||
protected function lookup(float $temperature, float $rainfall) : int{
|
||||
if($rainfall < 0.25){
|
||||
return Biome::MOUNTAINS;
|
||||
}elseif($rainfall < 0.70){
|
||||
return Biome::SMALL_MOUNTAINS;
|
||||
if($temperature < 0.7){
|
||||
return Biome::OCEAN;
|
||||
}elseif($temperature < 0.85){
|
||||
return Biome::RIVER;
|
||||
}else{
|
||||
return Biome::SWAMP;
|
||||
}
|
||||
}elseif($rainfall < 0.60){
|
||||
if($temperature < 0.25){
|
||||
return Biome::ICE_PLAINS;
|
||||
}elseif($temperature < 0.75){
|
||||
return Biome::PLAINS;
|
||||
}else{
|
||||
return Biome::DESERT;
|
||||
}
|
||||
}elseif($rainfall < 0.80){
|
||||
if($temperature < 0.25){
|
||||
return Biome::TAIGA;
|
||||
}elseif($temperature < 0.75){
|
||||
return Biome::FOREST;
|
||||
}else{
|
||||
return Biome::BIRCH_FOREST;
|
||||
}
|
||||
}else{
|
||||
return Biome::RIVER;
|
||||
//FIXME: This will always cause River to be used since the rainfall is always greater than 0.8 if we
|
||||
//reached this branch. However I don't think that substituting temperature for rainfall is correct given
|
||||
//that mountain biomes are supposed to be pretty cold.
|
||||
if($rainfall < 0.25){
|
||||
return Biome::MOUNTAINS;
|
||||
}elseif($rainfall < 0.70){
|
||||
return Biome::SMALL_MOUNTAINS;
|
||||
}else{
|
||||
return Biome::RIVER;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, Biome::getBiome(Biome::OCEAN));
|
||||
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::OCEAN));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::PLAINS));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::DESERT));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::MOUNTAINS));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::FOREST));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::TAIGA));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::SWAMP));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::RIVER));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::ICE_PLAINS));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::SMALL_MOUNTAINS));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::BIRCH_FOREST));
|
||||
};
|
||||
|
||||
$this->selector->recalculate();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user