From 7b345253c7e2ca96dc92669fbf18d95851b36d36 Mon Sep 17 00:00:00 2001 From: tastybento Date: Tue, 23 Mar 2021 21:45:52 -0700 Subject: [PATCH] Initial Nether implementation. --- .settings/.gitignore | 1 + src/main/java/world/bentobox/boxed/Boxed.java | 12 +- .../java/world/bentobox/boxed/Settings.java | 54 +------ .../boxed/generators/BoxedChunkGenerator.java | 22 ++- .../generators/NetherBiomeGenerator.java | 145 ++++++++++++++++++ .../boxed/generators/NetherGenerator.java | 79 ++++++++++ ...Generator.java => OverWorldGenerator.java} | 4 +- src/main/resources/biomes.yml | 17 ++ src/main/resources/blueprints/default.json | 4 +- src/main/resources/blueprints/island.blu | Bin 7019 -> 7026 bytes src/main/resources/blueprints/nether.blu | Bin 0 -> 2901 bytes src/main/resources/config.yml | 8 +- 12 files changed, 275 insertions(+), 71 deletions(-) create mode 100644 src/main/java/world/bentobox/boxed/generators/NetherBiomeGenerator.java create mode 100644 src/main/java/world/bentobox/boxed/generators/NetherGenerator.java rename src/main/java/world/bentobox/boxed/generators/{BasicWorldGenerator.java => OverWorldGenerator.java} (93%) create mode 100644 src/main/resources/blueprints/nether.blu diff --git a/.settings/.gitignore b/.settings/.gitignore index 6cb3d6d..1de83a6 100644 --- a/.settings/.gitignore +++ b/.settings/.gitignore @@ -1,2 +1,3 @@ /org.eclipse.core.resources.prefs /org.eclipse.jdt.core.prefs +/org.eclipse.m2e.core.prefs diff --git a/src/main/java/world/bentobox/boxed/Boxed.java b/src/main/java/world/bentobox/boxed/Boxed.java index 0c1cec6..2bfec90 100644 --- a/src/main/java/world/bentobox/boxed/Boxed.java +++ b/src/main/java/world/bentobox/boxed/Boxed.java @@ -51,6 +51,7 @@ public class Boxed extends GameModeAddon { private Config configObject = new Config<>(this, Settings.class); private AdvancementsManager advManager; private DeleteGen delChunks; + private ChunkGenerator netherChunkGenerator; @Override public void onLoad() { @@ -69,6 +70,7 @@ public class Boxed extends GameModeAddon { } // Chunk generator chunkGenerator = new BoxedChunkGenerator(this).getGenerator(); + netherChunkGenerator = new BoxedChunkGenerator(this).getNetherGenerator(); // Register commands playerCommand = new DefaultPlayerCommand(this) {}; @@ -163,7 +165,7 @@ public class Boxed extends GameModeAddon { if (getServer().getWorld(worldName + NETHER) == null) { log("Creating Boxed's Nether..."); } - netherWorld = settings.isNetherIslands() ? getWorld(worldName, World.Environment.NETHER, chunkGenerator) : getWorld(worldName, World.Environment.NETHER, null); + netherWorld = settings.isNetherIslands() ? getWorld(worldName, World.Environment.NETHER, netherChunkGenerator) : getWorld(worldName, World.Environment.NETHER, null); } // Make the end if it does not exist if (settings.isEndGenerate()) { @@ -187,9 +189,7 @@ public class Boxed extends GameModeAddon { worldName2 = env.equals(World.Environment.THE_END) ? worldName2 + THE_END : worldName2; World w = WorldCreator.name(worldName2).type(WorldType.FLAT).environment(env).generator(chunkGenerator2).createWorld(); // Backup world - if (env.equals(Environment.NORMAL)) { - WorldCreator.name(worldName2 + "_bak").type(WorldType.FLAT).environment(env).generator(chunkGenerator2).createWorld(); - } + WorldCreator.name(worldName2 + "_bak").type(WorldType.FLAT).environment(env).generator(chunkGenerator2).createWorld(); // Set spawn rates if (w != null) { setSpawnRates(w); @@ -216,7 +216,7 @@ public class Boxed extends GameModeAddon { } if (getSettings().getTicksPerMonsterSpawns() > 0) { w.setTicksPerMonsterSpawns(getSettings().getTicksPerMonsterSpawns()); - } + } } @Override @@ -229,7 +229,7 @@ public class Boxed extends GameModeAddon { if (id != null && id.equals("delete")) { return delChunks; } - return chunkGenerator; + return worldName.endsWith("_nether") ? netherChunkGenerator : chunkGenerator; } @Override diff --git a/src/main/java/world/bentobox/boxed/Settings.java b/src/main/java/world/bentobox/boxed/Settings.java index 6943068..c47dce4 100644 --- a/src/main/java/world/bentobox/boxed/Settings.java +++ b/src/main/java/world/bentobox/boxed/Settings.java @@ -146,16 +146,6 @@ public class Settings implements WorldSettings { @ConfigEntry(path = "world.default-game-mode") private GameMode defaultGameMode = GameMode.SURVIVAL; - @ConfigComment("The default biome for the overworld") - @ConfigEntry(path = "world.default-biome") - private Biome defaultBiome = Biome.PLAINS; - @ConfigComment("The default biome for the nether world (this may affect what mobs can spawn)") - @ConfigEntry(path = "world.default-nether-biome") - private Biome defaultNetherBiome = Enums.getIfPresent(Biome.class, "NETHER").or(Enums.getIfPresent(Biome.class, "NETHER_WASTES").or(Biome.BADLANDS)); - @ConfigComment("The default biome for the end world (this may affect what mobs can spawn)") - @ConfigEntry(path = "world.default-end-biome") - private Biome defaultEndBiome = Biome.THE_END; - @ConfigComment("The maximum number of players a player can ban at any one time in this game mode.") @ConfigComment("The permission boxed.ban.maxlimit.X where X is a number can also be used per player") @ConfigComment("-1 = unlimited") @@ -624,7 +614,7 @@ public class Settings implements WorldSettings { */ @Override public boolean isNetherIslands() { - return false; + return true; } /** @@ -1243,20 +1233,6 @@ public class Settings implements WorldSettings { return false; } - /** - * @return default biome - */ - public Biome getDefaultBiome() { - return defaultBiome; - } - - /** - * @param defaultBiome the defaultBiome to set - */ - public void setDefaultBiome(Biome defaultBiome) { - this.defaultBiome = defaultBiome; - } - /** * @return the banLimit */ @@ -1647,34 +1623,6 @@ public class Settings implements WorldSettings { this.mobLimitSettings = mobLimitSettings; } - /** - * @return the defaultNetherBiome - */ - public Biome getDefaultNetherBiome() { - return defaultNetherBiome; - } - - /** - * @param defaultNetherBiome the defaultNetherBiome to set - */ - public void setDefaultNetherBiome(Biome defaultNetherBiome) { - this.defaultNetherBiome = defaultNetherBiome; - } - - /** - * @return the defaultEndBiome - */ - public Biome getDefaultEndBiome() { - return defaultEndBiome; - } - - /** - * @param defaultEndBiome the defaultEndBiome to set - */ - public void setDefaultEndBiome(Biome defaultEndBiome) { - this.defaultEndBiome = defaultEndBiome; - } - /** * @return the seed */ diff --git a/src/main/java/world/bentobox/boxed/generators/BoxedChunkGenerator.java b/src/main/java/world/bentobox/boxed/generators/BoxedChunkGenerator.java index 9a719b6..6f6b517 100644 --- a/src/main/java/world/bentobox/boxed/generators/BoxedChunkGenerator.java +++ b/src/main/java/world/bentobox/boxed/generators/BoxedChunkGenerator.java @@ -15,10 +15,14 @@ public class BoxedChunkGenerator { private final WorldRef wordRef; private final Boxed addon; + private WorldRef wordRefNether; + private WorldRef wordRefEnd; public BoxedChunkGenerator(Boxed addon) { this.addon = addon; wordRef = WorldRef.ofName(addon.getSettings().getWorldName()); + wordRefNether = WorldRef.ofName(addon.getSettings().getWorldName() + "_nether"); + wordRefEnd = WorldRef.ofName(addon.getSettings().getWorldName() + "_end"); } public ChunkGenerator getGenerator() { @@ -26,7 +30,7 @@ public class BoxedChunkGenerator { .getInstance(addon.getPlugin(), 0, 5) .createCustomGenerator(wordRef, generator -> { // Set the noise generator - generator.setBaseNoiseGenerator(new BasicWorldGenerator(addon, addon.getSettings().getSeed())); + generator.setBaseNoiseGenerator(new OverWorldGenerator(addon, addon.getSettings().getSeed())); if (addon.getSettings().isAllowStructures()) { generator.getWorldDecorator().withoutDefaultDecorations(DecorationType.SURFACE_STRUCTURES); } @@ -37,4 +41,20 @@ public class BoxedChunkGenerator { }); } + public ChunkGenerator getNetherGenerator() { + return WorldGeneratorApi + .getInstance(addon.getPlugin(), 0, 5) + .createCustomGenerator(wordRefNether, generator -> { + // Set the noise generator + generator.setBaseNoiseGenerator(new NetherGenerator(addon, addon.getSettings().getSeed())); + if (addon.getSettings().isAllowStructures()) { + generator.getWorldDecorator().withoutDefaultDecorations(DecorationType.SURFACE_STRUCTURES); + } + if (addon.getSettings().isAllowStrongholds()) { + generator.getWorldDecorator().withoutDefaultDecorations(DecorationType.STRONGHOLDS); + } + generator.setBiomeGenerator(new NetherBiomeGenerator(addon)); + }); + } + } diff --git a/src/main/java/world/bentobox/boxed/generators/NetherBiomeGenerator.java b/src/main/java/world/bentobox/boxed/generators/NetherBiomeGenerator.java new file mode 100644 index 0000000..7337244 --- /dev/null +++ b/src/main/java/world/bentobox/boxed/generators/NetherBiomeGenerator.java @@ -0,0 +1,145 @@ +package world.bentobox.boxed.generators; + +import java.util.Collections; +import java.util.EnumMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.bukkit.block.Biome; +import org.bukkit.block.BlockFace; +import org.bukkit.util.Vector; + +import nl.rutgerkok.worldgeneratorapi.BiomeGenerator; +import world.bentobox.boxed.Boxed; + +/** + * @author tastybento + * + */ +public class NetherBiomeGenerator implements BiomeGenerator { + + private static final double ONE = 0.05; + private static final double TWO = 0.25; + private static final double THREE = 0.5; + private static final double FOUR = 0.75; + private static final double FIVE = 1.0; + private static final double SIX = 1.25; + private static final double SEVEN = 1.50; + private static final double EIGHT = 1.75; + private static final double LAST = 2.0; + + + private static final TreeMap NORTH_EAST = new TreeMap<>(); + static { + NORTH_EAST.put(ONE, Biome.BASALT_DELTAS); + NORTH_EAST.put(TWO, Biome.CRIMSON_FOREST); + NORTH_EAST.put(THREE, Biome.SOUL_SAND_VALLEY); + NORTH_EAST.put(FOUR, Biome.WARPED_FOREST); + NORTH_EAST.put(FIVE, Biome.BASALT_DELTAS); + NORTH_EAST.put(SIX, Biome.CRIMSON_FOREST); + NORTH_EAST.put(SEVEN, Biome.SOUL_SAND_VALLEY); + NORTH_EAST.put(EIGHT, Biome.WARPED_FOREST); + NORTH_EAST.put(LAST, Biome.NETHER_WASTES); + } + private static final TreeMap SOUTH_EAST = new TreeMap<>(); + static { + SOUTH_EAST.put(ONE, Biome.NETHER_WASTES); + SOUTH_EAST.put(TWO, Biome.BASALT_DELTAS); + SOUTH_EAST.put(THREE, Biome.SOUL_SAND_VALLEY); + SOUTH_EAST.put(FOUR, Biome.WARPED_FOREST); + SOUTH_EAST.put(FIVE, Biome.NETHER_WASTES); + SOUTH_EAST.put(SIX, Biome.BASALT_DELTAS); + SOUTH_EAST.put(SEVEN, Biome.SOUL_SAND_VALLEY); + SOUTH_EAST.put(EIGHT, Biome.WARPED_FOREST); + SOUTH_EAST.put(LAST, Biome.CRIMSON_FOREST); + } + + private static final TreeMap NORTH_WEST = new TreeMap<>(); + static { + NORTH_WEST.put(ONE, Biome.NETHER_WASTES); + NORTH_WEST.put(TWO, Biome.NETHER_WASTES); + NORTH_WEST.put(THREE, Biome.SOUL_SAND_VALLEY); + NORTH_WEST.put(FOUR, Biome.WARPED_FOREST); + NORTH_WEST.put(FIVE, Biome.BASALT_DELTAS); + NORTH_WEST.put(SIX, Biome.CRIMSON_FOREST); + NORTH_WEST.put(SEVEN, Biome.SOUL_SAND_VALLEY); + NORTH_WEST.put(EIGHT, Biome.WARPED_FOREST); + NORTH_WEST.put(LAST, Biome.NETHER_WASTES); + } + + private static final TreeMap SOUTH_WEST = new TreeMap<>(); + static { + SOUTH_WEST.put(ONE, Biome.NETHER_WASTES); + SOUTH_WEST.put(TWO, Biome.SOUL_SAND_VALLEY); + SOUTH_WEST.put(THREE, Biome.WARPED_FOREST); + SOUTH_WEST.put(FOUR, Biome.SOUL_SAND_VALLEY); + SOUTH_WEST.put(FIVE, Biome.BASALT_DELTAS); + SOUTH_WEST.put(SIX, Biome.CRIMSON_FOREST); + SOUTH_WEST.put(SEVEN, Biome.WARPED_FOREST); + SOUTH_WEST.put(EIGHT, Biome.SOUL_SAND_VALLEY); + SOUTH_WEST.put(LAST, Biome.NETHER_WASTES); + } + private static final Map> QUADRANTS; + static { + Map> q = new EnumMap<>(BlockFace.class); + q.put(BlockFace.NORTH_EAST, NORTH_EAST); + q.put(BlockFace.NORTH_WEST, NORTH_WEST); + q.put(BlockFace.SOUTH_EAST, SOUTH_EAST); + q.put(BlockFace.SOUTH_WEST, SOUTH_WEST); + QUADRANTS = Collections.unmodifiableMap(q); + } + + + private final Boxed addon; + private final int dist; + private final int offsetX; + private final int offsetZ; + + + public NetherBiomeGenerator(Boxed boxed) { + this.addon = boxed; + dist = addon.getSettings().getIslandDistance(); + offsetX = addon.getSettings().getIslandXOffset(); + offsetZ = addon.getSettings().getIslandZOffset(); + } + + @Override + public Biome getZoomedOutBiome(int x, int y, int z) { + /* + * The given x, y and z coordinates are scaled down by a factor of 4. So when Minecraft + * wants to know the biome at x=112, it will ask the biome generator for a biome at x=112/4=28. + */ + /* + * Biomes go around the island centers + * + */ + Vector s = new Vector(x * 4, 0, z * 4); + Vector l = getClosestIsland(s); + double dis = l.distanceSquared(s); + double d = dis / (dist * dist); + Vector direction = s.subtract(l); + if (direction.getBlockX() <= 0 && direction.getBlockZ() <= 0) { + return getBiome(BlockFace.NORTH_WEST, d); + } else if (direction.getBlockX() > 0 && direction.getBlockZ() <= 0) { + return getBiome(BlockFace.NORTH_EAST, d); + } else if (direction.getBlockX() <= 0 && direction.getBlockZ() > 0) { + return getBiome(BlockFace.SOUTH_WEST, d); + } + return getBiome(BlockFace.SOUTH_EAST, d); + } + + private Biome getBiome(BlockFace dir, double d) { + Entry en = ((TreeMap) QUADRANTS.get(dir)).ceilingEntry(d); + return en == null ? Biome.NETHER_WASTES : en.getValue(); + } + + Vector getClosestIsland(Vector v) { + int d = dist * 2; + long x = Math.round((double) v.getBlockX() / d) * d + offsetX; + long z = Math.round((double) v.getBlockZ() / d) * d + offsetZ; + return new Vector(x, 0, z); + } + +} diff --git a/src/main/java/world/bentobox/boxed/generators/NetherGenerator.java b/src/main/java/world/bentobox/boxed/generators/NetherGenerator.java new file mode 100644 index 0000000..d4b4ede --- /dev/null +++ b/src/main/java/world/bentobox/boxed/generators/NetherGenerator.java @@ -0,0 +1,79 @@ +package world.bentobox.boxed.generators; + +import java.io.File; + +import org.bukkit.Material; +import org.bukkit.block.Biome; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.util.noise.PerlinNoiseGenerator; + +import nl.rutgerkok.worldgeneratorapi.BaseNoiseGenerator; +import nl.rutgerkok.worldgeneratorapi.BiomeGenerator; +import world.bentobox.boxed.Boxed; + +/** + * Generates the Nether + * @author tastybento + * + */ +public class NetherGenerator implements BaseNoiseGenerator { + + private final PerlinNoiseGenerator mainNoiseGenerator; + private final Boxed addon; + private final YamlConfiguration config; + + + public NetherGenerator(Boxed addon, long seed) { + this.addon = addon; + // Initialize the noise generator based on the world seed + this.mainNoiseGenerator = new PerlinNoiseGenerator(seed); + // Load the config + File biomeFile = new File(addon.getDataFolder(), "biomes.yml"); + if (!biomeFile.exists()) { + addon.saveResource("biomes.yml", true); + } + config = YamlConfiguration.loadConfiguration(biomeFile); + } + + @Override + public TerrainSettings getTerrainSettings() { + TerrainSettings ts = new TerrainSettings(); + ts.stoneBlock = Material.NETHERRACK.createBlockData(); + ts.waterBlock = Material.LAVA.createBlockData(); + return ts; + } + + @Override + public void getNoise(BiomeGenerator biomeGenerator, double[] buffer, int scaledX, int scaledZ) { + // Repeat on an island boundary + int dist = addon.getSettings().getIslandDistance(); + double noiseScaleHorizontal = 10D; + double height = 8D; + Biome biome = biomeGenerator.getZoomedOutBiome(scaledX, scaledZ); + if (biome == null) { + // edge of island + biome = Biome.NETHER_WASTES; + height = 6; + } else { + noiseScaleHorizontal = config.getDouble("nether.biomes." + biome.name() + ".scale", 10D); + height = config.getDouble("nether.biomes." + biome.name() + ".height", 8D); + } + double x = ((((double)scaledX*4) % dist) / 4) / noiseScaleHorizontal; + double z = ((((double)scaledZ*4) % dist) / 4) / noiseScaleHorizontal; + + for (int y = 0; y < buffer.length; y++) { + double noise = this.mainNoiseGenerator.noise(x, y, z); + double heightOffset = height - y; + buffer[y] = noise + heightOffset; + } + // Ceiling + x = ((((double)scaledX*4) % dist) / 4); + z = ((((double)scaledZ*4) % dist) / 4); + for (int y = 15; y > height + 2; y--) { + double noise = this.mainNoiseGenerator.noise(x, y, z); + double heightOffset = y - height; + buffer[y] = noise + heightOffset; + } + } + +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/boxed/generators/BasicWorldGenerator.java b/src/main/java/world/bentobox/boxed/generators/OverWorldGenerator.java similarity index 93% rename from src/main/java/world/bentobox/boxed/generators/BasicWorldGenerator.java rename to src/main/java/world/bentobox/boxed/generators/OverWorldGenerator.java index ef87206..687fa55 100644 --- a/src/main/java/world/bentobox/boxed/generators/BasicWorldGenerator.java +++ b/src/main/java/world/bentobox/boxed/generators/OverWorldGenerator.java @@ -14,14 +14,14 @@ import world.bentobox.boxed.Boxed; * @author tastybento * */ -public class BasicWorldGenerator implements BaseNoiseGenerator { +public class OverWorldGenerator implements BaseNoiseGenerator { private final SimplexNoiseGenerator mainNoiseGenerator; private final Boxed addon; private final YamlConfiguration config; - public BasicWorldGenerator(Boxed addon, long seed) { + public OverWorldGenerator(Boxed addon, long seed) { this.addon = addon; // Initialize the noise generator based on the world seed this.mainNoiseGenerator = new SimplexNoiseGenerator(seed); diff --git a/src/main/resources/biomes.yml b/src/main/resources/biomes.yml index b06edb0..96e5868 100644 --- a/src/main/resources/biomes.yml +++ b/src/main/resources/biomes.yml @@ -1,3 +1,20 @@ +nether: + biomes: + NETHER_WASTES: + height: 8 + scale: 5 + BASALT_DELTAS: + height: 8 + scale: 1 + CRIMSON_FOREST: + height: 9 + scale: 10 + SOUL_SAND_VALLEY: + height: 9 + scale: 16 + WARPED_FOREST: + height: 10 + scale: 10 biomes: BADLANDS_PLATEAU: height: 9 diff --git a/src/main/resources/blueprints/default.json b/src/main/resources/blueprints/default.json index cf4caab..8a087cc 100644 --- a/src/main/resources/blueprints/default.json +++ b/src/main/resources/blueprints/default.json @@ -3,12 +3,12 @@ "icon": "PAPER", "displayName": "Default bundle", "description": [ - "§bDefault bundle of blueprints" + "Boxed Starter Blocks" ], "requirePermission": false, "blueprints": { "NORMAL": "island", - "NETHER": "bedrock", + "NETHER": "nether", "THE_END": "bedrock" }, "slot": 0 diff --git a/src/main/resources/blueprints/island.blu b/src/main/resources/blueprints/island.blu index fa8b9a1f36ab820886d38bbb8805ff744ba8ede7..56f1eaea907fc4b0620ea2a5642f5dc0708c003e 100644 GIT binary patch delta 6021 zcma)Ac{o(>`#yFPlbNv_!q~DC23cb)BYU>UE|k4OvdoAWdzutkvy1E^`!dMKR`xYf zDcQGd;Ww(!_xfJnzkcVR=RDWBuID}HeeUPJ@AoVtw~%1Nt7PO105vr=fGYM+1j8{5 zE@R=R(c`j#pR?}9gXgQh(D;QR7@nc*RaPHeF>~}!gE~|8)1DVuXjz{C*O^cCSxN2E z8Y4m_zpiaJ78JA4b#@=$+RL9)`IsqETEEnJ(suGC2x)!%tJ{`S2@~?Owm{|dxL}m3 z;cKPBX;aIDin5YYT%eEl_Nl+GxXlGj&r;=+h_yx7LN+poQieh>Pd3O@y9_g z%<48qobIQso^$Q^LGQ4ZL59-Y>CTL`@bq->YCnl z_%gq>7|1_9IW<(-_e!kse-+Kk1+#J+nSI0#MNALY;NCJxj;npEKlbHq;|C$p<>-fc zr0zp5^uPVfmnJ5>9O;>@8mDR+Ep37>A`Hih@YKL1Cg)?8<7;pg@u)tog-SuJ9*MiL(_P7>kA3;fE>&+4H@l$E z4PoIhuBXZ`TY*RKTzK+o9+%cHukE~QSu4~3ans6eHCEG-Zraj{hbA+*-Z(qrxo@8- z0tvFAOp1#u3eC7vlG)}{P=pW*7HppB`C1;fv~{9mj5Q<0URX)sZb#t z0@FNV2GavdLgCAhkSi0Nc38h5kKMA0pIRlAW`UQLZO+6g;=( zPBIt9-d)P~vAoN7L6cC0yB`gqOPnXcXeWQs9VC~5nCqYlC( zHPJGeE(h~*O!^zfL(B*_Vv^mUcSRja9}SGwVDNe{yz+TgjDtNcn0xHe1e4u$tTp#c zs00-O9hV%*Ri~ndNMC!*$6A-28RQWUI^(fpA9%8SO+Jw1vrrc^uY6tif{vsSyrPC8X0~@2P^DN5D1h9R(1 z4$A~eXdyh4Z!yOS9@}WGmP%gl0!ynJ6!jKREAwp`X|P|;`;cgQgG}~-w@m<~+_QnU z3mht-2hxBEax6dU(u;@#M1rmSZDVd^gawPQtu$DQ^&mKhpRwJAI@QQpbJ&{2PfLeJtxiwLbDUWCIxa#r(GutE z>bcLGkEpJ7vA9F-^2ICrY>kGA-%h^L%^Q|>zg>=c&d@`BM4}_!6w+JQ`Iu(vYbdd_ z-;_*vW$bog%eBoM5+m8=YeEsml26aQG@;Jpy_lzsX53WA_y4I6V2prpHFDnDT=W%T z&wDhjGh=$(#fp+Y^ycKO#pl~=iR0sLt1eF#o*&T{xd0yxlm@F@s1{}x=ikoDnlC8x zTgn#uTu|m5oKvz8B3>4!&FEuWS_xuQFNCP&L7!BH_FM#M_DJYN7{OvmSv-L%uupY% zM0!ej#Z*d7{P?^EwOpc~Tfn#k#@V9H% zwL(M|n&Bmu_lZO#m~u98a7+UEgdvI+JA1DAb9qVVV0?|n+6|EXF>W`90h7B`la z`--1g&@^B)-4QAa;r^O##`#0LLk3e;;3u*`iNND4$eHjxFiwd`ZrX?q=2jbOf7a$= zosme+7pvVoP^lyi*zA0ys(g*YAL=8H?aR9|!(qMn5!#Cw$eL<0**q940~wHzQ!!!- znee1BwfS+G_i_ha)WC*%*3+qU_b9xn#*8BBX|Us6-=;YyJb5bb86gEDNn&NNMU;Pe zPH&ykvR+S!U?iY_+57!)f!YMw{P}Qs$VXL+4!h~qdPBj;YPh;@N)3MW=E^$H1=6Is zNDkGnwS$E$nw=6tiHUNovg{K(yNQZ1}k^LSKDCW#c?FmCevx&qsA>6qHk&5~ll#3YEoR2H%>xh5C+J~YDdr4Xg5 znj4;kmI?D-wT8!<-dF*1-$2kR*LZxllg*PaGx?mV z*);T-(WtEcFtw(V*#C)@eNS@dsmkKRt{Xeap=qCnb#P{C)v)X&+Se#o3Nm39v$m{r z5hh$&r1RvWjMdlP>lDF9<4yInUxcTi8mY7D@r9!H{893(1(8qmih`zA+H@=8P$ck@h@rkFdtrl(0GYqZTR5V#)h@BDTLd6A;MXC zyAGF;i>@n6<4YVEda-0dj&trw4t_9MOIY&smQ>B!a=Eb+8`}C_vnKRBq%g=5^g$=W z3{m|sl##aB&X7V~NZN4K3y$QjS^Zc=?n{9Jf3oS@anv)}g>lzaR}>=L-s&z^Els*} zi_JhJ7^HyfuFBmMWe&a~3;7qzoP0&p`Ut_R@0YKOJ|qqs5~ESW!fjeppjyfTga~{h9y2p5wP4g}Fbm!z-r(U2Lr;WGuGOihDv1^9|Zd_^h-XYmHoBEoU5^<@8xcFVtbGE(Y%cSCG zFQUj9#{G+;oVndPV#kijIsr9f;(QixDRo7(g;D;Kcnnwpvq9O)M(x6qHYbvIA*#;) zok(6Yy$^U?Fl-GLVHR?mh@~csR&KEaCk3-U;RFr6g_F3pE=`)OLU3R(u(;nGRWC?{CjU8ys<-6OnNTb)n| z9~Q1dH%oB$%}|QMhzv%^;5nSjZ^KEH^1n)c*8Jau*Lp^HSs=SYl0NBQi4iJB3+43x zBH*(30O$__;QvD4gcoKCixP@LC84r)xQzH77wY0mVeBXtZD5-k!#-~iCRg}FKFQ|h z?XV9wu|>%#?;O+>KaGBCFt%K(;?CG4d=4nLEz2Xqxr?m~eD4g-aWXbVu4lda_e1}y zDQY6I_2R~2e~WGYxA zaW}dBG{meK^*UnIh|F|@+KanH!kdSE>%I$mQ2 z^>vgOi1SkFG4SX7NVN3dZ&-hmoy0f6STMg&h(2RcIp@>veafOgQT?Msz?lwt(`yi3 zA{GpxK7}|rlV>tBx9T?AoQQf)ocA{38H1u6ov3&8TG>N7I|5#)dkORyovFKT*ZvI# zJOc{D>G*>9TPuMEwUTocg00y=pjtA`yl8k0WPp@~W?op`QyZa!;e+Nq!5M=ld@aqa z7A!Qtvg9e(L_J=;#&mwu@A3_%(^^VmOWXk3Vb32X#Nf#28!vrh+^g!AOEQvZg8Q%V zrwqNrxXQl^!vksLpK__SMu0nyYvq=zK%9E}PVH3m2xfG9>=h>HnL8&j)0kyIx9bwq zRx7~GC|v_T@r>G@sy1Y<^q?`#fBe|giV$mrlB^xy4k)m9zE~XW>-~WF>Me^G(CCXl zkws4p@`8kljBaT%C7^HXN`vfJoSnDem?lw#JsBuf%T5}(i^o(1~AC6XQqT;2op~;%-1J&3R zCW&EEXUk8z{dKA)e)Ms({7K}6xdwb5LNBivscFGuN*tQLfAROTGHiE7h($kPb?0BG zd?)1hbZfcg_@`HDoU2%6bcZHJn#Htp;F>D9ZZbNkz`jec%;a7!Nnb~he6KL0C{~BW zu<#@m=Yb*8QGJk!2)GN=dpTol8rDgsOiyYa7soyb6e>?NHG%V|h za#D5CZ|u(Vfa0`;#ScbjLZoC3`@lPQ$ZBR(g==m9S@pj|mNcWvQ`=2e4xQ5g=nssu zZ#GhV(RWy`#m=DJk3%LEqC7Iq{bBj913!yk(!M5C{qkXw)0bkrj~WtqqMW!Bm21H3 zfw0G%^?pLGJs*mUEx*3sJHH;$!gk`kA2YxH$cv--?ta(&5v3j6)>3KKaM+V-l(*4v zc#SfNcPzeY>GMbF-c09%VG+!WPQBr~J_%ZfibLNLl@qE1a(j8)+3km^;hGa7?HW)q z5JvJ>)5duF+_^x(mGlkK4u9G?iJ+G9)r;-^G2-;zRfh5+nlKwCAr+dW&rG(AjpXy2 z7c;dE6#w~VgiNRm$Q{J5Ga)h0r6*1rEMht*SIg4H@~c#o(|(Oviyl7rn5+qU^W2)o zBXF|j?u@0M=*QQlAKYHgriD%Q%NPC)>BOu@{v9!!>6h1DWtS$qFsSI?uIS&U==V_3 zPfgLUa=!vE1!<5Yt%#v7PtBX_ABYidsrjM5sFQ(BS0wYpIG#56oX#endNyJN##$7< zdDZp~)Q`KESM?6B9Xt7Ky%c@(6E=uhXYL)ItKaU~*~(pK>J9rES$o{$usd9Q@+rY{ za?sp%xw56MVW(%OK2ywPo9?Kab8ov%$Fou{L~?doL0J5t4c8~3(zG6ynL4!v6}C6cNvd4+1{+GYB~P3Z6M1TJS^T|)pTWZQTEhv_vJi~Y|H-a#*wG; z>B<3~GgIlre!u3cQ-JUG=H$_d!@l;O=fRPCNMr4dILv8BacVKE^2wWq73R#AF8@Hx z@xB9QvcrJUsklYoYVr?%bS<*GHS(-jK3}y0!do$EisZ3yb)} zJ+FqP&X19k4F?~KCyxd*PkK%F>>t}&6Gq*q$|$SGMSs>e-JENBD;qR)Dt_1$^5DbZ z+|SuxKg=+z{VVmSuWH8oUDorNsRGMBikjDM)XyobFCN?4tY8k8Z~x3ZnNgn>XiDu{ zAA3DJ6V%|jJofUK0P|m7q{FU0SN7~YT&uTDv(aVhg>wl+Qh6O6IP6w2vd$Z%teR_` zj58OBHou!H2oDh0{Tfr^i~JGe-scqNGdiYwCq^7oDTx)}8hl3Evp<-O zx9IM8T7W{c)s4-rpxqDMvKGtxlvW&bc154zEB97H@@j40h&Fef5B54@ocX1U?DDBj z39g;{KuVtYGt~ymH56h?drzPNWo+x|G;Z&rum;x*U#;_E@q6>Rxifv`@``(Ra7TF+ z#~wHb6ZX!UMqzq$b|KoI-OsOSY!m$Ea&wz;x9r5y#IcT7i@Q}&97kk&y47+&&zs}D zaO}7HM`bXQ_>q%o&DOA>1|N@o3x@7IrFXUPUT3;^>+LwN(7i?4nqB}aH6kXO)%;G( zd~KO|vci6ap{FA(@x@~Cdd#o4HLJ_DF3QJL qhF7VmL6@xqch~@6MTrzJyh=jK0Q~3gy77OW;V;95DKEkS;`M(X78P9p delta 6110 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 V^V;6{-;Z;|tMK#G0&sxx{0B*%Mgaf- diff --git a/src/main/resources/blueprints/nether.blu b/src/main/resources/blueprints/nether.blu new file mode 100644 index 0000000000000000000000000000000000000000..a4a46194db118d1f758734dd1bca2e759e9235e5 GIT binary patch literal 2901 zcmaKue>~Is9>=Nt>X%z8?3R8YCu*+DuUP6v%nC^}~Zd%s_w&*STf zUA|(2il(Nf%C?6wUMfpI>fk#uBq}T>pqjkoKbZup?nJ~HCzh`1KO?UfaXX^FObdn{adt!iF9&?*pUmhAdPUSjo ziD+qQEe#OG$4rmLlPBhSMW3g-%I0TM1-Qqpp*t-f??dTdCv3GGZBZs{wiP z?abV0d~e+K*x1+)G?QVEvEO*J)0Bx`gZ&fS=ZZe!tFDJJ^-`PLTZwtXebqeP?3|#a zth6WM%uF9v_(dx1jhlZF6Dktf&d)Q?rYl=_qF+a>z4knC8E(r|QC<7FM0y&;X_(Fn zW_}1igt@`>Z(@K1l`%C68j>_g*&qh5r z{X;4GY5H&rpb4F!hob_!t;AlDeao$%^a7hRi#pRf(_%#<#)iegD>72H)vaz=-O#K> z;xl&9chQaMR_8!}>uSE+<56xrL2mOq?AfGwUjLw=S&qLId@Hyxcqoy#FU9#UL7BlF ziM%vK3aF<p1d;yoU9M|dN=vtWF^A!IOo!W@}Gx}1eI>U#98IA5%Xp;hK$(%sVCC@>5+2%7;i zopc#7+aOb8``Y7Em`##jJ*3&F5|oRW#SRr0019CUqxj*O8=0l%VWU^i@E>gF#3+~; zmR%asgB0QN!(g}x`+gLDl+G#P$mvAC*7Lckl2dq&i!{he5lagxQvJbK#Ffh(62y_}UM3WtAFRz^B;e+Rt9z&(gZ-VY1v z%E2F(pG*&wYC5_HC2jaPH5w;xoIVXU1NLGlSE4dq-N9xI5Zc&})OP;Q#Gr-F_9IrZgX3+{U#-mdUiFvOcx@Z5 zs0B%2+^9dq;Lnn(fVfU)(QWm3Bzq*IYq6@Ke6sINpK|8Yhbwbm8d~PncJHd(^eB9t zr9=1R_YPc?$Wa3*o}ziuG%kCnpAsNl`o}7^`BU`Okt@eqRmarc&v;Iww-XXI2#NJX z$wx(BJ@+=PYgqmC0kb|TA~Wj&!0@ft%D;i#9s|3@DwfH#V#I`Tyop1Yi9@uYFr4m$ zU1AmYNKRHkV}nBDa5uzDeDVscDv&Alj3HOP_-Pc#X@MAlp%wk)6uFSpo+buoV=k;} zbN|@9WH^Dq4Q-Hc(;g2J=I7zp=MEyXrhxsP3F)HA8q+nVFYZU}IuvI13PW)VH}cON z$~ClrV~9<1^6SIES{xh*3y6r;ssGiraLgCIMCj3fF7?%2fF_VlU@fF= z;aosNgAN4%I%3T^u$FFP@f=p~yKal?4KN^A&%FOa%i*rDYPW7DGCTxb7@&xWrEx)E ze4;CgD~l_6l`gXMo&2bRJ!4EDn`?_nH%sL)WlUMW41xE^8u3a_W>Bsf1<+E}_9 zO>~D{pJcD~_Q(0-x?zOIHDv9{2T4cfY};6R8#Q$6fnmLO4lV~+me^P|TC6u%SG%`sHtY$tG!s>ou{{l;vSFB695h6}ytKA|b5qC1)3e zrDcOGJVrJoGk^xb_jR6CENE$77LyQ3eMuHbz()R`snK5xN+_;Tg%5TPq*nGKNM9BdGGgZg`eA^he zy$)In4ddc>8$#Di|Ig za22a{Rb%3f6C+p<*FMP1fBPCR0N7koroC00Caq7cA077CdGKkOH+Q7p<@b!LsR{r2 z`BK3e%d00y7cWCZ!d$uxL4uf!^KxKWO%;8d8LCf0HDuc!?jOXXo@QH@So6S1`}6~^ zs_JtM;Z3wze$!uFObT;8^?!LcJYGr`&U)wN%_-lvNo*ZwC;Og1nKe(1AdXenbe1PIllrPEo>