2017-04-23 14:42:51 +00:00
< ? php
2017-05-10 15:03:04 +00:00
/**
* ____ __ __ ____
* / \ _ `\ /\ \__ /\ \__ /\ _` \
* \ \ \L\ \ __ \ \ , _\\ \ , _\ __ _ __ \ \ \L\_\ __ ___
* \ \ _ < ' /' __ `\\ \ \/ \ \ \/ /'__` \ / \ `'__\\ \ \L_L /'__` \ / ' _ ` \
* \ \ \L\ \ / \ __ / \ \ \_ \ \ \_ / \ __ / \ \ \ / \ \ \ / , \ / \ __ / / \ \ / \ \
* \ \____ / \ \____\ \ \__\ \ \__\\ \____\\ \_\ \ \____ / \ \____\\ \_\ \_\
* \ / ___ / \ / ____ / \ / __ / \ / __ / \ / ____ / \ / _ / \ / ___ / \ / ____ / \ / _ / \ / _ /
2017-05-11 11:29:22 +00:00
* Tomorrow ' s pocketmine generator .
2017-05-10 15:03:04 +00:00
* @ author Ad5001
* @ link https :// github . com / Ad5001 / BetterGen
*/
2017-04-23 14:42:51 +00:00
namespace Ad5001\BetterGen\generator ;
2017-05-11 12:07:26 +00:00
use Ad5001\BetterGen\biome\BetterDesert ;
use Ad5001\BetterGen\biome\BetterForest ;
use Ad5001\BetterGen\biome\BetterIcePlains ;
use Ad5001\BetterGen\biome\BetterMesa ;
use Ad5001\BetterGen\biome\BetterMesaPlains ;
use Ad5001\BetterGen\biome\BetterRiver ;
use Ad5001\BetterGen\biome\Mountainable ;
use Ad5001\BetterGen\Main ;
use Ad5001\BetterGen\populator\CavePopulator ;
use Ad5001\BetterGen\populator\FloatingIslandPopulator ;
use Ad5001\BetterGen\populator\MineshaftPopulator ;
use Ad5001\BetterGen\populator\RavinePopulator ;
2017-04-23 14:42:51 +00:00
use pocketmine\block\Block ;
use pocketmine\block\CoalOre ;
use pocketmine\block\DiamondOre ;
use pocketmine\block\Dirt ;
use pocketmine\block\GoldOre ;
use pocketmine\block\Gravel ;
use pocketmine\block\IronOre ;
use pocketmine\block\LapisOre ;
use pocketmine\block\RedstoneOre ;
2017-05-11 12:07:26 +00:00
use pocketmine\level\ChunkManager ;
use pocketmine\level\generator\biome\Biome ;
use pocketmine\level\generator\Generator ;
use pocketmine\level\generator\noise\Simplex ;
use pocketmine\level\generator\normal\object\OreType as OreType2 ;
use pocketmine\level\generator\object\OreType ;
2017-04-23 14:42:51 +00:00
use pocketmine\level\Level ;
2017-05-11 12:07:26 +00:00
use pocketmine\math\Vector3 ;
use pocketmine\utils\Random ;
2017-04-23 14:42:51 +00:00
class BetterNormal extends Generator {
2017-05-11 11:29:22 +00:00
const NOT_OVERWRITABLE = [
2017-04-23 14:42:51 +00:00
Block :: STONE ,
Block :: GRAVEL ,
Block :: BEDROCK ,
Block :: DIAMOND_ORE ,
Block :: GOLD_ORE ,
Block :: LAPIS_ORE ,
Block :: REDSTONE_ORE ,
Block :: IRON_ORE ,
Block :: COAL_ORE ,
Block :: WATER ,
Block :: STILL_WATER
];
2017-05-11 12:07:26 +00:00
/** @var BetterBiomeSelector */
2017-04-23 14:42:51 +00:00
protected $selector ;
2017-05-11 09:49:57 +00:00
/** @var Level */
2017-04-23 14:42:51 +00:00
protected $level ;
2017-05-11 12:07:26 +00:00
/** @var Random */
2017-04-23 14:42:51 +00:00
protected $random ;
protected $populators = [ ];
protected $generationPopulators = [ ];
public static $biomes = [ ];
2017-05-11 11:29:22 +00:00
/** @var Biome[] */
2017-04-23 14:42:51 +00:00
public static $biomeById = [ ];
public static $levels = [ ];
protected static $GAUSSIAN_KERNEL = null ; // From main class
protected static $SMOOTH_SIZE = 2 ;
2017-05-10 15:03:04 +00:00
protected static $options = [
" deleteBiomes " => [
]
];
2017-04-23 14:42:51 +00:00
protected $waterHeight = 63 ;
2017-05-11 12:07:26 +00:00
private $noiseBase ;
2017-04-23 14:42:51 +00:00
/*
* Picks a biome by X and Z
* @ param $x int
* @ param $z int
* @ return Biome
*/
public function pickBiome ( $x , $z ) {
$hash = $x * 2345803 ^ $z * 9236449 ^ $this -> level -> getSeed ();
$hash *= $hash + 223 ;
$xNoise = $hash >> 20 & 3 ;
$zNoise = $hash >> 22 & 3 ;
if ( $xNoise == 3 ) {
$xNoise = 1 ;
}
if ( $zNoise == 3 ) {
$zNoise = 1 ;
}
2017-04-29 09:59:44 +00:00
$b = $this -> selector -> pickBiome ( $x + $xNoise - 1 , $z + $zNoise - 1 );
if ( $b instanceof Mountainable && $this -> random -> nextBoundedInt ( 1000 ) < 3 ) {
2017-04-23 14:42:51 +00:00
$b = clone $b ;
2017-04-29 09:59:44 +00:00
$b -> setElevation ( $b -> getMinElevation () + ( 50 * $b -> getMinElevation () / 100 ), $b -> getMaxElevation () + ( 50 * $b -> getMinElevation () / 100 ));
2017-04-23 14:42:51 +00:00
}
return $b ;
}
2017-05-10 15:03:04 +00:00
/**
* Inits the class for the var
* @ param ChunkManager $level
* @ param Random $random
* @ return void
*/
2017-04-23 14:42:51 +00:00
public function init ( ChunkManager $level , Random $random ) {
$this -> level = $level ;
$this -> random = $random ;
self :: $levels [] = $level ;
2017-04-29 09:59:44 +00:00
$this -> random -> setSeed ( $this -> level -> getSeed ());
$this -> noiseBase = new Simplex ( $this -> random , 4 , 1 / 4 , 1 / 32 );
$this -> random -> setSeed ( $this -> level -> getSeed ());
2017-04-23 14:42:51 +00:00
2017-04-29 09:59:44 +00:00
$this -> registerBiome ( Biome :: getBiome ( Biome :: OCEAN ));
$this -> registerBiome ( Biome :: getBiome ( Biome :: PLAINS ));
$this -> registerBiome ( new BetterDesert ());
$this -> registerBiome ( new BetterMesa ());
$this -> registerBiome ( new BetterMesaPlains ());
$this -> registerBiome ( Biome :: getBiome ( Biome :: TAIGA ));
$this -> registerBiome ( Biome :: getBiome ( Biome :: SWAMP ));
$this -> registerBiome ( new BetterRiver ());
$this -> registerBiome ( new BetterIcePlains ());
$this -> registerBiome ( new BetterForest ( 0 , [
2017-04-23 14:42:51 +00:00
0.6 ,
0.5
2017-04-29 09:59:44 +00:00
] ));
$this -> registerBiome ( new BetterForest ( 1 , [
2017-04-23 14:42:51 +00:00
0.7 ,
0.8
2017-04-29 09:59:44 +00:00
] ));
$this -> registerBiome ( new BetterForest ( 2 , [
2017-04-23 14:42:51 +00:00
0.6 ,
0.4
2017-04-29 09:59:44 +00:00
] ));
2017-04-23 14:42:51 +00:00
2017-04-29 09:59:44 +00:00
$this -> selector = new BetterBiomeSelector ( $random , [
2017-04-23 14:42:51 +00:00
self :: class ,
" getBiome "
2017-04-29 09:59:44 +00:00
], self :: getBiome ( 0 , 0 ));
2017-04-23 14:42:51 +00:00
2017-04-29 09:59:44 +00:00
foreach ( self :: $biomes as $rain ) {
foreach ( $rain as $biome ) {
$this -> selector -> addBiome ( $biome );
2017-04-23 14:42:51 +00:00
}
}
$this -> selector -> recalculate ();
2017-05-01 10:03:56 +00:00
$cover = Main :: isOtherNS () ? new \pocketmine\level\generator\normal\populator\GroundCover () : new \pocketmine\level\generator\populator\GroundCover ();
2017-04-23 14:42:51 +00:00
$this -> generationPopulators [] = $cover ;
2017-05-10 15:03:04 +00:00
// https://twitter.com/Ad5001P4F/status/859430935468670976
// $lake = new LakePopulator ();
// $lake->setBaseAmount(0);
// $lake->setRandomAmount(1);
// $this->generationPopulators [] = $lake;
2017-04-23 14:42:51 +00:00
$cave = new CavePopulator ();
2017-04-29 09:59:44 +00:00
$cave -> setBaseAmount ( 0 );
$cave -> setRandomAmount ( 2 );
2017-05-10 15:03:04 +00:00
$this -> generationPopulators [] = $cave ;
2017-04-23 14:42:51 +00:00
$ravine = new RavinePopulator ();
2017-04-29 09:59:44 +00:00
$ravine -> setBaseAmount ( 0 );
$ravine -> setRandomAmount ( 51 );
2017-05-11 06:52:55 +00:00
$this -> generationPopulators [] = $ravine ;
2017-04-23 14:42:51 +00:00
$mineshaft = new MineshaftPopulator ();
2017-04-29 09:59:44 +00:00
$mineshaft -> setBaseAmount ( 0 );
$mineshaft -> setRandomAmount ( 102 );
2017-04-23 14:42:51 +00:00
$this -> populators [] = $mineshaft ;
2017-05-02 17:52:53 +00:00
2017-04-23 14:42:51 +00:00
$fisl = new FloatingIslandPopulator ();
2017-04-29 09:59:44 +00:00
$fisl -> setBaseAmount ( 0 );
$fisl -> setRandomAmount ( 132 );
2017-04-23 14:42:51 +00:00
$this -> populators [] = $fisl ;
2017-05-01 10:03:56 +00:00
$ores = Main :: isOtherNS () ? new \pocketmine\level\generator\normal\populator\Ore () : new \pocketmine\level\generator\populator\Ore ();
if ( Main :: isOtherNS ()) $ores -> setOreTypes ([
new OreType2 ( new CoalOre (), 20 , 16 , 0 , 128 ),
new OreType2 ( new IronOre (), 20 , 8 , 0 , 64 ),
new OreType2 ( new RedstoneOre (), 8 , 7 , 0 , 16 ),
new OreType2 ( new LapisOre (), 1 , 6 , 0 , 32 ),
new OreType2 ( new GoldOre (), 2 , 8 , 0 , 32 ),
new OreType2 ( new DiamondOre (), 1 , 7 , 0 , 16 ),
new OreType2 ( new Dirt (), 20 , 32 , 0 , 128 ),
new OreType2 ( new Gravel (), 10 , 16 , 0 , 128 )
]);
if ( ! Main :: isOtherNS ()) $ores -> setOreTypes ([
2017-04-29 09:59:44 +00:00
new OreType ( new CoalOre (), 20 , 16 , 0 , 128 ),
new OreType ( new IronOre (), 20 , 8 , 0 , 64 ),
new OreType ( new RedstoneOre (), 8 , 7 , 0 , 16 ),
new OreType ( new LapisOre (), 1 , 6 , 0 , 32 ),
new OreType ( new GoldOre (), 2 , 8 , 0 , 32 ),
new OreType ( new DiamondOre (), 1 , 7 , 0 , 16 ),
new OreType ( new Dirt (), 20 , 32 , 0 , 128 ),
new OreType ( new Gravel (), 10 , 16 , 0 , 128 )
]);
2017-04-23 14:42:51 +00:00
$this -> populators [] = $ores ;
}
/*
* Adds a biome to the selector . Do not use this method directly use Main :: registerBiome which registers it properly
* @ param $biome Biome
* @ return bool
*/
public static function registerBiome ( Biome $biome ) : bool {
2017-05-10 15:03:04 +00:00
if ( \Ad5001\BetterGen\utils\CommonUtils :: in_arrayi ( $biome -> getName (), self :: $options [ " deleteBiomes " ])) {
return false ;
}
foreach ( self :: $levels as $lvl ) if ( isset ( $lvl -> selector )) $lvl -> selector -> addBiome ( $biome ); // If no selector created, it would cause errors. These will be added when selectoes
if ( ! isset ( self :: $biomes [( string ) $biome -> getRainfall ()] )) self :: $biomes [( string ) $biome -> getRainfall ()] = [ ];
2017-04-23 14:42:51 +00:00
self :: $biomes [( string ) $biome -> getRainfall ()] [( string ) $biome -> getTemperature ()] = $biome ;
2017-04-29 09:59:44 +00:00
ksort ( self :: $biomes [( string ) $biome -> getRainfall ()]);
ksort ( self :: $biomes );
2017-04-23 14:42:51 +00:00
self :: $biomeById [ $biome -> getId ()] = $biome ;
return true ;
}
/*
* Returns a biome by temperature
* @ param $temperature float
* @ param $rainfall float
*/
public static function getBiome ( $temperature , $rainfall ) {
2017-05-11 11:29:22 +00:00
$ret = null ;
2017-04-29 09:59:44 +00:00
if ( ! isset ( self :: $biomes [( string ) round ( $rainfall , 1 )] )) {
while ( ! isset ( self :: $biomes [( string ) round ( $rainfall , 1 )] ) ) {
if ( abs ( $rainfall - round ( $rainfall , 1 ) ) >= 0.05 )
2017-04-23 14:42:51 +00:00
$rainfall += 0.1 ;
2017-04-29 09:59:44 +00:00
if ( abs ( $rainfall - round ( $rainfall , 1 ) ) < 0.05 )
2017-04-23 14:42:51 +00:00
$rainfall -= 0.1 ;
2017-04-29 09:59:44 +00:00
if ( round ( $rainfall , 1 ) < 0 )
2017-04-23 14:42:51 +00:00
$rainfall = 0 ;
2017-04-29 09:59:44 +00:00
if ( round ( $rainfall , 1 ) >= 0.9 )
2017-04-23 14:42:51 +00:00
$rainfall = 0.9 ;
}
}
2017-04-29 09:59:44 +00:00
$b = self :: $biomes [( string ) round ( $rainfall , 1 )];
foreach ( $b as $t => $biome ) {
if ( $temperature <= ( float ) $t ) {
2017-04-23 14:42:51 +00:00
$ret = $biome ;
break ;
}
}
2017-04-29 09:59:44 +00:00
if ( is_string ( $ret )) {
2017-04-23 14:42:51 +00:00
$ret = new $ret ();
}
2017-05-11 11:29:22 +00:00
return $ret ;
2017-04-23 14:42:51 +00:00
}
/*
* Returns a biome by its id
* @ param $id int
* @ return Biome
*/
public function getBiomeById ( int $id ) : Biome {
2017-05-11 11:29:22 +00:00
return self :: $biomeById [ $id ] ? ? self :: $biomeById [ Biome :: OCEAN ];
2017-04-23 14:42:51 +00:00
}
/*
* Generates a chunk .
* Cloning method to make it work with new methods .
* @ param $chunkX int
* @ param $chunkZ int
*/
public function generateChunk ( $chunkX , $chunkZ ) {
2017-04-29 09:59:44 +00:00
$this -> random -> setSeed ( 0xdeadbeef ^ ( $chunkX << 8 ) ^ $chunkZ ^ $this -> level -> getSeed ());
2017-04-23 14:42:51 +00:00
2017-04-29 09:59:44 +00:00
$noise = Generator :: getFastNoise3D ( $this -> noiseBase , 16 , 128 , 16 , 4 , 8 , 4 , $chunkX * 16 , 0 , $chunkZ * 16 );
2017-04-23 14:42:51 +00:00
2017-04-29 09:59:44 +00:00
$chunk = $this -> level -> getChunk ( $chunkX , $chunkZ );
2017-04-23 14:42:51 +00:00
$biomeCache = [ ];
2017-04-29 09:59:44 +00:00
for ( $x = 0 ; $x < 16 ; $x ++ ) {
for ( $z = 0 ; $z < 16 ; $z ++ ) {
2017-04-23 14:42:51 +00:00
$minSum = 0 ;
$maxSum = 0 ;
$weightSum = 0 ;
2017-04-29 09:59:44 +00:00
$biome = $this -> pickBiome ( $chunkX * 16 + $x , $chunkZ * 16 + $z );
$chunk -> setBiomeId ( $x , $z , $biome -> getId ());
2017-04-23 14:42:51 +00:00
2017-04-29 09:59:44 +00:00
for ( $sx = - self :: $SMOOTH_SIZE ; $sx <= self :: $SMOOTH_SIZE ; $sx ++ ) {
for ( $sz = - self :: $SMOOTH_SIZE ; $sz <= self :: $SMOOTH_SIZE ; $sz ++ ) {
2017-04-23 14:42:51 +00:00
$weight = self :: $GAUSSIAN_KERNEL [ $sx + self :: $SMOOTH_SIZE ] [ $sz + self :: $SMOOTH_SIZE ];
if ( $sx === 0 and $sz === 0 ) {
$adjacent = $biome ;
} else {
2017-04-29 09:59:44 +00:00
$index = Level :: chunkHash ( $chunkX * 16 + $x + $sx , $chunkZ * 16 + $z + $sz );
if ( isset ( $biomeCache [ $index ] )) {
2017-04-23 14:42:51 +00:00
$adjacent = $biomeCache [ $index ];
} else {
2017-04-29 09:59:44 +00:00
$biomeCache [ $index ] = $adjacent = $this -> pickBiome ( $chunkX * 16 + $x + $sx , $chunkZ * 16 + $z + $sz );
2017-04-23 14:42:51 +00:00
}
}
$minSum += ( $adjacent -> getMinElevation () - 1 ) * $weight ;
$maxSum += $adjacent -> getMaxElevation () * $weight ;
$weightSum += $weight ;
}
}
$minSum /= $weightSum ;
$maxSum /= $weightSum ;
$smoothHeight = ( $maxSum - $minSum ) / 2 ;
2017-04-29 09:59:44 +00:00
for ( $y = 0 ; $y < 128 ; $y ++ ) {
2017-04-23 14:42:51 +00:00
if ( $y < 3 || ( $y < 5 && $this -> random -> nextBoolean ())) {
2017-04-29 09:59:44 +00:00
$chunk -> setBlockId ( $x , $y , $z , Block :: BEDROCK );
2017-04-23 14:42:51 +00:00
continue ;
}
$noiseValue = $noise [ $x ] [ $z ] [ $y ] - 1 / $smoothHeight * ( $y - $smoothHeight - $minSum );
if ( $noiseValue > 0 ) {
2017-04-29 09:59:44 +00:00
$chunk -> setBlockId ( $x , $y , $z , Block :: STONE );
2017-04-23 14:42:51 +00:00
} elseif ( $y <= $this -> waterHeight ) {
2017-04-29 09:59:44 +00:00
$chunk -> setBlockId ( $x , $y , $z , Block :: STILL_WATER );
2017-04-23 14:42:51 +00:00
}
}
}
}
2017-04-29 09:59:44 +00:00
foreach ( $this -> generationPopulators as $populator ) {
$populator -> populate ( $this -> level , $chunkX , $chunkZ , $this -> random );
2017-04-23 14:42:51 +00:00
}
}
/*
* Populates a chunk .
* @ param $chunkX int
* @ param $chunk2 int
*/
public function populateChunk ( $chunkX , $chunkZ ) {
2017-04-29 09:59:44 +00:00
$this -> random -> setSeed ( 0xdeadbeef ^ ( $chunkX << 8 ) ^ $chunkZ ^ $this -> level -> getSeed ());
foreach ( $this -> populators as $populator ) {
$populator -> populate ( $this -> level , $chunkX , $chunkZ , $this -> random );
2017-04-23 14:42:51 +00:00
}
// Filling lava (lakes & rivers underground)...
for ( $x = $chunkX ; $x < $chunkX + 16 ; $x ++ )
for ( $z = $chunkZ ; $z < $chunkZ + 16 ; $z ++ )
for ( $y = 1 ; $y < 11 ; $y ++ ) {
2017-04-29 09:59:44 +00:00
if ( ! in_array ( $this -> level -> getBlockIdAt ( $x , $y , $z ), self :: NOT_OVERWRITABLE ))
$this -> level -> setBlockIdAt ( $x , $y , $z , Block :: LAVA );
2017-04-23 14:42:51 +00:00
}
2017-04-29 09:59:44 +00:00
$chunk = $this -> level -> getChunk ( $chunkX , $chunkZ );
2017-05-10 15:03:04 +00:00
$biome = self :: getBiomeById ( $chunk -> getBiomeId ( 7 , 7 ));
2017-04-29 09:59:44 +00:00
$biome -> populateChunk ( $this -> level , $chunkX , $chunkZ , $this -> random );
2017-04-23 14:42:51 +00:00
}
/*
* Constructs the class
* @ param $options array
*/
public function __construct ( array $options = []) {
2017-05-10 15:03:04 +00:00
self :: $options [ " preset " ] = $options [ " preset " ];
$options = ( array ) json_decode ( $options [ " preset " ]);
if ( isset ( $options [ " deleteBiomes " ]) && is_string ( $options [ " deleteBiomes " ])) {
$options [ " deleteBiomes " ] = explode ( " , " , $options [ " deleteBiomes " ]);
if ( count ( $options [ " deleteBiomes " ]) !== 0 ) {
self :: $options [ " deleteBiomes " ] = $options [ " deleteBiomes " ];
}
}
if ( isset ( $options [ " deleteBiomes " ]) && count ( $options [ " deleteBiomes " ]) !== 0 ) {
self :: $options [ " deleteBiomes " ] = $options [ " deleteBiomes " ];
}
2017-04-23 14:42:51 +00:00
if ( self :: $GAUSSIAN_KERNEL === null ) {
self :: generateKernel ();
}
}
/*
2017-05-11 11:29:22 +00:00
* Generates the generation kernel based on smooth size ( here 2 )
2017-04-23 14:42:51 +00:00
*/
private static function generateKernel () {
self :: $GAUSSIAN_KERNEL = [ ];
$bellSize = 1 / self :: $SMOOTH_SIZE ;
$bellHeight = 2 * self :: $SMOOTH_SIZE ;
2017-04-29 09:59:44 +00:00
for ( $sx = - self :: $SMOOTH_SIZE ; $sx <= self :: $SMOOTH_SIZE ; $sx ++ ) {
2017-04-23 14:42:51 +00:00
self :: $GAUSSIAN_KERNEL [ $sx + self :: $SMOOTH_SIZE ] = [ ];
2017-04-29 09:59:44 +00:00
for ( $sz = - self :: $SMOOTH_SIZE ; $sz <= self :: $SMOOTH_SIZE ; $sz ++ ) {
2017-04-23 14:42:51 +00:00
$bx = $bellSize * $sx ;
$bz = $bellSize * $sz ;
2017-04-29 09:59:44 +00:00
self :: $GAUSSIAN_KERNEL [ $sx + self :: $SMOOTH_SIZE ] [ $sz + self :: $SMOOTH_SIZE ] = $bellHeight * exp ( - ( $bx * $bx + $bz * $bz ) / 2 );
2017-04-23 14:42:51 +00:00
}
}
}
// Returns the name of the generator
public function getName () {
return " betternormal " ;
}
/*
* Gives the generators settings .
* @ return array
*/
public function getSettings () : array {
2017-05-10 15:03:04 +00:00
return self :: $options ;
2017-04-23 14:42:51 +00:00
}
public function getSpawn () {
2017-04-29 09:59:44 +00:00
return new Vector3 ( 127.5 , 128 , 127.5 );
2017-04-23 14:42:51 +00:00
}
/*
* Returns a safe spawn location
*/
public function getSafeSpawn () {
2017-04-29 09:59:44 +00:00
return new Vector3 ( 127.5 , $this -> getHighestWorkableBlock ( 127 , 127 ), 127.5 );
2017-04-23 14:42:51 +00:00
}
/*
* Gets the top block ( y ) on an x and z axes
* @ param $x int
* @ param $z int
*/
protected function getHighestWorkableBlock ( $x , $z ) {
2017-05-11 09:49:57 +00:00
for ( $y = Level :: Y_MAX ; $y > 0 ; -- $y ) {
2017-04-29 09:59:44 +00:00
$b = $this -> level -> getBlockIdAt ( $x , $y , $z );
2017-04-23 14:42:51 +00:00
if ( $b === Block :: DIRT or $b === Block :: GRASS or $b === Block :: PODZOL ) {
break ;
} elseif ( $b !== 0 and $b !== Block :: SNOW_LAYER ) {
return - 1 ;
}
}
2017-05-11 13:12:17 +00:00
return ++ $y ;
2017-04-23 14:42:51 +00:00
}
}