From 12c0d561d105d5add0dc8c44af59ef8c4927185f Mon Sep 17 00:00:00 2001 From: dfsek Date: Mon, 28 Sep 2020 00:13:36 -0700 Subject: [PATCH] Implement ocean palettes and improve biome abstraction. --- .../com/dfsek/terra/config/WorldConfig.java | 2 - .../config/genconfig/AbstractBiomeConfig.java | 191 ++++++------------ .../terra/config/genconfig/BiomeConfig.java | 149 +++++++++++--- .../terra/generation/TerraChunkGenerator.java | 17 +- .../terra/population/FloraPopulator.java | 4 +- 5 files changed, 201 insertions(+), 162 deletions(-) diff --git a/src/main/java/com/dfsek/terra/config/WorldConfig.java b/src/main/java/com/dfsek/terra/config/WorldConfig.java index 5c2185d4d..61394b706 100644 --- a/src/main/java/com/dfsek/terra/config/WorldConfig.java +++ b/src/main/java/com/dfsek/terra/config/WorldConfig.java @@ -28,7 +28,6 @@ public class WorldConfig { public float zoneFreq; public float freq1; public float freq2; - public int seaLevel; public boolean fromImage; public UserDefinedGrid[] definedGrids; public ImageLoader.Channel biomeXChannel; @@ -72,7 +71,6 @@ public class WorldConfig { // Get values from config. - seaLevel = config.getInt("sea-level", 63); zoneFreq = 1f/config.getInt("frequencies.zone", 1536); freq1 = 1f/config.getInt("frequencies.grid-x", 256); freq2 = 1f/config.getInt("frequencies.grid-z", 512); 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 578608943..70d9a025e 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/AbstractBiomeConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/AbstractBiomeConfig.java @@ -20,6 +20,7 @@ import org.polydev.gaea.world.palette.RandomPalette; import java.io.File; import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Random; @@ -28,22 +29,25 @@ import java.util.TreeMap; public class AbstractBiomeConfig extends TerraConfigObject { private static final Map biomes = new HashMap<>(); private String biomeID; - private Map ores; - private Map oreHeights; - private Map carvers; - private ProbabilityCollection flora; - private ProbabilityCollection trees; private int floraChance; private int treeChance; private int treeDensity; private String equation; private int floraAttempts; - private Map floraHeights; - private TreeMap> paletteMap; private double slabThreshold; private Map> slabs; private Map> stairs; private boolean useStairs; + private boolean floraSimplex; + private int floraSeed; + private float floraFreq; + private String oceanPalette; + private int seaLevel; + private List> paletteData; + private Map floraData; + private Map oreData; + private Map treeData; + private List> carvingData; public AbstractBiomeConfig(File file) throws IOException, InvalidConfigurationException { super(file); @@ -54,101 +58,26 @@ public class AbstractBiomeConfig extends TerraConfigObject { if(!contains("id")) throw new InvalidConfigurationException("Abstract Biome ID unspecified!"); this.biomeID = getString("id"); - if(contains("palette")) { - paletteMap = new TreeMap<>(); - for(Map e : getMapList("palette")) { - for(Map.Entry entry : e.entrySet()) { - try { - if(((String) entry.getKey()).startsWith("BLOCK:")) { - try { - paletteMap.put((Integer) entry.getValue(), new RandomPalette(new Random(0)).add(new ProbabilityCollection().add(Bukkit.createBlockData(((String) entry.getKey()).substring(6)), 1), 1)); - } catch(IllegalArgumentException ex) { - throw new InvalidConfigurationException("SEVERE configuration error for Palettes in abstract biome, ID: " + biomeID + ". BlockData " + entry.getKey() + " is invalid!"); - } - } else { - try { - paletteMap.put((Integer) entry.getValue(), PaletteConfig.fromID((String) entry.getKey()).getPalette()); - } catch(NullPointerException ex) { - throw new InvalidConfigurationException("SEVERE configuration error for Palettes in abstract biome, ID: " + biomeID + "\n\nPalette " + entry.getKey() + " cannot be found!"); - } - } - } catch(ClassCastException ex) { - throw new InvalidConfigurationException("SEVERE configuration error for Palettes in abstract biome, ID: " + biomeID); - } - } - } - } + if(contains("carving")) carvingData = getMapList("carving"); - if(contains("carving")) { - carvers = new HashMap<>(); - for(Map e : getMapList("carving")) { - for(Map.Entry entry : e.entrySet()) { - try { - //carvers.add(new UserDefinedCarver((Integer) entry.getValue(), ConfigUtil.getCarver((String) entry.getKey()))); - CarverConfig c = CarverConfig.fromID((String) entry.getKey()); - Bukkit.getLogger().info("Got carver " + c + ". Adding with weight " + entry.getValue()); - carvers.put(c, (Integer) entry.getValue()); - } catch(ClassCastException ex) { - throw new InvalidConfigurationException("SEVERE configuration error for Carvers in abstract biom, ID: " + biomeID); - } catch(NullPointerException ex) { - throw new InvalidConfigurationException("SEVERE configuration error for Carvers in abstract biome, ID: " + biomeID + "\n\n" + "No such carver " + entry.getKey()); - } - } - } - } + if(contains("palette")) paletteData = getMapList("palette"); + + if(contains("flora")) floraData = Objects.requireNonNull(getConfigurationSection("flora")).getValues(false); + + if(contains("trees")) treeData = Objects.requireNonNull(getConfigurationSection("trees")).getValues(false); + + if(contains("ores")) oreData = Objects.requireNonNull(getConfigurationSection("ores")).getValues(false); - if(contains("flora")) { - floraHeights = new HashMap<>(); - flora = new ProbabilityCollection<>(); - 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] 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 Range((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 Range((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")) { - trees = new ProbabilityCollection<>(); - for(Map.Entry e : Objects.requireNonNull(getConfigurationSection("trees")).getValues(false).entrySet()) { - if(e.getKey().startsWith("TERRA:")) { - trees.add(TerraTree.valueOf(e.getKey().substring(6)), (Integer) e.getValue()); - } else { - trees.add(TreeType.valueOf(e.getKey()), (Integer) e.getValue()); - } - } - } floraChance = getInt("flora-chance", 0); floraAttempts = getInt("flora-attempts", 1); treeChance = getInt("tree-chance", 0); treeDensity = getInt("tree-density", 0); equation = getString("noise-equation"); - - if(contains("ores")) { - ores = new HashMap<>(); - oreHeights = new HashMap<>(); - for(Map.Entry m : Objects.requireNonNull(getConfigurationSection("ores")).getValues(false).entrySet()) { - ores.put(OreConfig.fromID(m.getKey()), new Range(((ConfigurationSection) m.getValue()).getInt("min"), ((ConfigurationSection) m.getValue()).getInt("max"))); - oreHeights.put(OreConfig.fromID(m.getKey()), new Range(((ConfigurationSection) m.getValue()).getInt("min-height"), ((ConfigurationSection) m.getValue()).getInt("max-height"))); - } - } + floraSimplex = getBoolean("flora-simplex.enable", false); + floraFreq = (float) getDouble("flora-simplex.frequency", 0.1); + floraSeed = getInt("flora-simplex.seed", 0); + seaLevel = getInt("ocean.level", 62); + oceanPalette = getString("ocean.palette"); // Get slab stuff useStairs = false; @@ -185,42 +114,10 @@ public class AbstractBiomeConfig extends TerraConfigObject { return treeDensity; } - public Map getCarvers() { - return carvers; - } - - public Map getOreHeights() { - return oreHeights; - } - - public Map getOres() { - return ores; - } - - public Map getFloraHeights() { - return floraHeights; - } - - public static Map getBiomes() { - return biomes; - } - - public ProbabilityCollection getFlora() { - return flora; - } - - public ProbabilityCollection getTrees() { - return trees; - } - public String getEquation() { return equation; } - public TreeMap> getPaletteMap() { - return paletteMap; - } - public static AbstractBiomeConfig fromID(String id) { return biomes.get(id); } @@ -240,4 +137,44 @@ public class AbstractBiomeConfig extends TerraConfigObject { public boolean shouldUseStairs() { return useStairs; } + + public float getFloraFreq() { + return floraFreq; + } + + public int getFloraSeed() { + return floraSeed; + } + + public boolean isFloraSimplex() { + return floraSimplex; + } + + public List> getPaletteData() { + return paletteData; + } + + public Map getFloraData() { + return floraData; + } + + public Map getOreData() { + return oreData; + } + + public Map getTreeData() { + return treeData; + } + + public List> getCarvingData() { + return carvingData; + } + + public int getSeaLevel() { + return seaLevel; + } + + public String getOceanPalette() { + return oceanPalette; + } } 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 d8342ce2f..8d34f28d5 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/BiomeConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/BiomeConfig.java @@ -14,6 +14,7 @@ import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.Stairs; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.InvalidConfigurationException; +import org.polydev.gaea.math.FastNoise; import org.polydev.gaea.math.ProbabilityCollection; import org.polydev.gaea.math.parsii.tokenizer.ParseException; import org.polydev.gaea.tree.Tree; @@ -37,6 +38,7 @@ import java.util.TreeMap; public class BiomeConfig extends TerraConfigObject { private static final Map biomes = new HashMap<>(); + private static final Palette oceanDefault = new RandomPalette(new Random(0)).add(Material.WATER.createBlockData(), 1); private UserDefinedBiome biome; private String biomeID; private String friendlyName; @@ -49,6 +51,10 @@ public class BiomeConfig extends TerraConfigObject { private Map> slabs; private Map> stairs; private double slabThreshold; + private boolean floraSimplex; + private FastNoise floraNoise; + private Palette ocean; + private int seaLevel; public BiomeConfig(File file) throws InvalidConfigurationException, IOException { super(file); @@ -78,12 +84,18 @@ public class BiomeConfig extends TerraConfigObject { TreeMap> paletteMap; // Check if biome is extending abstract biome, only use abstract biome's palette if palette is NOT defined for current biome. - if(extending && abstractBiome.getPaletteMap() != null && !contains("palette")) { - paletteMap = abstractBiome.getPaletteMap(); - Bukkit.getLogger().info("Using super palette"); - } else if(contains("palette")) { + List> paletteData; + try { + if(extending && abstractBiome.getPaletteData() != null && ! contains("palette")) { + paletteData = abstractBiome.getPaletteData(); + Bukkit.getLogger().info("Using super palette"); + } else paletteData = getMapList("palette"); + } catch(NullPointerException e) { + paletteData = null; + } + if(paletteData != null) { paletteMap = new TreeMap<>(); - for(Map e : getMapList("palette")) { + for(Map e : paletteData) { for(Map.Entry entry : e.entrySet()) { try { if(((String) entry.getKey()).startsWith("BLOCK:")) { @@ -108,12 +120,19 @@ public class BiomeConfig extends TerraConfigObject { } else throw new InvalidConfigurationException("No palette specified in biome or super biome."); // Check if carving should be handled by super biome. - if(extending && abstractBiome.getCarvers() != null && !contains("carving")) { - carvers = abstractBiome.getCarvers(); - Bukkit.getLogger().info("Using super carvers"); - } else if(contains("carving")) { - carvers = new HashMap<>(); - for(Map e : getMapList("carving")) { + List> carvingData; + try { + if(extending && abstractBiome.getCarvingData() != null && ! contains("carving")) { + carvingData = abstractBiome.getCarvingData(); + Bukkit.getLogger().info("Using super carvers"); + } else carvingData = getMapList("carving"); + } catch(NullPointerException e) { + carvingData = null; + } + + carvers = new HashMap<>(); + if(carvingData != null) { + for(Map e : carvingData) { for(Map.Entry entry : e.entrySet()) { try { CarverConfig c = CarverConfig.fromID((String) entry.getKey()); @@ -126,17 +145,23 @@ public class BiomeConfig extends TerraConfigObject { } } } - } else carvers = new HashMap<>(); + } int floraChance, treeChance, treeDensity; // Get various simple values using getOrDefault config methods. + float floraFreq; + int floraSeed; try { slabThreshold = getDouble("slabs.threshold", Objects.requireNonNull(abstractBiome).getSlabThreshold()); 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()); + floraSeed = getInt("flora-simplex.seed", Objects.requireNonNull(abstractBiome).getFloraSeed()); + floraSimplex = getBoolean("flora-simplex.enable", Objects.requireNonNull(abstractBiome).isFloraSimplex()); + floraFreq = (float) getDouble("flora-simplex.frequency", Objects.requireNonNull(abstractBiome).getFloraFreq()); + seaLevel = getInt("ocean.level", Objects.requireNonNull(abstractBiome).getSeaLevel()); eq = getString("noise-equation", Objects.requireNonNull(abstractBiome).getEquation()); } catch(NullPointerException e) { slabThreshold = getDouble("slabs.threshold", 0.1D); @@ -144,19 +169,34 @@ public class BiomeConfig extends TerraConfigObject { floraAttempts = getInt("flora-attempts", 1); treeChance = getInt("tree-chance", 0); treeDensity = getInt("tree-density", 0); + floraSeed = getInt("flora-simplex.seed", 0); + floraSimplex = getBoolean("flora-simplex.enable", false); + floraFreq = (float) getDouble("flora-simplex.frequency", 0.1); + seaLevel = getInt("ocean.level", 62); eq = getString("noise-equation", null); } + if(floraSimplex) { + floraNoise = new FastNoise(floraSeed); + floraNoise.setNoiseType(FastNoise.NoiseType.Simplex); + floraNoise.setFrequency(floraFreq); + } + // 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")) { + Map floraData; + try { + if(extending && abstractBiome.getFloraData() != null && ! contains("flora")) { + floraData = abstractBiome.getFloraData(); + Bukkit.getLogger().info("Using super flora (" + flora.size() + " entries, " + floraChance + " % chance)"); + } else floraData = Objects.requireNonNull(getConfigurationSection("flora")).getValues(false); + } catch(NullPointerException e) { + floraData = null; + } + if(floraData != null) { floraHeights = new HashMap<>(); try { - for(Map.Entry e : Objects.requireNonNull(getConfigurationSection("flora")).getValues(false).entrySet()) { + for(Map.Entry e : floraData.entrySet()) { Map val = ((ConfigurationSection) e.getValue()).getValues(false); Map y = ((ConfigurationSection) val.get("y")).getValues(false); try { @@ -182,12 +222,18 @@ public class BiomeConfig extends TerraConfigObject { } else flora = new ProbabilityCollection<>(); // Check if trees should be handled by super biome. + Map treeData; ProbabilityCollection trees = new ProbabilityCollection<>(); - if(extending && abstractBiome.getTrees() != null && !contains("trees")) { - trees = abstractBiome.getTrees(); - Bukkit.getLogger().info("Using super trees"); - } else if(contains("trees")) { - for(Map.Entry e : Objects.requireNonNull(getConfigurationSection("trees")).getValues(false).entrySet()) { + try { + if(extending && abstractBiome.getTreeData() != null && ! contains("trees")) { + treeData = abstractBiome.getTreeData(); + Bukkit.getLogger().info("Using super trees"); + } else treeData = Objects.requireNonNull(getConfigurationSection("trees")).getValues(false); + } catch(NullPointerException e) { + treeData = null; + } + if(treeData != null) { + for(Map.Entry e : treeData.entrySet()) { if(e.getKey().startsWith("TERRA:")) { trees.add(TerraTree.valueOf(e.getKey().substring(6)), (Integer) e.getValue()); } else { @@ -214,13 +260,17 @@ 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(); - Bukkit.getLogger().info("Using super ores"); - } else if(contains("ores")) { - ores.clear(); - for(Map.Entry m : Objects.requireNonNull(getConfigurationSection("ores")).getValues(false).entrySet()) { + Map oreData; + try { + if(extending && abstractBiome.getOreData() != null && ! contains("ores")) { + oreData = abstractBiome.getOreData(); + Bukkit.getLogger().info("Using super ores"); + } else oreData = Objects.requireNonNull(getConfigurationSection("ores")).getValues(false); + } catch(NullPointerException e) { + oreData = null; + } + if(oreData != null) { + for(Map.Entry m : oreData.entrySet()) { ores.put(OreConfig.fromID(m.getKey()), new Range(((ConfigurationSection) m.getValue()).getInt("min"), ((ConfigurationSection) m.getValue()).getInt("max"))); oreHeights.put(OreConfig.fromID(m.getKey()), new Range(((ConfigurationSection) m.getValue()).getInt("min-height"), ((ConfigurationSection) m.getValue()).getInt("max-height"))); } @@ -229,6 +279,29 @@ public class BiomeConfig extends TerraConfigObject { oreHeights = new HashMap<>(); } + // Ocean stuff + String oceanPalette; + try { + oceanPalette = getString("ocean.palette", Objects.requireNonNull(abstractBiome).getOceanPalette()); + } catch(NullPointerException e) { + oceanPalette = null; + } + if(contains("ocean") && oceanPalette != null) { + if(oceanPalette.startsWith("BLOCK:")) { + try { + ocean = new RandomPalette(new Random(0)).add(new ProbabilityCollection().add(Bukkit.createBlockData(oceanPalette.substring(6)), 1), 1); + } catch(IllegalArgumentException ex) { + throw new InvalidConfigurationException("SEVERE configuration error for Ocean Palette in biome " + getFriendlyName() + ", ID: " + biomeID + ". BlockData " + oceanPalette + " is invalid!"); + } + } else { + try { + ocean = PaletteConfig.fromID(oceanPalette).getPalette(); + } catch(NullPointerException ex) { + throw new InvalidConfigurationException("SEVERE configuration error for Ocean Palette in biome " + getFriendlyName() + ", ID: " + biomeID + "\n\nPalette " + oceanPalette + " cannot be found!"); + } + } + } else ocean = oceanDefault; + // Get slab stuff if(contains("slabs") && getBoolean("slabs.enable", false)) { @@ -338,4 +411,20 @@ public class BiomeConfig extends TerraConfigObject { public Map> getSlabs() { return slabs; } + + public boolean isFloraSimplex() { + return floraSimplex; + } + + public FastNoise getFloraNoise() { + return floraNoise; + } + + public Palette getOceanPalette() { + return ocean; + } + + public int getSeaLevel() { + return seaLevel; + } } diff --git a/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java b/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java index 93ce0a436..1d855ccf8 100644 --- a/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java +++ b/src/main/java/com/dfsek/terra/generation/TerraChunkGenerator.java @@ -1,7 +1,9 @@ package com.dfsek.terra.generation; import com.dfsek.terra.biome.TerraBiomeGrid; +import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.config.WorldConfig; +import com.dfsek.terra.config.genconfig.BiomeConfig; import com.dfsek.terra.population.CavePopulator; import com.dfsek.terra.population.FloraPopulator; import com.dfsek.terra.population.OrePopulator; @@ -13,11 +15,15 @@ import org.bukkit.World; import org.bukkit.block.data.BlockData; import org.bukkit.generator.BlockPopulator; import org.jetbrains.annotations.NotNull; +import org.polydev.gaea.biome.Biome; +import org.polydev.gaea.biome.BiomeGrid; import org.polydev.gaea.generation.GaeaChunkGenerator; +import org.polydev.gaea.generation.GenerationPhase; import org.polydev.gaea.generation.GenerationPopulator; import org.polydev.gaea.math.ChunkInterpolator; import org.polydev.gaea.math.FastNoise; import org.polydev.gaea.population.PopulationManager; +import org.polydev.gaea.world.palette.Palette; import java.io.FileNotFoundException; import java.io.IOException; @@ -46,12 +52,19 @@ public class TerraChunkGenerator extends GaeaChunkGenerator { public ChunkData generateBase(@NotNull World world, @NotNull Random random, int chunkX, int chunkZ, FastNoise fastNoise) { if(needsLoad) load(world); ChunkData chunk = createChunkData(world); - int sea = WorldConfig.fromWorld(world).seaLevel; + int xOrig = (chunkX << 4); + int zOrig = (chunkZ << 4); for(byte x = 0; x < 16; x++) { for(byte z = 0; z < 16; z++) { + Biome b = getBiomeGrid(world).getBiome(xOrig+x, zOrig+z, GenerationPhase.PALETTE_APPLY); + BiomeConfig c = BiomeConfig.fromBiome((UserDefinedBiome) b); + int sea = c.getSeaLevel(); + Palette seaPalette = c.getOceanPalette(); for(int y = 0; y < 256; y++) { if(super.getInterpolatedNoise(x, y, z) > 0) chunk.setBlock(x, y, z, STONE); - else if(y < sea) chunk.setBlock(x, y, z, WATER); + else if(y <= sea) { + chunk.setBlock(x, y, z, seaPalette.get(sea-y, x+xOrig, z+zOrig)); + } } } } diff --git a/src/main/java/com/dfsek/terra/population/FloraPopulator.java b/src/main/java/com/dfsek/terra/population/FloraPopulator.java index dfbd2326a..7b7305cd7 100644 --- a/src/main/java/com/dfsek/terra/population/FloraPopulator.java +++ b/src/main/java/com/dfsek/terra/population/FloraPopulator.java @@ -34,7 +34,9 @@ public class FloraPopulator extends GaeaBlockPopulator { try { BiomeConfig c = BiomeConfig.fromBiome(biome); for(int i = 0; i < c.getFloraAttempts(); i++) { - Flora item = biome.getDecorator().getFlora().get(random); + Flora item; + if(c.isFloraSimplex()) item = biome.getDecorator().getFlora().get(c.getFloraNoise(), (chunk.getX() << 4) + x, (chunk.getZ() << 4) + z); + else 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());