blob: 041bebd0d64182b3d574f9070871a080d67488ec [file] [log] [blame] [raw]
package net.glowstone.world;
import java.util.ArrayList;
import java.util.Map.Entry;
import net.glowstone.GlowChunk;
import org.bukkit.Material;
/**
* A slightly more complex {@link WorldGenerator} using the InfiniMap algorithm.
* @author Tad
*/
public class InfiniMapWorldGenerator implements WorldGenerator {
/**
* Class representing a biome type.
*/
abstract private class Biome {
public int weight;
public int midheight;
public int[] noise;
protected Biome(int w, int mid, int n1, int n2, int n3, int n4) {
weight = w; midheight = mid;
noise = new int[] { n1, n2, n3, n4 };
}
abstract public Material get(int y, int h);
// Block mappers
protected Material forestBlock(int y, int h) {
if (y > h) return Material.AIR;
else if (y == h) return Material.GRASS;
else if (y > h - 5) return Material.DIRT;
else if (y <= 1) return Material.BEDROCK;
else return Material.STONE;
}
protected Material desertBlock(int y, int h) {
if (y > h) return Material.AIR;
else if (y > h - 5) return Material.SAND;
else if (y <= 1) return Material.BEDROCK;
else return Material.STONE;
}
protected Material costalBlock(int y, int h) {
if (h >= 64) return forestBlock(y, h);
else if (y > 64) return Material.AIR;
else if (y > h) return Material.WATER;
else if (y <= 1) return Material.BEDROCK;
else return Material.STONE;
}
protected Material floatingBlock(int y, int h) {
int h2 = 128 - h + 32;
int base = 128 - 16;
int h3 = base - 3 * (h2 - base);
if (y > h2) return Material.AIR;
else if (y == h2 && y > h3) return Material.GRASS;
else if (y == h2 && y == h3) return Material.SAND;
else if (y >= h2 - 8 && y >= h3) return Material.DIRT;
else if (y >= h3) return Material.STONE;
else if (y > 64) return Material.AIR;
else if (y > h) return Material.WATER;
else if (y >= h - 5) return Material.GRAVEL;
else if (y > 1) return Material.STONE;
else return Material.BEDROCK;
}
}
// Biomes
private class Forest extends Biome {
public Forest() { super(100, 68, 16, 1, 4, 2); }
public Material get(int y, int h) { return costalBlock(y, h); }
}
private class Islands extends Biome {
public Islands() { super(50, 62, 16, 1, 2, 4); }
public Material get(int y, int h) { return costalBlock(y, h); }
}
private class Floating extends Biome {
public Floating() { super(10, 32, 16, 1, 2, 1); }
public Material get(int y, int h) { return floatingBlock(y, h); }
}
private class Desert extends Biome {
public Desert() { super(50, 74, 16, 1, 2, 4); }
public Material get(int y, int h) { return desertBlock(y, h); }
}
private class Mountains extends Biome {
public Mountains() { super(30, 80, 16, 2, 16, 1); }
public Material get(int y, int h) { return costalBlock(y, h); }
}
private ArrayList<Biome> biomes;
private Perlin2D biomeGen;
private Perlin2D[] perlins;
public InfiniMapWorldGenerator() {
biomes = new ArrayList<Biome>();
biomes.add(new Forest());
biomes.add(new Islands());
biomes.add(new Floating());
biomes.add(new Desert());
biomes.add(new Mountains());
int total = 0;
for (Biome b : biomes) {
total += b.weight;
}
biomeGen = new Perlin2D(total, 0.5, new int[] { 31, 15731, 789221, 1376312589 });
perlins = new Perlin2D[] {
new Perlin2D(16, 1, new int[] { 57, 60493, 19990303, 1376312589 }),
new Perlin2D(16, 1, new int[] { 29, 15731, 789221, 1376312589 })
};
}
private Biome biomeAt(double noiseX, double noiseY) {
double bNoise = biomeGen.amplitude/2 + biomeGen.noise(noiseX, noiseY);
int total = 0;
for (Biome b : biomes) {
total += b.weight;
if (bNoise < total) return b;
}
return biomes.get(0);
}
private int heightAt(double noiseX, double noiseY) {
Biome biome = biomeAt(noiseX, noiseY);
double noise = biome.midheight;
for (int i = 0; i < 2; ++i) {
Perlin2D perlin = perlins[i];
perlin.amplitude = biome.noise[2*i];
perlin.frequency = biome.noise[2*i + 1];
noise += perlin.noise(noiseX, noiseY);
}
return (int) noise;
}
private void nearestOtherBiome(double noiseX, double noiseY, Double newX, Double newY) {
newX = noiseX; newY = noiseY;
double d = 1.0 / 16.0;
Biome biome = biomeAt(noiseX, noiseY);
for (int i = 0; i < 5; ++i) {
if (biomeAt(noiseX - i*d, noiseY) != biome) {
newX = noiseX - i*d; return;
} else if (biomeAt(noiseX + i*d, noiseY) != biome) {
newX = noiseX + i*d; return;
} else if (biomeAt(noiseX, noiseY - i*d) != biome) {
newY = noiseY - i*d; return;
} else if (biomeAt(noiseX, noiseY + i*d) != biome) {
newY = noiseY + i*d; return;
}
}
}
public GlowChunk generate(int chunkX, int chunkZ) {
GlowChunk chunk = new GlowChunk(chunkX, chunkZ);
for (int x = 0; x < GlowChunk.WIDTH; x++) {
for (int z = 0; z < GlowChunk.HEIGHT; z++) {
double noiseX = chunkX + (x / 16.0);
double noiseY = chunkZ + (z / 16.0);
Biome biome = biomeAt(noiseX, noiseY);
int height = heightAt(noiseX, noiseY);
Double nearestX = 0.0, nearestY = 0.0;
nearestOtherBiome(noiseX, noiseY, nearestX, nearestY);
if (biomeAt(nearestX, nearestY) != biome) {
double dx = Math.abs(nearestX - noiseX);
double dy = Math.abs(nearestY - noiseY);
double bdist = 16 * Math.max(dx, dy);
double f = (bdist + 1.0) / (bdist + 2.0);
height = (int)(f * height + (1.0 - f) * heightAt(nearestX, nearestY));
}
for (int y = 0; y < GlowChunk.DEPTH; y++) {
chunk.setType(x, z, y, biome.get(y, height).getId());
chunk.setMetaData(x, z, y, 0);
chunk.setBlockLight(x, z, y, 0);
chunk.setSkyLight(x, z, y, 15);
}
}
}
return chunk;
}
}