World generation with biomes!

This commit is contained in:
Shoghi Cervantes 2015-03-22 22:57:40 +01:00
parent 1666602652
commit 9da26fdb88
No known key found for this signature in database
GPG Key ID: 78464DB0A7837F89
23 changed files with 404 additions and 104 deletions

View File

@ -616,7 +616,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
$Z = null; $Z = null;
Level::getXZ($index, $X, $Z); Level::getXZ($index, $X, $Z);
if(!$this->level->populateChunk($X, $Z)){ if(!$this->level->populateChunk($X, $Z, !$this->spawned or count($this->usedChunks) < ($this->viewDistance - 24))){
if($this->spawned){ if($this->spawned){
continue; continue;
}else{ }else{

View File

@ -583,15 +583,15 @@ class Level implements ChunkManager, Metadatable{
$this->timings->doTickTiles->stopTiming(); $this->timings->doTickTiles->stopTiming();
if(count($this->changedBlocks) > 0){ if(count($this->changedBlocks) > 0){
if(count($this->players) > 0){ if(count($this->players) > 5){
foreach($this->changedBlocks as $index => $blocks){ foreach($this->changedBlocks as $index => $blocks){
Level::getXZ($index, $X, $Z);
if(count($blocks) > 512){ if(count($blocks) > 512){
Level::getXZ($index, $X, $Z);
foreach($this->getUsingChunk($X, $Z) as $p){ foreach($this->getUsingChunk($X, $Z) as $p){
$p->unloadChunk($X, $Z); $p->unloadChunk($X, $Z);
} }
}else{ }else{
$this->sendBlocks($this->getUsingChunk($pos->x >> 4, $pos->z >> 4), $blocks); $this->sendBlocks($this->getUsingChunk($X, $Z), $blocks);
} }
} }
} }
@ -1821,14 +1821,18 @@ class Level implements ChunkManager, Metadatable{
} }
public function generateChunkCallback($x, $z, FullChunk $chunk){ public function generateChunkCallback($x, $z, FullChunk $chunk){
$oldChunk = $this->getChunk($x, $z, false); Timings::$generationTimer->startTiming();
unset($this->chunkGenerationQueue[Level::chunkHash($x, $z)]); if(isset($this->chunkGenerationQueue[$index = Level::chunkHash($x, $z)])){
$chunk->setProvider($this->provider); $oldChunk = $this->getChunk($x, $z, false);
$this->setChunk($x, $z, $chunk); unset($this->chunkGenerationQueue[$index = Level::chunkHash($x, $z)]);
$chunk = $this->getChunk($x, $z, false); $chunk->setProvider($this->provider);
if($chunk !== null and ($oldChunk === null or $oldChunk->isPopulated() === false) and $chunk->isPopulated()){ $this->setChunk($x, $z, $chunk);
$this->server->getPluginManager()->callEvent(new ChunkPopulateEvent($chunk)); $chunk = $this->getChunk($x, $z, false);
if($chunk !== null and ($oldChunk === null or $oldChunk->isPopulated() === false) and $chunk->isPopulated() and $chunk->getProvider() !== null){
$this->server->getPluginManager()->callEvent(new ChunkPopulateEvent($chunk));
}
} }
Timings::$generationTimer->stopTiming();
} }
public function setChunk($x, $z, FullChunk $chunk, $unload = true){ public function setChunk($x, $z, FullChunk $chunk, $unload = true){
@ -2079,7 +2083,12 @@ class Level implements ChunkManager, Metadatable{
} }
} }
$this->server->getPluginManager()->callEvent(new ChunkLoadEvent($chunk, !$chunk->isGenerated())); if($chunk->getProvider() !== null){
$this->server->getPluginManager()->callEvent(new ChunkLoadEvent($chunk, !$chunk->isGenerated()));
}else{
$this->unloadChunk($x, $z, false);
return false;
}
return true; return true;
} }
@ -2114,7 +2123,7 @@ class Level implements ChunkManager, Metadatable{
$chunk = $this->getChunk($x, $z); $chunk = $this->getChunk($x, $z);
if($chunk !== null){ if($chunk !== null and $chunk->getProvider() !== null){
$this->server->getPluginManager()->callEvent($ev = new ChunkUnloadEvent($chunk)); $this->server->getPluginManager()->callEvent($ev = new ChunkUnloadEvent($chunk));
if($ev->isCancelled()){ if($ev->isCancelled()){
return false; return false;
@ -2291,24 +2300,30 @@ class Level implements ChunkManager, Metadatable{
} }
public function populateChunk($x, $z){ public function populateChunk($x, $z, $force = false){
if(!$this->isChunkPopulated($x, $z)){ if(!$this->isChunkPopulated($x, $z)){
$populate = true; $populate = true;
for($xx = -1; $xx <= 1; ++$xx){ for($xx = -1; $xx <= 1; ++$xx){
for($zz = -1; $zz <= 1; ++$zz){ for($zz = -1; $zz <= 1; ++$zz){
if(!$this->isChunkGenerated($x + $xx, $z + $zz)){ if(!$this->isChunkGenerated($x + $xx, $z + $zz)){
$populate = false; $populate = false;
$this->generateChunk($x + $xx, $z + $zz); $this->generateChunk($x + $xx, $z + $zz, $force);
} }
} }
} }
if($this->chunksPopulated < $this->chunksPopulatedPerTick and $populate){ if($this->chunksPopulated < $this->chunksPopulatedPerTick and $populate){
Timings::$generationTimer->startTiming();
$this->generatorInstance->populateChunk($x, $z); $this->generatorInstance->populateChunk($x, $z);
$chunk = $this->getChunk($x, $z); $chunk = $this->getChunk($x, $z);
$chunk->setPopulated(true); $chunk->setPopulated(true);
$this->server->getPluginManager()->callEvent(new ChunkPopulateEvent($chunk)); if($chunk->getProvider() !== null){
$this->server->getPluginManager()->callEvent(new ChunkPopulateEvent($chunk));
}else{
$this->unloadChunk($x, $z, false);
}
++$this->chunksPopulated; ++$this->chunksPopulated;
Timings::$generationTimer->stopTiming();
return true; return true;
} }
@ -2317,10 +2332,15 @@ class Level implements ChunkManager, Metadatable{
return true; return true;
} }
public function generateChunk($x, $z){ public function generateChunk($x, $z, $force = false){
if(!isset($this->chunkGenerationQueue[$index = Level::chunkHash($x, $z)])){ if(!isset($this->chunkGenerationQueue[$index = Level::chunkHash($x, $z)])){
$this->chunkGenerationQueue[$index] = true; $this->chunkGenerationQueue[$index] = 0;
$this->server->getGenerationManager()->requestChunk($this, $x, $z); $this->server->getGenerationManager()->requestChunk($this, $x, $z, $this->getChunk($x, $z, true));
}elseif($force){
$value = ++$this->chunkGenerationQueue[$index];
if($value % 40 === 0){
$this->server->getGenerationManager()->requestChunk($this, $x, $z);
}
} }
} }

View File

@ -177,8 +177,9 @@ class GenerationManager{
if($this->needsChunk[$levelID][0] === $chunk->getX() and $this->needsChunk[$levelID][1] === $chunk->getZ()){ if($this->needsChunk[$levelID][0] === $chunk->getX() and $this->needsChunk[$levelID][1] === $chunk->getZ()){
$this->needsChunk[$levelID] = $chunk; $this->needsChunk[$levelID] = $chunk;
} }
}elseif(isset($this->levels[$levelID])){
$this->levels[$levelID]->setChunk($chunk->getX(), $chunk->getZ(), $chunk);
} }
//TODO: set new received chunks
} }
/** /**

View File

@ -74,9 +74,12 @@ class GenerationRequestManager{
$this->generationThread->pushMainToThreadPacket($buffer); $this->generationThread->pushMainToThreadPacket($buffer);
} }
public function requestChunk(Level $level, $chunkX, $chunkZ){ public function requestChunk(Level $level, $chunkX, $chunkZ, FullChunk $chunk = null){
$buffer = chr(GenerationManager::PACKET_REQUEST_CHUNK) . Binary::writeInt($level->getId()) . Binary::writeInt($chunkX) . Binary::writeInt($chunkZ); $buffer = chr(GenerationManager::PACKET_REQUEST_CHUNK) . Binary::writeInt($level->getId()) . Binary::writeInt($chunkX) . Binary::writeInt($chunkZ);
$this->generationThread->pushMainToThreadPacket($buffer); $this->generationThread->pushMainToThreadPacket($buffer);
if($chunk !== null){
$this->sendChunk($level->getId(), $chunk);
}
} }
protected function handleRequest($levelID, $chunkX, $chunkZ){ protected function handleRequest($levelID, $chunkX, $chunkZ){

View File

@ -23,14 +23,16 @@ namespace pocketmine\level\generator\biome;
use pocketmine\block\Block; use pocketmine\block\Block;
use pocketmine\level\ChunkManager; use pocketmine\level\ChunkManager;
use pocketmine\level\generator\normal\biome\BeachBiome; use pocketmine\level\generator\normal\biome\SwampBiome;
use pocketmine\level\generator\normal\biome\DesertBiome; use pocketmine\level\generator\normal\biome\DesertBiome;
use pocketmine\level\generator\normal\biome\ForestBiome; use pocketmine\level\generator\normal\biome\ForestBiome;
use pocketmine\level\generator\normal\biome\IcePlainsBiome;
use pocketmine\level\generator\normal\biome\MountainsBiome; use pocketmine\level\generator\normal\biome\MountainsBiome;
use pocketmine\level\generator\normal\biome\OceanBiome; use pocketmine\level\generator\normal\biome\OceanBiome;
use pocketmine\level\generator\normal\biome\PlainBiome; use pocketmine\level\generator\normal\biome\PlainBiome;
use pocketmine\level\generator\normal\biome\RiverBiome; use pocketmine\level\generator\normal\biome\RiverBiome;
use pocketmine\level\generator\normal\biome\SmallMountainsBiome; use pocketmine\level\generator\normal\biome\SmallMountainsBiome;
use pocketmine\level\generator\normal\biome\TaigaBiome;
use pocketmine\level\generator\populator\Populator; use pocketmine\level\generator\populator\Populator;
use pocketmine\utils\Random; use pocketmine\utils\Random;
@ -41,14 +43,19 @@ abstract class Biome{
const DESERT = 2; const DESERT = 2;
const MOUNTAINS = 3; const MOUNTAINS = 3;
const FOREST = 4; const FOREST = 4;
const TAIGA = 5;
const SWAMP = 6;
const RIVER = 7; const RIVER = 7;
const BEACH = 16; const ICE_PLAINS = 12;
const SMALL_MOUNTAINS = 20; const SMALL_MOUNTAINS = 20;
const BIRCH_FOREST = 27;
const MAX_BIOMES = 256; const MAX_BIOMES = 256;
/** @var Biome[] */ /** @var Biome[] */
@ -64,6 +71,9 @@ abstract class Biome{
private $groundCover = []; private $groundCover = [];
protected $rainfall = 0.5;
protected $temperature = 0.5;
protected static function register($id, Biome $biome){ protected static function register($id, Biome $biome){
self::$biomes[(int) $id] = $biome; self::$biomes[(int) $id] = $biome;
$biome->setId((int) $id); $biome->setId((int) $id);
@ -75,12 +85,17 @@ abstract class Biome{
self::register(self::DESERT, new DesertBiome()); self::register(self::DESERT, new DesertBiome());
self::register(self::MOUNTAINS, new MountainsBiome()); self::register(self::MOUNTAINS, new MountainsBiome());
self::register(self::FOREST, new ForestBiome()); self::register(self::FOREST, new ForestBiome());
self::register(self::TAIGA, new TaigaBiome());
self::register(self::RIVER, new RiverBiome()); self::register(self::RIVER, new RiverBiome());
self::register(self::BEACH, new BeachBiome()); self::register(self::ICE_PLAINS, new IcePlainsBiome());
self::register(self::SWAMP, new SwampBiome());
self::register(self::SMALL_MOUNTAINS, new SmallMountainsBiome()); self::register(self::SMALL_MOUNTAINS, new SmallMountainsBiome());
self::register(self::BIRCH_FOREST, new ForestBiome(ForestBiome::TYPE_BIRCH));
} }
/** /**
@ -89,7 +104,7 @@ abstract class Biome{
* @return Biome * @return Biome
*/ */
public static function getBiome($id){ public static function getBiome($id){
return isset(self::$biomes[$id]) ? self::$biomes[$id] : null; return isset(self::$biomes[$id]) ? self::$biomes[$id] : self::$biomes[self::OCEAN];
} }
public function clearPopulators(){ public function clearPopulators(){
@ -149,4 +164,18 @@ abstract class Biome{
public function setGroundCover(array $covers){ public function setGroundCover(array $covers){
$this->groundCover = $covers; $this->groundCover = $covers;
} }
public function getTemperature(){
return $this->temperature;
}
public function getRainfall(){
return $this->rainfall;
}
/**
* @return int (randomness|Red|Green|Blue)
*/
abstract public function getColor();
} }

View File

@ -37,17 +37,37 @@ class BiomeSelector{
/** @var Biome[] */ /** @var Biome[] */
private $biomes = []; private $biomes = [];
private $select = []; private $map = [];
public function __construct(Random $random, Biome $fallback){ private $lookup;
public function __construct(Random $random, callable $lookup, Biome $fallback){
$this->fallback = $fallback; $this->fallback = $fallback;
$this->temperature = new Simplex($random, 1, 0.004, 0.5, 2); $this->lookup = $lookup;
$this->rainfall = new Simplex($random, 2, 0.004, 0.5, 2); $this->temperature = new Simplex($random, 1, 0.001, 1, 1);
$this->rainfall = new Simplex($random, 1, 0.001, 1, 1);
} }
public function addBiome(Biome $biome, $start, $end){ 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);
}
}
}
public function addBiome(Biome $biome){
$this->biomes[$biome->getId()] = $biome; $this->biomes[$biome->getId()] = $biome;
$this->select[$biome->getId()] = [$biome->getId(), $start, $end]; }
public function getTemperature($x, $z){
return ($this->temperature->noise2D($x, $z, true) + 1) / 2;
}
public function getRainfall($x, $z){
return ($this->rainfall->noise2D($x, $z, true) + 1) / 2;
} }
/** /**
@ -57,27 +77,10 @@ class BiomeSelector{
* @return Biome * @return Biome
*/ */
public function pickBiome($x, $z){ public function pickBiome($x, $z){
$temperature = (int) ($this->getTemperature($x, $z) * 63);
$rainfall = (int) ($this->getRainfall($x, $z) * 63);
//$temperature = $this->temperature->noise2D($x, $z); $biomeId = $this->map[$temperature + ($rainfall << 6)];
$rainfall = $this->rainfall->noise2D($x, $z); return isset($this->biomes[$biomeId]) ? $this->biomes[$biomeId] : $this->fallback;
if($rainfall > 0.9){
return Biome::getBiome(Biome::OCEAN);
}elseif($rainfall > 0.7){
return Biome::getBiome(Biome::RIVER);
}elseif($rainfall > 0.6){
return Biome::getBiome(Biome::BEACH);
}elseif($rainfall > 0.2){
return Biome::getBiome(Biome::FOREST);
}elseif($rainfall > -0.3){
return Biome::getBiome(Biome::PLAINS);
}elseif($rainfall > -0.6){
return Biome::getBiome(Biome::DESERT);
}elseif($rainfall > -0.7){
return Biome::getBiome(Biome::BEACH);
}else{
return Biome::getBiome(Biome::OCEAN);
}
} }
} }

View File

@ -83,9 +83,7 @@ class Normal extends Generator{
$bellHeight = 2 * self::$SMOOTH_SIZE; $bellHeight = 2 * self::$SMOOTH_SIZE;
for($sx = -self::$SMOOTH_SIZE; $sx <= self::$SMOOTH_SIZE; ++$sx){ for($sx = -self::$SMOOTH_SIZE; $sx <= self::$SMOOTH_SIZE; ++$sx){
for($sz = -self::$SMOOTH_SIZE; $sz <= self::$SMOOTH_SIZE; ++$sz){ for($sz = -self::$SMOOTH_SIZE; $sz <= self::$SMOOTH_SIZE; ++$sz){
$bx = $bellSize * $sx; self::$GAUSSIAN_KERNEL[$sx + self::$SMOOTH_SIZE][$sz + self::$SMOOTH_SIZE] = 10 / sqrt($sx ** 2 + $sz ** 2 + 0.2);
$bz = $bellSize * $sz;
self::$GAUSSIAN_KERNEL[$sx + self::$SMOOTH_SIZE][$sz + self::$SMOOTH_SIZE] = $bellHeight * exp(-($bx * $bx + $bz * $bz) / 2);
} }
} }
} }
@ -102,9 +100,54 @@ class Normal extends Generator{
$this->level = $level; $this->level = $level;
$this->random = $random; $this->random = $random;
$this->random->setSeed($this->level->getSeed()); $this->random->setSeed($this->level->getSeed());
$this->noiseBase = new Simplex($this->random, 16, 0.012, 0.5, 2); $this->noiseBase = new Simplex($this->random, 16, 0.02, 0.5, 2);
$this->random->setSeed($this->level->getSeed()); $this->random->setSeed($this->level->getSeed());
$this->selector = new BiomeSelector($this->random, Biome::getBiome(Biome::OCEAN)); $this->selector = new BiomeSelector($this->random, function($temperature, $rainfall){
$rainfall *= $temperature;
if($temperature < 0.10){
return Biome::ICE_PLAINS;
}elseif($rainfall < 0.20){
if($temperature < 0.50){
return Biome::ICE_PLAINS;
}elseif($temperature < 0.95){
return Biome::SMALL_MOUNTAINS;
}else{
return Biome::DESERT;
}
}elseif($rainfall > 0.5 and $temperature < 0.7){
return Biome::SWAMP;
}elseif($temperature < 0.50){
return Biome::TAIGA;
}elseif($temperature < 0.97){
if($rainfall < 0.35){
return Biome::MOUNTAINS;
}else {
return Biome::RIVER;
}
}else{
if($rainfall < 0.45){
return Biome::PLAINS;
}elseif($rainfall < 0.90){
return Biome::FOREST;
}else{
return Biome::BIRCH_FOREST;
}
}
}, 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();
$cover = new GroundCover(); $cover = new GroundCover();
$this->generationPopulators[] = $cover; $this->generationPopulators[] = $cover;
@ -127,7 +170,7 @@ class Normal extends Generator{
public function generateChunk($chunkX, $chunkZ){ public function generateChunk($chunkX, $chunkZ){
$this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed()); $this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed());
$noise = Generator::getFastNoise3D($this->noiseBase, 16, 128, 16, 8, 8, 8, $chunkX * 16, 0, $chunkZ * 16); $noise = Generator::getFastNoise3D($this->noiseBase, 16, 128, 16, 4, 8, 4, $chunkX * 16, 0, $chunkZ * 16);
$chunk = $this->level->getChunk($chunkX, $chunkZ); $chunk = $this->level->getChunk($chunkX, $chunkZ);
@ -139,16 +182,16 @@ class Normal extends Generator{
$biome = $this->selector->pickBiome($chunkX * 16 + $x, $chunkZ * 16 + $z); $biome = $this->selector->pickBiome($chunkX * 16 + $x, $chunkZ * 16 + $z);
$chunk->setBiomeId($x, $z, $biome->getId()); $chunk->setBiomeId($x, $z, $biome->getId());
$chunk->setBiomeColor($x, $z, 255 - (($biome->getId() * 1377) % 255), 255 - (($biome->getId() * 4096) % 255), 255 - (($biome->getId() * 31337) % 255)); $color = $biome->getColor();
$chunk->setBiomeColor($x, $z, $color >> 16, ($color >> 8) & 0xff, $color & 0xff);
for($sx = -self::$SMOOTH_SIZE; $sx <= self::$SMOOTH_SIZE; ++$sx){ for($sx = -self::$SMOOTH_SIZE; $sx <= self::$SMOOTH_SIZE; ++$sx){
for($sz = -self::$SMOOTH_SIZE; $sz <= self::$SMOOTH_SIZE; ++$sz){ for($sz = -self::$SMOOTH_SIZE; $sz <= self::$SMOOTH_SIZE; ++$sz){
if($sx === 0 and $sz === 0){ if($sx === 0 and $sz === 0){
$adjacent = $biome; continue;
}else{ }else{
$adjacent = $this->selector->pickBiome($chunkX * 16 + $x + $sx * 16, $chunkZ * 16 + $z + $sz * 16); $adjacent = $this->selector->pickBiome($chunkX * 16 + $x + $sx * 8, $chunkZ * 16 + $z + $sz * 8);
} }
/** @var NormalBiome $adjacent */
$weight = self::$GAUSSIAN_KERNEL[$sx + self::$SMOOTH_SIZE][$sz + self::$SMOOTH_SIZE]; $weight = self::$GAUSSIAN_KERNEL[$sx + self::$SMOOTH_SIZE][$sz + self::$SMOOTH_SIZE];
$minSum += $adjacent->getMinElevation() * $weight; $minSum += $adjacent->getMinElevation() * $weight;
$maxSum += $adjacent->getMaxElevation() * $weight; $maxSum += $adjacent->getMaxElevation() * $weight;

View File

@ -28,6 +28,9 @@ class DesertBiome extends SandyBiome{
public function __construct(){ public function __construct(){
parent::__construct(); parent::__construct();
$this->setElevation(63, 74); $this->setElevation(63, 74);
$this->temperature = 2;
$this->rainfall = 0;
} }
public function getName(){ public function getName(){

View File

@ -21,15 +21,23 @@
namespace pocketmine\level\generator\normal\biome; namespace pocketmine\level\generator\normal\biome;
use pocketmine\block\Sapling;
use pocketmine\level\generator\populator\TallGrass; use pocketmine\level\generator\populator\TallGrass;
use pocketmine\level\generator\populator\Tree; use pocketmine\level\generator\populator\Tree;
class ForestBiome extends GrassyBiome{ class ForestBiome extends GrassyBiome{
public function __construct(){ const TYPE_NORMAL = 0;
const TYPE_BIRCH = 1;
public $type;
public function __construct($type = self::TYPE_NORMAL){
parent::__construct(); parent::__construct();
$trees = new Tree(); $this->type = $type;
$trees = new Tree($type === self::TYPE_BIRCH ? Sapling::BIRCH : Sapling::OAK);
$trees->setBaseAmount(5); $trees->setBaseAmount(5);
$this->addPopulator($trees); $this->addPopulator($trees);
@ -39,9 +47,21 @@ class ForestBiome extends GrassyBiome{
$this->addPopulator($tallGrass); $this->addPopulator($tallGrass);
$this->setElevation(63, 81); $this->setElevation(63, 81);
if($type === self::TYPE_BIRCH){
$this->temperature = 0.5;
$this->rainfall = 0.5;
}else{
$this->temperature = 0.7;
$this->temperature = 0.8;
}
} }
public function getName(){ public function getName(){
return "Forest"; return $this->type === self::TYPE_BIRCH ? "Birch Forest" : "Forest";
}
public function getColor(){
return 0x056621;
} }
} }

View File

@ -0,0 +1,49 @@
<?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/
*
*
*/
namespace pocketmine\level\generator\normal\biome;
use pocketmine\level\generator\populator\TallGrass;
class IcePlainsBiome extends SnowyBiome{
public function __construct(){
parent::__construct();
$tallGrass = new TallGrass();
$tallGrass->setBaseAmount(5);
$this->addPopulator($tallGrass);
$this->setElevation(63, 74);
$this->temperature = 0.05;
$this->rainfall = 0.8;
}
public function getName(){
return "Ice Plains";
}
public function getColor(){
return 0x163933;
}
}

View File

@ -41,6 +41,9 @@ class MountainsBiome extends GrassyBiome{
//TODO: add emerald //TODO: add emerald
$this->setElevation(63, 127); $this->setElevation(63, 127);
$this->temperature = 0.4;
$this->rainfall = 0.5;
} }
public function getName(){ public function getName(){

View File

@ -24,5 +24,7 @@ namespace pocketmine\level\generator\normal\biome;
use pocketmine\level\generator\biome\Biome; use pocketmine\level\generator\biome\Biome;
abstract class NormalBiome extends Biome{ abstract class NormalBiome extends Biome{
public function getColor(){
return 0xffb360; //Detect wrong biomes
}
} }

View File

@ -34,9 +34,16 @@ class OceanBiome extends GrassyBiome{
$this->addPopulator($tallGrass); $this->addPopulator($tallGrass);
$this->setElevation(46, 58); $this->setElevation(46, 58);
$this->temperature = 0.5;
$this->rainfall = 0.5;
} }
public function getName(){ public function getName(){
return "Ocean"; return "Ocean";
} }
public function getColor(){
return 0x8da360;
}
} }

View File

@ -29,14 +29,21 @@ class PlainBiome extends GrassyBiome{
parent::__construct(); parent::__construct();
$tallGrass = new TallGrass(); $tallGrass = new TallGrass();
$tallGrass->setBaseAmount(5); $tallGrass->setBaseAmount(12);
$this->addPopulator($tallGrass); $this->addPopulator($tallGrass);
$this->setElevation(63, 74); $this->setElevation(63, 74);
$this->temperature = 0.8;
$this->rainfall = 0.4;
} }
public function getName(){ public function getName(){
return "Plains"; return "Plains";
} }
public function getColor(){
return 0x8db360;
}
} }

View File

@ -34,9 +34,16 @@ class RiverBiome extends GrassyBiome{
$this->addPopulator($tallGrass); $this->addPopulator($tallGrass);
$this->setElevation(58, 62); $this->setElevation(58, 62);
$this->temperature = 0.5;
$this->rainfall = 0.7;
} }
public function getName(){ public function getName(){
return "River"; return "River";
} }
public function getColor(){
return 0x8dc360;
}
} }

View File

@ -0,0 +1,37 @@
<?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/
*
*
*/
namespace pocketmine\level\generator\normal\biome;
use pocketmine\block\Block;
abstract class SnowyBiome extends NormalBiome{
public function __construct(){
$this->setGroundCover([
Block::get(Block::SNOW_LAYER, 0),
Block::get(Block::GRASS, 0),
Block::get(Block::DIRT, 0),
Block::get(Block::DIRT, 0),
Block::get(Block::DIRT, 0),
]);
}
}

View File

@ -21,15 +21,22 @@
namespace pocketmine\level\generator\normal\biome; namespace pocketmine\level\generator\normal\biome;
class BeachBiome extends GrassyBiome{ class SwampBiome extends GrassyBiome{
public function __construct(){ public function __construct(){
parent::__construct(); parent::__construct();
$this->setElevation(62, 65); $this->setElevation(62, 63);
$this->temperature = 0.8;
$this->rainfall = 0.9;
} }
public function getName(){ public function getName(){
return "Beach"; return "Swamp";
}
public function getColor(){
return 0x07f9b2;
} }
} }

View File

@ -0,0 +1,55 @@
<?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/
*
*
*/
namespace pocketmine\level\generator\normal\biome;
use pocketmine\block\Sapling;
use pocketmine\level\generator\populator\TallGrass;
use pocketmine\level\generator\populator\Tree;
class TaigaBiome extends SnowyBiome{
public function __construct(){
parent::__construct();
$trees = new Tree(Sapling::SPRUCE);
$trees->setBaseAmount(10);
$this->addPopulator($trees);
$tallGrass = new TallGrass();
$tallGrass->setBaseAmount(1);
$this->addPopulator($tallGrass);
$this->setElevation(63, 81);
$this->temperature = 0.05;
$this->rainfall = 0.8;
}
public function getName(){
return "Taiga";
}
public function getColor(){
return 0x0b6659;
}
}

View File

@ -21,6 +21,7 @@
namespace pocketmine\level\generator\object; namespace pocketmine\level\generator\object;
use pocketmine\block\Block;
use pocketmine\block\Sapling; use pocketmine\block\Sapling;
use pocketmine\level\ChunkManager; use pocketmine\level\ChunkManager;
use pocketmine\utils\Random; use pocketmine\utils\Random;
@ -33,6 +34,9 @@ class Tree{
6 => true, 6 => true,
17 => true, 17 => true,
18 => true, 18 => true,
Block::SNOW_LAYER => true,
Block::LOG2 => true,
Block::LEAVES2 => true
]; ];
public static function growTree(ChunkManager $level, $x, $y, $z, Random $random, $type = 0){ public static function growTree(ChunkManager $level, $x, $y, $z, Random $random, $type = 0){

View File

@ -35,16 +35,24 @@ class GroundCover extends Populator{
$biome = Biome::getBiome($chunk->getBiomeId($x, $z)); $biome = Biome::getBiome($chunk->getBiomeId($x, $z));
$cover = $biome->getGroundCover(); $cover = $biome->getGroundCover();
if(count($cover) > 0){ if(count($cover) > 0){
$diffY = 0;
if(!$cover[0]->isSolid()){
$diffY = 1;
}
$column = $chunk->getBlockIdColumn($x, $z); $column = $chunk->getBlockIdColumn($x, $z);
for($y = 127; $y > 0; --$y){ for($y = 127; $y > 0; --$y){
if($column{$y} !== "\x00" and Block::get(ord($column{$y}))->isSolid()){ if($column{$y} !== "\x00" and !Block::get(ord($column{$y}))->isTransparent()){
break; break;
} }
} }
$startY = $y; $startY = min(127, $y + $diffY);
$endY = $startY - count($cover); $endY = $startY - count($cover);
for($y = $startY; $y > $endY and $y >= 0; --$y){ for($y = $startY; $y > $endY and $y >= 0; --$y){
$b = $cover[$startY - $y]; $b = $cover[$startY - $y];
if($column{$y} === "\x00" and $b->isSolid()){
break;
}
if($b->getDamage() === 0){ if($b->getDamage() === 0){
$chunk->setBlockId($x, $y, $z, $b->getId()); $chunk->setBlockId($x, $y, $z, $b->getId());
}else{ }else{

View File

@ -43,37 +43,30 @@ class TallGrass extends Populator{
$this->level = $level; $this->level = $level;
$amount = $random->nextRange(0, $this->randomAmount + 1) + $this->baseAmount; $amount = $random->nextRange(0, $this->randomAmount + 1) + $this->baseAmount;
for($i = 0; $i < $amount; ++$i){ for($i = 0; $i < $amount; ++$i){
$x = $random->nextRange($chunkX << 4, ($chunkX << 4) + 15); $x = $random->nextRange($chunkX * 16, $chunkX * 16 + 15);
$z = $random->nextRange($chunkZ << 4, ($chunkZ << 4) + 15); $z = $random->nextRange($chunkZ * 16, $chunkZ * 16 + 15);
for($size = 30; $size > 0; --$size){ $y = $this->getHighestWorkableBlock($x, $z);
$xx = $x - 7 + $random->nextRange(0, 15);
$zz = $z - 7 + $random->nextRange(0, 15);
$yy = $this->getHighestWorkableBlock($xx, $zz);
if($yy !== -1 and $this->canTallGrassStay($xx, $yy, $zz)){ if($y !== -1 and $this->canTallGrassStay($x, $y, $z)){
$this->level->setBlockIdAt($xx, $yy, $zz, Block::TALL_GRASS); $this->level->setBlockIdAt($x, $y, $z, Block::TALL_GRASS);
$this->level->setBlockDataAt($xx, $yy, $zz, 1); $this->level->setBlockDataAt($x, $y, $z, 1);
}
} }
} }
} }
private function canTallGrassStay($x, $y, $z){ private function canTallGrassStay($x, $y, $z){
return $this->level->getBlockIdAt($x, $y, $z) === Block::AIR and $this->level->getBlockIdAt($x, $y - 1, $z) === Block::GRASS; $b = $this->level->getBlockIdAt($x, $y, $z);
return ($b === Block::AIR or $b === Block::SNOW_LAYER) and $this->level->getBlockIdAt($x, $y - 1, $z) === Block::GRASS;
} }
private function getHighestWorkableBlock($x, $z){ private function getHighestWorkableBlock($x, $z){
for($y = 128; $y > 0; --$y){ for($y = 127; $y >= 0; --$y){
$b = $this->level->getBlockIdAt($x, $y, $z); $b = $this->level->getBlockIdAt($x, $y, $z);
if($b === Block::AIR or $b === Block::LEAVES){ if($b !== Block::AIR and $b !== Block::LEAVES and $b !== Block::SNOW_LAYER){
if(--$y <= 0){
return -1;
}
}else{
break; break;
} }
} }
return ++$y; return $y === 0 ? -1 : ++$y;
} }
} }

View File

@ -33,6 +33,12 @@ class Tree extends Populator{
private $randomAmount; private $randomAmount;
private $baseAmount; private $baseAmount;
private $type;
public function __construct($type = Sapling::OAK){
$this->type = $type;
}
public function setRandomAmount($amount){ public function setRandomAmount($amount){
$this->randomAmount = $amount; $this->randomAmount = $amount;
} }
@ -51,24 +57,17 @@ class Tree extends Populator{
if($y === -1){ if($y === -1){
continue; continue;
} }
if($random->nextFloat() > 0.75){ ObjectTree::growTree($this->level, $x, $y, $z, $random, $this->type);
$meta = Sapling::BIRCH;
}else{
$meta = Sapling::OAK;
}
ObjectTree::growTree($this->level, $x, $y, $z, $random, $meta);
} }
} }
private function getHighestWorkableBlock($x, $z){ private function getHighestWorkableBlock($x, $z){
for($y = 128; $y > 0; --$y){ for($y = 127; $y > 0; --$y){
$b = $this->level->getBlockIdAt($x, $y, $z); $b = $this->level->getBlockIdAt($x, $y, $z);
if($b !== Block::DIRT and $b !== Block::GRASS){ if($b === Block::DIRT or $b === Block::GRASS){
if(--$y <= 0){
return -1;
}
}else{
break; break;
}elseif($b !== 0 and $b !== Block::SNOW_LAYER){
return -1;
} }
} }

@ -1 +1 @@
Subproject commit 5ad5b3535c9519b2e013a61a2d55fccae014e037 Subproject commit 3db41e1266811b20c3f52f749265f6c9d416c1bb