package net.glowstone.generator;
import net.glowstone.GlowServer;
import net.glowstone.generator.populators.NetherPopulator;
import net.glowstone.util.config.WorldConfig;
import net.glowstone.util.noise.PerlinOctaveGenerator;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.util.noise.OctaveGenerator;
import java.util.Map;
import java.util.Random;
public class NetherGenerator 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 final double[][][] density = new double[5][5][17];
public NetherGenerator() {
super(new NetherPopulator());
WorldConfig config = GlowServer.getWorldConfig();
coordinateScale = config.getDouble(WorldConfig.Key.NETHER_COORDINATE_SCALE);
heightScale = config.getDouble(WorldConfig.Key.NETHER_HEIGHT_SCALE);
heightNoiseScaleX = config.getDouble(WorldConfig.Key.NETHER_HEIGHT_NOISE_SCALE_X);
heightNoiseScaleZ = config.getDouble(WorldConfig.Key.NETHER_HEIGHT_NOISE_SCALE_Z);
detailNoiseScaleX = config.getDouble(WorldConfig.Key.NETHER_DETAIL_NOISE_SCALE_X);
detailNoiseScaleY = config.getDouble(WorldConfig.Key.NETHER_DETAIL_NOISE_SCALE_Y);
detailNoiseScaleZ = config.getDouble(WorldConfig.Key.NETHER_DETAIL_NOISE_SCALE_Z);
surfaceScale = config.getDouble(WorldConfig.Key.NETHER_SURFACE_SCALE);
}
@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 = ((PerlinOctaveGenerator) getWorldOctaves(world).get("surface")).fBm(cx, cz, 0, 0.5D, 2.0D);
double[] soulsandNoise = ((PerlinOctaveGenerator) getWorldOctaves(world).get("soulsand")).fBm(cx, cz, 0, 0.5D, 2.0D);
double[] gravelNoise = ((PerlinOctaveGenerator) getWorldOctaves(world).get("gravel")).fBm(cx, 0, cz, 0.5D, 2.0D);
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
generateTerrainColumn(chunkData, world, random, cx + x, cz + z, surfaceNoise[x | z << 4], soulsandNoise[x | z << 4], gravelNoise[x | z << 4]);
}
}
return chunkData;
}
@Override
public boolean canSpawn(World world, int x, int z) {
Block block = world.getHighestBlockAt(x, z).getRelative(BlockFace.DOWN);
return block.getType() == Material.NETHERRACK;
}
@Override
public Location getFixedSpawnLocation(World world, Random random) {
while (true) {
int x = random.nextInt(128) - 64;
int y = 128 * 3 / 4;
int z = random.nextInt(128) - 64;
if (world.getBlockAt(x, y, z).isEmpty()) {
while (world.getBlockAt(x, y - 1, z).isEmpty() && y > 0) {
y--;
}
return new Location(world, x, y, z);
}
}
}
@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, 17, 5);
gen.setXScale(coordinateScale);
gen.setYScale(heightScale);
gen.setZScale(coordinateScale);
octaves.put("roughness", gen);
gen = new PerlinOctaveGenerator(seed, 16, 5, 17, 5);
gen.setXScale(coordinateScale);
gen.setYScale(heightScale);
gen.setZScale(coordinateScale);
octaves.put("roughness2", gen);
gen = new PerlinOctaveGenerator(seed, 8, 5, 17, 5);
gen.setXScale(coordinateScale / detailNoiseScaleX);
gen.setYScale(heightScale / detailNoiseScaleY);
gen.setZScale(coordinateScale / detailNoiseScaleZ);
octaves.put("detail", gen);
gen = new PerlinOctaveGenerator(seed, 4, 16, 16, 1);
gen.setScale(surfaceScale);
octaves.put("surface", gen);
gen = new PerlinOctaveGenerator(seed, 4, 16, 16, 1);
gen.setXScale(surfaceScale / 2.0);
gen.setYScale(surfaceScale / 2.0);
octaves.put("soulsand", gen);
gen = new PerlinOctaveGenerator(seed, 4, 16, 1, 16);
gen.setXScale(surfaceScale / 2.0);
gen.setZScale(surfaceScale / 2.0);
octaves.put("gravel", gen);
}
private ChunkData generateRawTerrain(World world, int chunkX, int chunkZ) {
generateTerrainDensity(world, chunkX * 4, chunkZ * 4);
ChunkData chunkData = createChunkData(world);
for (int i = 0; i < 5 - 1; i++) {
for (int j = 0; j < 5 - 1; j++) {
for (int k = 0; k < 17 - 1; k++) {
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];
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 0 is ground, any density lower or equal to 0 is air
// (or lava if under the lava level).
if (dens > 0) {
chunkData.setBlock(m + (i << 2), l + (k << 3), n + (j << 2), Material.NETHERRACK);
} else if (l + (k << 3) < 32) {
chunkData.setBlock(m + (i << 2), l + (k << 3), n + (j << 2), Material.STATIONARY_LAVA);
}
// 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) {
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);
double[] nV = new double[17];
for (int i = 0; i < 17; i++) {
nV[i] = Math.cos(i * Math.PI * 6.0D / 17.0D) * 2.0D;
double nH = i > 17 / 2 ? 17 - 1 - i : i;
if (nH < 4.0D) {
nH = 4.0D - nH;
nV[i] -= nH * nH * nH * 10.0D;
}
}
int index = 0;
int indexHeight = 0;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
double noiseH = heightNoise[indexHeight++] / 8000.0D;
if (noiseH < 0) {
noiseH = Math.abs(noiseH);
}
noiseH = noiseH * 3.0D - 3.0D;
if (noiseH < 0) {
noiseH = Math.max(noiseH * 0.5D, -1) / 1.4D * 0.5D;
} else {
noiseH = Math.min(noiseH, 1) / 6.0D;
}
noiseH = noiseH * 17 / 16.0D;
for (int k = 0; k < 17; k++) {
double noiseR = roughnessNoise[index] / 512.0D;
double noiseR2 = roughnessNoise2[index] / 512.0D;
double noiseD = (detailNoise[index] / 10.0D + 1.0D) / 2.0D;
double nH = nV[k];
// linear interpolation
double dens = noiseD < 0 ? noiseR : noiseD > 1 ? noiseR2 : noiseR + (noiseR2 - noiseR) * noiseD;
dens -= nH;
index++;
if (k > 13) {
double lowering = (k - 13) / 3.0D;
dens = dens * (1.0D - lowering) + lowering * -10.0D;
}
density[i][j][k] = dens;
}
}
}
}
public void generateTerrainColumn(ChunkData chunkData, World world, Random random, int x, int z, double surfaceNoise, double soulsandNoise, double gravelNoise) {
Material topMat = Material.NETHERRACK;
Material groundMat = Material.NETHERRACK;
x = x & 0xF;
z = z & 0xF;
boolean soulSand = soulsandNoise + random.nextDouble() * 0.2D > 0;
boolean gravel = gravelNoise + random.nextDouble() * 0.2D > 0;
int surfaceHeight = (int) (surfaceNoise / 3.0D + 3.0D + random.nextDouble() * 0.25D);
int deep = -1;
for (int y = 127; y >= 0; y--) {
if (y <= random.nextInt(5) || y >= 127 - random.nextInt(5)) {
chunkData.setBlock(x, y, z, Material.BEDROCK);
} else {
Material mat = chunkData.getType(x, y, z);
if (mat == Material.AIR) {
deep = -1;
} else if (mat == Material.NETHERRACK) {
if (deep == -1) {
if (surfaceHeight <= 0) {
topMat = Material.AIR;
groundMat = Material.NETHERRACK;
} else if (y >= 60 && y <= 65) {
topMat = Material.NETHERRACK;
groundMat = Material.NETHERRACK;
if (gravel) {
topMat = Material.GRAVEL;
groundMat = Material.NETHERRACK;
}
if (soulSand) {
topMat = Material.SOUL_SAND;
groundMat = Material.SOUL_SAND;
}
}
deep = surfaceHeight;
if (y >= 63) {
chunkData.setBlock(x, y, z, topMat);
} else {
chunkData.setBlock(x, y, z, groundMat);
}
} else if (deep > 0) {
deep--;
chunkData.setBlock(x, y, z, groundMat);
}
}
}
}
}
}