MAP = Maps.newEnumMap(ClimateType.class);
+
+ static {
+ MAP.put(ClimateType.WARM_WET, new Climate(2, new int[]{3, 1}, 4));
+ MAP.put(ClimateType.COLD_DRY, new Climate(3, new int[]{2, 4}, 1));
+ }
+
+ private final MapLayer belowLayer;
+ private final ClimateType type;
+
+ /**
+ * Creates a map layer.
+ *
+ * @param seed the layer random seed
+ * @param belowLayer the layer generated before this one
+ * @param type the climate-type parameter
+ */
+ public MapLayerWhittaker(long seed, MapLayer belowLayer, ClimateType type) {
+ super(seed);
+ this.belowLayer = belowLayer;
+ this.type = type;
+ }
+
+ @Override
+ public int[] generateValues(int x, int z, int sizeX, int sizeZ) {
+ if (type == ClimateType.WARM_WET || type == ClimateType.COLD_DRY) {
+ return swapValues(x, z, sizeX, sizeZ);
+ } else {
+ return modifyValues(x, z, sizeX, sizeZ);
+ }
+ }
+
+ private int[] swapValues(int x, int z, int sizeX, int sizeZ) {
+ int gridX = x - 1;
+ int gridZ = z - 1;
+ int gridSizeX = sizeX + 2;
+ int gridSizeZ = sizeZ + 2;
+ int[] values = belowLayer.generateValues(gridX, gridZ, gridSizeX, gridSizeZ);
+
+ Climate climate = MAP.get(type);
+ int[] finalValues = new int[sizeX * sizeZ];
+ for (int i = 0; i < sizeZ; i++) {
+ for (int j = 0; j < sizeX; j++) {
+ int centerVal = values[j + 1 + (i + 1) * gridSizeX];
+ if (centerVal == climate.value) {
+ int upperVal = values[j + 1 + i * gridSizeX];
+ int lowerVal = values[j + 1 + (i + 2) * gridSizeX];
+ int leftVal = values[j + (i + 1) * gridSizeX];
+ int rightVal = values[j + 2 + (i + 1) * gridSizeX];
+ for (int type : climate.crossTypes) {
+ if (upperVal == type || lowerVal == type || leftVal == type || rightVal == type) {
+ centerVal = climate.finalValue;
+ break;
+ }
+ }
+ }
+ finalValues[j + i * sizeX] = centerVal;
+ }
+ }
+ return finalValues;
+ }
+
+ private int[] modifyValues(int x, int z, int sizeX, int sizeZ) {
+ int[] values = belowLayer.generateValues(x, z, sizeX, sizeZ);
+ int[] finalValues = new int[sizeX * sizeZ];
+ for (int i = 0; i < sizeZ; i++) {
+ for (int j = 0; j < sizeX; j++) {
+ int val = values[j + i * sizeX];
+ if (val != 0) {
+ setCoordsSeed(x + j, z + i);
+ if (nextInt(13) == 0) {
+ val += 1000;
+ }
+ }
+ finalValues[j + i * sizeX] = val;
+ }
+ }
+ return finalValues;
+ }
+
+ public enum ClimateType {
+ WARM_WET,
+ COLD_DRY,
+ LARGER_BIOMES
+ }
+
+ private static class Climate {
+
+ public final int value;
+ public final int[] crossTypes;
+ public final int finalValue;
+
+ public Climate(int value, int[] crossTypes, int finalValue) {
+ this.value = value;
+ this.crossTypes = crossTypes;
+ this.finalValue = finalValue;
+ }
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/biomegrid/MapLayerZoom.java b/src/main/java/worldgeneratorextension/vanillagenerator/biomegrid/MapLayerZoom.java
new file mode 100644
index 0000000..aa0e110
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/biomegrid/MapLayerZoom.java
@@ -0,0 +1,95 @@
+package worldgeneratorextension.vanillagenerator.biomegrid;
+
+public class MapLayerZoom extends MapLayer {
+
+ private final MapLayer belowLayer;
+ private final ZoomType zoomType;
+
+ public MapLayerZoom(long seed, MapLayer belowLayer) {
+ this(seed, belowLayer, ZoomType.NORMAL);
+ }
+
+ /**
+ * Creates a map layer.
+ *
+ * @param seed the layer random seed
+ * @param belowLayer the layer generated before this one
+ * @param zoomType the zoom-type parameter
+ */
+ public MapLayerZoom(long seed, MapLayer belowLayer, ZoomType zoomType) {
+ super(seed);
+ this.belowLayer = belowLayer;
+ this.zoomType = zoomType;
+ }
+
+ @Override
+ public int[] generateValues(int x, int z, int sizeX, int sizeZ) {
+ int gridX = x >> 1;
+ int gridZ = z >> 1;
+ int gridSizeX = (sizeX >> 1) + 2;
+ int gridSizeZ = (sizeZ >> 1) + 2;
+ int[] values = belowLayer.generateValues(gridX, gridZ, gridSizeX, gridSizeZ);
+
+ int zoomSizeX = gridSizeX - 1 << 1;
+ int zoomSizeZ = gridSizeZ - 1 << 1;
+ int[] tmpValues = new int[zoomSizeX * zoomSizeZ];
+ for (int i = 0; i < gridSizeZ - 1; i++) {
+ int n = i * 2 * zoomSizeX;
+ int upperLeftVal = values[i * gridSizeX];
+ int lowerLeftVal = values[(i + 1) * gridSizeX];
+ for (int j = 0; j < gridSizeX - 1; j++) {
+ setCoordsSeed(gridX + j << 1, gridZ + i << 1);
+ tmpValues[n] = upperLeftVal;
+ tmpValues[n + zoomSizeX] = nextInt(2) > 0 ? upperLeftVal : lowerLeftVal;
+ int upperRightVal = values[j + 1 + i * gridSizeX];
+ int lowerRightVal = values[j + 1 + (i + 1) * gridSizeX];
+ tmpValues[n + 1] = nextInt(2) > 0 ? upperLeftVal : upperRightVal;
+ tmpValues[n + 1 + zoomSizeX] = getNearest(upperLeftVal, upperRightVal, lowerLeftVal, lowerRightVal);
+ upperLeftVal = upperRightVal;
+ lowerLeftVal = lowerRightVal;
+ n += 2;
+ }
+ }
+ int[] finalValues = new int[sizeX * sizeZ];
+ for (int i = 0; i < sizeZ; i++) {
+ for (int j = 0; j < sizeX; j++) {
+ finalValues[j + i * sizeX] = tmpValues[j + (i + (z & 1)) * zoomSizeX + (x & 1)];
+ }
+ }
+
+ return finalValues;
+ }
+
+ private int getNearest(int upperLeftVal, int upperRightVal, int lowerLeftVal, int lowerRightVal) {
+ if (zoomType == ZoomType.NORMAL) {
+ if (upperRightVal == lowerLeftVal && lowerLeftVal == lowerRightVal) {
+ return upperRightVal;
+ } else if (upperLeftVal == upperRightVal && upperLeftVal == lowerLeftVal) {
+ return upperLeftVal;
+ } else if (upperLeftVal == upperRightVal && upperLeftVal == lowerRightVal) {
+ return upperLeftVal;
+ } else if (upperLeftVal == lowerLeftVal && upperLeftVal == lowerRightVal) {
+ return upperLeftVal;
+ } else if (upperLeftVal == upperRightVal && lowerLeftVal != lowerRightVal) {
+ return upperLeftVal;
+ } else if (upperLeftVal == lowerLeftVal && upperRightVal != lowerRightVal) {
+ return upperLeftVal;
+ } else if (upperLeftVal == lowerRightVal && upperRightVal != lowerLeftVal) {
+ return upperLeftVal;
+ } else if (upperRightVal == lowerLeftVal && upperLeftVal != lowerRightVal) {
+ return upperRightVal;
+ } else if (upperRightVal == lowerRightVal && upperLeftVal != lowerLeftVal) {
+ return upperRightVal;
+ } else if (lowerLeftVal == lowerRightVal && upperLeftVal != upperRightVal) {
+ return lowerLeftVal;
+ }
+ }
+ int[] values = new int[]{upperLeftVal, upperRightVal, lowerLeftVal, lowerRightVal};
+ return values[nextInt(values.length)];
+ }
+
+ public enum ZoomType {
+ NORMAL,
+ BLURRY
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGenerator.java b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGenerator.java
new file mode 100644
index 0000000..326477f
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGenerator.java
@@ -0,0 +1,101 @@
+package worldgeneratorextension.vanillagenerator.ground;
+
+import cn.nukkit.block.Block;
+import cn.nukkit.block.BlockID;
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.format.generic.BaseFullChunk;
+import cn.nukkit.math.NukkitRandom;
+import worldgeneratorextension.vanillagenerator.biome.BiomeClimate;
+
+public class GroundGenerator implements BlockID {
+
+ protected int topMaterial;
+ protected int topData;
+ protected int groundMaterial;
+ protected int groundData;
+
+ public GroundGenerator() {
+ setTopMaterial(GRASS);
+ setGroundMaterial(DIRT);
+ }
+
+ /**
+ * Generates a terrain column.
+ *
+ * @param chunkData the affected chunk
+ * @param world the affected world
+ * @param random the PRNG to use
+ * @param chunkX the chunk X coordinate
+ * @param chunkZ the chunk Z coordinate
+ * @param biome the biome this column is in
+ * @param surfaceNoise the amplitude of random variation in surface height
+ */
+ public void generateTerrainColumn(ChunkManager world, BaseFullChunk chunkData, NukkitRandom random, int chunkX, int chunkZ, int biome, double surfaceNoise) {
+ int seaLevel = 64;
+
+ int topMat = this.topMaterial;
+ int groundMat = this.groundMaterial;
+
+ int x = chunkX & 0xF;
+ int z = chunkZ & 0xF;
+
+ int surfaceHeight = Math.max((int) (surfaceNoise / 3.0D + 3.0D + random.nextDouble() * 0.25D), 1);
+ int deep = -1;
+ for (int y = 255; y >= 0; y--) {
+ if (y <= random.nextBoundedInt(5)) {
+ chunkData.setBlock(x, y, z, BEDROCK);
+ } else {
+ int mat = chunkData.getBlockId(x, y, z);
+ if (mat == AIR) {
+ deep = -1;
+ } else if (mat == STONE) {
+ if (deep == -1) {
+ if (y >= seaLevel - 5 && y <= seaLevel) {
+ topMat = this.topMaterial;
+ groundMat = this.groundMaterial;
+ }
+
+ deep = surfaceHeight;
+ if (y >= seaLevel - 2) {
+ chunkData.setBlock(x, y, z, topMat, this.topData);
+ } else if (y < seaLevel - 8 - surfaceHeight) {
+ topMat = AIR;
+ groundMat = STONE;
+ chunkData.setBlock(x, y, z, GRAVEL);
+ } else {
+ chunkData.setBlock(x, y, z, groundMat, this.groundData);
+ }
+ } else if (deep > 0) {
+ deep--;
+ chunkData.setBlock(x, y, z, groundMat, this.groundData);
+
+ if (deep == 0 && groundMat == SAND) {
+ deep = random.nextBoundedInt(4) + Math.max(0, y - seaLevel - 1);
+ groundMat = SANDSTONE;
+ }
+ }
+ } else if (mat == Block.STILL_WATER && y == seaLevel - 2 && BiomeClimate.isCold(biome, chunkX, y, chunkZ)) {
+ chunkData.setBlock(x, y, z, ICE);
+ }
+ }
+ }
+ }
+
+ protected final void setTopMaterial(int topMaterial) {
+ this.setTopMaterial(topMaterial, 0);
+ }
+
+ protected final void setTopMaterial(int topMaterial, int topData) {
+ this.topMaterial = topMaterial;
+ this.topData = topData;
+ }
+
+ protected final void setGroundMaterial(int groundMaterial) {
+ this.setGroundMaterial(groundMaterial, 0);
+ }
+
+ protected final void setGroundMaterial(int groundMaterial, int groundData) {
+ this.groundMaterial = groundMaterial;
+ this.groundData = groundData;
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorMesa.java b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorMesa.java
new file mode 100644
index 0000000..68075c3
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorMesa.java
@@ -0,0 +1,173 @@
+package worldgeneratorextension.vanillagenerator.ground;
+
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.format.generic.BaseFullChunk;
+import cn.nukkit.math.NukkitRandom;
+import worldgeneratorextension.vanillagenerator.noise.SimplexOctaveGenerator;
+
+import java.util.Arrays;
+
+import static worldgeneratorextension.vanillagenerator.NormalGenerator.SEA_LEVEL;
+
+public class GroundGeneratorMesa extends GroundGenerator {
+
+ private final MesaType type;
+ private final int[] colorLayer = new int[64];
+ private SimplexOctaveGenerator colorNoise;
+ private SimplexOctaveGenerator canyonHeightNoise;
+ private SimplexOctaveGenerator canyonScaleNoise;
+ private long seed;
+
+ public GroundGeneratorMesa() {
+ this(MesaType.NORMAL);
+ }
+
+ /**
+ * Creates a ground generator for mesa biomes.
+ *
+ * @param type the type of mesa biome to generate
+ */
+ public GroundGeneratorMesa(MesaType type) {
+ this.type = type;
+ }
+
+ private void initialize(long seed) {
+ if (seed != this.seed || this.colorNoise == null || this.canyonScaleNoise == null || this.canyonHeightNoise == null) {
+ NukkitRandom random = new NukkitRandom(seed);
+ this.colorNoise = new SimplexOctaveGenerator(random, 1);
+ this.colorNoise.setScale(1 / 512.0D);
+ this.initializeColorLayers(random);
+
+ this.canyonHeightNoise = new SimplexOctaveGenerator(random, 4);
+ this.canyonHeightNoise.setScale(1 / 4.0D);
+ this.canyonScaleNoise = new SimplexOctaveGenerator(random, 1);
+ this.canyonScaleNoise.setScale(1 / 512.0D);
+ this.seed = seed;
+ }
+ }
+
+ @Override
+ public void generateTerrainColumn(ChunkManager world, BaseFullChunk chunkData, NukkitRandom random, int chunkX, int chunkZ, int biome, double surfaceNoise) {
+ this.initialize(world.getSeed());
+
+ int surfaceHeight = Math.max((int) (surfaceNoise / 3.0D + 3.0D + random.nextDouble() * 0.25D), 1);
+ boolean colored = Math.cos(surfaceNoise / 3.0D * Math.PI) <= 0;
+ double bryceCanyonHeight = 0;
+ if (type == MesaType.BRYCE) {
+ int noiseX = (chunkX & 0xFFFFFFF0) + (chunkZ & 0xF);
+ int noiseZ = (chunkZ & 0xFFFFFFF0) + (chunkX & 0xF);
+ double noiseCanyonHeight = Math.min(Math.abs(surfaceNoise), this.canyonHeightNoise.noise(noiseX, noiseZ, 0.5D, 2.0D));
+ if (noiseCanyonHeight > 0) {
+ double heightScale = Math.abs(this.canyonScaleNoise.noise(noiseX, noiseZ, 0.5D, 2.0D));
+ bryceCanyonHeight = Math.pow(noiseCanyonHeight, 2) * 2.5D;
+ double maxHeight = Math.ceil(50 * heightScale) + 14;
+ if (bryceCanyonHeight > maxHeight) {
+ bryceCanyonHeight = maxHeight;
+ }
+ bryceCanyonHeight += SEA_LEVEL;
+ }
+ }
+
+ int x = chunkX & 0xF;
+ int z = chunkZ & 0xF;
+
+ int deep = -1;
+ boolean groundSet = false;
+ for (int y = 255; y >= 0; y--) {
+ if (y < (int) bryceCanyonHeight && chunkData.getBlockId(x, y, z) == AIR) {
+ chunkData.setBlock(x, y, z, STONE);
+ }
+ if (y <= random.nextBoundedInt(5)) {
+ chunkData.setBlock(x, y, z, BEDROCK);
+ } else {
+ int mat = chunkData.getBlockId(x, y, z);
+ if (mat == AIR) {
+ deep = -1;
+ } else if (mat == STONE) { // revert 9747d77 -- hennick
+ if (deep == -1) {
+ groundSet = false;
+ //if (y >= SEA_LEVEL - 5 && y <= SEA_LEVEL) {
+ // groundMat = this.groundMaterial;
+ //}
+
+ deep = surfaceHeight + Math.max(0, y - SEA_LEVEL - 1);
+ if (y >= SEA_LEVEL - 2) {
+ if (type == MesaType.FOREST && y > SEA_LEVEL + 22 + (surfaceHeight << 1)) {
+ int topMat = colored ? GRASS : DIRT;
+ int topData = colored ? 0 : 1;
+ chunkData.setBlock(x, y, z, topMat, topData);
+ } else if (y > SEA_LEVEL + 2 + surfaceHeight) {
+ int color = this.colorLayer[(y + (int) Math.round(this.colorNoise.noise(chunkX, chunkZ, 0.5D, 2.0D) * 2.0D)) % this.colorLayer.length];
+ this.setColoredGroundLayer(chunkData, x, y, z, y < SEA_LEVEL || y > 128 ? 1 : colored ? color : -1);
+ } else {
+ chunkData.setBlock(x, y, z, SAND, 1);
+ groundSet = true;
+ }
+ } else {
+ chunkData.setBlock(x, y, z, STAINED_HARDENED_CLAY, 1);
+ }
+ } else if (deep > 0) {
+ deep--;
+ if (groundSet) {
+ chunkData.setBlock(x, y, z, STAINED_HARDENED_CLAY, 1);
+ } else {
+ int color = this.colorLayer[(y + (int) Math.round(this.colorNoise.noise(chunkX, chunkZ, 0.5D, 2.0D) * 2.0D)) % this.colorLayer.length];
+ this.setColoredGroundLayer(chunkData, x, y, z, color);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void setColoredGroundLayer(BaseFullChunk chunkData, int x, int y, int z, int color) {
+ if (color >= 0) {
+ chunkData.setBlock(x, y, z, STAINED_HARDENED_CLAY, color & 0xf);
+ } else {
+ chunkData.setBlock(x, y, z, TERRACOTTA);
+ }
+ }
+
+ private void setRandomLayerColor(NukkitRandom random, int minLayerCount, int minLayerHeight, int color) {
+ for (int i = 0; i < random.nextBoundedInt(4) + minLayerCount; i++) {
+ int j = random.nextBoundedInt(this.colorLayer.length);
+ int k = 0;
+ while (k < random.nextBoundedInt(3) + minLayerHeight && j < this.colorLayer.length) {
+ this.colorLayer[j++] = color;
+ k++;
+ }
+ }
+ }
+
+ private void initializeColorLayers(NukkitRandom random) {
+ Arrays.fill(this.colorLayer, -1); // hard clay, other values are stained clay
+ int i = 0;
+ while (i < this.colorLayer.length) {
+ i += random.nextBoundedInt(5) + 1;
+ if (i < this.colorLayer.length) {
+ this.colorLayer[i++] = 1; // orange
+ }
+ }
+ this.setRandomLayerColor(random, 2, 1, 4); // yellow
+ this.setRandomLayerColor(random, 2, 2, 12); // brown
+ this.setRandomLayerColor(random, 2, 1, 14); // red
+ int j = 0;
+ for (i = 0; i < random.nextBoundedInt(3) + 3; i++) {
+ j += random.nextBoundedInt(16) + 4;
+ if (j >= this.colorLayer.length) {
+ break;
+ }
+ if (random.nextBoundedInt(2) == 0 || j < this.colorLayer.length - 1 && random.nextBoundedInt(2) == 0) {
+ this.colorLayer[j - 1] = 8; // light gray
+ } else {
+ this.colorLayer[j] = 0; // white
+ }
+ }
+ }
+
+ public enum MesaType {
+ NORMAL,
+ BRYCE,
+ FOREST
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorMycel.java b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorMycel.java
new file mode 100644
index 0000000..32292f3
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorMycel.java
@@ -0,0 +1,8 @@
+package worldgeneratorextension.vanillagenerator.ground;
+
+public class GroundGeneratorMycel extends GroundGenerator {
+
+ public GroundGeneratorMycel() {
+ setTopMaterial(MYCELIUM);
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchDirt.java b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchDirt.java
new file mode 100644
index 0000000..d70b258
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchDirt.java
@@ -0,0 +1,22 @@
+package worldgeneratorextension.vanillagenerator.ground;
+
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.format.generic.BaseFullChunk;
+import cn.nukkit.math.NukkitRandom;
+
+public class GroundGeneratorPatchDirt extends GroundGenerator {
+
+ @Override
+ public void generateTerrainColumn(ChunkManager world, BaseFullChunk chunkData, NukkitRandom random, int chunkX, int chunkZ, int biome, double surfaceNoise) {
+ if (surfaceNoise > 1.75D) {
+ setTopMaterial(DIRT, 1);
+ } else if (surfaceNoise > -0.95D) {
+ setTopMaterial(PODZOL);
+ } else {
+ setTopMaterial(GRASS);
+ }
+ setGroundMaterial(DIRT);
+
+ super.generateTerrainColumn(world, chunkData, random, chunkX, chunkZ, biome, surfaceNoise);
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchDirtAndStone.java b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchDirtAndStone.java
new file mode 100644
index 0000000..97c08b8
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchDirtAndStone.java
@@ -0,0 +1,24 @@
+package worldgeneratorextension.vanillagenerator.ground;
+
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.format.generic.BaseFullChunk;
+import cn.nukkit.math.NukkitRandom;
+
+public class GroundGeneratorPatchDirtAndStone extends GroundGenerator {
+
+ @Override
+ public void generateTerrainColumn(ChunkManager world, BaseFullChunk chunkData, NukkitRandom random, int chunkX, int chunkZ, int biome, double surfaceNoise) {
+ if (surfaceNoise > 1.75D) {
+ setTopMaterial(STONE);
+ setGroundMaterial(STONE);
+ } else if (surfaceNoise > -0.5D) {
+ setTopMaterial(DIRT, 1);
+ setGroundMaterial(DIRT);
+ } else {
+ setTopMaterial(GRASS);
+ setGroundMaterial(DIRT);
+ }
+
+ super.generateTerrainColumn(world, chunkData, random, chunkX, chunkZ, biome, surfaceNoise);
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchGravel.java b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchGravel.java
new file mode 100644
index 0000000..027e74b
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchGravel.java
@@ -0,0 +1,20 @@
+package worldgeneratorextension.vanillagenerator.ground;
+
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.format.generic.BaseFullChunk;
+import cn.nukkit.math.NukkitRandom;
+
+public class GroundGeneratorPatchGravel extends GroundGenerator {
+
+ @Override
+ public void generateTerrainColumn(ChunkManager world, BaseFullChunk chunkData, NukkitRandom random, int chunkX, int chunkZ, int biome, double surfaceNoise) {
+ if (surfaceNoise < -1.0D || surfaceNoise > 2.0D) {
+ setTopMaterial(GRAVEL);
+ setGroundMaterial(GRAVEL);
+ } else {
+ setTopMaterial(GRASS);
+ setGroundMaterial(DIRT);
+ }
+ super.generateTerrainColumn(world, chunkData, random, chunkX, chunkZ, biome, surfaceNoise);
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchPodzol.java b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchPodzol.java
new file mode 100644
index 0000000..128d4b0
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchPodzol.java
@@ -0,0 +1,20 @@
+package worldgeneratorextension.vanillagenerator.ground;
+
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.format.generic.BaseFullChunk;
+import cn.nukkit.math.NukkitRandom;
+
+public class GroundGeneratorPatchPodzol extends GroundGenerator {
+
+ @Override
+ public void generateTerrainColumn(ChunkManager world, BaseFullChunk chunkData, NukkitRandom random, int chunkX, int chunkZ, int biome, double surfaceNoise) {
+ if (surfaceNoise > -0.95D) {
+ setTopMaterial(PODZOL);
+ } else {
+ setTopMaterial(GRASS);
+ }
+ setGroundMaterial(DIRT);
+
+ super.generateTerrainColumn(world, chunkData, random, chunkX, chunkZ, biome, surfaceNoise);
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchStone.java b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchStone.java
new file mode 100644
index 0000000..e0326c7
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorPatchStone.java
@@ -0,0 +1,20 @@
+package worldgeneratorextension.vanillagenerator.ground;
+
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.format.generic.BaseFullChunk;
+import cn.nukkit.math.NukkitRandom;
+
+public class GroundGeneratorPatchStone extends GroundGenerator {
+
+ @Override
+ public void generateTerrainColumn(ChunkManager world, BaseFullChunk chunkData, NukkitRandom random, int chunkX, int chunkZ, int biome, double surfaceNoise) {
+ if (surfaceNoise > 1.0D) {
+ setTopMaterial(STONE);
+ setGroundMaterial(STONE);
+ } else {
+ setTopMaterial(GRASS);
+ setGroundMaterial(DIRT);
+ }
+ super.generateTerrainColumn(world, chunkData, random, chunkX, chunkZ, biome, surfaceNoise);
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorRocky.java b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorRocky.java
new file mode 100644
index 0000000..86e1f68
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorRocky.java
@@ -0,0 +1,9 @@
+package worldgeneratorextension.vanillagenerator.ground;
+
+public class GroundGeneratorRocky extends GroundGenerator {
+
+ public GroundGeneratorRocky() {
+ setTopMaterial(STONE);
+ setGroundMaterial(STONE);
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorSandOcean.java b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorSandOcean.java
new file mode 100644
index 0000000..1f9d021
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorSandOcean.java
@@ -0,0 +1,63 @@
+package worldgeneratorextension.vanillagenerator.ground;
+
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.format.generic.BaseFullChunk;
+import cn.nukkit.math.NukkitRandom;
+
+public class GroundGeneratorSandOcean extends GroundGenerator {
+
+ public GroundGeneratorSandOcean() {
+ setTopMaterial(SAND);
+ setGroundMaterial(SANDSTONE);
+ }
+
+ @Override
+ public void generateTerrainColumn(ChunkManager world, BaseFullChunk chunkData, NukkitRandom random, int chunkX, int chunkZ, int biome, double surfaceNoise) {
+ int seaLevel = 64;
+
+ int topMat = this.topMaterial;
+ int groundMat = this.groundMaterial;
+
+ int x = chunkX & 0xF;
+ int z = chunkZ & 0xF;
+
+ int surfaceHeight = Math.max((int) (surfaceNoise / 3.0D + 3.0D + random.nextDouble() * 0.25D), 1);
+ int deep = -1;
+ for (int y = 255; y >= 0; y--) {
+ if (y <= random.nextBoundedInt(5)) {
+ chunkData.setBlock(x, y, z, BEDROCK);
+ } else {
+ int mat = chunkData.getBlockId(x, y, z);
+ if (mat == AIR) {
+ deep = -1;
+ } else if (mat == STONE) {
+ if (deep == -1) {
+ if (y >= seaLevel - 5 && y <= seaLevel) {
+ topMat = this.topMaterial;
+ groundMat = this.groundMaterial;
+ }
+
+ deep = surfaceHeight;
+ if (y >= seaLevel - 2) {
+ chunkData.setBlock(x, y, z, topMat, this.topData);
+ } else if (y < seaLevel - 8 - surfaceHeight) {
+ topMat = AIR;
+ groundMat = STONE;
+ chunkData.setBlock(x, y, z, SAND);
+ } else {
+ chunkData.setBlock(x, y, z, groundMat, this.groundData);
+ }
+ } else if (deep > 0) {
+ deep--;
+ chunkData.setBlock(x, y, z, groundMat, this.groundData);
+
+ if (deep == 0 && groundMat == SAND) {
+ deep = random.nextBoundedInt(4) + Math.max(0, y - seaLevel - 1);
+ groundMat = SANDSTONE;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorSandy.java b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorSandy.java
new file mode 100644
index 0000000..b0a0172
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorSandy.java
@@ -0,0 +1,9 @@
+package worldgeneratorextension.vanillagenerator.ground;
+
+public class GroundGeneratorSandy extends GroundGenerator {
+
+ public GroundGeneratorSandy() {
+ setTopMaterial(SAND);
+ setGroundMaterial(SAND);
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorSnowy.java b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorSnowy.java
new file mode 100644
index 0000000..585116d
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/ground/GroundGeneratorSnowy.java
@@ -0,0 +1,8 @@
+package worldgeneratorextension.vanillagenerator.ground;
+
+public class GroundGeneratorSnowy extends GroundGenerator {
+
+ public GroundGeneratorSnowy() {
+ setTopMaterial(SNOW);
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/noise/PerlinNoise.java b/src/main/java/worldgeneratorextension/vanillagenerator/noise/PerlinNoise.java
new file mode 100644
index 0000000..048d5c6
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/noise/PerlinNoise.java
@@ -0,0 +1,142 @@
+package worldgeneratorextension.vanillagenerator.noise;
+
+import worldgeneratorextension.vanillagenerator.noise.bukkit.PerlinNoiseGenerator;
+import cn.nukkit.math.NukkitRandom;
+
+public class PerlinNoise extends PerlinNoiseGenerator {
+
+ /**
+ * Creates an instance using the given PRNG.
+ *
+ * @param rand the PRNG used to generate the seed permutation
+ */
+ public PerlinNoise(NukkitRandom rand) {
+ offsetX = rand.nextDouble() * 256;
+ offsetY = rand.nextDouble() * 256;
+ offsetZ = rand.nextDouble() * 256;
+ // The only reason why I'm re-implementing the constructor code is that I've read
+ // on at least 3 different sources that the permutation table should initially be
+ // populated with indices.
+ // "The permutation table is his answer to the issue of random numbers.
+ // First take an array of decent length, usually 256 values. Fill it sequentially with each
+ // number in that range: so index 1 gets 1, index 8 gets 8, index 251 gets 251, etc...
+ // Then randomly shuffle the values so you have a table of 256 random values, but only
+ // contains the values between 0 and 255."
+ // source: https://code.google.com/p/fractalterraingeneration/wiki/Perlin_Noise
+ for (int i = 0; i < 256; i++) {
+ perm[i] = i;
+ }
+ for (int i = 0; i < 256; i++) {
+ int pos = rand.nextBoundedInt(256 - i) + i;
+ int old = perm[i];
+ perm[i] = perm[pos];
+ perm[pos] = old;
+ perm[i + 256] = perm[i];
+ }
+ }
+
+ public static int floor(double x) {
+ int floored = (int) x;
+ return x < floored ? floored - 1 : floored;
+ }
+
+ /**
+ * Generates a rectangular section of this generator's noise.
+ *
+ * @param noise the output of the previous noise layer
+ * @param x the X offset
+ * @param y the Y offset
+ * @param z the Z offset
+ * @param sizeX the size on the X axis
+ * @param sizeY the size on the Y axis
+ * @param sizeZ the size on the Z axis
+ * @param scaleX the X scale parameter
+ * @param scaleY the Y scale parameter
+ * @param scaleZ the Z scale parameter
+ * @param amplitude the amplitude parameter
+ * @return {@code noise} with this layer of noise added
+ */
+ public double[] getNoise(double[] noise, double x, double y, double z, int sizeX, int sizeY, int sizeZ, double scaleX, double scaleY, double scaleZ, double amplitude) {
+ if (sizeY == 1) {
+ return get2dNoise(noise, x, z, sizeX, sizeZ, scaleX, scaleZ, amplitude);
+ } else {
+ return get3dNoise(noise, x, y, z, sizeX, sizeY, sizeZ, scaleX, scaleY, scaleZ, amplitude);
+ }
+ }
+
+ protected double[] get2dNoise(double[] noise, double x, double z, int sizeX, int sizeZ, double scaleX, double scaleZ, double amplitude) {
+ int index = 0;
+ for (int i = 0; i < sizeX; i++) {
+ double dx = x + offsetX + i * scaleX;
+ int floorX = floor(dx);
+ int ix = floorX & 255;
+ dx -= floorX;
+ double fx = fade(dx);
+ for (int j = 0; j < sizeZ; j++) {
+ double dz = z + offsetZ + j * scaleZ;
+ int floorZ = floor(dz);
+ int iz = floorZ & 255;
+ dz -= floorZ;
+ double fz = fade(dz);
+ // Hash coordinates of the square corners
+ int a = perm[ix];
+ int aa = perm[a] + iz;
+ int b = perm[ix + 1];
+ int ba = perm[b] + iz;
+ double x1 = lerp(fx, grad(perm[aa], dx, 0, dz), grad(perm[ba], dx - 1, 0, dz));
+ double x2 = lerp(fx, grad(perm[aa + 1], dx, 0, dz - 1), grad(perm[ba + 1], dx - 1, 0, dz - 1));
+ noise[index++] += lerp(fz, x1, x2) * amplitude;
+ }
+ }
+ return noise;
+ }
+
+ protected double[] get3dNoise(double[] noise, double x, double y, double z, int sizeX, int sizeY, int sizeZ, double scaleX, double scaleY, double scaleZ, double amplitude) {
+ int n = -1;
+ double x1 = 0;
+ double x2 = 0;
+ double x3 = 0;
+ double x4 = 0;
+ int index = 0;
+ for (int i = 0; i < sizeX; i++) {
+ double dx = x + offsetX + i * scaleX;
+ int floorX = floor(dx);
+ int ix = floorX & 255;
+ dx -= floorX;
+ double fx = fade(dx);
+ for (int j = 0; j < sizeZ; j++) {
+ double dz = z + offsetZ + j * scaleZ;
+ int floorZ = floor(dz);
+ int iz = floorZ & 255;
+ dz -= floorZ;
+ double fz = fade(dz);
+ for (int k = 0; k < sizeY; k++) {
+ double dy = y + offsetY + k * scaleY;
+ int floorY = floor(dy);
+ int iy = floorY & 255;
+ dy -= floorY;
+ double fy = fade(dy);
+ if (k == 0 || iy != n) {
+ n = iy;
+ // Hash coordinates of the cube corners
+ int a = perm[ix] + iy;
+ int aa = perm[a] + iz;
+ int ab = perm[a + 1] + iz;
+ int b = perm[ix + 1] + iy;
+ int ba = perm[b] + iz;
+ int bb = perm[b + 1] + iz;
+ x1 = lerp(fx, grad(perm[aa], dx, dy, dz), grad(perm[ba], dx - 1, dy, dz));
+ x2 = lerp(fx, grad(perm[ab], dx, dy - 1, dz), grad(perm[bb], dx - 1, dy - 1, dz));
+ x3 = lerp(fx, grad(perm[aa + 1], dx, dy, dz - 1), grad(perm[ba + 1], dx - 1, dy, dz - 1));
+ x4 = lerp(fx, grad(perm[ab + 1], dx, dy - 1, dz - 1), grad(perm[bb + 1], dx - 1, dy - 1, dz - 1));
+ }
+ double y1 = lerp(fy, x1, x2);
+ double y2 = lerp(fy, x3, x4);
+
+ noise[index++] += lerp(fz, y1, y2) * amplitude;
+ }
+ }
+ }
+ return noise;
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/noise/PerlinOctaveGenerator.java b/src/main/java/worldgeneratorextension/vanillagenerator/noise/PerlinOctaveGenerator.java
new file mode 100644
index 0000000..cbdb935
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/noise/PerlinOctaveGenerator.java
@@ -0,0 +1,132 @@
+package worldgeneratorextension.vanillagenerator.noise;
+
+import cn.nukkit.math.NukkitRandom;
+import worldgeneratorextension.vanillagenerator.noise.bukkit.NoiseGenerator;
+import worldgeneratorextension.vanillagenerator.noise.bukkit.OctaveGenerator;
+
+import java.util.Arrays;
+
+public class PerlinOctaveGenerator extends OctaveGenerator {
+
+ protected final int sizeX;
+ protected final int sizeY;
+ protected final int sizeZ;
+ protected double[] noise;
+
+ public PerlinOctaveGenerator(NukkitRandom rand, int octaves, int sizeX, int sizeZ) {
+ this(rand, octaves, sizeX, 1, sizeZ);
+ }
+
+ public PerlinOctaveGenerator(NukkitRandom rand, int octaves, int sizeX, int sizeY, int sizeZ) {
+ this(createOctaves(rand, octaves), rand, sizeX, sizeY, sizeZ);
+ }
+
+ /**
+ * Creates a generator for multiple layers of Perlin noise.
+ *
+ * @param octaves the noise generators
+ * @param rand the PRNG
+ * @param sizeX the size on the X axis
+ * @param sizeY the size on the Y axis
+ * @param sizeZ the size on the Z axis
+ */
+ public PerlinOctaveGenerator(NoiseGenerator[] octaves, NukkitRandom rand, int sizeX, int sizeY, int sizeZ) {
+ super(octaves);
+ this.sizeX = sizeX;
+ this.sizeY = sizeY;
+ this.sizeZ = sizeZ;
+ noise = new double[sizeX * sizeY * sizeZ];
+ }
+
+ protected static NoiseGenerator[] createOctaves(NukkitRandom rand, int octaves) {
+ NoiseGenerator[] result = new NoiseGenerator[octaves];
+
+ for (int i = 0; i < octaves; i++) {
+ result[i] = new PerlinNoise(rand);
+ }
+
+ return result;
+ }
+
+ protected static long floor(double x) {
+ return x >= 0 ? (long) x : (long) x - 1;
+ }
+
+ /**
+ * Generates multiple layers of noise.
+ *
+ * @param x the starting X coordinate
+ * @param z the starting Z coordinate
+ * @param lacunarity layer n's frequency as a fraction of layer
+ * {@code n - 1}'s frequency
+ * @param persistence layer n's amplitude as a multiple of layer
+ * {@code n - 1}'s amplitude
+ * @return The noise array
+ */
+ public double[] getFractalBrownianMotion(double x, double z, double lacunarity, double persistence) {
+ return getFractalBrownianMotion(x, 0, z, lacunarity, persistence);
+ }
+
+ /**
+ * Generates multiple layers of noise.
+ *
+ * @param x the starting X coordinate
+ * @param y the starting Y coordinate
+ * @param z the starting Z coordinate
+ * @param lacunarity layer n's frequency as a fraction of layer
+ * {@code n - 1}'s frequency
+ * @param persistence layer n's amplitude as a multiple of layer
+ * {@code n - 1}'s amplitude
+ * @return The noise array
+ */
+ public double[] getFractalBrownianMotion(double x, double y, double z, double lacunarity, double persistence) {
+ Arrays.fill(noise, 0);
+
+ double freq = 1;
+ double amp = 1;
+
+ x *= xScale;
+ y *= yScale;
+ z *= zScale;
+
+ // fBm
+ // the noise have to be periodic over x and z axis: otherwise it can go crazy with high
+ // input, leading to strange oddities in terrain generation like the old minecraft farland
+ // symptoms.
+ for (NoiseGenerator octave : octaves) {
+ double dx = x * freq;
+ double dz = z * freq;
+ // compute integer part
+ long lx = floor(dx);
+ long lz = floor(dz);
+ // compute fractional part
+ dx -= lx;
+ dz -= lz;
+ // wrap integer part to 0..16777216
+ lx %= 16777216;
+ lz %= 16777216;
+ // add to fractional part
+ dx += lx;
+ dz += lz;
+
+ double dy = y * freq;
+ noise = ((PerlinNoise) octave).getNoise(noise, dx, dy, dz, sizeX, sizeY, sizeZ, xScale * freq, yScale * freq, zScale * freq, amp);
+ freq *= lacunarity;
+ amp *= persistence;
+ }
+
+ return noise;
+ }
+
+ public int getSizeX() {
+ return this.sizeX;
+ }
+
+ public int getSizeY() {
+ return this.sizeY;
+ }
+
+ public int getSizeZ() {
+ return this.sizeZ;
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/noise/SimplexNoise.java b/src/main/java/worldgeneratorextension/vanillagenerator/noise/SimplexNoise.java
new file mode 100644
index 0000000..72c84e3
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/noise/SimplexNoise.java
@@ -0,0 +1,318 @@
+package worldgeneratorextension.vanillagenerator.noise;
+
+import cn.nukkit.math.NukkitRandom;
+
+/**
+ * A speed-improved simplex noise algorithm.
+ *
+ *
+ * Based on example code by Stefan Gustavson (stegu@itn.liu.se). Optimisations
+ * by Peter Eastman (peastman@drizzle.stanford.edu). Better rank ordering method
+ * by Stefan Gustavson in 2012.
+ *
+ *
+ * This could be sped up even further, but it's useful as is.
+ */
+public class SimplexNoise extends PerlinNoise {
+
+ protected static final double SQRT_3 = 1.7320508075688772; // Math.sqrt(3)
+ protected static final double F2 = 0.5 * (SQRT_3 - 1);
+ protected static final double G2 = (3 - SQRT_3) / 6;
+ protected static final double G22 = G2 * 2.0 - 1;
+ protected static final double F3 = 1.0 / 3.0;
+ protected static final double G3 = 1.0 / 6.0;
+ protected static final double G32 = G3 * 2.0;
+ protected static final double G33 = G3 * 3.0 - 1.0;
+ private static final Grad[] grad3 = {new Grad(1, 1, 0), new Grad(-1, 1, 0), new Grad(1, -1, 0), new Grad(-1, -1, 0), new Grad(1, 0, 1), new Grad(-1, 0, 1), new Grad(1, 0, -1), new Grad(-1, 0, -1), new Grad(0, 1, 1), new Grad(0, -1, 1), new Grad(0, 1, -1), new Grad(0, -1, -1)};
+ protected final int[] permMod12 = new int[512];
+
+ /**
+ * Creates a simplex noise generator.
+ *
+ * @param rand the PRNG to use
+ */
+ public SimplexNoise(NukkitRandom rand) {
+ super(rand);
+ for (int i = 0; i < 512; i++) {
+ permMod12[i] = perm[i] % 12;
+ }
+ }
+
+ public static int floor(double x) {
+ return x > 0 ? (int) x : (int) x - 1;
+ }
+
+ protected static double dot(Grad g, double x, double y) {
+ return g.x * x + g.y * y;
+ }
+
+ protected static double dot(Grad g, double x, double y, double z) {
+ return g.x * x + g.y * y + g.z * z;
+ }
+
+ @Override
+ protected double[] get2dNoise(double[] noise, double x, double z, int sizeX, int sizeY, double scaleX, double scaleY, double amplitude) {
+ int index = 0;
+ for (int i = 0; i < sizeY; i++) {
+ double zin = offsetY + (z + i) * scaleY;
+ for (int j = 0; j < sizeX; j++) {
+ double xin = offsetX + (x + j) * scaleX;
+ noise[index++] += simplex2D(xin, zin) * amplitude;
+ }
+ }
+ return noise;
+ }
+
+ @Override
+ protected double[] get3dNoise(double[] noise, double x, double y, double z, int sizeX, int sizeY, int sizeZ, double scaleX, double scaleY, double scaleZ, double amplitude) {
+ int index = 0;
+ for (int i = 0; i < sizeZ; i++) {
+ double zin = offsetZ + (z + i) * scaleZ;
+ for (int j = 0; j < sizeX; j++) {
+ double xin = offsetX + (x + j) * scaleX;
+ for (int k = 0; k < sizeY; k++) {
+ double yin = offsetY + (y + k) * scaleY;
+ noise[index++] += simplex3D(xin, yin, zin) * amplitude;
+ }
+ }
+ }
+ return noise;
+ }
+
+ @Override
+ public double noise(double xin, double yin) {
+ xin += offsetX;
+ yin += offsetY;
+ return simplex2D(xin, yin);
+ }
+
+ @Override
+ public double noise(double xin, double yin, double zin) {
+ xin += offsetX;
+ yin += offsetY;
+ zin += offsetZ;
+ return simplex3D(xin, yin, zin);
+ }
+
+ private double simplex2D(double xin, double yin) {
+ // Skew the input space to determine which simplex cell we're in
+ double s = (xin + yin) * F2; // Hairy factor for 2D
+ int i = floor(xin + s);
+ int j = floor(yin + s);
+ double t = (i + j) * G2;
+ double dx0 = i - t; // Unskew the cell origin back to (x,y) space
+ double dy0 = j - t;
+ double x0 = xin - dx0; // The x,y distances from the cell origin
+ double y0 = yin - dy0;
+
+ // For the 2D case, the simplex shape is an equilateral triangle.
+ // Determine which simplex we are in.
+ int i1; // Offsets for second (middle) corner of simplex in (i,j) coords
+ int j1;
+ if (x0 > y0) {
+ i1 = 1; // lower triangle, XY order: (0,0)->(1,0)->(1,1)
+ j1 = 0;
+ } else {
+ i1 = 0; // upper triangle, YX order: (0,0)->(0,1)->(1,1)
+ j1 = 1;
+ }
+
+ // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
+ // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
+ // c = (3-sqrt(3))/6
+ double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
+ double y1 = y0 - j1 + G2;
+ double x2 = x0 + G22; // Offsets for last corner in (x,y) unskewed coords
+ double y2 = y0 + G22;
+
+ // Work out the hashed gradient indices of the three simplex corners
+ int ii = i & 255;
+ int jj = j & 255;
+ int gi0 = permMod12[ii + perm[jj]];
+ int gi1 = permMod12[ii + i1 + perm[jj + j1]];
+ int gi2 = permMod12[ii + 1 + perm[jj + 1]];
+
+ // Calculate the contribution from the three corners
+ double t0 = 0.5 - x0 * x0 - y0 * y0;
+ double n0;
+ if (t0 < 0) {
+ n0 = 0.0;
+ } else {
+ t0 *= t0;
+ n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient
+ }
+
+ double t1 = 0.5 - x1 * x1 - y1 * y1;
+ double n1;
+ if (t1 < 0) {
+ n1 = 0.0;
+ } else {
+ t1 *= t1;
+ n1 = t1 * t1 * dot(grad3[gi1], x1, y1);
+ }
+
+ double t2 = 0.5 - x2 * x2 - y2 * y2;
+ double n2;
+ if (t2 < 0) {
+ n2 = 0.0;
+ } else {
+ t2 *= t2;
+ n2 = t2 * t2 * dot(grad3[gi2], x2, y2);
+ }
+
+ // Add contributions from each corner to get the final noise value.
+ // The result is scaled to return values in the interval [-1,1].
+ return 70.0 * (n0 + n1 + n2);
+ }
+
+ private double simplex3D(double xin, double yin, double zin) {
+ // Skew the input space to determine which simplex cell we're in
+ double s = (xin + yin + zin) * F3; // Very nice and simple skew factor for 3D
+ int i = floor(xin + s);
+ int j = floor(yin + s);
+ int k = floor(zin + s);
+ double t = (i + j + k) * G3;
+ double dx0 = i - t; // Unskew the cell origin back to (x,y,z) space
+ double dy0 = j - t;
+ double dz0 = k - t;
+
+ // For the 3D case, the simplex shape is a slightly irregular tetrahedron.
+ int i1; // Offsets for second corner of simplex in (i,j,k) coords
+ int j1;
+ int k1;
+ int i2; // Offsets for third corner of simplex in (i,j,k) coords
+ int j2;
+ int k2;
+
+ double x0 = xin - dx0; // The x,y,z distances from the cell origin
+ double y0 = yin - dy0;
+ double z0 = zin - dz0;
+ // Determine which simplex we are in
+ if (x0 >= y0) {
+ if (y0 >= z0) {
+ i1 = 1; // X Y Z order
+ j1 = 0;
+ k1 = 0;
+ i2 = 1;
+ j2 = 1;
+ k2 = 0;
+ } else if (x0 >= z0) {
+ i1 = 1; // X Z Y order
+ j1 = 0;
+ k1 = 0;
+ i2 = 1;
+ j2 = 0;
+ k2 = 1;
+ } else {
+ i1 = 0; // Z X Y order
+ j1 = 0;
+ k1 = 1;
+ i2 = 1;
+ j2 = 0;
+ k2 = 1;
+ }
+ } else { // x0= 0 ? (int) x : (int) x - 1;
+ }
+
+ protected static double fade(double x) {
+ return x * x * x * (x * (x * 6 - 15) + 10);
+ }
+
+ protected static double lerp(double x, double y, double z) {
+ return y + x * (z - y);
+ }
+
+ protected static double grad(int hash, double x, double y, double z) {
+ hash &= 15;
+ double u = hash < 8 ? x : y;
+ double v = hash < 4 ? y : hash == 12 || hash == 14 ? x : z;
+ return ((hash & 1) == 0 ? u : -u) + ((hash & 2) == 0 ? v : -v);
+ }
+
+ /**
+ * Computes and returns the 1D noise for the given coordinate in 1D space
+ *
+ * @param x X coordinate
+ * @return Noise at given location, from range -1 to 1
+ */
+ public double noise(double x) {
+ return this.noise(x, 0, 0);
+ }
+
+ /**
+ * Computes and returns the 2D noise for the given coordinates in 2D space
+ *
+ * @param x X coordinate
+ * @param y Y coordinate
+ * @return Noise at given location, from range -1 to 1
+ */
+ public double noise(double x, double y) {
+ return this.noise(x, y, 0);
+ }
+
+ /**
+ * Computes and returns the 3D noise for the given coordinates in 3D space
+ *
+ * @param x X coordinate
+ * @param y Y coordinate
+ * @param z Z coordinate
+ * @return Noise at given location, from range -1 to 1
+ */
+ public abstract double noise(double x, double y, double z);
+
+ /**
+ * Generates noise for the 1D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param octaves Number of octaves to use
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @return Resulting noise
+ */
+ public double noise(double x, int octaves, double frequency, double amplitude) {
+ return this.noise(x, 0, 0, octaves, frequency, amplitude);
+ }
+
+ /**
+ * Generates noise for the 1D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param octaves Number of octaves to use
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @param normalized If true, normalize the value to [-1, 1]
+ * @return Resulting noise
+ */
+ public double noise(double x, int octaves, double frequency, double amplitude, boolean normalized) {
+ return this.noise(x, 0, 0, octaves, frequency, amplitude, normalized);
+ }
+
+ /**
+ * Generates noise for the 2D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param y Y-coordinate
+ * @param octaves Number of octaves to use
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @return Resulting noise
+ */
+ public double noise(double x, double y, int octaves, double frequency, double amplitude) {
+ return this.noise(x, y, 0, octaves, frequency, amplitude);
+ }
+
+ /**
+ * Generates noise for the 2D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param y Y-coordinate
+ * @param octaves Number of octaves to use
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @param normalized If true, normalize the value to [-1, 1]
+ * @return Resulting noise
+ */
+ public double noise(double x, double y, int octaves, double frequency, double amplitude, boolean normalized) {
+ return this.noise(x, y, 0, octaves, frequency, amplitude, normalized);
+ }
+
+ /**
+ * Generates noise for the 3D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param y Y-coordinate
+ * @param z Z-coordinate
+ * @param octaves Number of octaves to use
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @return Resulting noise
+ */
+ public double noise(double x, double y, double z, int octaves, double frequency, double amplitude) {
+ return this.noise(x, y, z, octaves, frequency, amplitude, false);
+ }
+
+ /**
+ * Generates noise for the 3D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param y Y-coordinate
+ * @param z Z-coordinate
+ * @param octaves Number of octaves to use
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @param normalized If true, normalize the value to [-1, 1]
+ * @return Resulting noise
+ */
+ public double noise(double x, double y, double z, int octaves, double frequency, double amplitude, boolean normalized) {
+ double result = 0;
+ double amp = 1;
+ double freq = 1;
+ double max = 0;
+
+ for (int i = 0; i < octaves; i++) {
+ result += this.noise(x * freq, y * freq, z * freq) * amp;
+ max += amp;
+ freq *= frequency;
+ amp *= amplitude;
+ }
+
+ if (normalized) {
+ result /= max;
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/OctaveGenerator.java b/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/OctaveGenerator.java
new file mode 100644
index 0000000..b2f28eb
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/OctaveGenerator.java
@@ -0,0 +1,200 @@
+package worldgeneratorextension.vanillagenerator.noise.bukkit;
+
+/**
+ * Creates noise using unbiased octaves
+ */
+public abstract class OctaveGenerator {
+
+ protected final NoiseGenerator[] octaves;
+ protected double xScale = 1;
+ protected double yScale = 1;
+ protected double zScale = 1;
+
+ protected OctaveGenerator(NoiseGenerator[] octaves) {
+ this.octaves = octaves;
+ }
+
+ /**
+ * Sets the scale used for all coordinates passed to this generator.
+ *
+ * This is the equivalent to setting each coordinate to the specified
+ * value.
+ *
+ * @param scale New value to scale each coordinate by
+ */
+ public void setScale(double scale) {
+ this.setXScale(scale);
+ this.setYScale(scale);
+ this.setZScale(scale);
+ }
+
+ /**
+ * Gets the scale used for each X-coordinates passed
+ *
+ * @return X scale
+ */
+ public double getXScale() {
+ return this.xScale;
+ }
+
+ /**
+ * Sets the scale used for each X-coordinates passed
+ *
+ * @param scale New X scale
+ */
+ public void setXScale(double scale) {
+ this.xScale = scale;
+ }
+
+ /**
+ * Gets the scale used for each Y-coordinates passed
+ *
+ * @return Y scale
+ */
+ public double getYScale() {
+ return this.yScale;
+ }
+
+ /**
+ * Sets the scale used for each Y-coordinates passed
+ *
+ * @param scale New Y scale
+ */
+ public void setYScale(double scale) {
+ this.yScale = scale;
+ }
+
+ /**
+ * Gets the scale used for each Z-coordinates passed
+ *
+ * @return Z scale
+ */
+ public double getZScale() {
+ return this.zScale;
+ }
+
+ /**
+ * Sets the scale used for each Z-coordinates passed
+ *
+ * @param scale New Z scale
+ */
+ public void setZScale(double scale) {
+ this.zScale = scale;
+ }
+
+ /**
+ * Gets a clone of the individual octaves used within this generator
+ *
+ * @return Clone of the individual octaves
+ */
+ public NoiseGenerator[] getOctaves() {
+ return this.octaves.clone();
+ }
+
+ /**
+ * Generates noise for the 1D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @return Resulting noise
+ */
+ public double noise(double x, double frequency, double amplitude) {
+ return this.noise(x, 0, 0, frequency, amplitude);
+ }
+
+ /**
+ * Generates noise for the 1D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @param normalized If true, normalize the value to [-1, 1]
+ * @return Resulting noise
+ */
+ public double noise(double x, double frequency, double amplitude, boolean normalized) {
+ return this.noise(x, 0, 0, frequency, amplitude, normalized);
+ }
+
+ /**
+ * Generates noise for the 2D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param y Y-coordinate
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @return Resulting noise
+ */
+ public double noise(double x, double y, double frequency, double amplitude) {
+ return this.noise(x, y, 0, frequency, amplitude);
+ }
+
+ /**
+ * Generates noise for the 2D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param y Y-coordinate
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @param normalized If true, normalize the value to [-1, 1]
+ * @return Resulting noise
+ */
+ public double noise(double x, double y, double frequency, double amplitude, boolean normalized) {
+ return this.noise(x, y, 0, frequency, amplitude, normalized);
+ }
+
+ /**
+ * Generates noise for the 3D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param y Y-coordinate
+ * @param z Z-coordinate
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @return Resulting noise
+ */
+ public double noise(double x, double y, double z, double frequency, double amplitude) {
+ return this.noise(x, y, z, frequency, amplitude, false);
+ }
+
+ /**
+ * Generates noise for the 3D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param y Y-coordinate
+ * @param z Z-coordinate
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @param normalized If true, normalize the value to [-1, 1]
+ * @return Resulting noise
+ */
+ public double noise(double x, double y, double z, double frequency, double amplitude, boolean normalized) {
+ double result = 0;
+ double amp = 1;
+ double freq = 1;
+ double max = 0;
+
+ x *= this.xScale;
+ y *= this.yScale;
+ z *= this.zScale;
+
+ for (NoiseGenerator octave : this.octaves) {
+ result += octave.noise(x * freq, y * freq, z * freq) * amp;
+ max += amp;
+ freq *= frequency;
+ amp *= amplitude;
+ }
+
+ if (normalized) {
+ result /= max;
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/PerlinNoiseGenerator.java b/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/PerlinNoiseGenerator.java
new file mode 100644
index 0000000..87af2da
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/PerlinNoiseGenerator.java
@@ -0,0 +1,223 @@
+package worldgeneratorextension.vanillagenerator.noise.bukkit;
+
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.NukkitRandom;
+
+/**
+ * Generates noise using the "classic" perlin generator
+ *
+ * @see SimplexNoiseGenerator "Improved" and faster version with slightly
+ * different results
+ */
+public class PerlinNoiseGenerator extends NoiseGenerator {
+
+ protected static final int[][] grad3 = {
+ {1, 1, 0}, {-1, 1, 0}, {1, -1, 0}, {-1, -1, 0},
+ {1, 0, 1}, {-1, 0, 1}, {1, 0, -1}, {-1, 0, -1},
+ {0, 1, 1}, {0, -1, 1}, {0, 1, -1}, {0, -1, -1}
+ };
+ private static final PerlinNoiseGenerator instance = new PerlinNoiseGenerator();
+
+ protected PerlinNoiseGenerator() {
+ int[] p = {151, 160, 137, 91, 90, 15, 131, 13, 201,
+ 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37,
+ 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62,
+ 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56,
+ 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139,
+ 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133,
+ 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25,
+ 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200,
+ 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3,
+ 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255,
+ 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42,
+ 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153,
+ 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79,
+ 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242,
+ 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249,
+ 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204,
+ 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222,
+ 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180};
+
+ for (int i = 0; i < 512; ++i) {
+ this.perm[i] = p[i & 255];
+ }
+ }
+
+ /**
+ * Creates a seeded perlin noise generator for the given world
+ *
+ * @param world World to construct this generator for
+ */
+ public PerlinNoiseGenerator(Level world) {
+ this(new NukkitRandom(world.getSeed()));
+ }
+
+ /**
+ * Creates a seeded perlin noise generator for the given level
+ *
+ * @param level Level to construct this generator for
+ */
+ public PerlinNoiseGenerator(ChunkManager level) {
+ this(new NukkitRandom(level.getSeed()));
+ }
+
+ /**
+ * Creates a seeded perlin noise generator for the given seed
+ *
+ * @param seed Seed to construct this generator for
+ */
+ public PerlinNoiseGenerator(long seed) {
+ this(new NukkitRandom(seed));
+ }
+
+ /**
+ * Creates a seeded perlin noise generator with the given {@link NukkitRandom}
+ *
+ * @param rand NukkitRandom to construct with
+ */
+ public PerlinNoiseGenerator(NukkitRandom rand) {
+ this.offsetX = rand.nextDouble() * 256;
+ this.offsetY = rand.nextDouble() * 256;
+ this.offsetZ = rand.nextDouble() * 256;
+
+ for (int i = 0; i < 256; ++i) {
+ this.perm[i] = rand.nextBoundedInt(256);
+ }
+
+ for (int i = 0; i < 256; ++i) {
+ int pos = rand.nextBoundedInt(256 - i) + i;
+ int old = this.perm[i];
+
+ this.perm[i] = this.perm[pos];
+ this.perm[pos] = old;
+ this.perm[i + 256] = this.perm[i];
+ }
+ }
+
+ /**
+ * Computes and returns the 1D unseeded perlin noise for the given
+ * coordinates in 1D space
+ *
+ * @param x X coordinate
+ * @return Noise at given location, from range -1 to 1
+ */
+ public static double getNoise(double x) {
+ return instance.noise(x);
+ }
+
+ /**
+ * Computes and returns the 2D unseeded perlin noise for the given
+ * coordinates in 2D space
+ *
+ * @param x X coordinate
+ * @param y Y coordinate
+ * @return Noise at given location, from range -1 to 1
+ */
+ public static double getNoise(double x, double y) {
+ return instance.noise(x, y);
+ }
+
+ /**
+ * Computes and returns the 3D unseeded perlin noise for the given
+ * coordinates in 3D space
+ *
+ * @param x X coordinate
+ * @param y Y coordinate
+ * @param z Z coordinate
+ * @return Noise at given location, from range -1 to 1
+ */
+ public static double getNoise(double x, double y, double z) {
+ return instance.noise(x, y, z);
+ }
+
+ /**
+ * Gets the singleton unseeded instance of this generator
+ *
+ * @return Singleton
+ */
+ public static PerlinNoiseGenerator getInstance() {
+ return instance;
+ }
+
+ @Override
+ public double noise(double x, double y, double z) {
+ x += this.offsetX;
+ y += this.offsetY;
+ z += this.offsetZ;
+
+ int floorX = floor(x);
+ int floorY = floor(y);
+ int floorZ = floor(z);
+
+ // Find unit cube containing the point
+ int X = floorX & 255;
+ int Y = floorY & 255;
+ int Z = floorZ & 255;
+
+ // Get relative xyz coordinates of the point within the cube
+ x -= floorX;
+ y -= floorY;
+ z -= floorZ;
+
+ // Compute fade curves for xyz
+ double fX = fade(x);
+ double fY = fade(y);
+ double fZ = fade(z);
+
+ // Hash coordinates of the cube corners
+ int A = this.perm[X] + Y;
+ int AA = this.perm[A] + Z;
+ int AB = this.perm[A + 1] + Z;
+ int B = this.perm[X + 1] + Y;
+ int BA = this.perm[B] + Z;
+ int BB = this.perm[B + 1] + Z;
+
+ return lerp(fZ, lerp(fY, lerp(fX, grad(this.perm[AA], x, y, z), grad(this.perm[BA], x - 1, y, z)), lerp(fX, grad(this.perm[AB], x, y - 1, z), grad(this.perm[BB], x - 1, y - 1, z))), lerp(fY, lerp(fX, grad(this.perm[AA + 1], x, y, z - 1), grad(this.perm[BA + 1], x - 1, y, z - 1)), lerp(fX, grad(this.perm[AB + 1], x, y - 1, z - 1), grad(this.perm[BB + 1], x - 1, y - 1, z - 1))));
+ }
+
+ /**
+ * Generates noise for the 1D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param octaves Number of octaves to use
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @return Resulting noise
+ */
+ public static double getNoise(double x, int octaves, double frequency, double amplitude) {
+ return instance.noise(x, octaves, frequency, amplitude);
+ }
+
+ /**
+ * Generates noise for the 2D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param y Y-coordinate
+ * @param octaves Number of octaves to use
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @return Resulting noise
+ */
+ public static double getNoise(double x, double y, int octaves, double frequency, double amplitude) {
+ return instance.noise(x, y, octaves, frequency, amplitude);
+ }
+
+ /**
+ * Generates noise for the 3D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param y Y-coordinate
+ * @param z Z-coordinate
+ * @param octaves Number of octaves to use
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @return Resulting noise
+ */
+ public static double getNoise(double x, double y, double z, int octaves, double frequency, double amplitude) {
+ return instance.noise(x, y, z, octaves, frequency, amplitude);
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/PerlinOctaveGenerator.java b/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/PerlinOctaveGenerator.java
new file mode 100644
index 0000000..37fd0a4
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/PerlinOctaveGenerator.java
@@ -0,0 +1,59 @@
+package worldgeneratorextension.vanillagenerator.noise.bukkit;
+
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.NukkitRandom;
+
+/**
+ * Creates perlin noise through unbiased octaves
+ */
+public class PerlinOctaveGenerator extends OctaveGenerator {
+
+ /**
+ * Creates a perlin octave generator for the given world
+ *
+ * @param world World to construct this generator for
+ * @param octaves Amount of octaves to create
+ */
+ public PerlinOctaveGenerator(Level world, int octaves) {
+ this(new NukkitRandom(world.getSeed()), octaves);
+ }
+
+ /**
+ * Creates a perlin octave generator for the given level
+ *
+ * @param level Level to construct this generator for
+ * @param octaves Amount of octaves to create
+ */
+ public PerlinOctaveGenerator(ChunkManager level, int octaves) {
+ this(new NukkitRandom(level.getSeed()), octaves);
+ }
+
+ /**
+ * Creates a perlin octave generator for the given world
+ *
+ * @param seed Seed to construct this generator for
+ * @param octaves Amount of octaves to create
+ */
+ public PerlinOctaveGenerator(long seed, int octaves) {
+ this(new NukkitRandom(seed), octaves);
+ }
+
+ /**
+ * Creates a perlin octave generator for the given {@link NukkitRandom}
+ *
+ * @param rand NukkitRandom object to construct this generator for
+ * @param octaves Amount of octaves to create
+ */
+ public PerlinOctaveGenerator(NukkitRandom rand, int octaves) {
+ super(createOctaves(rand, octaves));
+ }
+
+ private static NoiseGenerator[] createOctaves(NukkitRandom rand, int octaves) {
+ NoiseGenerator[] result = new NoiseGenerator[octaves];
+ for (int i = 0; i < octaves; ++i) {
+ result[i] = new PerlinNoiseGenerator(rand);
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/SimplexNoiseGenerator.java b/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/SimplexNoiseGenerator.java
new file mode 100644
index 0000000..a30fb1c
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/SimplexNoiseGenerator.java
@@ -0,0 +1,534 @@
+package worldgeneratorextension.vanillagenerator.noise.bukkit;
+
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.NukkitRandom;
+
+/**
+ * Generates simplex-based noise.
+ *
+ * This is a modified version of the freely published version in the paper by
+ * Stefan Gustavson at
+ *
+ * http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
+ */
+public class SimplexNoiseGenerator extends PerlinNoiseGenerator {
+
+ protected static final double SQRT_3 = Math.sqrt(3);
+ protected static final double SQRT_5 = Math.sqrt(5);
+ protected static final double F2 = 0.5 * (SQRT_3 - 1);
+ protected static final double G2 = (3 - SQRT_3) / 6;
+ protected static final double G22 = G2 * 2.0 - 1;
+ protected static final double F3 = 1.0 / 3.0;
+ protected static final double G3 = 1.0 / 6.0;
+ protected static final double F4 = (SQRT_5 - 1.0) / 4.0;
+ protected static final double G4 = (5.0 - SQRT_5) / 20.0;
+ protected static final double G42 = G4 * 2.0;
+ protected static final double G43 = G4 * 3.0;
+ protected static final double G44 = G4 * 4.0 - 1.0;
+ protected static final int[][] grad4 = {
+ {0, 1, 1, 1}, {0, 1, 1, -1}, {0, 1, -1, 1}, {0, 1, -1, -1},
+ {0, -1, 1, 1}, {0, -1, 1, -1}, {0, -1, -1, 1}, {0, -1, -1, -1},
+ {1, 0, 1, 1}, {1, 0, 1, -1}, {1, 0, -1, 1}, {1, 0, -1, -1},
+ {-1, 0, 1, 1}, {-1, 0, 1, -1}, {-1, 0, -1, 1}, {-1, 0, -1, -1},
+ {1, 1, 0, 1}, {1, 1, 0, -1}, {1, -1, 0, 1}, {1, -1, 0, -1},
+ {-1, 1, 0, 1}, {-1, 1, 0, -1}, {-1, -1, 0, 1}, {-1, -1, 0, -1},
+ {1, 1, 1, 0}, {1, 1, -1, 0}, {1, -1, 1, 0}, {1, -1, -1, 0},
+ {-1, 1, 1, 0}, {-1, 1, -1, 0}, {-1, -1, 1, 0}, {-1, -1, -1, 0}
+ };
+ protected static final int[][] simplex = {
+ {0, 1, 2, 3}, {0, 1, 3, 2}, {0, 0, 0, 0}, {0, 2, 3, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {1, 2, 3, 0},
+ {0, 2, 1, 3}, {0, 0, 0, 0}, {0, 3, 1, 2}, {0, 3, 2, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {1, 3, 2, 0},
+ {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0},
+ {1, 2, 0, 3}, {0, 0, 0, 0}, {1, 3, 0, 2}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {2, 3, 0, 1}, {2, 3, 1, 0},
+ {1, 0, 2, 3}, {1, 0, 3, 2}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {2, 0, 3, 1}, {0, 0, 0, 0}, {2, 1, 3, 0},
+ {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0},
+ {2, 0, 1, 3}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {3, 0, 1, 2}, {3, 0, 2, 1}, {0, 0, 0, 0}, {3, 1, 2, 0},
+ {2, 1, 0, 3}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {3, 1, 0, 2}, {0, 0, 0, 0}, {3, 2, 0, 1}, {3, 2, 1, 0}
+ };
+ protected double offsetW;
+ private static final SimplexNoiseGenerator instance = new SimplexNoiseGenerator();
+
+ protected SimplexNoiseGenerator() {
+
+ }
+
+ /**
+ * Creates a seeded simplex noise generator for the given world
+ *
+ * @param world World to construct this generator for
+ */
+ public SimplexNoiseGenerator(Level world) {
+ this(new NukkitRandom(world.getSeed()));
+ }
+
+ /**
+ * Creates a seeded simplex noise generator for the given level
+ *
+ * @param level Level to construct this generator for
+ */
+ public SimplexNoiseGenerator(ChunkManager level) {
+ this(new NukkitRandom(level.getSeed()));
+ }
+
+ /**
+ * Creates a seeded simplex noise generator for the given seed
+ *
+ * @param seed Seed to construct this generator for
+ */
+ public SimplexNoiseGenerator(long seed) {
+ this(new NukkitRandom(seed));
+ }
+
+ /**
+ * Creates a seeded simplex noise generator with the given {@link NukkitRandom}
+ *
+ * @param rand NukkitRandom to construct with
+ */
+ public SimplexNoiseGenerator(NukkitRandom rand) {
+ super(rand);
+ this.offsetW = rand.nextDouble() * 256;
+ }
+
+ protected static double dot(int[] g, double x, double y) {
+ return g[0] * x + g[1] * y;
+ }
+
+ protected static double dot(int[] g, double x, double y, double z) {
+ return g[0] * x + g[1] * y + g[2] * z;
+ }
+
+ protected static double dot(int[] g, double x, double y, double z, double w) {
+ return g[0] * x + g[1] * y + g[2] * z + g[3] * w;
+ }
+
+ /**
+ * Computes and returns the 1D unseeded simplex noise for the given
+ * coordinates in 1D space
+ *
+ * @param xin X coordinate
+ * @return Noise at given location, from range -1 to 1
+ */
+ public static double getNoise(double xin) {
+ return instance.noise(xin);
+ }
+
+ /**
+ * Computes and returns the 2D unseeded simplex noise for the given
+ * coordinates in 2D space
+ *
+ * @param xin X coordinate
+ * @param yin Y coordinate
+ * @return Noise at given location, from range -1 to 1
+ */
+ public static double getNoise(double xin, double yin) {
+ return instance.noise(xin, yin);
+ }
+
+ /**
+ * Computes and returns the 3D unseeded simplex noise for the given
+ * coordinates in 3D space
+ *
+ * @param xin X coordinate
+ * @param yin Y coordinate
+ * @param zin Z coordinate
+ * @return Noise at given location, from range -1 to 1
+ */
+ public static double getNoise(double xin, double yin, double zin) {
+ return instance.noise(xin, yin, zin);
+ }
+
+ /**
+ * Computes and returns the 4D simplex noise for the given coordinates in
+ * 4D space
+ *
+ * @param x X coordinate
+ * @param y Y coordinate
+ * @param z Z coordinate
+ * @param w W coordinate
+ * @return Noise at given location, from range -1 to 1
+ */
+ public static double getNoise(double x, double y, double z, double w) {
+ return instance.noise(x, y, z, w);
+ }
+
+ @Override
+ public double noise(double xin, double yin, double zin) {
+ xin += this.offsetX;
+ yin += this.offsetY;
+ zin += this.offsetZ;
+
+ double n0, n1, n2, n3; // Noise contributions from the four corners
+
+ // Skew the input space to determine which simplex cell we're in
+ double s = (xin + yin + zin) * F3; // Very nice and simple skew factor for 3D
+ int i = floor(xin + s);
+ int j = floor(yin + s);
+ int k = floor(zin + s);
+ double t = (i + j + k) * G3;
+ double X0 = i - t; // Unskew the cell origin back to (x,y,z) space
+ double Y0 = j - t;
+ double Z0 = k - t;
+ double x0 = xin - X0; // The x,y,z distances from the cell origin
+ double y0 = yin - Y0;
+ double z0 = zin - Z0;
+
+ // For the 3D case, the simplex shape is a slightly irregular tetrahedron.
+
+ // Determine which simplex we are in.
+ int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
+ int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
+ if (x0 >= y0) {
+ if (y0 >= z0) {
+ i1 = 1;
+ j1 = 0;
+ k1 = 0;
+ i2 = 1;
+ j2 = 1;
+ k2 = 0;
+ } // X Y Z order
+ else if (x0 >= z0) {
+ i1 = 1;
+ j1 = 0;
+ k1 = 0;
+ i2 = 1;
+ j2 = 0;
+ k2 = 1;
+ } // X Z Y order
+ else {
+ i1 = 0;
+ j1 = 0;
+ k1 = 1;
+ i2 = 1;
+ j2 = 0;
+ k2 = 1;
+ } // Z X Y order
+ } else { // x0 y0) {
+ i1 = 1;
+ j1 = 0;
+ } // lower triangle, XY order: (0,0)->(1,0)->(1,1)
+ else {
+ i1 = 0;
+ j1 = 1;
+ } // upper triangle, YX order: (0,0)->(0,1)->(1,1)
+
+ // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
+ // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
+ // c = (3-sqrt(3))/6
+
+ double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
+ double y1 = y0 - j1 + G2;
+ double x2 = x0 + G22; // Offsets for last corner in (x,y) unskewed coords
+ double y2 = y0 + G22;
+
+ // Work out the hashed gradient indices of the three simplex corners
+ int ii = i & 255;
+ int jj = j & 255;
+ int gi0 = this.perm[ii + this.perm[jj]] % 12;
+ int gi1 = this.perm[ii + i1 + this.perm[jj + j1]] % 12;
+ int gi2 = this.perm[ii + 1 + this.perm[jj + 1]] % 12;
+
+ // Calculate the contribution from the three corners
+ double t0 = 0.5 - x0 * x0 - y0 * y0;
+ if (t0 < 0) {
+ n0 = 0;
+ } else {
+ t0 *= t0;
+ n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient
+ }
+
+ double t1 = 0.5 - x1 * x1 - y1 * y1;
+ if (t1 < 0) {
+ n1 = 0;
+ } else {
+ t1 *= t1;
+ n1 = t1 * t1 * dot(grad3[gi1], x1, y1);
+ }
+
+ double t2 = 0.5 - x2 * x2 - y2 * y2;
+ if (t2 < 0) {
+ n2 = 0;
+ } else {
+ t2 *= t2;
+ n2 = t2 * t2 * dot(grad3[gi2], x2, y2);
+ }
+
+ // Add contributions from each corner to get the final noise value.
+ // The result is scaled to return values in the interval [-1,1].
+ return 70.0 * (n0 + n1 + n2);
+ }
+
+ /**
+ * Computes and returns the 4D simplex noise for the given coordinates in
+ * 4D space
+ *
+ * @param x X coordinate
+ * @param y Y coordinate
+ * @param z Z coordinate
+ * @param w W coordinate
+ * @return Noise at given location, from range -1 to 1
+ */
+ public double noise(double x, double y, double z, double w) {
+ x += this.offsetX;
+ y += this.offsetY;
+ z += this.offsetZ;
+ w += this.offsetW;
+
+ double n0, n1, n2, n3, n4; // Noise contributions from the five corners
+
+ // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
+ double s = (x + y + z + w) * F4; // Factor for 4D skewing
+ int i = floor(x + s);
+ int j = floor(y + s);
+ int k = floor(z + s);
+ int l = floor(w + s);
+
+ double t = (i + j + k + l) * G4; // Factor for 4D unskewing
+ double X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space
+ double Y0 = j - t;
+ double Z0 = k - t;
+ double W0 = l - t;
+ double x0 = x - X0; // The x,y,z,w distances from the cell origin
+ double y0 = y - Y0;
+ double z0 = z - Z0;
+ double w0 = w - W0;
+
+ // For the 4D case, the simplex is a 4D shape I won't even try to describe.
+ // To find out which of the 24 possible simplices we're in, we need to
+ // determine the magnitude ordering of x0, y0, z0 and w0.
+ // The method below is a good way of finding the ordering of x,y,z,w and
+ // then find the correct traversal order for the simplex we’re in.
+ // First, six pair-wise comparisons are performed between each possible pair
+ // of the four coordinates, and the results are used to add up binary bits
+ // for an integer index.
+ int c1 = (x0 > y0) ? 32 : 0;
+ int c2 = (x0 > z0) ? 16 : 0;
+ int c3 = (y0 > z0) ? 8 : 0;
+ int c4 = (x0 > w0) ? 4 : 0;
+ int c5 = (y0 > w0) ? 2 : 0;
+ int c6 = (z0 > w0) ? 1 : 0;
+ int c = c1 + c2 + c3 + c4 + c5 + c6;
+ int i1, j1, k1, l1; // The integer offsets for the second simplex corner
+ int i2, j2, k2, l2; // The integer offsets for the third simplex corner
+ int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
+
+ // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
+ // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0;
+ j1 = simplex[c][1] >= 3 ? 1 : 0;
+ k1 = simplex[c][2] >= 3 ? 1 : 0;
+ l1 = simplex[c][3] >= 3 ? 1 : 0;
+
+ // The number 2 in the "simplex" array is at the second largest coordinate.
+ i2 = simplex[c][0] >= 2 ? 1 : 0;
+ j2 = simplex[c][1] >= 2 ? 1 : 0;
+ k2 = simplex[c][2] >= 2 ? 1 : 0;
+ l2 = simplex[c][3] >= 2 ? 1 : 0;
+
+ // The number 1 in the "simplex" array is at the second smallest coordinate.
+ i3 = simplex[c][0] >= 1 ? 1 : 0;
+ j3 = simplex[c][1] >= 1 ? 1 : 0;
+ k3 = simplex[c][2] >= 1 ? 1 : 0;
+ l3 = simplex[c][3] >= 1 ? 1 : 0;
+
+ // The fifth corner has all coordinate offsets = 1, so no need to look that up.
+
+ double x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
+ double y1 = y0 - j1 + G4;
+ double z1 = z0 - k1 + G4;
+ double w1 = w0 - l1 + G4;
+
+ double x2 = x0 - i2 + G42; // Offsets for third corner in (x,y,z,w) coords
+ double y2 = y0 - j2 + G42;
+ double z2 = z0 - k2 + G42;
+ double w2 = w0 - l2 + G42;
+
+ double x3 = x0 - i3 + G43; // Offsets for fourth corner in (x,y,z,w) coords
+ double y3 = y0 - j3 + G43;
+ double z3 = z0 - k3 + G43;
+ double w3 = w0 - l3 + G43;
+
+ double x4 = x0 + G44; // Offsets for last corner in (x,y,z,w) coords
+ double y4 = y0 + G44;
+ double z4 = z0 + G44;
+ double w4 = w0 + G44;
+
+ // Work out the hashed gradient indices of the five simplex corners
+ int ii = i & 255;
+ int jj = j & 255;
+ int kk = k & 255;
+ int ll = l & 255;
+
+ int gi0 = this.perm[ii + this.perm[jj + this.perm[kk + this.perm[ll]]]] % 32;
+ int gi1 = this.perm[ii + i1 + this.perm[jj + j1 + this.perm[kk + k1 + this.perm[ll + l1]]]] % 32;
+ int gi2 = this.perm[ii + i2 + this.perm[jj + j2 + this.perm[kk + k2 + this.perm[ll + l2]]]] % 32;
+ int gi3 = this.perm[ii + i3 + this.perm[jj + j3 + this.perm[kk + k3 + this.perm[ll + l3]]]] % 32;
+ int gi4 = this.perm[ii + 1 + this.perm[jj + 1 + this.perm[kk + 1 + this.perm[ll + 1]]]] % 32;
+
+ // Calculate the contribution from the five corners
+ double t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;
+ if (t0 < 0) {
+ n0 = 0;
+ } else {
+ t0 *= t0;
+ n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0);
+ }
+
+ double t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;
+ if (t1 < 0) {
+ n1 = 0;
+ } else {
+ t1 *= t1;
+ n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1);
+ }
+
+ double t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;
+ if (t2 < 0) {
+ n2 = 0;
+ } else {
+ t2 *= t2;
+ n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2);
+ }
+
+ double t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;
+ if (t3 < 0) {
+ n3 = 0;
+ } else {
+ t3 *= t3;
+ n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3);
+ }
+
+ double t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;
+ if (t4 < 0) {
+ n4 = 0;
+ } else {
+ t4 *= t4;
+ n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4);
+ }
+
+ // Sum up and scale the result to cover the range [-1,1]
+ return 27.0 * (n0 + n1 + n2 + n3 + n4);
+ }
+
+ /**
+ * Gets the singleton unseeded instance of this generator
+ *
+ * @return Singleton
+ */
+ public static SimplexNoiseGenerator getInstance() {
+ return instance;
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/SimplexOctaveGenerator.java b/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/SimplexOctaveGenerator.java
new file mode 100644
index 0000000..d1c739d
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/noise/bukkit/SimplexOctaveGenerator.java
@@ -0,0 +1,141 @@
+package worldgeneratorextension.vanillagenerator.noise.bukkit;
+
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.Level;
+import cn.nukkit.math.NukkitRandom;
+
+/**
+ * Creates simplex noise through unbiased octaves
+ */
+public class SimplexOctaveGenerator extends OctaveGenerator {
+
+ private double wScale = 1;
+
+ /**
+ * Creates a simplex octave generator for the given world
+ *
+ * @param world World to construct this generator for
+ * @param octaves Amount of octaves to create
+ */
+ public SimplexOctaveGenerator(Level world, int octaves) {
+ this(new NukkitRandom(world.getSeed()), octaves);
+ }
+
+ /**
+ * Creates a simplex octave generator for the given level
+ *
+ * @param level Level to construct this generator for
+ * @param octaves Amount of octaves to create
+ */
+ public SimplexOctaveGenerator(ChunkManager level, int octaves) {
+ this(new NukkitRandom(level.getSeed()), octaves);
+ }
+
+ /**
+ * Creates a simplex octave generator for the given world
+ *
+ * @param seed Seed to construct this generator for
+ * @param octaves Amount of octaves to create
+ */
+ public SimplexOctaveGenerator(long seed, int octaves) {
+ this(new NukkitRandom(seed), octaves);
+ }
+
+ /**
+ * Creates a simplex octave generator for the given {@link NukkitRandom}
+ *
+ * @param rand NukkitRandom object to construct this generator for
+ * @param octaves Amount of octaves to create
+ */
+ public SimplexOctaveGenerator(NukkitRandom rand, int octaves) {
+ super(createOctaves(rand, octaves));
+ }
+
+ @Override
+ public void setScale(double scale) {
+ super.setScale(scale);
+ this.setWScale(scale);
+ }
+
+ /**
+ * Gets the scale used for each W-coordinates passed
+ *
+ * @return W scale
+ */
+ public double getWScale() {
+ return this.wScale;
+ }
+
+ /**
+ * Sets the scale used for each W-coordinates passed
+ *
+ * @param scale New W scale
+ */
+ public void setWScale(double scale) {
+ this.wScale = scale;
+ }
+
+ /**
+ * Generates noise for the 3D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param y Y-coordinate
+ * @param z Z-coordinate
+ * @param w W-coordinate
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @return Resulting noise
+ */
+ public double noise(double x, double y, double z, double w, double frequency, double amplitude) {
+ return this.noise(x, y, z, w, frequency, amplitude, false);
+ }
+
+ /**
+ * Generates noise for the 3D coordinates using the specified number of
+ * octaves and parameters
+ *
+ * @param x X-coordinate
+ * @param y Y-coordinate
+ * @param z Z-coordinate
+ * @param w W-coordinate
+ * @param frequency How much to alter the frequency by each octave
+ * @param amplitude How much to alter the amplitude by each octave
+ * @param normalized If true, normalize the value to [-1, 1]
+ * @return Resulting noise
+ */
+ public double noise(double x, double y, double z, double w, double frequency, double amplitude, boolean normalized) {
+ double result = 0;
+ double amp = 1;
+ double freq = 1;
+ double max = 0;
+
+ x *= this.xScale;
+ y *= this.yScale;
+ z *= this.zScale;
+ w *= this.wScale;
+
+ for (NoiseGenerator octave : this.octaves) {
+ result += ((SimplexNoiseGenerator) octave).noise(x * freq, y * freq, z * freq, w * freq) * amp;
+ max += amp;
+ freq *= frequency;
+ amp *= amplitude;
+ }
+
+ if (normalized) {
+ result /= max;
+ }
+
+ return result;
+ }
+
+ private static NoiseGenerator[] createOctaves(NukkitRandom rand, int octaves) {
+ NoiseGenerator[] result = new NoiseGenerator[octaves];
+
+ for (int i = 0; i < octaves; ++i) {
+ result[i] = new SimplexNoiseGenerator(rand);
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/object/OreType.java b/src/main/java/worldgeneratorextension/vanillagenerator/object/OreType.java
new file mode 100644
index 0000000..7c33372
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/object/OreType.java
@@ -0,0 +1,77 @@
+package worldgeneratorextension.vanillagenerator.object;
+
+import cn.nukkit.block.Block;
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.math.MathHelper;
+import cn.nukkit.math.NukkitRandom;
+
+public class OreType {
+
+ public final int fullId;
+ public final int clusterCount;
+ public final int clusterSize;
+ public final int maxHeight;
+ public final int minHeight;
+ public final int replaceBlockId;
+
+ public OreType(Block material, int clusterCount, int clusterSize, int minHeight, int maxHeight) {
+ this(material, clusterCount, clusterSize, minHeight, maxHeight, Block.STONE);
+ }
+
+ public OreType(Block material, int clusterCount, int clusterSize, int minHeight, int maxHeight, int replaceBlockId) {
+ this.fullId = material.getFullId();
+ this.clusterCount = clusterCount;
+ this.clusterSize = clusterSize;
+ this.maxHeight = maxHeight;
+ this.minHeight = minHeight;
+ this.replaceBlockId = replaceBlockId;
+ }
+
+ public void spawn(ChunkManager level, NukkitRandom rand, int replaceId, int x, int y, int z) {
+ float piScaled = rand.nextFloat() * (float) Math.PI;
+ double scaleMaxX = (float) (x + 8) + MathHelper.sin(piScaled) * (float) clusterSize / 8.0F;
+ double scaleMinX = (float) (x + 8) - MathHelper.sin(piScaled) * (float) clusterSize / 8.0F;
+ double scaleMaxZ = (float) (z + 8) + MathHelper.cos(piScaled) * (float) clusterSize / 8.0F;
+ double scaleMinZ = (float) (z + 8) - MathHelper.cos(piScaled) * (float) clusterSize / 8.0F;
+ double scaleMaxY = y + rand.nextBoundedInt(3) - 2;
+ double scaleMinY = y + rand.nextBoundedInt(3) - 2;
+
+ for (int i = 0; i < clusterSize; ++i) {
+ float sizeIncr = (float) i / (float) clusterSize;
+ double scaleX = scaleMaxX + (scaleMinX - scaleMaxX) * (double) sizeIncr;
+ double scaleY = scaleMaxY + (scaleMinY - scaleMaxY) * (double) sizeIncr;
+ double scaleZ = scaleMaxZ + (scaleMinZ - scaleMaxZ) * (double) sizeIncr;
+ double randSizeOffset = rand.nextDouble() * (double) clusterSize / 16.0D;
+ double randVec1 = (double) (MathHelper.sin((float) Math.PI * sizeIncr) + 1.0F) * randSizeOffset + 1.0D;
+ double randVec2 = (double) (MathHelper.sin((float) Math.PI * sizeIncr) + 1.0F) * randSizeOffset + 1.0D;
+ int minX = MathHelper.floor(scaleX - randVec1 / 2.0D);
+ int minY = MathHelper.floor(scaleY - randVec2 / 2.0D);
+ int minZ = MathHelper.floor(scaleZ - randVec1 / 2.0D);
+ int maxX = MathHelper.floor(scaleX + randVec1 / 2.0D);
+ int maxY = MathHelper.floor(scaleY + randVec2 / 2.0D);
+ int maxZ = MathHelper.floor(scaleZ + randVec1 / 2.0D);
+
+ for (int xSeg = minX; xSeg <= maxX; ++xSeg) {
+ double xVal = ((double) xSeg + 0.5D - scaleX) / (randVec1 / 2.0D);
+
+ if (xVal * xVal < 1.0D) {
+ for (int ySeg = minY; ySeg <= maxY; ++ySeg) {
+ double yVal = ((double) ySeg + 0.5D - scaleY) / (randVec2 / 2.0D);
+
+ if (xVal * xVal + yVal * yVal < 1.0D) {
+ for (int zSeg = minZ; zSeg <= maxZ; ++zSeg) {
+ double zVal = ((double) zSeg + 0.5D - scaleZ) / (randVec1 / 2.0D);
+
+ if (xVal * xVal + yVal * yVal + zVal * zVal < 1.0D) {
+ if (level.getBlockIdAt(xSeg, ySeg, zSeg) == replaceBlockId) {
+ level.setBlockFullIdAt(xSeg, ySeg, zSeg, fullId);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/populator/PopulatorOre.java b/src/main/java/worldgeneratorextension/vanillagenerator/populator/PopulatorOre.java
new file mode 100644
index 0000000..ce5a0d5
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/populator/PopulatorOre.java
@@ -0,0 +1,38 @@
+package worldgeneratorextension.vanillagenerator.populator;
+
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.level.generator.populator.type.Populator;
+import cn.nukkit.math.NukkitMath;
+import cn.nukkit.math.NukkitRandom;
+import worldgeneratorextension.vanillagenerator.object.OreType;
+
+public class PopulatorOre extends Populator {
+
+ private final int replaceId;
+ private final OreType[] oreTypes;
+
+ public PopulatorOre(int replaceId, OreType[] oreTypes) {
+ this.replaceId = replaceId;
+ this.oreTypes = oreTypes;
+ }
+
+ @Override
+ public void populate(ChunkManager level, int chunkX, int chunkZ, NukkitRandom random, FullChunk chunk) {
+ int sx = chunkX << 4;
+ int ex = sx + 15;
+ int sz = chunkZ << 4;
+ int ez = sz + 15;
+ for (OreType type : this.oreTypes) {
+ for (int i = 0; i < type.clusterCount; i++) {
+ int x = NukkitMath.randomRange(random, sx, ex);
+ int z = NukkitMath.randomRange(random, sz, ez);
+ int y = NukkitMath.randomRange(random, type.minHeight, type.maxHeight);
+ if (level.getBlockIdAt(x, y, z) != replaceId) {
+ continue;
+ }
+ type.spawn(level, random, replaceId, x, y, z);
+ }
+ }
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/populator/overworld/PopulatorCaves.java b/src/main/java/worldgeneratorextension/vanillagenerator/populator/overworld/PopulatorCaves.java
new file mode 100644
index 0000000..5bc0903
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/populator/overworld/PopulatorCaves.java
@@ -0,0 +1,272 @@
+package worldgeneratorextension.vanillagenerator.populator.overworld;
+
+import cn.nukkit.block.Block;
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.biome.Biome;
+import cn.nukkit.level.biome.type.CoveredBiome;
+import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.level.generator.populator.type.Populator;
+import cn.nukkit.math.MathHelper;
+import cn.nukkit.math.NukkitRandom;
+
+import java.util.Random;
+
+public class PopulatorCaves extends Populator {
+
+ protected int checkAreaSize = 8;
+
+ private Random random;
+
+ public static int caveRarity = 7;
+ public static int caveFrequency = 40;
+ public static int caveMinAltitude = 8;
+ public static int caveMaxAltitude = 67;
+ public static int individualCaveRarity = 25;
+ public static int caveSystemFrequency = 1;
+ public static int caveSystemPocketChance = 0;
+ public static int caveSystemPocketMinSize = 0;
+ public static int caveSystemPocketMaxSize = 4;
+ public static boolean evenCaveDistribution = false;
+
+ public int worldHeightCap = 128;
+
+ @Override
+ public void populate(ChunkManager level, int chunkX, int chunkZ, NukkitRandom random, FullChunk chunk) {
+ this.random = new Random(level.getSeed());
+ long worldLong1 = this.random.nextLong();
+ long worldLong2 = this.random.nextLong();
+
+ int size = this.checkAreaSize;
+
+ for (int x = chunkX - size; x <= chunkX + size; x++) {
+ for (int z = chunkZ - size; z <= chunkZ + size; z++) {
+ long randomX = x * worldLong1;
+ long randomZ = z * worldLong2;
+ this.random.setSeed(randomX ^ randomZ ^ level.getSeed());
+ generateChunk(x, z, chunk);
+ }
+ }
+ }
+
+ private void generateLargeCaveNode(long seed, FullChunk chunk, double x, double y, double z) {
+ generateCaveNode(seed, chunk, x, y, z, 1.0F + this.random.nextFloat() * 6.0F, 0.0F, 0.0F, -1, -1, 0.5D);
+ }
+
+ private void generateCaveNode(long seed, FullChunk chunk, double x, double y, double z, float radius, float angelOffset, float angel, int angle, int maxAngle, double scale) {
+ int chunkX = chunk.getX();
+ int chunkZ = chunk.getZ();
+
+ double realX = chunkX * 16 + 8;
+ double realZ = chunkZ * 16 + 8;
+
+ float f1 = 0.0F;
+ float f2 = 0.0F;
+
+ Random localRandom = new Random(seed);
+
+ if (maxAngle <= 0) {
+ int checkAreaSize = this.checkAreaSize * 16 - 16;
+ maxAngle = checkAreaSize - localRandom.nextInt(checkAreaSize / 4);
+ }
+ boolean isLargeCave = false;
+
+ if (angle == -1) {
+ angle = maxAngle / 2;
+ isLargeCave = true;
+ }
+
+ int randomAngel = localRandom.nextInt(maxAngle / 2) + maxAngle / 4;
+ boolean bigAngel = localRandom.nextInt(6) == 0;
+
+ for (; angle < maxAngle; angle++) {
+ double offsetXZ = 1.5D + MathHelper.sin(angle * 3.141593F / maxAngle) * radius * 1.0F;
+ double offsetY = offsetXZ * scale;
+
+ float cos = MathHelper.cos(angel);
+ float sin = MathHelper.sin(angel);
+ x += MathHelper.cos(angelOffset) * cos;
+ y += sin;
+ z += MathHelper.sin(angelOffset) * cos;
+
+ if (bigAngel) {
+ angel *= 0.92F;
+ } else {
+ angel *= 0.7F;
+ }
+ angel += f2 * 0.1F;
+ angelOffset += f1 * 0.1F;
+
+ f2 *= 0.9F;
+ f1 *= 0.75F;
+ f2 += (localRandom.nextFloat() - localRandom.nextFloat()) * localRandom.nextFloat() * 2.0F;
+ f1 += (localRandom.nextFloat() - localRandom.nextFloat()) * localRandom.nextFloat() * 4.0F;
+
+ if ((!isLargeCave) && (angle == randomAngel) && (radius > 1.0F) && (maxAngle > 0)) {
+ generateCaveNode(localRandom.nextLong(), chunk, x, y, z, localRandom.nextFloat() * 0.5F + 0.5F, angelOffset - 1.570796F, angel / 3.0F, angle, maxAngle, 1.0D);
+ generateCaveNode(localRandom.nextLong(), chunk, x, y, z, localRandom.nextFloat() * 0.5F + 0.5F, angelOffset + 1.570796F, angel / 3.0F, angle, maxAngle, 1.0D);
+ return;
+ }
+ if ((!isLargeCave) && (localRandom.nextInt(4) == 0)) {
+ continue;
+ }
+
+ // Check if distance to working point (x and z) too larger than working radius (maybe ??)
+ double distanceX = x - realX;
+ double distanceZ = z - realZ;
+ double angelDiff = maxAngle - angle;
+ double newRadius = radius + 2.0F + 16.0F;
+ if (distanceX * distanceX + distanceZ * distanceZ - angelDiff * angelDiff > newRadius * newRadius) {
+ return;
+ }
+
+ //Boundaries check.
+ if ((x < realX - 16.0D - offsetXZ * 2.0D) || (z < realZ - 16.0D - offsetXZ * 2.0D) || (x > realX + 16.0D + offsetXZ * 2.0D) || (z > realZ + 16.0D + offsetXZ * 2.0D)) {
+ continue;
+ }
+
+ int xFrom = MathHelper.floor(x - offsetXZ) - chunkX * 16 - 1;
+ int xTo = MathHelper.floor(x + offsetXZ) - chunkX * 16 + 1;
+
+ int yFrom = MathHelper.floor(y - offsetY) - 1;
+ int yTo = MathHelper.floor(y + offsetY) + 1;
+
+ int zFrom = MathHelper.floor(z - offsetXZ) - chunkZ * 16 - 1;
+ int zTo = MathHelper.floor(z + offsetXZ) - chunkZ * 16 + 1;
+
+ if (xFrom < 0) {
+ xFrom = 0;
+ }
+ if (xTo > 16) {
+ xTo = 16;
+ }
+
+ if (yFrom < 1) {
+ yFrom = 1;
+ }
+ if (yTo > this.worldHeightCap - 8) {
+ yTo = this.worldHeightCap - 8;
+ }
+ if (zFrom < 0) {
+ zFrom = 0;
+ }
+ if (zTo > 16) {
+ zTo = 16;
+ }
+
+ // Search for water
+ boolean waterFound = false;
+ for (int xx = xFrom; (!waterFound) && (xx < xTo); xx++) {
+ for (int zz = zFrom; (!waterFound) && (zz < zTo); zz++) {
+ for (int yy = yTo + 1; (!waterFound) && (yy >= yFrom - 1); yy--) {
+ if (yy >= 0 && yy < this.worldHeightCap) {
+ int block = chunk.getBlockId(xx, yy, zz);
+ if (block == Block.WATER || block == Block.STILL_WATER) {
+ waterFound = true;
+ }
+ if ((yy != yFrom - 1) && (xx != xFrom) && (xx != xTo - 1) && (zz != zFrom) && (zz != zTo - 1)) {
+ yy = yFrom;
+ }
+ }
+ }
+ }
+ }
+
+ if (waterFound) {
+ continue;
+ }
+
+ // Generate cave
+ for (int xx = xFrom; xx < xTo; xx++) {
+ double modX = (xx + chunkX * 16 + 0.5D - x) / offsetXZ;
+ for (int zz = zFrom; zz < zTo; zz++) {
+ double modZ = (zz + chunkZ * 16 + 0.5D - z) / offsetXZ;
+
+ boolean grassFound = false;
+ if (modX * modX + modZ * modZ < 1.0D) {
+ for (int yy = yTo; yy > yFrom; yy--) {
+ double modY = ((yy - 1) + 0.5D - y) / offsetY;
+ if ((modY > -0.7D) && (modX * modX + modY * modY + modZ * modZ < 1.0D)) {
+ Biome biome = Biome.getBiome(chunk.getBiomeId(xx, zz));
+ if (!(biome instanceof CoveredBiome)) {
+ continue;
+ }
+
+ int material = chunk.getBlockId(xx, yy, zz);
+ //int materialAbove = chunk.getBlockId(xx, yy + 1, zz);
+ if (material == Block.GRASS || material == Block.MYCELIUM) {
+ grassFound = true;
+ }
+ //TODO: check this
+// if (this.isSuitableBlock(material, materialAbove, biome))
+ {
+ if (yy - 1 < 10) {
+ chunk.setBlock(xx, yy, zz, Block.LAVA);
+ } else {
+ chunk.setBlock(xx, yy, zz, Block.AIR);
+
+ // If grass was just deleted, try to
+ // move it down
+ if (grassFound && (chunk.getBlockId(xx, yy - 1, zz) == Block.DIRT)) {
+ chunk.setFullBlockId(xx, yy - 1, zz, ((CoveredBiome) biome).getSurfaceId(xx, yy - 1, zz));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (isLargeCave) {
+ break;
+ }
+ }
+ }
+
+ private void generateChunk(int chunkX, int chunkZ, FullChunk generatingChunkBuffer) {
+ int i = this.random.nextInt(this.random.nextInt(this.random.nextInt(caveFrequency) + 1) + 1);
+ if (evenCaveDistribution) {
+ i = caveFrequency;
+ }
+ if (this.random.nextInt(100) >= caveRarity) {
+ i = 0;
+ }
+
+ for (int j = 0; j < i; j++) {
+ double x = chunkX * 16 + this.random.nextInt(16);
+
+ double y;
+
+ if (evenCaveDistribution) {
+ y = numberInRange(random, caveMinAltitude, caveMaxAltitude);
+ } else {
+ y = this.random.nextInt(this.random.nextInt(caveMaxAltitude - caveMinAltitude + 1) + 1) + caveMinAltitude;
+ }
+
+ double z = chunkZ * 16 + this.random.nextInt(16);
+
+ int count = caveSystemFrequency;
+ boolean largeCaveSpawned = false;
+ if (this.random.nextInt(100) <= individualCaveRarity) {
+ generateLargeCaveNode(this.random.nextLong(), generatingChunkBuffer, x, y, z);
+ largeCaveSpawned = true;
+ }
+
+ if ((largeCaveSpawned) || (this.random.nextInt(100) <= caveSystemPocketChance - 1)) {
+ count += numberInRange(random, caveSystemPocketMinSize, caveSystemPocketMaxSize);
+ }
+ while (count > 0) {
+ count--;
+ float f1 = this.random.nextFloat() * 3.141593F * 2.0F;
+ float f2 = (this.random.nextFloat() - 0.5F) * 2.0F / 8.0F;
+ float f3 = this.random.nextFloat() * 2.0F + this.random.nextFloat();
+
+ generateCaveNode(this.random.nextLong(), generatingChunkBuffer, x, y, z, f3, f1, f2, 0, 0, 1.0D);
+ }
+ }
+ }
+
+ private static int numberInRange(Random random, int min, int max) {
+ return min + random.nextInt(max - min + 1);
+ }
+}
diff --git a/src/main/java/worldgeneratorextension/vanillagenerator/populator/overworld/PopulatorSnowLayers.java b/src/main/java/worldgeneratorextension/vanillagenerator/populator/overworld/PopulatorSnowLayers.java
new file mode 100644
index 0000000..c5aba9f
--- /dev/null
+++ b/src/main/java/worldgeneratorextension/vanillagenerator/populator/overworld/PopulatorSnowLayers.java
@@ -0,0 +1,166 @@
+package cn.wode490390.nukkit.vanillagenerator.populator.overworld;
+
+import cn.nukkit.block.Block;
+import cn.nukkit.level.ChunkManager;
+import cn.nukkit.level.biome.EnumBiome;
+import cn.nukkit.level.format.FullChunk;
+import cn.nukkit.level.generator.populator.type.Populator;
+import cn.nukkit.math.NukkitRandom;
+
+public class PopulatorSnowLayers extends Populator {
+
+ protected static final boolean[] coverableBiome = new boolean[Block.MAX_BLOCK_ID];
+ protected static final boolean[] uncoverableBlock = new boolean[Block.MAX_BLOCK_ID];
+
+ static {
+ coverableBiome[EnumBiome.ICE_PLAINS.id] = true;
+ coverableBiome[EnumBiome.ICE_PLAINS_SPIKES.id] = true;
+ coverableBiome[EnumBiome.COLD_BEACH.id] = true;
+ coverableBiome[EnumBiome.COLD_TAIGA.id] = true;
+ coverableBiome[EnumBiome.COLD_TAIGA_HILLS.id] = true;
+ coverableBiome[EnumBiome.COLD_TAIGA_M.id] = true;
+
+ uncoverableBlock[SAPLING] = true;
+ uncoverableBlock[WATER] = true;
+ uncoverableBlock[STILL_WATER] = true;
+ uncoverableBlock[LAVA] = true;
+ uncoverableBlock[STILL_LAVA] = true;
+ uncoverableBlock[BED_BLOCK] = true;
+ uncoverableBlock[POWERED_RAIL] = true;
+ uncoverableBlock[DETECTOR_RAIL] = true;
+ uncoverableBlock[COBWEB] = true;
+ uncoverableBlock[TALL_GRASS] = true;
+ uncoverableBlock[DEAD_BUSH] = true;
+ uncoverableBlock[PISTON_HEAD] = true;
+ uncoverableBlock[DANDELION] = true;
+ uncoverableBlock[RED_FLOWER] = true;
+ uncoverableBlock[BROWN_MUSHROOM] = true;
+ uncoverableBlock[RED_MUSHROOM] = true;
+ uncoverableBlock[STONE_SLAB] = true;
+ uncoverableBlock[TORCH] = true;
+ uncoverableBlock[FIRE] = true;
+ uncoverableBlock[OAK_WOOD_STAIRS] = true;
+ uncoverableBlock[CHEST] = true;
+ uncoverableBlock[REDSTONE_WIRE] = true;
+ uncoverableBlock[WHEAT_BLOCK] = true;
+ uncoverableBlock[SIGN_POST] = true;
+ uncoverableBlock[WOODEN_DOOR_BLOCK] = true;
+ uncoverableBlock[LADDER] = true;
+ uncoverableBlock[RAIL] = true;
+ uncoverableBlock[COBBLESTONE_STAIRS] = true;
+ uncoverableBlock[WALL_SIGN] = true;
+ uncoverableBlock[LEVER] = true;
+ uncoverableBlock[STONE_PRESSURE_PLATE] = true;
+ uncoverableBlock[IRON_DOOR_BLOCK] = true;
+ uncoverableBlock[WOODEN_PRESSURE_PLATE] = true;
+ uncoverableBlock[REDSTONE_ORE] = true;
+ uncoverableBlock[UNLIT_REDSTONE_TORCH] = true;
+ uncoverableBlock[REDSTONE_TORCH] = true;
+ uncoverableBlock[STONE_BUTTON] = true;
+ uncoverableBlock[SNOW_LAYER] = true; //existed
+ uncoverableBlock[ICE] = true;
+ uncoverableBlock[CACTUS] = true;
+ uncoverableBlock[REEDS] = true;
+ uncoverableBlock[FENCE] = true;
+ uncoverableBlock[NETHER_PORTAL] = true;
+ uncoverableBlock[CAKE_BLOCK] = true;
+ uncoverableBlock[UNPOWERED_REPEATER] = true;
+ uncoverableBlock[POWERED_REPEATER] = true;
+ uncoverableBlock[TRAPDOOR] = true;
+ uncoverableBlock[IRON_BARS] = true;
+ uncoverableBlock[GLASS_PANE] = true;
+ uncoverableBlock[PUMPKIN_STEM] = true;
+ uncoverableBlock[MELON_STEM] = true;
+ uncoverableBlock[VINE] = true;
+ uncoverableBlock[FENCE_GATE] = true;
+ uncoverableBlock[BRICK_STAIRS] = true;
+ uncoverableBlock[STONE_BRICK_STAIRS] = true;
+ uncoverableBlock[WATER_LILY] = true;
+ uncoverableBlock[NETHER_BRICK_FENCE] = true;
+ uncoverableBlock[NETHER_BRICKS_STAIRS] = true;
+ uncoverableBlock[NETHER_WART_BLOCK] = true;
+ uncoverableBlock[ENCHANTING_TABLE] = true;
+ uncoverableBlock[BREWING_STAND_BLOCK] = true;
+ uncoverableBlock[END_PORTAL] = true;
+ uncoverableBlock[END_PORTAL_FRAME] = true;
+ uncoverableBlock[DRAGON_EGG] = true;
+ uncoverableBlock[ACTIVATOR_RAIL] = true;
+ uncoverableBlock[COCOA] = true;
+ uncoverableBlock[SANDSTONE_STAIRS] = true;
+ uncoverableBlock[ENDER_CHEST] = true;
+ uncoverableBlock[TRIPWIRE_HOOK] = true;
+ uncoverableBlock[TRIPWIRE] = true;
+ uncoverableBlock[SPRUCE_WOOD_STAIRS] = true;
+ uncoverableBlock[BIRCH_WOOD_STAIRS] = true;
+ uncoverableBlock[JUNGLE_WOOD_STAIRS] = true;
+ uncoverableBlock[COBBLESTONE_WALL] = true;
+ uncoverableBlock[FLOWER_POT_BLOCK] = true;
+ uncoverableBlock[CARROT_BLOCK] = true;
+ uncoverableBlock[POTATO_BLOCK] = true;
+ uncoverableBlock[WOODEN_BUTTON] = true;
+ uncoverableBlock[SKULL_BLOCK] = true;
+ uncoverableBlock[ANVIL] = true;
+ uncoverableBlock[TRAPPED_CHEST] = true;
+ uncoverableBlock[LIGHT_WEIGHTED_PRESSURE_PLATE] = true;
+ uncoverableBlock[HEAVY_WEIGHTED_PRESSURE_PLATE] = true;
+ uncoverableBlock[UNPOWERED_COMPARATOR] = true;
+ uncoverableBlock[POWERED_COMPARATOR] = true;
+ uncoverableBlock[DAYLIGHT_DETECTOR] = true;
+ uncoverableBlock[QUARTZ_STAIRS] = true;
+ uncoverableBlock[WOODEN_SLABS] = true;
+ uncoverableBlock[STAINED_GLASS_PANE] = true;
+ uncoverableBlock[ACACIA_WOOD_STAIRS] = true;
+ uncoverableBlock[DARK_OAK_WOOD_STAIRS] = true;
+ uncoverableBlock[IRON_TRAPDOOR] = true;
+ uncoverableBlock[CARPET] = true;
+ uncoverableBlock[DOUBLE_PLANT] = true;
+ uncoverableBlock[STANDING_BANNER] = true;
+ uncoverableBlock[WALL_BANNER] = true;
+ uncoverableBlock[DAYLIGHT_DETECTOR_INVERTED] = true;
+ uncoverableBlock[RED_SANDSTONE_STAIRS] = true;
+ uncoverableBlock[RED_SANDSTONE_SLAB] = true;
+ uncoverableBlock[FENCE_GATE_SPRUCE] = true;
+ uncoverableBlock[FENCE_GATE_BIRCH] = true;
+ uncoverableBlock[FENCE_GATE_JUNGLE] = true;
+ uncoverableBlock[FENCE_GATE_DARK_OAK] = true;
+ uncoverableBlock[FENCE_GATE_ACACIA] = true;
+ uncoverableBlock[190] = true; // HARD_GLASS_PANE
+ uncoverableBlock[191] = true; // HARD_STAINED_GLASS_PANE
+ uncoverableBlock[SPRUCE_DOOR_BLOCK] = true;
+ uncoverableBlock[BIRCH_DOOR_BLOCK] = true;
+ uncoverableBlock[JUNGLE_DOOR_BLOCK] = true;
+ uncoverableBlock[ACACIA_DOOR_BLOCK] = true;
+ uncoverableBlock[DARK_OAK_DOOR_BLOCK] = true;
+ uncoverableBlock[ITEM_FRAME_BLOCK] = true;
+ uncoverableBlock[CHORUS_FLOWER] = true;
+ uncoverableBlock[202] = true; // COLORED_TORCH_RG
+ uncoverableBlock[PURPUR_STAIRS] = true;
+ uncoverableBlock[204] = true; // COLORED_TORCH_BP
+ uncoverableBlock[UNDYED_SHULKER_BOX] = true;
+ uncoverableBlock[ICE_FROSTED] = true;
+ uncoverableBlock[END_ROD] = true;
+ uncoverableBlock[END_GATEWAY] = true;
+ uncoverableBlock[217] = true; // STRUCTURE_VOID
+ uncoverableBlock[SHULKER_BOX] = true;
+ uncoverableBlock[239] = true; // UNDERWATER_TORCH
+ uncoverableBlock[CHORUS_PLANT] = true;
+ uncoverableBlock[242] = true; // CAMERA
+ uncoverableBlock[BEETROOT_BLOCK] = true;
+ uncoverableBlock[PISTON_EXTENSION] = true;
+ }
+
+ @Override
+ public void populate(ChunkManager level, int chunkX, int chunkZ, NukkitRandom random, FullChunk chunk) {
+ for (int x = 0; x < 16; x++) {
+ for (int z = 0; z < 16; z++) {
+ if (coverableBiome[chunk.getBiomeId(x, z)]) {
+ int y = chunk.getHighestBlockAt(x, z);
+ if (y > 0 && y < 255 && !uncoverableBlock[chunk.getBlockId(x, y, z)]) {
+ int chance = random.nextBoundedInt(10);
+ chunk.setBlock(x, y + 1, z, SNOW_LAYER, chance < 6 ? 0 : chance == 6 ? 2 : 1);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 3494b4c..213a5ea 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -2,3 +2,4 @@ name: WorldGeneratorExtension
main: worldgeneratorextension.Loader
version: "${pom.version}"
api: ["1.1.0"]
+load: STARTUP