package world.bentobox.boxed.generators.biomes; import java.io.File; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.SortedMap; import java.util.TreeMap; import org.bukkit.World.Environment; import org.bukkit.block.Biome; import org.bukkit.block.BlockFace; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.generator.BiomeParameterPoint; import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.WorldInfo; import org.bukkit.util.Vector; import org.eclipse.jdt.annotation.NonNull; import com.google.common.base.Enums; import world.bentobox.boxed.Boxed; /** * Generates the biomes for the seed world. A seed world is the template for the chunks that * are used to generate areas for the players to play it.s * @author tastybento * */ public abstract class AbstractSeedBiomeProvider extends BiomeProvider { private static final Map ENV_MAP; static { Map e = new EnumMap<>(Environment.class); e.put(Environment.NORMAL, "distribution.overworld"); e.put(Environment.NETHER, "distribution.nether"); e.put(Environment.THE_END, "distribution.the_end"); ENV_MAP = Collections.unmodifiableMap(e); } /** * Heights / ridges by erosion */ private enum Ridges { VALLEYS(-1.0, -0.85), LOW(-0.85, -0.6), MID(-0.6, 0.2), HIGH(0.2, 0.7), PEAKS(0.7, 1.0); private double low; private double high; Ridges(double low, double high) { this.low = low; this.high = high; } public static Ridges getRidge(double erosion) { for (Ridges r : Ridges.values()) { if (erosion >= r.low && erosion < r.high) { return r; } } return MID; } } /** * Badland Biones by Humidity Zone */ private enum BadlandBiome { HZONE0(0, Biome.BADLANDS, Biome.ERODED_BADLANDS), HZONE1(1, Biome.BADLANDS, Biome.ERODED_BADLANDS), HZONE2(2, Biome.BADLANDS, Biome.BADLANDS), HZONE3(3, Biome.WOODED_BADLANDS, Biome.WOODED_BADLANDS), HZONE4(4, Biome.WOODED_BADLANDS, Biome.WOODED_BADLANDS); private int humidityZone; private Biome biome; private Biome biome2; BadlandBiome(int h, Biome biome, Biome biome2) { this.humidityZone = h; this.biome = biome; this.biome2 = biome2; } public static Biome getBiome(int humidity, double weirdness) { for (BadlandBiome mb : BadlandBiome.values()) { if (mb.humidityZone == humidity) { if (weirdness < 0) { return mb.biome; } else { return mb.biome2; } } } throw new IllegalArgumentException("badlands biome h = " + humidity); } } /** * Middle Biomes by temperature and humidity zones */ private enum MiddleBiome { X00(0, 0, Biome.SNOWY_PLAINS, Biome.ICE_SPIKES), X01(0, 1, Biome.PLAINS, Biome.PLAINS), X02(0, 2, Biome.FLOWER_FOREST, Biome.SUNFLOWER_PLAINS), x03(0, 3, Biome.SAVANNA, Biome.SAVANNA), X04(0, 4, Biome.DESERT, Biome.DESERT), X10(1, 0, Biome.SNOWY_PLAINS, Biome.SNOWY_PLAINS), X11(1, 1, Biome.PLAINS, Biome.PLAINS), X12(1, 2, Biome.PLAINS, Biome.PLAINS), X13(1, 3, Biome.SAVANNA, Biome.SAVANNA), X14(1, 4, Biome.DESERT, Biome.DESERT), X20(2, 0, Biome.SNOWY_PLAINS, Biome.SNOWY_TAIGA), X21(2, 1, Biome.FOREST, Biome.FOREST), X22(2, 2, Biome.FOREST, Biome.FOREST), X23(2, 3, Biome.FOREST, Biome.PLAINS), X24(2, 4, Biome.DESERT, Biome.DESERT), X30(3, 0, Biome.SNOWY_TAIGA, Biome.SNOWY_TAIGA), X31(3, 1, Biome.TAIGA, Biome.TAIGA), X32(3, 2, Biome.BIRCH_FOREST, Biome.OLD_GROWTH_BIRCH_FOREST), X33(3, 3, Biome.JUNGLE, Biome.SPARSE_JUNGLE), X34(3, 4, Biome.DESERT, Biome.DESERT), X40(4, 0, Biome.TAIGA, Biome.TAIGA), X41(4, 1, Biome.OLD_GROWTH_SPRUCE_TAIGA, Biome.OLD_GROWTH_PINE_TAIGA), X42(4, 2, Biome.DARK_FOREST, Biome.DARK_FOREST), X43(4, 3, Biome.JUNGLE, Biome.BAMBOO_JUNGLE), X44(4, 4, Biome.DESERT, Biome.DESERT), ; private int temperature; private int humidity; private Biome biome; private Biome weirdBiome; // What the biome be if the weirdness is high enough MiddleBiome(int h, int t, Biome b, Biome weirdBiome) { this.humidity = h; this.temperature = t; this.weirdBiome = weirdBiome; this.biome = b; } public static Biome getBiome(int humidity, int temperature, double weirdness) { for (MiddleBiome mb : MiddleBiome.values()) { if (mb.humidity == humidity && mb.temperature == temperature) { if (weirdness < 0) { return mb.biome; } else { return mb.weirdBiome; } } } throw new IllegalArgumentException("middle biome h = " + humidity + " t = " + temperature); } } /** * Plateau biomes by temperature and humidity zones */ private enum PlateauBiome { X00(0, 0, Biome.SNOWY_PLAINS, Biome.ICE_SPIKES), x01(0, 1, Biome.MEADOW, Biome.CHERRY_GROVE), X02(0, 2, Biome.MEADOW, Biome.CHERRY_GROVE), x03(0, 3, Biome.SAVANNA_PLATEAU, Biome.SAVANNA_PLATEAU), X04(0, 4, Biome.BADLANDS, Biome.ERODED_BADLANDS), X10(1, 0, Biome.SNOWY_PLAINS, Biome.SNOWY_PLAINS), x11(1, 1, Biome.MEADOW, Biome.MEADOW), X12(1, 2, Biome.MEADOW, Biome.CHERRY_GROVE), x13(1, 3, Biome.SAVANNA_PLATEAU, Biome.SAVANNA_PLATEAU), X14(1, 4, Biome.BADLANDS, Biome.ERODED_BADLANDS), X20(2, 0, Biome.SNOWY_PLAINS, Biome.SNOWY_TAIGA), x21(2, 1, Biome.FOREST, Biome.MEADOW), X22(2, 2, Biome.MEADOW, Biome.BIRCH_FOREST), x23(2, 3, Biome.FOREST, Biome.FOREST), X24(2, 4, Biome.BADLANDS, Biome.BADLANDS), X30(3, 0, Biome.SNOWY_TAIGA, Biome.SNOWY_TAIGA), x31(3, 1, Biome.TAIGA, Biome.MEADOW), X32(3, 2, Biome.MEADOW, Biome.BIRCH_FOREST), x33(3, 3, Biome.FOREST, Biome.FOREST), X34(3, 4, Biome.WOODED_BADLANDS, Biome.WOODED_BADLANDS), X40(4, 0, Biome.SNOWY_TAIGA, Biome.SNOWY_TAIGA), X41(4, 1, Biome.OLD_GROWTH_SPRUCE_TAIGA, Biome.OLD_GROWTH_PINE_TAIGA), X42(4, 2, Biome.DARK_FOREST, Biome.DARK_FOREST), x43(4, 3, Biome.JUNGLE, Biome.JUNGLE), X44(4, 4, Biome.WOODED_BADLANDS, Biome.WOODED_BADLANDS),; private int temp; private int humidity; private Biome b; private Biome weirdBiome; PlateauBiome(int humidity, int temp, Biome b, Biome weirdBiome) { this.humidity = humidity; this.temp = temp; this.weirdBiome = weirdBiome; this.b = b; } public static Biome getBiome(int humidity, int temp, double weirdness) { for (PlateauBiome mb : PlateauBiome.values()) { if (mb.humidity == humidity && mb.temp == temp) { if (weirdness < 0) { return mb.b; } else { return mb.weirdBiome; } } } throw new IllegalArgumentException("plateau biome h = " + humidity + " t = " + temp); } } /** * Shattered biomes by temperature and humidity */ private enum ShatteredBiome { X00(0, 0, Biome.WINDSWEPT_GRAVELLY_HILLS, Biome.WINDSWEPT_GRAVELLY_HILLS), X01(0, 1, Biome.WINDSWEPT_GRAVELLY_HILLS, Biome.WINDSWEPT_GRAVELLY_HILLS), X02(0, 2, Biome.WINDSWEPT_HILLS, Biome.WINDSWEPT_HILLS), X03(0, 3, Biome.SAVANNA, Biome.SAVANNA), X04(0, 4, Biome.DESERT, Biome.DESERT), X10(1, 0, Biome.WINDSWEPT_GRAVELLY_HILLS, Biome.WINDSWEPT_GRAVELLY_HILLS), X11(1, 1, Biome.WINDSWEPT_GRAVELLY_HILLS, Biome.WINDSWEPT_GRAVELLY_HILLS), X12(1, 2, Biome.WINDSWEPT_HILLS, Biome.WINDSWEPT_HILLS), X13(1, 3, Biome.SAVANNA_PLATEAU, Biome.SAVANNA_PLATEAU), X14(1, 4, Biome.DESERT, Biome.DESERT), X20(2, 0, Biome.WINDSWEPT_HILLS, Biome.WINDSWEPT_HILLS), X21(2, 1, Biome.WINDSWEPT_HILLS, Biome.WINDSWEPT_HILLS), X22(2, 2, Biome.WINDSWEPT_HILLS, Biome.WINDSWEPT_HILLS), X23(2, 3, Biome.FOREST, Biome.FOREST), X24(2, 4, Biome.DESERT, Biome.DESERT), X30(3, 0, Biome.WINDSWEPT_FOREST, Biome.WINDSWEPT_FOREST), X31(3, 1, Biome.WINDSWEPT_FOREST, Biome.WINDSWEPT_FOREST), X32(3, 2, Biome.WINDSWEPT_FOREST, Biome.WINDSWEPT_FOREST), X33(3, 3, Biome.JUNGLE, Biome.SPARSE_JUNGLE), X34(3, 4, Biome.DESERT, Biome.DESERT), X40(4, 0, Biome.WINDSWEPT_FOREST, Biome.WINDSWEPT_FOREST), X41(4, 1, Biome.WINDSWEPT_FOREST, Biome.WINDSWEPT_FOREST), X42(4, 2, Biome.WINDSWEPT_FOREST, Biome.WINDSWEPT_FOREST), X43(4, 3, Biome.JUNGLE, Biome.BAMBOO_JUNGLE), X44(4, 4, Biome.DESERT, Biome.DESERT),; private int temperature; private int humidity; private Biome biome; private Biome weirdBiome; ShatteredBiome(int h, int t, Biome b, Biome weirdBiome) { this.humidity = h; this.temperature = t; this.weirdBiome = weirdBiome; this.biome = b; } public static Biome getBiome(int h, int t, double we) { for (ShatteredBiome mb : ShatteredBiome.values()) { if (mb.humidity == h && mb.temperature == t) { if (we < 0) { return mb.biome; } else { return mb.weirdBiome; } } } throw new IllegalArgumentException("shattered biome h = " + h + " t = " + t); } } /** * Continental location by continentalness */ private enum ContLoc { MUSHROOM_FIELDS(-1.2, -1.05), DEEP_OCEAN(-1.05, -0.455), OCEAN(-0.455, -0.19), COAST(-0.19, -0.11), NEAR_INLAND(-0.11, 0.03), MID_INLAND(0.03, 0.3), FAR_INLAND(0.3, 10.0); private double min; private double max; ContLoc(double min, double max) { this.min = min; this.max = max; } public static ContLoc getCont(double continentalness) { for (ContLoc c : ContLoc.values()) { if (continentalness >= c.min && continentalness < c.max) { return c; } } throw new IllegalArgumentException("contloc out of spec value = " + continentalness); } } private final Boxed addon; protected final int dist; private final int spawnX; private final int spawnZ; protected final Map> quadrants; protected AbstractSeedBiomeProvider(Boxed boxed, Environment env) { this.addon = boxed; // These fields are used to determine the biomes around the spawn point this.dist = addon.getSettings().getIslandDistance(); if (env.equals(Environment.NORMAL)) { spawnX = addon.getSettings().getSeedX(); spawnZ = addon.getSettings().getSeedZ(); } else { spawnX = addon.getSettings().getNetherSeedX(); spawnZ = addon.getSettings().getNetherSeedZ(); } // Load the config File biomeFile = new File(addon.getDataFolder(), "biomes.yml"); // Check if it exists and if not, save it from the jar if (!biomeFile.exists()) { addon.saveResource("biomes.yml", true); } YamlConfiguration config = YamlConfiguration.loadConfiguration(biomeFile); SortedMap northEast = loadQuad(config, ENV_MAP.get(env) + ".north-east"); SortedMap southEast = loadQuad(config, ENV_MAP.get(env) + ".south-east"); SortedMap northWest = loadQuad(config, ENV_MAP.get(env) + ".north-west"); SortedMap southWest = loadQuad(config, ENV_MAP.get(env) + ".south-west"); quadrants = new EnumMap<>(BlockFace.class); quadrants.put(BlockFace.NORTH_EAST, northEast); quadrants.put(BlockFace.NORTH_WEST, northWest); quadrants.put(BlockFace.SOUTH_EAST, southEast); quadrants.put(BlockFace.SOUTH_WEST, southWest); } private Biome getQuadrantBiome(BlockFace dir, double d) { Entry en = ((TreeMap) quadrants.get(dir)).ceilingEntry(d); return en == null ? null : en.getValue(); } @Override public Biome getBiome(WorldInfo worldInfo, int x, int y, int z, BiomeParameterPoint biomeParameterPoint) { return this.getMappedBiome(worldInfo, x, y, z, biomeParameterPoint); } @Override public Biome getBiome(WorldInfo worldInfo, int x, int y, int z) { throw new IllegalStateException("This method should never be called."); } /** * Get the vanilla biome at this coordinate * @param worldInfo world info * @param x x * @param y y * @param z z * @return Biome */ @NonNull private Biome getVanillaBiome(WorldInfo worldInfo, int x, int y, int z, BiomeParameterPoint bpb, Biome def) { if (worldInfo.getEnvironment() == Environment.NORMAL) { return getNormalBiome(bpb, def); } return getNetherBiome(bpb); } private @NonNull Biome getNetherBiome(BiomeParameterPoint bpb) { // Bring these values to 1 decimal place double temp = Math.round(bpb.getTemperature() * 10.0) / 10.0; double humidity = Math.round(bpb.getHumidity() * 10.0) / 10.0; if (temp == -0.5D && humidity == 0.0D) { return Biome.BASALT_DELTAS; } else if (temp == 0.4D && humidity == 0.0D) { return Biome.CRIMSON_FOREST; } else if (temp == 0.0D && humidity == -0.5D) { return Biome.SOUL_SAND_VALLEY; } else if (temp == -0.5D && humidity == 0.5D) { return Biome.WARPED_FOREST; } return Biome.NETHER_WASTES; } private @NonNull Biome getNormalBiome(BiomeParameterPoint bpb, Biome def) { /* * Caves */ double d = bpb.getDepth(); if (d > 0.2 && d < 0.9) { if (bpb.getHumidity() >= 0.4 && bpb.getHumidity() < 1.0) { def = Biome.LUSH_CAVES; } if (bpb.getContinentalness() >= 0.8D && bpb.getContinentalness() <= 1.0D) { def = Biome.DRIPSTONE_CAVES; } } if (d >= 0.9) { // Vanilla has this as 1.1 def = Biome.DEEP_DARK; } if (def != null) { return def; } /* * Continentalness (also known as continents) is used to decide between ocean/beach/land biomes. */ int temp = getTemp(bpb.getTemperature()); int humidity = getHumidity(bpb.getHumidity()); int erosion = getErosion(bpb.getErosion()); double we = bpb.getWeirdness(); return switch (ContLoc.getCont(bpb.getContinentalness())) { case COAST -> coastBiome(humidity, temp, erosion, we); case DEEP_OCEAN -> deepOceanBiome(temp); case FAR_INLAND -> farInlandBiome(humidity, temp, erosion, we); case MID_INLAND -> midInlandBiome(humidity, temp, erosion, we); case MUSHROOM_FIELDS -> Biome.MUSHROOM_FIELDS; case NEAR_INLAND -> nearInlandBiome(humidity, temp, erosion, we); case OCEAN -> oceanBiome(temp); default -> farInlandBiome(humidity, temp, erosion, we); }; } /** * Erosion values are divided into 7 levels. The corresponding ranges from level 0 to level 6 are: * -1.0~-0.78, -0.78~-0.375, -0.375~-0.2225, -0.2225~0.05, 0.05~0.45, 0.45~0.55, 0.55~1.0. * * @param erosion The erosion value * @return The erosion level (0-6) */ private int getErosion(double erosion) { if (erosion < -0.78) { return 0; } else if (erosion < -0.375) { return 1; } else if (erosion < -0.2225) { return 2; } else if (erosion < 0.05) { return 3; } else if (erosion < 0.45) { return 4; } else if (erosion < 0.55) { return 5; } else { return 6; } } /** * Humidity values are also divided into 5 levels. The corresponding ranges from level 0 to level 4 are: * -1.0~-0.35, -0.35~-0.1, -0.1~0.1, 0.1~0.3, 0.3~1.0. * * @param humidity The humidity value * @return The humidity level (0-4) */ private int getHumidity(double humidity) { if (humidity < -0.35) { return 0; } else if (humidity < -0.1) { return 1; } else if (humidity < 0.1) { return 2; } else if (humidity < 0.3) { return 3; } else { return 4; } } /** * Temperature is a noise parameter used only in biome generation and does not affect terrain generation. * Temperature values are divided into 5 levels. The corresponding ranges from level 0 to level 4 are: * -1.0~-0.45, -0.45~-0.15, -0.15~0.2, 0.2~0.55, 0.55~1.0. * * @param temp The temperature value * @return The temperature level (0-4) */ private int getTemp(double temp) { if (temp < -0.45) { return 0; } else if (temp < -0.15) { return 1; } else if (temp < 0.2) { return 2; } else if (temp < 0.55) { return 3; } else { return 4; } } private @NonNull Biome oceanBiome(int temp) { return switch (temp) { case 0 -> Biome.FROZEN_OCEAN; case 1 -> Biome.COLD_OCEAN; case 2 -> Biome.OCEAN; case 3 -> Biome.LUKEWARM_OCEAN; case 4 -> Biome.WARM_OCEAN; default -> Biome.OCEAN; }; } private @NonNull Biome deepOceanBiome(int temp) { return switch (temp) { case 0 -> Biome.DEEP_FROZEN_OCEAN; case 1 -> Biome.DEEP_COLD_OCEAN; case 2 -> Biome.DEEP_OCEAN; case 3 -> Biome.DEEP_LUKEWARM_OCEAN; case 4 -> Biome.WARM_OCEAN; default -> Biome.DEEP_OCEAN; }; } private @NonNull Biome farInlandBiome(int humidity, int temperature, int erosion, double weirdness) { return switch (Ridges.getRidge(convertToY(weirdness))) { case HIGH -> getFarInlandHighBiome(humidity, temperature, erosion, weirdness); case LOW -> getFarInlandLowBiome(humidity, temperature, erosion, weirdness); case MID -> getFarInlandMidBiome(humidity, temperature, erosion, weirdness); case PEAKS -> getFarInlandPeaksBiome(humidity, temperature, erosion, weirdness); default -> getFarInlandValleysBiome(humidity, temperature, erosion, weirdness); }; } private @NonNull Biome getFarInlandValleysBiome(int humidity, int temperature, int erosion, double weirdness) { if (erosion >= 0 && erosion < 6) { if (temperature > 0D) { return Biome.RIVER; } else { return Biome.FROZEN_RIVER; } } // e == 6 if (temperature == 0) { return Biome.FROZEN_RIVER; } if (temperature == 1 || temperature == 2) { return Biome.SWAMP; } return Biome.MANGROVE_SWAMP; } private @NonNull Biome getValleysNearInlandBiome(int humidity, int temperature, int erosion, double weirdness) { return getFarInlandValleysBiome(humidity, temperature, erosion, weirdness); } private @NonNull Biome getFarInlandPeaksBiome(int humidity, int temperature, int erosion, double weirdness) { if (erosion == 0) { if (temperature >= 0 && temperature <= 2) { if (weirdness < 0) { return Biome.JAGGED_PEAKS; } else { return Biome.FROZEN_PEAKS; } } else if (temperature == 3) { return Biome.STONY_PEAKS; } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion == 1) { if (temperature == 0 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } else if (temperature == 0 && humidity > 1) { return Biome.GROVE; } else if (temperature < 4) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion >= 2 && erosion <= 4) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 5) { if (weirdness < 0 && (temperature == 0 || temperature == 1 || humidity == 4)) { // Shattered biomes return ShatteredBiome.getBiome(humidity, temperature, weirdness); } if (weirdness > 0 && (temperature > 2 && temperature <= 4 && humidity >= 0 && humidity <= 4)) { return Biome.WINDSWEPT_SAVANNA; } } // middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } private @NonNull Biome getFarInlandMidBiome(int humidity, int temperature, int erosion, double weirdness) { if (erosion == 0) { if (temperature < 3 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } if (temperature < 3 && (humidity == 2 || humidity == 3 || humidity == 4)) { return Biome.GROVE; } return PlateauBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 1) { if (temperature == 0 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } if (temperature == 0 && (humidity == 2 || humidity == 3 || humidity == 4)) { return Biome.GROVE; } return PlateauBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 2) { return PlateauBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 3) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 5) { if (weirdness < 0 && (temperature == 0 || temperature == 1) || humidity == 4) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } if (weirdness > 0 && (temperature > 2 && temperature <= 4 && humidity >= 0 && humidity <= 4)) { return Biome.WINDSWEPT_SAVANNA; } } if (temperature == 0) { // Middle Biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } if (temperature == 1 || temperature == 2) { return Biome.SWAMP; } return Biome.MANGROVE_SWAMP; } private @NonNull Biome getFarInlandLowBiome(int humidity, int temperature, int erosion, double weirdness) { if (erosion >= 0 && erosion < 2) { if (temperature < 4) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion >= 2 && erosion < 5) { // Middle Biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 5) { if (weirdness < 0 && (temperature == 0 || temperature == 1 || humidity == 4)) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } if (weirdness > 0 && (temperature > 2 && temperature <= 4 && humidity >= 0 && humidity <= 4)) { return Biome.WINDSWEPT_SAVANNA; } } if (temperature == 0) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } else if (temperature == 1 || temperature == 2) { return Biome.SWAMP; } return Biome.MANGROVE_SWAMP; } private @NonNull Biome getFarInlandHighBiome(int humidity, int temperature, int erosion, double weirdness) { if (erosion == 0) { if (temperature < 3 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } if (temperature < 3 && (humidity == 2 || humidity == 3 || humidity == 4)) { return Biome.GROVE; } return PlateauBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 1) { if (temperature == 0 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } if (temperature == 0 && (humidity == 2 || humidity == 3 || humidity == 4)) { return Biome.GROVE; } if (temperature > 0 && temperature < 4) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion == 2 || erosion == 3 || erosion == 4) { return PlateauBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 4 || erosion == 6) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } return ShatteredBiome.getBiome(humidity, temperature, weirdness); } private @NonNull Biome nearInlandBiome(int humidity, int temperature, int erosion, double weirdness) { return switch (Ridges.getRidge(convertToY(weirdness))) { case HIGH -> getHighNearInlandBiome(humidity, temperature, erosion, weirdness); case LOW -> getLowNearInlandBiome(humidity, temperature, erosion, weirdness); case MID -> getMidNearInlandBiome(humidity, temperature, erosion, weirdness); case PEAKS -> getPeaksNearInlandBiome(humidity, temperature, erosion, weirdness); default -> getValleysNearInlandBiome(humidity, temperature, erosion, weirdness); }; } private @NonNull Biome getPeaksNearInlandBiome(int humidity, int temperature, int erosion, double weirdness) { if (erosion == 0) { if (temperature >= 0 && temperature <= 2) { if (weirdness < 0) { return Biome.JAGGED_PEAKS; } else { return Biome.FROZEN_PEAKS; } } else if (temperature == 3) { return Biome.STONY_PEAKS; } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion == 1) { if (temperature == 0 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } else if (temperature == 0 && humidity > 1) { return Biome.GROVE; } else if (temperature < 4) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion >= 2 && erosion <= 4) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 5) { if (weirdness < 0 && (temperature == 0 || temperature == 1 || humidity == 4)) { // Shattered biomes return ShatteredBiome.getBiome(humidity, temperature, weirdness); } if (weirdness > 0 && (temperature > 2 && temperature <= 4 && humidity >= 0 && humidity <= 4)) { return Biome.WINDSWEPT_SAVANNA; } } // middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } private @NonNull Biome getMidNearInlandBiome(int humidity, int temperature, int erosion, double weirdness) { if (erosion == 0) { if (temperature < 3 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } if (temperature < 3 && (humidity == 2 || humidity == 3 || humidity == 4)) { return Biome.GROVE; } return PlateauBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 1) { if (temperature == 0 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } if (temperature == 0 && (humidity == 2 || humidity == 3 || humidity == 4)) { return Biome.GROVE; } if (temperature > 0 && temperature < 4) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion >= 2 && erosion <= 4) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 5) { if (weirdness < 0 && (temperature == 0 || temperature == 1) || humidity == 4) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } if (weirdness > 0 && (temperature > 2 && temperature <= 4 && humidity >= 0 && humidity <= 4)) { return Biome.WINDSWEPT_SAVANNA; } } if (temperature == 0) { // Middle Biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } if (temperature == 1 || temperature == 2) { return Biome.SWAMP; } return Biome.MANGROVE_SWAMP; } private @NonNull Biome getLowNearInlandBiome(int humidity, int temperature, int erosion, double weirdness) { if (erosion >= 0 && erosion < 2) { if (temperature < 4) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion >= 2 && erosion < 5) { // Middle Biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 5) { if (weirdness < 0 && (temperature == 0 || temperature == 1 || humidity == 4)) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } if (weirdness > 0 && (temperature > 2 && temperature <= 4 && humidity >= 0 && humidity <= 4)) { return Biome.WINDSWEPT_SAVANNA; } } if (temperature == 0) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } else if (temperature == 1 || temperature == 2) { return Biome.SWAMP; } return Biome.MANGROVE_SWAMP; } private @NonNull Biome getHighNearInlandBiome(int humidity, int temperature, int erosion, double weirdness) { if (erosion == 0) { if (temperature < 3 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } if (temperature < 3 && (humidity == 2 || humidity == 3 || humidity == 4)) { return Biome.GROVE; } return PlateauBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 1) { if (temperature == 0 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } if (temperature == 0 && (humidity == 2 || humidity == 3 || humidity == 4)) { return Biome.GROVE; } if (temperature > 0 && temperature < 4) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion >= 2 && erosion <= 4) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 5) { if (weirdness < 0 && (temperature == 0 || temperature == 1) || humidity == 4) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } if (weirdness > 0 && (temperature > 2 && temperature <= 4 && humidity >= 0 && humidity <= 4)) { return Biome.WINDSWEPT_SAVANNA; } } return MiddleBiome.getBiome(humidity, temperature, weirdness); } private @NonNull Biome midInlandBiome(int humidity, int temperature, int erosion, double weirdness) { return switch (Ridges.getRidge(convertToY(weirdness))) { case HIGH -> getHighMidInlandBiome(humidity, temperature, erosion, weirdness); case LOW -> getLowMidInlandBiome(humidity, temperature, erosion, weirdness); case MID -> getMidMidInlandBiome(humidity, temperature, erosion, weirdness); case PEAKS -> getPeaksMidInlandBiome(humidity, temperature, erosion, weirdness); default -> getValleysMidInlandBiome(humidity, temperature, erosion, weirdness); }; } private @NonNull Biome getValleysMidInlandBiome(int humidity, int temperature, int erosion, double weirdness) { if (erosion == 0 || erosion == 1) { if (temperature < 4) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } else { return BadlandBiome.getBiome(humidity, weirdness); } } if (erosion >= 2 && erosion <= 5) { if (temperature == 0) { return Biome.FROZEN_RIVER; } else { return Biome.RIVER; } } if (temperature == 0) { return Biome.FROZEN_RIVER; } if (temperature == 1 || temperature == 2) { return Biome.SWAMP; } return Biome.MANGROVE_SWAMP; } private @NonNull Biome getPeaksMidInlandBiome(int humidity, int temperature, int erosion, double weirdness) { if (erosion == 0 || erosion == 1) { if (temperature >= 0 && temperature <= 2) { if (weirdness < 0) { return Biome.JAGGED_PEAKS; } else { return Biome.FROZEN_PEAKS; } } else if (temperature == 3) { return Biome.STONY_PEAKS; } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion == 2) { return PlateauBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 3) { if (temperature < 4) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion == 4 || erosion == 6) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } return ShatteredBiome.getBiome(humidity, temperature, weirdness); } private @NonNull Biome getMidMidInlandBiome(int humidity, int temperature, int erosion, double weirdness) { if (erosion == 0) { if (temperature < 3 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } if (temperature < 3 && (humidity == 2 || humidity == 3 || humidity == 4)) { return Biome.GROVE; } return PlateauBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 1) { if (temperature == 0 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } if (temperature == 0 && (humidity == 2 || humidity == 3 || humidity == 4)) { return Biome.GROVE; } if (temperature > 0 && temperature < 4) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion == 2 || erosion == 3) { if (temperature < 4) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion == 4) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 5) { return ShatteredBiome.getBiome(humidity, temperature, weirdness); } if (temperature == 0) { // Middle Biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } if (temperature == 1 || temperature == 2) { return Biome.SWAMP; } return Biome.MANGROVE_SWAMP; } private @NonNull Biome getLowMidInlandBiome(int humidity, int temperature, int erosion, double weirdness) { if (erosion == 0 || erosion == 1) { if (temperature == 0 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } else if (temperature == 0 && humidity > 1) { return Biome.GROVE; } else if (temperature < 4) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } return BadlandBiome.getBiome(humidity, weirdness); } if (erosion == 2 || erosion == 3) { if (temperature < 4) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } return BadlandBiome.getBiome(humidity, weirdness); } // e == 6 if (temperature == 0) { // Middle Biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } if (temperature == 1 || temperature == 2) { return Biome.SWAMP; } return Biome.MANGROVE_SWAMP; } private @NonNull Biome getHighMidInlandBiome(int humidity, int temperature, int erosion, double weirdness) { if (erosion == 0) { if (temperature < 3 && weirdness < 0D) { return Biome.JAGGED_PEAKS; } if (temperature < 3 && weirdness > 0.0D) { return Biome.FROZEN_PEAKS; } if (temperature == 3) { return Biome.STONY_PEAKS; } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion == 1) { if (temperature < 3 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } if (temperature < 3 && (humidity == 2 || humidity == 3 || humidity == 4)) { return Biome.GROVE; } return PlateauBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 2) { return PlateauBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 3) { if (temperature < 4) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion == 4) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 5) { return ShatteredBiome.getBiome(humidity, temperature, weirdness); } return MiddleBiome.getBiome(humidity, temperature, weirdness); } private @NonNull Biome coastBiome(int humidity, int temperature, int erosion, double weirdness) { return switch (Ridges.getRidge(convertToY(weirdness))) { case HIGH -> getHighCoastBionme(humidity, temperature, erosion, weirdness); case LOW -> getLowCoastBionme(humidity, temperature, erosion, weirdness); case MID -> getMidCoastBionme(humidity, temperature, erosion, weirdness); case PEAKS -> getPeaksCoastBionme(humidity, temperature, erosion, weirdness); default -> getValleysCoastBionme(humidity, temperature, erosion, weirdness); }; } private @NonNull Biome getValleysCoastBionme(int humidity, int temperature, int erosion, double weirdness) { if (temperature > 0D) { return Biome.RIVER; } return Biome.FROZEN_RIVER; } private @NonNull Biome getPeaksCoastBionme(int humidity, int temperature, int erosion, double weirdness) { if (erosion == 0) { if (temperature >= 0 && temperature <= 2) { if (weirdness < 0) { return Biome.JAGGED_PEAKS; } else { return Biome.FROZEN_PEAKS; } } else if (temperature == 3) { return Biome.STONY_PEAKS; } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion == 1) { if (temperature == 0 && (humidity == 0 || humidity == 1)) { return Biome.SNOWY_SLOPES; } else if (temperature == 0 && humidity > 1) { return Biome.GROVE; } else if (temperature < 4) { return MiddleBiome.getBiome(humidity, temperature, weirdness); } return BadlandBiome.getBiome(humidity, weirdness); } else if (erosion >= 2 && erosion <= 4) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 5) { if (weirdness < 0 && (temperature == 0 || temperature == 1 || humidity == 4)) { // Shattered biomes return ShatteredBiome.getBiome(humidity, temperature, weirdness); } if (weirdness > 0 && (temperature > 2 && temperature <= 4 && humidity >= 0 && humidity <= 4)) { return Biome.WINDSWEPT_SAVANNA; } } // middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } private @NonNull Biome getMidCoastBionme(int humidity, int temperature, int erosion, double weirdness) { if (erosion > 0 && erosion < 3) { return Biome.STONY_SHORE; } else if (erosion == 3) { // Middle Biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 4) { if (weirdness < 0) { // Beach Biomes return getBeachBiome(temperature); } else { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } } else if (erosion == 5) { if (weirdness < 0) { // Beach Biomes return getBeachBiome(temperature); } if (weirdness < 0 && (temperature == 0 || temperature == 1 || humidity == 4)) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } if (weirdness > 0 && (temperature > 2 && temperature <= 4 && humidity >= 0 && humidity <= 4)) { return Biome.WINDSWEPT_SAVANNA; } } else if (erosion == 6) { if (weirdness < 0D) { // Beach Biomes return getBeachBiome(temperature); } else { // Middle Biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } } // Else Beach biomes return getBeachBiome(temperature); } private @NonNull Biome getLowCoastBionme(int humidity, int temperature, int erosion, double weirdness) { if (erosion >= 0 && erosion < 3) { return Biome.STONY_SHORE; } else if (erosion >= 3 && erosion < 5) { // Beach Biomes return getBeachBiome(temperature); } else if (erosion == 5) { if (weirdness < 0) { // Beach Biomes return getBeachBiome(temperature); } if (weirdness < 0 && (temperature == 0 || temperature == 1 || humidity == 4)) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } if (weirdness > 0 && (temperature > 2 && temperature <= 4 && humidity >= 0 && humidity <= 4)) { return Biome.WINDSWEPT_SAVANNA; } } // Else Beach biomes return getBeachBiome(temperature); } private @NonNull Biome getHighCoastBionme(int humidity, int temperature, int erosion, double weirdness) { if (erosion >= 0 && erosion < 5) { // Middle Biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } else if (erosion == 5) { if (weirdness < 0 && (temperature == 0 || temperature == 1 || humidity == 4)) { // Middle biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } if (weirdness > 0 && (temperature > 2 && temperature <= 4 && humidity >= 0 && humidity <= 4)) { return Biome.WINDSWEPT_SAVANNA; } } // Middle Biomes return MiddleBiome.getBiome(humidity, temperature, weirdness); } Biome getBeachBiome(int t) { return switch (t) { case 0 -> Biome.SNOWY_BEACH; case 4 -> Biome.DESERT; default -> Biome.BEACH; }; } public static double convertToY(double x) { x = Math.max(-1, Math.min(1, x)); // Clamp value if (x >= -1 && x < -0.5) { return 2 * x + 1; } else if (x >= -0.5 && x < 0) { return -2 * x; } else if (x >= 0 && x < 0.5) { return 2 * x; } else if (x >= 0.5 && x <= 1) { return -2 * x + 1; } else { throw new IllegalArgumentException("Invalid x value. x must be in the range [-1, 1]. Value = " + x); } } /** * Get the mapped 2D biome at position x,z * @param worldInfo world info * @param x - block coord * @param y - block coord * @param z - block coord * @param biomeParameterPoint * @return Biome */ private Biome getMappedBiome(WorldInfo worldInfo, int x, int y, int z, BiomeParameterPoint biomeParameterPoint) { /* * Biomes go around the island centers * */ Vector s = new Vector(x, 0, z); Vector l = new Vector(spawnX, 0, spawnZ); double dis = l.distance(s); if (dis > dist * 2) { // Only customize biomes around the spawn location return getVanillaBiome(worldInfo, x, y, z, biomeParameterPoint, null); } // Provide custom biomes Biome result; double d = dis / dist; // Normalize Vector direction = s.subtract(l); if (direction.getBlockX() <= 0 && direction.getBlockZ() <= 0) { result = getQuadrantBiome(BlockFace.NORTH_WEST, d); } else if (direction.getBlockX() > 0 && direction.getBlockZ() <= 0) { result = getQuadrantBiome(BlockFace.NORTH_EAST, d); } else if (direction.getBlockX() <= 0 && direction.getBlockZ() > 0) { result = getQuadrantBiome(BlockFace.SOUTH_WEST, d); } else { result = getQuadrantBiome(BlockFace.SOUTH_EAST, d); } if (result == null || result.equals(Biome.CUSTOM)) { result = getVanillaBiome(worldInfo, x, y, z, biomeParameterPoint, null); } // Caves if (biomeParameterPoint.getDepth() > 0.2) { result = getVanillaBiome(worldInfo, x, y, z, biomeParameterPoint, null); } return result; } @Override public List getBiomes(WorldInfo worldInfo) { // Return all of them for now! return Arrays.stream(Biome.values()).filter(b -> !b.equals(Biome.CUSTOM)).toList(); } /** * Loads the custom biomes from the config file * @param config - Yaml configuration object * @param sector - the direction section to load * @return sorted map of the biomes and their probabilities as keys */ private SortedMap loadQuad(YamlConfiguration config, String sector) { SortedMap result = new TreeMap<>(); if (!config.contains(sector)) { return result; } for (String ring : config.getStringList(sector)) { String[] split = ring.split(":"); if (split.length == 2) { try { double d = Double.parseDouble(split[0]); Biome biome = Enums.getIfPresent(Biome.class, split[1].toUpperCase(Locale.ENGLISH)).orNull(); if (biome == null) { addon.logError(split[1].toUpperCase(Locale.ENGLISH) + " is an unknown biome on this server."); result.put(d, Biome.CUSTOM); } else { // A biome of null means that no alternative biome should be applied result.put(d, biome); } } catch (Exception e) { addon.logError(sector + ": " + split[0] + " does not seem to be a double. For integers add a .0 to the end"); } } else { addon.logError(ring + " must be in the format ratio:biome where ratio is a double."); } } return result; } }