diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index f689e9701..95430d7fa 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -81,5 +81,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/dfsek/terra/config/base/ConfigUtil.java b/src/main/java/com/dfsek/terra/config/base/ConfigUtil.java
index 04f0687fc..6e66110c8 100644
--- a/src/main/java/com/dfsek/terra/config/base/ConfigUtil.java
+++ b/src/main/java/com/dfsek/terra/config/base/ConfigUtil.java
@@ -11,6 +11,7 @@ import org.bukkit.Material;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
+import org.polydev.gaea.util.JarUtil;
import java.io.File;
import java.io.IOException;
@@ -24,8 +25,6 @@ import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
-import static org.polydev.gaea.util.JarUtil.copyResourcesToDirectory;
-
public final class ConfigUtil {
public static boolean debug;
public static long dataSave; // Period of population data saving, in ticks.
@@ -46,7 +45,7 @@ public final class ConfigUtil {
if(config.getBoolean("dump-default", true)) {
try(JarFile jar = new JarFile(new File(Terra.class.getProtectionDomain().getCodeSource().getLocation().toURI()))) {
- copyResourcesToDirectory(jar, "default-config", new File(main.getDataFolder(), "packs" + File.separator + "default").toString());
+ JarUtil.copyResourcesToDirectory(jar, "packs", new File(main.getDataFolder(), "packs").toString());
} catch(IOException | URISyntaxException e) {
Debug.error("Failed to dump default config files!");
e.printStackTrace();
diff --git a/src/main/java/com/dfsek/terra/generation/entities/Carver.java b/src/main/java/com/dfsek/terra/generation/entities/Carver.java
new file mode 100644
index 000000000..021371f99
--- /dev/null
+++ b/src/main/java/com/dfsek/terra/generation/entities/Carver.java
@@ -0,0 +1,87 @@
+package com.dfsek.terra.generation.entities;
+
+import com.dfsek.terra.carving.UserDefinedCarver;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.polydev.gaea.math.ProbabilityCollection;
+
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+public class Carver implements GenerationEntity {
+ private final UserDefinedCarver carver;
+ private final String id;
+ private final Set update;
+ private final Map> shift;
+ private final Map> inner;
+ private final Map> outer;
+ private final Map> top;
+ private final Map> bottom;
+ private final boolean updateOcean;
+ private final boolean replaceIsBlacklistInner;
+ private final boolean replaceIsBlacklistOuter;
+ private final boolean replaceIsBlacklistTop;
+ private final boolean replaceIsBlacklistBottom;
+ private final Set replaceableInner;
+ private final Set replaceableOuter;
+ private final Set replaceableTop;
+ private final Set replaceableBottom;
+
+ public Carver(UserDefinedCarver carver, String id, Set update, Map> shift,
+ Map> inner, Map> outer,
+ Map> top, Map> bottom,
+ boolean updateOcean, ReplaceableCarverConfig config) {
+ this.carver = carver;
+ this.id = id;
+ this.update = update;
+ this.shift = shift;
+ this.inner = inner;
+ this.outer = outer;
+ this.top = top;
+ this.bottom = bottom;
+ this.updateOcean = updateOcean;
+ this.replaceIsBlacklistInner = config.replaceIsBlacklistInner;
+ this.replaceIsBlacklistOuter = config.replaceIsBlacklistOuter;
+ this.replaceIsBlacklistTop = config.replaceIsBlacklistTop;
+ this.replaceIsBlacklistBottom = config.replaceIsBlacklistBottom;
+ this.replaceableInner = config.replaceableInner;
+ this.replaceableOuter = config.replaceableOuter;
+ this.replaceableTop = config.replaceableTop;
+ this.replaceableBottom = config.replaceableBottom;
+ }
+
+ @Override
+ public void generate(Location location, Random random, JavaPlugin plugin) {
+ //TODO
+ }
+
+ @Override
+ public boolean isValidLocation(Location location, JavaPlugin plugin) {
+ return false; //TODO
+ }
+
+ public static class ReplaceableCarverConfig {
+ private final boolean replaceIsBlacklistInner;
+ private final boolean replaceIsBlacklistOuter;
+ private final boolean replaceIsBlacklistTop;
+ private final boolean replaceIsBlacklistBottom;
+ private final Set replaceableInner;
+ private final Set replaceableOuter;
+ private final Set replaceableTop;
+ private final Set replaceableBottom;
+
+ public ReplaceableCarverConfig(boolean replaceIsBlacklistInner, boolean replaceIsBlacklistOuter, boolean replaceIsBlacklistTop, boolean replaceIsBlacklistBottom, Set replaceableInner, Set replaceableOuter, Set replaceableTop, Set replaceableBottom) {
+ this.replaceIsBlacklistInner = replaceIsBlacklistInner;
+ this.replaceIsBlacklistOuter = replaceIsBlacklistOuter;
+ this.replaceIsBlacklistTop = replaceIsBlacklistTop;
+ this.replaceIsBlacklistBottom = replaceIsBlacklistBottom;
+ this.replaceableInner = replaceableInner;
+ this.replaceableOuter = replaceableOuter;
+ this.replaceableTop = replaceableTop;
+ this.replaceableBottom = replaceableBottom;
+ }
+ }
+}
diff --git a/src/main/java/com/dfsek/terra/generation/entities/Flora.java b/src/main/java/com/dfsek/terra/generation/entities/Flora.java
new file mode 100644
index 000000000..11f3ff8b4
--- /dev/null
+++ b/src/main/java/com/dfsek/terra/generation/entities/Flora.java
@@ -0,0 +1,99 @@
+package com.dfsek.terra.generation.entities;
+
+import org.bukkit.Chunk;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.polydev.gaea.math.Range;
+import org.polydev.gaea.world.palette.Palette;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+public class Flora implements GenerationEntity, org.polydev.gaea.world.Flora {
+ private final Palette floraPalette;
+ private final String id;
+ private final boolean physics;
+ private final boolean ceiling;
+
+ private final Set irrigable;
+
+ private final Set spawnable;
+ private final Set replaceable;
+
+ public Flora(Palette floraPalette, String id, boolean physics, boolean ceiling, Set irrigable, Set spawnable, Set replaceable) {
+ this.floraPalette = floraPalette;
+ this.id = id;
+ this.physics = physics;
+ this.ceiling = ceiling;
+ this.irrigable = irrigable;
+ this.spawnable = spawnable;
+ this.replaceable = replaceable;
+ }
+
+ @Override
+ public void generate(Location location, Random random, JavaPlugin plugin) {
+
+ }
+
+ @Override
+ public boolean isValidLocation(Location location, JavaPlugin plugin) {
+ Block check = location.getBlock();
+ if(ceiling) {
+ Block other = check.getRelative(BlockFace.DOWN);
+ return spawnable.contains(check.getType()) && replaceable.contains(other.getType());
+ } else {
+ Block other = check.getRelative(BlockFace.UP);
+ return spawnable.contains(check.getType()) && replaceable.contains(other.getType()) && isIrrigated(check);
+ }
+ }
+
+ @Override
+ public List getValidSpawnsAt(Chunk chunk, int x, int z, Range range) {
+ List blocks = new ArrayList<>();
+ if(ceiling) for(int y : range) {
+ if(y > 255 || y < 1) continue;
+ Block check = chunk.getBlock(x, y, z);
+ if(isValidLocation(check.getLocation(), null)) {
+ blocks.add(check);
+ }
+ }
+ else for(int y : range) {
+ if(y > 254 || y < 0) continue;
+ Block check = chunk.getBlock(x, y, z);
+ if(isValidLocation(check.getLocation(), null)) {
+ blocks.add(check);
+ }
+ }
+ return blocks;
+ }
+
+ private boolean isIrrigated(Block b) {
+ if(irrigable == null) return true;
+ return irrigable.contains(b.getRelative(BlockFace.NORTH).getType())
+ || irrigable.contains(b.getRelative(BlockFace.SOUTH).getType())
+ || irrigable.contains(b.getRelative(BlockFace.EAST).getType())
+ || irrigable.contains(b.getRelative(BlockFace.WEST).getType());
+ }
+
+ @SuppressWarnings("DuplicatedCode")
+ @Override
+ public boolean plant(Location location) {
+ int size = floraPalette.getSize();
+ int c = ceiling ? -1 : 1;
+ for(int i = 0; Math.abs(i) < size; i += c) { // Down if ceiling, up if floor
+ if(i + 1 > 255) return false;
+ if(!replaceable.contains(location.clone().add(0, i + c, 0).getBlock().getType())) return false;
+ }
+ for(int i = 0; Math.abs(i) < size; i += c) { // Down if ceiling, up if floor
+ int lvl = (Math.abs(i));
+ location.clone().add(0, i + c, 0).getBlock().setBlockData(floraPalette.get((ceiling ? lvl : size - lvl - 1), location.getBlockX(), location.getBlockZ()), physics);
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/com/dfsek/terra/generation/entities/GenerationEntity.java b/src/main/java/com/dfsek/terra/generation/entities/GenerationEntity.java
new file mode 100644
index 000000000..d3d0fb27b
--- /dev/null
+++ b/src/main/java/com/dfsek/terra/generation/entities/GenerationEntity.java
@@ -0,0 +1,13 @@
+package com.dfsek.terra.generation.entities;
+
+import org.bukkit.Location;
+import org.bukkit.plugin.java.JavaPlugin;
+
+import java.util.Random;
+
+@SuppressWarnings("unused")
+public interface GenerationEntity {
+ void generate(Location location, Random random, JavaPlugin plugin);
+
+ boolean isValidLocation(Location location, JavaPlugin plugin);
+}
diff --git a/src/main/java/com/dfsek/terra/generation/entities/Ore.java b/src/main/java/com/dfsek/terra/generation/entities/Ore.java
new file mode 100644
index 000000000..fc59518a0
--- /dev/null
+++ b/src/main/java/com/dfsek/terra/generation/entities/Ore.java
@@ -0,0 +1,118 @@
+package com.dfsek.terra.generation.entities;
+
+import org.bukkit.Chunk;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.util.Vector;
+import org.polydev.gaea.math.FastNoiseLite;
+import org.polydev.gaea.population.ChunkCoordinate;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+public class Ore implements GenerationEntity {
+ private final BlockData oreData;
+ private final int min;
+ private final int max;
+ private final double deform;
+ private final double deformFrequency;
+ private final String id;
+ private final boolean update;
+ private final boolean crossChunks;
+ private final int chunkEdgeOffset;
+ Set replaceable;
+
+ public Ore(BlockData oreData, int min, int max, double deform, double deformFrequency, String id, boolean update, boolean crossChunks,
+ int chunkEdgeOffset, Set replaceable) {
+ this.oreData = oreData;
+ this.min = min;
+ this.max = max;
+ this.deform = deform;
+ this.deformFrequency = deformFrequency;
+ this.id = id;
+ this.update = update;
+ this.crossChunks = crossChunks;
+ this.chunkEdgeOffset = chunkEdgeOffset;
+ this.replaceable = replaceable;
+ }
+
+ @Override
+ public void generate(Location location, Random random, JavaPlugin plugin) {
+ if(crossChunks)
+ doVeinMulti(location, random);
+ else
+ doVeinSingle(location, random);
+ }
+
+ @Override
+ public boolean isValidLocation(Location location, JavaPlugin plugin) {
+ Block block = location.getBlock();
+ return (replaceable.contains(block.getType()) && (block.getLocation().getY() >= 0));
+ }
+
+ @SuppressWarnings("DuplicatedCode")
+ private void doVeinMulti(Location location, Random random) {
+ FastNoiseLite ore = new FastNoiseLite(random.nextInt());
+ Chunk chunk = location.getChunk();
+
+ ore.setNoiseType(FastNoiseLite.NoiseType.OpenSimplex2);
+ ore.setFrequency(deformFrequency);
+ int rad = randomInRange(random);
+ Map chunks = new HashMap<>(); // Cache chunks to prevent re-loading chunks every time one is needed.
+ chunks.put(new ChunkCoordinate(chunk), chunk);
+ for(int x = -rad; x <= rad; x++) {
+ for(int y = -rad; y <= rad; y++) {
+ for(int z = -rad; z <= rad; z++) {
+ Vector origin = location.toVector();
+ Vector source = origin.clone().add(new Vector(x, y, z));
+
+ Vector orig = new Vector(location.getBlockX() + (chunk.getX() << 4), location.getBlockY(), location.getBlockZ() + (chunk.getZ() << 4));
+ Vector oreLocation = orig.clone().add(new Vector(x, y, z));
+
+ if(oreLocation.getBlockY() > 255 || oreLocation.getBlockY() < 0) continue;
+ if(source.distance(origin) < (rad + 0.5) * ((ore.getNoise(x, y, z) + 1) * deform)) {
+ ChunkCoordinate coord = new ChunkCoordinate(Math.floorDiv(oreLocation.getBlockX(), 16), Math.floorDiv(oreLocation.getBlockZ(), 16), chunk.getWorld().getUID());
+
+ Block block = chunks.computeIfAbsent(coord, k -> chunk.getWorld().getChunkAt(oreLocation.toLocation(chunk.getWorld())))
+ .getBlock(Math.floorMod(source.getBlockX(), 16), source.getBlockY(), Math.floorMod(source.getBlockZ(), 16)); // Chunk caching conditional computation
+ if(replaceable.contains(block.getType()) && block.getLocation().getY() >= 0)
+ block.setBlockData(oreData, update);
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("DuplicatedCode")
+ private void doVeinSingle(Location location, Random random) {
+ FastNoiseLite ore = new FastNoiseLite(random.nextInt());
+ Chunk chunk = location.getChunk();
+
+ ore.setNoiseType(FastNoiseLite.NoiseType.OpenSimplex2);
+ ore.setFrequency(deformFrequency);
+ int rad = randomInRange(random);
+ for(int x = -rad; x <= rad; x++) {
+ for(int y = -rad; y <= rad; y++) {
+ for(int z = -rad; z <= rad; z++) {
+ Vector oreLoc = location.toVector().clone().add(new Vector(x, y, z));
+ if(oreLoc.getBlockX() > 15 || oreLoc.getBlockZ() > 15 || oreLoc.getBlockY() > 255 || oreLoc.getBlockX() < 0 || oreLoc.getBlockZ() < 0 || oreLoc.getBlockY() < 0)
+ continue;
+ if(oreLoc.distance(location.toVector()) < (rad + 0.5) * ((ore.getNoise(x, y, z) + 1) * deform)) {
+ Block b = chunk.getBlock(oreLoc.getBlockX(), oreLoc.getBlockY(), oreLoc.getBlockZ());
+ if(replaceable.contains(b.getType()) && b.getLocation().getY() >= 0)
+ b.setBlockData(oreData, update);
+ }
+ }
+ }
+ }
+ }
+
+ private int randomInRange(Random r) {
+ return r.nextInt(max - min + 1) + min;
+ }
+}
diff --git a/src/main/java/com/dfsek/terra/generation/entities/Tree.java b/src/main/java/com/dfsek/terra/generation/entities/Tree.java
new file mode 100644
index 000000000..de6d4d18d
--- /dev/null
+++ b/src/main/java/com/dfsek/terra/generation/entities/Tree.java
@@ -0,0 +1,43 @@
+package com.dfsek.terra.generation.entities;
+
+import com.dfsek.terra.structure.Rotation;
+import com.dfsek.terra.structure.Structure;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.polydev.gaea.math.ProbabilityCollection;
+
+import java.util.Random;
+import java.util.Set;
+
+public class Tree implements GenerationEntity {
+ private final Set spawnable;
+ private final String id;
+ private final int yOffset;
+ private final ProbabilityCollection structure;
+
+ public Tree(Set spawnable, String id, int yOffset, ProbabilityCollection structure) {
+ this.spawnable = spawnable;
+ this.id = id;
+ this.yOffset = yOffset;
+ this.structure = structure;
+ }
+
+ @Override
+ public void generate(Location location, Random random, JavaPlugin plugin) {
+ location.subtract(0, 1, 0);
+ Location mut = location.clone().subtract(0, yOffset, 0);
+ if(!spawnable.contains(location.getBlock().getType()))
+ return;
+ Structure structure = this.structure.get(random);
+ Rotation rotation = Rotation.fromDegrees(random.nextInt(4) * 90);
+ if(!structure.checkSpawns(mut, rotation))
+ return;
+ structure.paste(mut, rotation);
+ }
+
+ @Override
+ public boolean isValidLocation(Location location, JavaPlugin plugin) {
+ return spawnable.contains(location.getBlock().getType());
+ }
+}
diff --git a/src/main/java/com/dfsek/terra/population/CavePopulator.java b/src/main/java/com/dfsek/terra/population/CavePopulator.java
index dbed28e7b..a8630ec3b 100644
--- a/src/main/java/com/dfsek/terra/population/CavePopulator.java
+++ b/src/main/java/com/dfsek/terra/population/CavePopulator.java
@@ -46,20 +46,24 @@ public class CavePopulator extends BlockPopulator {
Block b = chunk.getBlock(v.getBlockX(), v.getBlockY(), v.getBlockZ());
Material m = b.getType();
if(e.getValue().equals(CarvingData.CarvingType.CENTER) && c.isReplaceableInner(m)) {
- if(c.getShiftedBlocks().containsKey(b.getType()))
+ if(c.getShiftedBlocks().containsKey(b.getType())) {
shiftCandidate.put(b.getLocation(), b.getType());
+ }
b.setBlockData(c.getPaletteInner(v.getBlockY()).get(random), c.shouldUpdateOcean() && borderingOcean(b));
} else if(e.getValue().equals(CarvingData.CarvingType.WALL) && c.isReplaceableOuter(m)) {
- if(c.getShiftedBlocks().containsKey(b.getType()))
+ if(c.getShiftedBlocks().containsKey(b.getType())) {
shiftCandidate.put(b.getLocation(), b.getType());
+ }
b.setBlockData(c.getPaletteOuter(v.getBlockY()).get(random), c.shouldUpdateOcean() && borderingOcean(b));
} else if(e.getValue().equals(CarvingData.CarvingType.TOP) && c.isReplaceableTop(m)) {
- if(c.getShiftedBlocks().containsKey(b.getType()))
+ if(c.getShiftedBlocks().containsKey(b.getType())) {
shiftCandidate.put(b.getLocation(), b.getType());
+ }
b.setBlockData(c.getPaletteTop(v.getBlockY()).get(random), c.shouldUpdateOcean() && borderingOcean(b));
} else if(e.getValue().equals(CarvingData.CarvingType.BOTTOM) && c.isReplaceableBottom(m)) {
- if(c.getShiftedBlocks().containsKey(b.getType()))
+ if(c.getShiftedBlocks().containsKey(b.getType())) {
shiftCandidate.put(b.getLocation(), b.getType());
+ }
b.setBlockData(c.getPaletteBottom(v.getBlockY()).get(random), c.shouldUpdateOcean() && borderingOcean(b));
}
if(c.getUpdateBlocks().contains(m)) {
diff --git a/src/test/java/LookupGenerator.java b/src/test/java/LookupGeneratorTest.java
similarity index 89%
rename from src/test/java/LookupGenerator.java
rename to src/test/java/LookupGeneratorTest.java
index fe15ca665..1422d2e8b 100644
--- a/src/test/java/LookupGenerator.java
+++ b/src/test/java/LookupGeneratorTest.java
@@ -93,17 +93,11 @@ class LookupGenerator {
public static int normalizeNew(double d) {
for(int i = 0; i < lookup.length; i++) {
- if (d < lookup[i]) return i;
+ if(d < lookup[i]) return i;
}
return lookup.length - 1;
}
- public static int normalize(double i, int n) {
- i *= 1.42; // Magic simplex value (sqrt(2) plus a little)
- i = Math.min(Math.max(i, -1), 1);
- return Math.min((int) Math.floor((i + 1) * ((double) n / 2)), n - 1);
- }
-
private static class Worker extends Thread {
private final List l;
private final int searches;
@@ -126,9 +120,5 @@ class LookupGenerator {
public List getResult() {
return l;
}
-
- public String getStatus() {
- return "Generating values. " + l.size() + "/" + searches + " (" + ((long) l.size() * 100L) / searches + "%)";
- }
}
}