package net.glowstone.generator; import net.glowstone.GlowWorld; import net.glowstone.constants.GlowBiome; import net.glowstone.generator.ground.*; import net.glowstone.generator.ground.MesaGroundGenerator.MesaType; import net.glowstone.generator.populators.OverworldPopulator; import net.glowstone.generator.populators.StructurePopulator; import net.glowstone.generator.populators.overworld.SnowPopulator; import net.glowstone.util.noise.PerlinOctaveGenerator; import net.glowstone.util.noise.SimplexOctaveGenerator; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.WorldType; import org.bukkit.block.Biome; import org.bukkit.util.noise.OctaveGenerator; import java.util.HashMap; import java.util.Map; import java.util.Random; import static net.glowstone.GlowServer.getWorldConfig; import static net.glowstone.util.config.WorldConfig.Key.*; import static org.bukkit.block.Biome.*; public class OverworldGenerator extends GlowChunkGenerator { private static double coordinateScale; private static double heightScale; private static double heightNoiseScaleX; // depthNoiseScaleX private static double heightNoiseScaleZ; // depthNoiseScaleZ private static double detailNoiseScaleX; // mainNoiseScaleX private static double detailNoiseScaleY; // mainNoiseScaleY private static double detailNoiseScaleZ; // mainNoiseScaleZ private static double surfaceScale; private static double baseSize; private static double stretchY; private static double biomeHeightOffset; // biomeDepthOffset private static double biomeHeightWeight; // biomeDepthWeight private static double biomeScaleOffset; private static double biomeScaleWeight; private static final double[][] ELEVATION_WEIGHT = new double[5][5]; private static final Map<Biome, GroundGenerator> GROUND_MAP = new HashMap<>(); private static final Map<Biome, BiomeHeight> HEIGHT_MAP = new HashMap<>(); static { setBiomeSpecificGround(new SandyGroundGenerator(), BEACHES, COLD_BEACH, DESERT, DESERT_HILLS, MUTATED_DESERT); setBiomeSpecificGround(new RockyGroundGenerator(), STONE_BEACH); setBiomeSpecificGround(new SnowyGroundGenerator(), MUTATED_ICE_FLATS); setBiomeSpecificGround(new MycelGroundGenerator(), MUSHROOM_ISLAND, MUSHROOM_ISLAND_SHORE); setBiomeSpecificGround(new StonePatchGroundGenerator(), EXTREME_HILLS); setBiomeSpecificGround(new GravelPatchGroundGenerator(), MUTATED_EXTREME_HILLS, MUTATED_EXTREME_HILLS_WITH_TREES); setBiomeSpecificGround(new DirtAndStonePatchGroundGenerator(), MUTATED_SAVANNA, MUTATED_SAVANNA_ROCK); setBiomeSpecificGround(new DirtPatchGroundGenerator(), REDWOOD_TAIGA, REDWOOD_TAIGA_HILLS, MUTATED_REDWOOD_TAIGA, MUTATED_REDWOOD_TAIGA_HILLS); setBiomeSpecificGround(new MesaGroundGenerator(), MESA, MESA_CLEAR_ROCK, MESA_ROCK); setBiomeSpecificGround(new MesaGroundGenerator(MesaType.BRYCE), MUTATED_MESA); setBiomeSpecificGround(new MesaGroundGenerator(MesaType.FOREST), MESA_ROCK, MUTATED_MESA_ROCK); setBiomeHeight(BiomeHeight.OCEAN, OCEAN, FROZEN_OCEAN); setBiomeHeight(BiomeHeight.DEEP_OCEAN, DEEP_OCEAN); setBiomeHeight(BiomeHeight.RIVER, RIVER, FROZEN_RIVER); setBiomeHeight(BiomeHeight.FLAT_SHORE, BEACHES, COLD_BEACH, MUSHROOM_ISLAND_SHORE); setBiomeHeight(BiomeHeight.ROCKY_SHORE, STONE_BEACH); setBiomeHeight(BiomeHeight.FLATLANDS, DESERT, ICE_FLATS, SAVANNA); setBiomeHeight(BiomeHeight.EXTREME_HILLS, EXTREME_HILLS, EXTREME_HILLS_WITH_TREES, MUTATED_EXTREME_HILLS, MUTATED_EXTREME_HILLS_WITH_TREES); setBiomeHeight(BiomeHeight.MID_PLAINS, TAIGA, TAIGA_COLD, REDWOOD_TAIGA); setBiomeHeight(BiomeHeight.SWAMPLAND, SWAMPLAND); setBiomeHeight(BiomeHeight.LOW_HILLS, MUSHROOM_ISLAND); setBiomeHeight(BiomeHeight.HILLS, ICE_MOUNTAINS, DESERT_HILLS, FOREST_HILLS, TAIGA_HILLS, SMALLER_EXTREME_HILLS, JUNGLE_HILLS, BIRCH_FOREST_HILLS, TAIGA_COLD_HILLS, REDWOOD_TAIGA_HILLS, MUTATED_MESA_ROCK, MUTATED_MESA_CLEAR_ROCK); setBiomeHeight(BiomeHeight.HIGH_PLATEAU, SAVANNA_ROCK, MESA_ROCK, MESA_CLEAR_ROCK); setBiomeHeight(BiomeHeight.FLATLANDS_HILLS, MUTATED_DESERT); setBiomeHeight(BiomeHeight.BIG_HILLS, MUTATED_ICE_FLATS); setBiomeHeight(BiomeHeight.BIG_HILLS2, MUTATED_BIRCH_FOREST_HILLS); setBiomeHeight(BiomeHeight.SWAMPLAND_HILLS, MUTATED_SWAMPLAND); setBiomeHeight(BiomeHeight.DEFAULT_HILLS, MUTATED_JUNGLE, MUTATED_JUNGLE_EDGE, MUTATED_BIRCH_FOREST, MUTATED_ROOFED_FOREST); setBiomeHeight(BiomeHeight.MID_HILLS, MUTATED_TAIGA, MUTATED_TAIGA_COLD, MUTATED_REDWOOD_TAIGA, MUTATED_REDWOOD_TAIGA_HILLS); setBiomeHeight(BiomeHeight.MID_HILLS2, MUTATED_FOREST); setBiomeHeight(BiomeHeight.LOW_SPIKES, MUTATED_SAVANNA); setBiomeHeight(BiomeHeight.HIGH_SPIKES, MUTATED_SAVANNA_ROCK); // fill a 5x5 array with values that acts as elevation weight on chunk neighboring, // this can be viewed as a parabolic field: the center gets the more weight, and the // weight decreases as distance increases from the center. This is applied on the // lower scale biome grid. for (int x = 0; x < 5; x++) { for (int z = 0; z < 5; z++) { int sqX = x - 2; sqX *= sqX; int sqZ = z - 2; sqZ *= sqZ; ELEVATION_WEIGHT[x][z] = 10.0D / Math.sqrt(sqX + sqZ + 0.2D); } } } private final double[][][] density = new double[5][5][33]; private final GroundGenerator groundGen = new GroundGenerator(); private final BiomeHeight defaultHeight = BiomeHeight.DEFAULT; public OverworldGenerator() { super(new OverworldPopulator(), new StructurePopulator(), new SnowPopulator()); coordinateScale = getWorldConfig().getDouble(OVERWORLD_COORDINATE_SCALE); heightScale = getWorldConfig().getDouble(OVERWORLD_HEIGHT_SCALE); heightNoiseScaleX = getWorldConfig().getDouble(OVERWORLD_HEIGHT_NOISE_SCALE_X); heightNoiseScaleZ = getWorldConfig().getDouble(OVERWORLD_HEIGHT_NOISE_SCALE_Z); detailNoiseScaleX = getWorldConfig().getDouble(OVERWORLD_DETAIL_NOISE_SCALE_X); detailNoiseScaleY = getWorldConfig().getDouble(OVERWORLD_DETAIL_NOISE_SCALE_Y); detailNoiseScaleZ = getWorldConfig().getDouble(OVERWORLD_DETAIL_NOISE_SCALE_Z); surfaceScale = getWorldConfig().getDouble(OVERWORLD_SURFACE_SCALE); baseSize = getWorldConfig().getDouble(OVERWORLD_BASE_SIZE); stretchY = getWorldConfig().getDouble(OVERWORLD_STRETCH_Y); biomeHeightOffset = getWorldConfig().getDouble(OVERWORLD_BIOME_HEIGHT_OFFSET); biomeHeightWeight = getWorldConfig().getDouble(OVERWORLD_BIOME_HEIGHT_WEIGHT); biomeScaleOffset = getWorldConfig().getDouble(OVERWORLD_BIOME_SCALE_OFFSET); biomeScaleWeight = getWorldConfig().getDouble(OVERWORLD_BIOME_SCALE_WEIGHT); } private static void setBiomeSpecificGround(GroundGenerator gen, Biome... biomes) { for (Biome biome : biomes) { GROUND_MAP.put(biome, gen); } } private static void setBiomeHeight(BiomeHeight height, Biome... biomes) { for (Biome biome : biomes) { HEIGHT_MAP.put(biome, height); } } @Override public ChunkData generateChunkData(World world, Random random, int chunkX, int chunkZ, BiomeGrid biomes) { ChunkData chunkData = generateRawTerrain(world, chunkX, chunkZ); int cx = chunkX << 4; int cz = chunkZ << 4; double[] surfaceNoise = ((SimplexOctaveGenerator) getWorldOctaves(world).get("surface")).fBm(cx, cz, 0.5D, 0.5D); for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { if (GROUND_MAP.containsKey(biomes.getBiome(x, z))) { GROUND_MAP.get(biomes.getBiome(x, z)).generateTerrainColumn(chunkData, world, random, cx + x, cz + z, biomes.getBiome(x, z), surfaceNoise[x | z << 4]); } else { groundGen.generateTerrainColumn(chunkData, world, random, cx + x, cz + z, biomes.getBiome(x, z), surfaceNoise[x | z << 4]); } } } return chunkData; } @Override protected void createWorldOctaves(World world, Map<String, OctaveGenerator> octaves) { Random seed = new Random(world.getSeed()); OctaveGenerator gen = new PerlinOctaveGenerator(seed, 16, 5, 5); gen.setXScale(heightNoiseScaleX); gen.setZScale(heightNoiseScaleZ); octaves.put("height", gen); gen = new PerlinOctaveGenerator(seed, 16, 5, 33, 5); gen.setXScale(coordinateScale); gen.setYScale(heightScale); gen.setZScale(coordinateScale); octaves.put("roughness", gen); gen = new PerlinOctaveGenerator(seed, 16, 5, 33, 5); gen.setXScale(coordinateScale); gen.setYScale(heightScale); gen.setZScale(coordinateScale); octaves.put("roughness2", gen); gen = new PerlinOctaveGenerator(seed, 8, 5, 33, 5); gen.setXScale(coordinateScale / detailNoiseScaleX); gen.setYScale(heightScale / detailNoiseScaleY); gen.setZScale(coordinateScale / detailNoiseScaleZ); octaves.put("detail", gen); gen = new SimplexOctaveGenerator(seed, 4, 16, 16); gen.setScale(surfaceScale); octaves.put("surface", gen); } private ChunkData generateRawTerrain(World world, int chunkX, int chunkZ) { generateTerrainDensity(world, chunkX, chunkZ); int seaLevel = world.getSeaLevel(); ChunkData chunkData = createChunkData(world); // Terrain densities are sampled at different resolutions (1/4x on x,z and 1/8x on y by default) // so it's needed to re-scale it. Linear interpolation is used to fill in the gaps. int fill = getWorldConfig().getInt(OVERWORLD_DENSITY_FILL_MODE); int afill = Math.abs(fill); int seaFill = getWorldConfig().getInt(OVERWORLD_DENSITY_FILL_SEA_MODE); double densityOffset = getWorldConfig().getDouble(OVERWORLD_DENSITY_FILL_OFFSET); for (int i = 0; i < 5 - 1; i++) { for (int j = 0; j < 5 - 1; j++) { for (int k = 0; k < 33 - 1; k++) { // 2x2 grid double d1 = density[i][j][k]; double d2 = density[i + 1][j][k]; double d3 = density[i][j + 1][k]; double d4 = density[i + 1][j + 1][k]; // 2x2 grid (row above) double d5 = (density[i][j][k + 1] - d1) / 8; double d6 = (density[i + 1][j][k + 1] - d2) / 8; double d7 = (density[i][j + 1][k + 1] - d3) / 8; double d8 = (density[i + 1][j + 1][k + 1] - d4) / 8; for (int l = 0; l < 8; l++) { double d9 = d1; double d10 = d3; for (int m = 0; m < 4; m++) { double dens = d9; for (int n = 0; n < 4; n++) { // any density higher than density offset is ground, any density lower or equal to the density offset is air // (or water if under the sea level). // this can be flipped if the mode is negative, so lower or equal to is ground, and higher is air/water // and, then data can be shifted by afill the order is air by default, ground, then water. they can shift places // within each if statement // the target is densityOffset + 0, since the default target is 0, so don't get too confused by the naming :) if (afill == 1 || afill == 10 || afill == 13 || afill == 16) { chunkData.setBlock(m + (i << 2), l + (k << 3), n + (j << 2), Material.STATIONARY_WATER); } else if (afill == 2 || afill == 9 || afill == 12 || afill == 15) { chunkData.setBlock(m + (i << 2), l + (k << 3), n + (j << 2), Material.STONE); } if (dens > densityOffset && fill > -1 || dens <= densityOffset && fill < 0) { if (afill == 0 || afill == 3 || afill == 6 || afill == 9 || afill == 12) { chunkData.setBlock(m + (i << 2), l + (k << 3), n + (j << 2), Material.STONE); } else if (afill == 2 || afill == 7 || afill == 10 || afill == 16) { chunkData.setBlock(m + (i << 2), l + (k << 3), n + (j << 2), Material.STATIONARY_WATER); } } else if (l + (k << 3) < seaLevel - 1 && seaFill == 0 || l + (k << 3) >= seaLevel - 1 && seaFill == 1) { if (afill == 0 || afill == 3 || afill == 7 || afill == 10 || afill == 13) { chunkData.setBlock(m + (i << 2), l + (k << 3), n + (j << 2), Material.STATIONARY_WATER); } else if (afill == 1 || afill == 6 || afill == 9 || afill == 15) { chunkData.setBlock(m + (i << 2), l + (k << 3), n + (j << 2), Material.STONE); } } // interpolation along z dens += (d10 - d9) / 4; } // interpolation along x d9 += (d2 - d1) / 4; // interpolate along z d10 += (d4 - d3) / 4; } // interpolation along y d1 += d5; d3 += d7; d2 += d6; d4 += d8; } } } } return chunkData; } private void generateTerrainDensity(World world, int x, int z) { WorldType type = world.getWorldType(); // Scaling chunk x and z coordinates (4x, see below) x <<= 2; z <<= 2; // Get biome grid data at lower res (scaled 4x, at this scale a chunk is 4x4 columns of the biome grid), // we are loosing biome detail but saving huge amount of computation. // We need 1 chunk (4 columns) + 1 column for later needed outer edges (1 column) and at least 2 columns // on each side to be able to cover every value. // 4 + 1 + 2 + 2 = 9 columns but the biomegrid generator needs a multiple of 2 so we ask 10 columns wide // to the biomegrid generator. // This gives a total of 81 biome grid columns to work with, and this includes the chunk neighborhood. int[] biomeGrid = ((GlowWorld) world).getChunkManager().getBiomeGridAtLowerRes(x - 2, z - 2, 10, 10); Map<String, OctaveGenerator> octaves = getWorldOctaves(world); double[] heightNoise = ((PerlinOctaveGenerator) octaves.get("height")).fBm(x, z, 0.5D, 2.0D); double[] roughnessNoise = ((PerlinOctaveGenerator) octaves.get("roughness")).fBm(x, 0, z, 0.5D, 2.0D); double[] roughnessNoise2 = ((PerlinOctaveGenerator) octaves.get("roughness2")).fBm(x, 0, z, 0.5D, 2.0D); double[] detailNoise = ((PerlinOctaveGenerator) octaves.get("detail")).fBm(x, 0, z, 0.5D, 2.0D); int index = 0; int indexHeight = 0; // Sampling densities. // Ideally we would sample 512 (4x4x32) values but in reality we need 825 values (5x5x33). // This is because linear interpolation is done later to re-scale so we need right and // bottom edge values if we want it to be "seamless". // You can check this picture to have a visualization of how the biomegrid is traversed (2D plan): // http://i.imgur.com/s4whlZE.png // The big square grid represents our lower res biomegrid columns, and the very small square grid // represents the normal biome grid columns (at block level) and the reason why it's required to // re-scale it and do linear interpolation before densities can be used to generate raw terrain. for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { double avgHeightScale = 0; double avgHeightBase = 0; double totalWeight = 0; Biome biome = GlowBiome.getBiome(biomeGrid[i + 2 + (j + 2) * 10]); BiomeHeight biomeHeight = HEIGHT_MAP.containsKey(biome) ? HEIGHT_MAP.get(biome) : defaultHeight; // Sampling an average height base and scale by visiting the neighborhood // of the current biomegrid column. for (int m = 0; m < 5; m++) { for (int n = 0; n < 5; n++) { Biome nearBiome = GlowBiome.getBiome(biomeGrid[i + m + (j + n) * 10]); BiomeHeight nearBiomeHeight = HEIGHT_MAP.containsKey(nearBiome) ? HEIGHT_MAP.get(nearBiome) : defaultHeight; double heightBase = biomeHeightOffset + nearBiomeHeight.getHeight() * biomeHeightWeight; double heightScale = biomeScaleOffset + nearBiomeHeight.getScale() * biomeScaleWeight; if (type == WorldType.AMPLIFIED && heightBase > 0) { heightBase = 1.0D + heightBase * 2.0D; heightScale = 1.0D + heightScale * 4.0D; } double weight = ELEVATION_WEIGHT[m][n] / (heightBase + 2.0D); if (nearBiomeHeight.getHeight() > biomeHeight.getHeight()) { weight *= 0.5D; } avgHeightScale += heightScale * weight; avgHeightBase += heightBase * weight; totalWeight += weight; } } avgHeightScale /= totalWeight; avgHeightBase /= totalWeight; avgHeightScale = avgHeightScale * 0.9D + 0.1D; avgHeightBase = (avgHeightBase * 4.0D - 1.0D) / 8.0D; double noiseH = heightNoise[indexHeight++] / 8000.0D; if (noiseH < 0) { noiseH = Math.abs(noiseH) * 0.3D; } noiseH = noiseH * 3.0D - 2.0D; if (noiseH < 0) { noiseH = Math.max(noiseH * 0.5D, -1) / 1.4D * 0.5D; } else { noiseH = Math.min(noiseH, 1) / 8.0D; } noiseH = (noiseH * 0.2D + avgHeightBase) * baseSize / 8.0D * 4.0D + baseSize; for (int k = 0; k < 33; k++) { // density should be lower and lower as we climb up, this gets a height value to // subtract from the noise. double nH = (k - noiseH) * stretchY * 128.0D / 256.0D / avgHeightScale; if (nH < 0.0D) { nH *= 4.0D; } double noiseR = roughnessNoise[index] / 512.0D; double noiseR2 = roughnessNoise2[index] / 512.0D; double noiseD = (detailNoise[index] / 10.0D + 1.0D) / 2.0D; // linear interpolation double dens = noiseD < 0 ? noiseR : noiseD > 1 ? noiseR2 : noiseR + (noiseR2 - noiseR) * noiseD; dens -= nH; index++; if (k > 29) { double lowering = (k - 29) / 3.0D; // linear interpolation dens = dens * (1.0D - lowering) + -10.0D * lowering; } density[i][j][k] = dens; } } } } private static class BiomeHeight { public static final BiomeHeight DEFAULT = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_DEFAULT), getWorldConfig().getDouble(BIOME_SCALE_DEFAULT)); public static final BiomeHeight FLAT_SHORE = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_FLAT_SHORE), getWorldConfig().getDouble(BIOME_SCALE_FLAT_SHORE)); public static final BiomeHeight HIGH_PLATEAU = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_HIGH_PLATEAU), getWorldConfig().getDouble(BIOME_SCALE_HIGH_PLATEAU)); public static final BiomeHeight FLATLANDS = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_FLATLANDS), getWorldConfig().getDouble(BIOME_SCALE_FLATLANDS)); public static final BiomeHeight SWAMPLAND = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_SWAMPLAND), getWorldConfig().getDouble(BIOME_SCALE_SWAMPLAND)); public static final BiomeHeight MID_PLAINS = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_MID_PLAINS), getWorldConfig().getDouble(BIOME_SCALE_MID_PLAINS)); public static final BiomeHeight FLATLANDS_HILLS = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_FLATLANDS_HILLS), getWorldConfig().getDouble(BIOME_SCALE_FLATLANDS_HILLS)); public static final BiomeHeight SWAMPLAND_HILLS = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_SWAMPLAND_HILLS), getWorldConfig().getDouble(BIOME_SCALE_SWAMPLAND_HILLS)); public static final BiomeHeight LOW_HILLS = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_LOW_HILLS), getWorldConfig().getDouble(BIOME_SCALE_LOW_HILLS)); public static final BiomeHeight HILLS = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_HILLS), getWorldConfig().getDouble(BIOME_SCALE_HILLS)); public static final BiomeHeight MID_HILLS2 = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_MID_HILLS2), getWorldConfig().getDouble(BIOME_SCALE_MID_HILLS2)); public static final BiomeHeight DEFAULT_HILLS = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_DEFAULT_HILLS), getWorldConfig().getDouble(BIOME_SCALE_DEFAULT_HILLS)); public static final BiomeHeight MID_HILLS = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_MID_HILLS), getWorldConfig().getDouble(BIOME_SCALE_MID_HILLS)); public static final BiomeHeight BIG_HILLS = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_BIG_HILLS), getWorldConfig().getDouble(BIOME_SCALE_BIG_HILLS)); public static final BiomeHeight BIG_HILLS2 = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_BIG_HILLS2), getWorldConfig().getDouble(BIOME_SCALE_BIG_HILLS2)); public static final BiomeHeight EXTREME_HILLS = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_EXTREME_HILLS), getWorldConfig().getDouble(BIOME_SCALE_EXTREME_HILLS)); public static final BiomeHeight ROCKY_SHORE = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_ROCKY_SHORE), getWorldConfig().getDouble(BIOME_SCALE_ROCKY_SHORE)); public static final BiomeHeight LOW_SPIKES = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_LOW_SPIKES), getWorldConfig().getDouble(BIOME_SCALE_LOW_SPIKES)); public static final BiomeHeight HIGH_SPIKES = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_HIGH_SPIKES), getWorldConfig().getDouble(BIOME_SCALE_HIGH_SPIKES)); public static final BiomeHeight RIVER = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_RIVER), getWorldConfig().getDouble(BIOME_SCALE_RIVER)); public static final BiomeHeight OCEAN = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_OCEAN), getWorldConfig().getDouble(BIOME_SCALE_OCEAN)); public static final BiomeHeight DEEP_OCEAN = new BiomeHeight(getWorldConfig().getDouble(BIOME_HEIGHT_DEEP_OCEAN), getWorldConfig().getDouble(BIOME_SCALE_DEEP_OCEAN)); private final double height; private final double scale; public BiomeHeight(double height, double scale) { this.height = height; this.scale = scale; } public double getHeight() { return height; } public double getScale() { return scale; } } }