diff --git a/src/main/java/com/dfsek/terra/MaxMin.java b/src/main/java/com/dfsek/terra/MaxMin.java index 97ce924be..a2c54a644 100644 --- a/src/main/java/com/dfsek/terra/MaxMin.java +++ b/src/main/java/com/dfsek/terra/MaxMin.java @@ -10,6 +10,10 @@ public class MaxMin { this.min = min; } + public boolean isInRange(int test) { + return test >= min && test < max; + } + public int getMax() { return max; } diff --git a/src/main/java/com/dfsek/terra/config/genconfig/AbstractBiomeConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/AbstractBiomeConfig.java index 0d657e8fe..99a467eee 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/AbstractBiomeConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/AbstractBiomeConfig.java @@ -2,6 +2,7 @@ package com.dfsek.terra.config.genconfig; import com.dfsek.terra.MaxMin; import com.dfsek.terra.TerraTree; +import com.dfsek.terra.config.ConfigUtil; import com.dfsek.terra.config.TerraConfigObject; import org.bukkit.Bukkit; import org.bukkit.block.data.BlockData; @@ -36,6 +37,8 @@ public class AbstractBiomeConfig extends TerraConfigObject { private int treeChance; private int treeDensity; private String equation; + private int floraAttempts; + private Map floraHeights; private TreeMap> paletteMap; public AbstractBiomeConfig(File file) throws IOException, InvalidConfigurationException { @@ -91,20 +94,31 @@ public class AbstractBiomeConfig extends TerraConfigObject { } if(contains("flora")) { + floraHeights = new HashMap<>(); flora = new ProbabilityCollection<>(); - for(Map.Entry e : Objects.requireNonNull(getConfigurationSection("flora")).getValues(false).entrySet()) { - try { - Bukkit.getLogger().info("[Terra] Adding " + e.getKey() + " to abstract biome's flora list with weight " + e.getValue()); - flora.add(FloraType.valueOf(e.getKey()), (Integer) e.getValue()); - } catch(IllegalArgumentException ex) { + try { + for(Map.Entry e : Objects.requireNonNull(getConfigurationSection("flora")).getValues(false).entrySet()) { + Map val = ((ConfigurationSection) e.getValue()).getValues(false); + Map y = ((ConfigurationSection) val.get("y")).getValues(false); try { - Bukkit.getLogger().info("[Terra] Is custom flora: true"); - Flora floraCustom = FloraConfig.fromID(e.getKey()); - flora.add(floraCustom, (Integer) e.getValue()); - } catch(NullPointerException ex2) { - throw new IllegalArgumentException("SEVERE configuration error for flora in abstract biome, ID " + getID() + "\n\nFlora with ID " + e.getKey() + " cannot be found!"); + Bukkit.getLogger().info("[Terra] Adding " + e.getKey() + " to biome's flora list with weight " + e.getValue()); + Flora floraObj = FloraType.valueOf(e.getKey()); + flora.add(floraObj, (Integer) val.get("weight")); + floraHeights.put(floraObj, new MaxMin((Integer) y.get("min"), (Integer) y.get("max"))); + } catch(IllegalArgumentException ex) { + try { + Bukkit.getLogger().info("[Terra] Is custom flora: true"); + Flora floraCustom = FloraConfig.fromID(e.getKey()); + flora.add(floraCustom, (Integer) val.get("weight")); + floraHeights.put(floraCustom, new MaxMin((Integer) y.get("min"), (Integer) y.get("max"))); + } catch(NullPointerException ex2) { + throw new InvalidConfigurationException("SEVERE configuration error for flora in biome, ID " + getID() + "\n\nFlora with ID " + e.getKey() + " cannot be found!"); + } } } + } catch(ClassCastException e) { + if(ConfigUtil.debug) e.printStackTrace(); + throw new InvalidConfigurationException("SEVERE configuration error for flora in biome, ID " + getID()); } } if(contains("trees")) { @@ -118,6 +132,7 @@ public class AbstractBiomeConfig extends TerraConfigObject { } } floraChance = getInt("flora-chance", 0); + floraAttempts = getInt("flora-attempts", 1); treeChance = getInt("tree-chance", 0); treeDensity = getInt("tree-density", 0); equation = getString("noise-equation"); @@ -139,6 +154,10 @@ public class AbstractBiomeConfig extends TerraConfigObject { return biomeID; } + public int getFloraAttempts() { + return floraAttempts; + } + public int getFloraChance() { return floraChance; } @@ -163,6 +182,10 @@ public class AbstractBiomeConfig extends TerraConfigObject { return ores; } + public Map getFloraHeights() { + return floraHeights; + } + public static Map getBiomes() { return biomes; } diff --git a/src/main/java/com/dfsek/terra/config/genconfig/BiomeConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/BiomeConfig.java index c7882b8e6..b2a031aab 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/BiomeConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/BiomeConfig.java @@ -6,6 +6,7 @@ import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.biome.UserDefinedDecorator; import com.dfsek.terra.biome.UserDefinedGenerator; import com.dfsek.terra.carving.UserDefinedCarver; +import com.dfsek.terra.config.ConfigUtil; import com.dfsek.terra.config.TerraConfigObject; import org.bukkit.Bukkit; import org.bukkit.block.data.BlockData; @@ -39,7 +40,9 @@ public class BiomeConfig extends TerraConfigObject { private Map ores; private Map oreHeights; private Map carvers; + private Map floraHeights; private String eq; + private int floraAttempts; public BiomeConfig(File file) throws InvalidConfigurationException, IOException { super(file); @@ -47,10 +50,6 @@ public class BiomeConfig extends TerraConfigObject { @Override public void init() throws InvalidConfigurationException { - oreHeights = new HashMap<>(); - ores = new HashMap<>(); - ProbabilityCollection flora = new ProbabilityCollection<>(); - ProbabilityCollection trees = new ProbabilityCollection<>(); if(!contains("id")) throw new InvalidConfigurationException("Biome ID unspecified!"); this.biomeID = getString("id"); if(!contains("name")) throw new InvalidConfigurationException("Biome Name unspecified!"); @@ -128,38 +127,55 @@ public class BiomeConfig extends TerraConfigObject { // Get various simple values using getOrDefault config methods. try { floraChance = getInt("flora-chance", Objects.requireNonNull(abstractBiome).getFloraChance()); + floraAttempts = getInt("flora-attempts", Objects.requireNonNull(abstractBiome).getFloraAttempts()); treeChance = getInt("tree-chance", Objects.requireNonNull(abstractBiome).getTreeChance()); treeDensity = getInt("tree-density", Objects.requireNonNull(abstractBiome).getTreeDensity()); eq = getString("noise-equation", Objects.requireNonNull(abstractBiome).getEquation()); } catch(NullPointerException e) { floraChance = getInt("flora-chance", 0); + floraAttempts = getInt("flora-attempts", 1); treeChance = getInt("tree-chance", 0); treeDensity = getInt("tree-density", 0); eq = getString("noise-equation", null); } // Check if flora should be handled by super biome. + ProbabilityCollection flora = new ProbabilityCollection<>(); if(extending && abstractBiome.getFlora() != null && !contains("flora")) { flora = abstractBiome.getFlora(); + floraHeights = abstractBiome.getFloraHeights(); Bukkit.getLogger().info("Using super flora (" + flora.size() + " entries, " + floraChance + " % chance)"); } else if(contains("flora")) { - for(Map.Entry e : Objects.requireNonNull(getConfigurationSection("flora")).getValues(false).entrySet()) { - try { - Bukkit.getLogger().info("[Terra] Adding " + e.getKey() + " to biome's flora list with weight " + e.getValue()); - flora.add(FloraType.valueOf(e.getKey()), (Integer) e.getValue()); - } catch(IllegalArgumentException ex) { + floraHeights = new HashMap<>(); + try { + for(Map.Entry e : Objects.requireNonNull(getConfigurationSection("flora")).getValues(false).entrySet()) { + Map val = ((ConfigurationSection) e.getValue()).getValues(false); + Map y = ((ConfigurationSection) val.get("y")).getValues(false); try { - Bukkit.getLogger().info("[Terra] Is custom flora: true"); - Flora floraCustom = FloraConfig.fromID(e.getKey()); - flora.add(floraCustom, (Integer) e.getValue()); - } catch(NullPointerException ex2) { - throw new IllegalArgumentException("SEVERE configuration error for flora in biome " + getFriendlyName() + ", ID " + getID() + "\n\nFlora with ID " + e.getKey() + " cannot be found!"); + Bukkit.getLogger().info("[Terra] Adding " + e.getKey() + " to biome's flora list with weight " + e.getValue()); + Flora floraObj = FloraType.valueOf(e.getKey()); + flora.add(floraObj, (Integer) val.get("weight")); + floraHeights.put(floraObj, new MaxMin((Integer) y.get("min"), (Integer) y.get("max"))); + } catch(IllegalArgumentException ex) { + try { + Bukkit.getLogger().info("[Terra] Is custom flora: true"); + Flora floraCustom = FloraConfig.fromID(e.getKey()); + flora.add(floraCustom, (Integer) val.get("weight")); + floraHeights.put(floraCustom, new MaxMin((Integer) y.get("min"), (Integer) y.get("max"))); + } catch(NullPointerException ex2) { + throw new InvalidConfigurationException("SEVERE configuration error for flora in biome " + getFriendlyName() + ", ID " + getID() + "\n\nFlora with ID " + e.getKey() + " cannot be found!"); + } } } + } catch(ClassCastException e) { + if(ConfigUtil.debug) e.printStackTrace(); + throw new InvalidConfigurationException("SEVERE configuration error for flora in biome " + getFriendlyName() + ", ID " + getID()); } } else flora = new ProbabilityCollection<>(); - if(extending && abstractBiome.getTrees() != null && !contains("trees")) { // Check if trees should be handled by super biome. + // Check if trees should be handled by super biome. + ProbabilityCollection trees = new ProbabilityCollection<>(); + if(extending && abstractBiome.getTrees() != null && !contains("trees")) { trees = abstractBiome.getTrees(); Bukkit.getLogger().info("Using super trees"); } else if(contains("trees")) { @@ -188,6 +204,8 @@ public class BiomeConfig extends TerraConfigObject { } // Check if ores should be handled by super biome. + oreHeights = new HashMap<>(); + ores = new HashMap<>(); if(extending && abstractBiome.getOres() != null && !contains("ores")) { ores = abstractBiome.getOres(); oreHeights = abstractBiome.getOreHeights(); @@ -221,6 +239,10 @@ public class BiomeConfig extends TerraConfigObject { return biome; } + public int getFloraAttempts() { + return floraAttempts; + } + public String getID() { return biomeID; } @@ -233,6 +255,10 @@ public class BiomeConfig extends TerraConfigObject { return ores; } + public MaxMin getFloraHeights(Flora f) { + return floraHeights.computeIfAbsent(f, input -> new MaxMin(-1, -1)); + } + public static BiomeConfig fromBiome(UserDefinedBiome b) { for(BiomeConfig biome : biomes.values()) { if(biome.getBiome().equals(b)) return biome; diff --git a/src/main/java/com/dfsek/terra/config/genconfig/FloraConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/FloraConfig.java index 0ac2e3486..fd1b00143 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/FloraConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/FloraConfig.java @@ -16,9 +16,11 @@ import org.polydev.gaea.world.palette.RandomPalette; import java.io.File; import java.io.IOException; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.Set; public class FloraConfig extends TerraConfigObject implements Flora { private static final Map floraConfig = new HashMap<>(); @@ -26,7 +28,9 @@ public class FloraConfig extends TerraConfigObject implements Flora { private String id; private String friendlyName; - List spawnable; + Set spawnable; + Set replaceable; + public FloraConfig(File file) throws IOException, InvalidConfigurationException { super(file); } @@ -38,12 +42,25 @@ public class FloraConfig extends TerraConfigObject implements Flora { if(!contains("name")) throw new InvalidConfigurationException("Flora name unspecified!"); if(!contains("spawnable")) throw new InvalidConfigurationException("Flora spawnable blocks unspecified!"); + spawnable = new HashSet<>(); + replaceable = new HashSet<>(); + try { - spawnable = ConfigUtil.getElements(getStringList("spawnable"), Material.class); + for(String s : getStringList("spawnable")) { + spawnable.add(Bukkit.createBlockData(s).getMaterial()); + } Bukkit.getLogger().info("[Terra] Added " + spawnable.size() + " items to spawnable list."); } catch(IllegalArgumentException e) { throw new InvalidConfigurationException("Invalid material ID in spawnable list: " + e.getMessage()); } + try { + for(String s : getStringList("replaceable")) { + replaceable.add(Bukkit.createBlockData(s).getMaterial()); + } + Bukkit.getLogger().info("[Terra] Added " + spawnable.size() + " items to replaceable list."); + } catch(IllegalArgumentException e) { + throw new InvalidConfigurationException("Invalid material ID in replaceable list: " + e.getMessage()); + } Palette p = new RandomPalette<>(new Random(getInt("seed", 4))); @@ -75,7 +92,7 @@ public class FloraConfig extends TerraConfigObject implements Flora { public boolean plant(Location location) { int size = floraPalette.getSize(); for(int i = 0; i < size; i++) { - if(!location.clone().add(0, i+1, 0).getBlock().isEmpty()) return false; + if(!replaceable.contains(location.clone().add(0, i+1, 0).getBlock().getType())) return false; } for(int i = 0; i < size; i++) { location.clone().add(0, i+1, 0).getBlock().setBlockData(floraPalette.get(size-(i+1), location.getBlockX(), location.getBlockZ()), false); diff --git a/src/main/java/com/dfsek/terra/population/FloraPopulator.java b/src/main/java/com/dfsek/terra/population/FloraPopulator.java index d16d235a0..a017b3b17 100644 --- a/src/main/java/com/dfsek/terra/population/FloraPopulator.java +++ b/src/main/java/com/dfsek/terra/population/FloraPopulator.java @@ -2,6 +2,8 @@ package com.dfsek.terra.population; import com.dfsek.terra.TerraProfiler; import com.dfsek.terra.biome.TerraBiomeGrid; +import com.dfsek.terra.biome.UserDefinedBiome; +import com.dfsek.terra.config.genconfig.BiomeConfig; import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.block.Block; @@ -19,13 +21,16 @@ public class FloraPopulator extends GaeaBlockPopulator { ProfileFuture flora = TerraProfiler.fromWorld(world).measure("FloraTime"); for(int x = 0; x < 16; x++) { for(int z = 0; z < 16; z++) { - Biome biome = TerraBiomeGrid.fromWorld(world).getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z); + UserDefinedBiome biome = (UserDefinedBiome) TerraBiomeGrid.fromWorld(world).getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z); if(biome.getDecorator().getFloraChance() <= 0 || random.nextInt(100) > biome.getDecorator().getFloraChance()) continue; try { - Flora item = biome.getDecorator().getFlora().get(random); - Block highest = item.getHighestValidSpawnAt(chunk, x, z); - if(highest != null) item.plant(highest.getLocation()); + BiomeConfig c = BiomeConfig.fromBiome(biome); + for(int i = 0; i < c.getFloraAttempts(); i++) { + Flora item = biome.getDecorator().getFlora().get(random); + Block highest = item.getHighestValidSpawnAt(chunk, x, z); + if(highest != null && c.getFloraHeights(item).isInRange(highest.getY())) item.plant(highest.getLocation()); + } } catch(NullPointerException ignored) {} } }