Merge pull request #2475 from MellowArpeggiation/bubble-butt

Remove all remaining non-structure worldgen cascades
This commit is contained in:
HbmMods 2025-10-08 10:28:38 +02:00 committed by GitHub
commit d1c5355362
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 432 additions and 221 deletions

View File

@ -1,13 +1,22 @@
package com.hbm.lib;
import com.hbm.blocks.ModBlocks;
import com.hbm.config.GeneralConfig;
import com.hbm.config.WorldConfig;
import com.hbm.world.gen.MapGenChainloader;
import com.hbm.world.gen.MapGenNTMFeatures;
import com.hbm.world.gen.NTMWorldGenerator;
import com.hbm.world.gen.component.*;
import com.hbm.world.gen.component.BunkerComponents.BunkerStart;
import com.hbm.world.gen.nbt.NBTStructure;
import com.hbm.world.gen.terrain.MapGenBedrockOil;
import com.hbm.world.gen.terrain.MapGenBubble;
import com.hbm.world.gen.terrain.MapGenCrater;
import cpw.mods.fml.common.IWorldGenerator;
import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.init.Blocks;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.world.gen.structure.MapGenStructureIO;
import net.minecraftforge.common.MinecraftForge;
@ -31,6 +40,9 @@ public class HbmWorld {
MinecraftForge.EVENT_BUS.register(worldGenerator);
NBTStructure.register();
MapGenChainloader.register();
registerNTMTerrain();
}
private static void registerWorldGen(IWorldGenerator nukerWorldGen, int weightedProbability) {
@ -45,4 +57,40 @@ public class HbmWorld {
BunkerComponents.registerComponents();
MapGenStructureIO.func_143031_a(SiloComponent.class, "NTMSiloComponent");
}
/** Register multi-chunk spanning terrain features using chainloader */
private static void registerNTMTerrain() {
if(GeneralConfig.enableRad && WorldConfig.radfreq > 0) {
MapGenCrater sellafieldCrater = new MapGenCrater(WorldConfig.radfreq);
sellafieldCrater.regolith = sellafieldCrater.rock = ModBlocks.sellafield_slaked;
sellafieldCrater.targetBiome = BiomeGenBase.desert;
MapGenChainloader.addOverworldGenerator(sellafieldCrater);
}
if(WorldConfig.oilSpawn > 0) {
MapGenBubble oilBubble = new MapGenBubble(WorldConfig.oilSpawn);
oilBubble.block = ModBlocks.ore_oil;
oilBubble.setSize(8, 16);
MapGenChainloader.addOverworldGenerator(oilBubble);
}
if(WorldConfig.bedrockOilSpawn > 0) {
MapGenBedrockOil bedrockBubble = new MapGenBedrockOil(WorldConfig.bedrockOilSpawn);
MapGenChainloader.addOverworldGenerator(bedrockBubble);
}
int sandBubbleSpawn = 200;
if(sandBubbleSpawn > 0) {
MapGenBubble sandOilBubble = new MapGenBubble(sandBubbleSpawn);
sandOilBubble.replace = Blocks.sand;
sandOilBubble.block = ModBlocks.ore_oil_sand;
sandOilBubble.canSpawn = biome -> !biome.canSpawnLightningBolt() && biome.temperature >= 1.5F;
sandOilBubble.minY = 56;
sandOilBubble.rangeY = 16;
sandOilBubble.setSize(16, 48);
sandOilBubble.fuzzy = true;
MapGenChainloader.addOverworldGenerator(sandOilBubble);
}
}
}

View File

@ -223,18 +223,6 @@ public class HbmWorldGen implements IWorldGenerator {
}
}
// if(biome == BiomeGenBase.plains || biome == BiomeGenBase.desert) {
// if(WorldConfig.radioStructure > 0 && rand.nextInt(WorldConfig.radioStructure) == 0) {
// for(int a = 0; a < 1; a++) {
// int x = i + rand.nextInt(16);
// int z = j + rand.nextInt(16);
// int y = world.getHeightValue(x, z);
//
// new Radio01().generate(world, rand, x, y, z);
// }
// }
// }
if(biome.temperature >= 0.4F && biome.rainfall <= 0.6F) {
if(WorldConfig.antennaStructure > 0 && rand.nextInt(WorldConfig.antennaStructure) == 0) {
for(int a = 0; a < 1; a++) {
@ -278,26 +266,6 @@ public class HbmWorldGen implements IWorldGenerator {
}
}
if(!biome.canSpawnLightningBolt() && biome.temperature >= 1.5F) {
if(rand.nextInt(200) == 0) {
for(int a = 0; a < 1; a++) {
int x = i + rand.nextInt(16);
int z = j + rand.nextInt(16);
int y = world.getHeightValue(x, z);
OilSandBubble.spawnOil(world, x, y, z, 15 + rand.nextInt(31));
}
}
}
// if(WorldConfig.factoryStructure > 0 && rand.nextInt(WorldConfig.factoryStructure) == 0) {
// int x = i + rand.nextInt(16);
// int z = j + rand.nextInt(16);
// int y = world.getHeightValue(x, z);
//
// new Factory().generate(world, rand, x, y, z);
// }
if(WorldConfig.dudStructure > 0 && rand.nextInt(WorldConfig.dudStructure) == 0) {
int x = i + 8 + rand.nextInt(16);
int z = j + 8 + rand.nextInt(16);
@ -386,24 +354,6 @@ public class HbmWorldGen implements IWorldGenerator {
}
}
if(WorldConfig.radfreq > 0 && GeneralConfig.enableRad && rand.nextInt(WorldConfig.radfreq) == 0 && biome == BiomeGenBase.desert) {
for (int a = 0; a < 1; a++) {
int x = i + rand.nextInt(16);
int z = j + rand.nextInt(16);
double r = rand.nextInt(15) + 10;
if(rand.nextInt(50) == 0)
r = 50;
new Sellafield().generate(world, x, z, r, r * 0.35D);
if(GeneralConfig.enableDebugMode)
MainRegistry.logger.info("[Debug] Successfully spawned raditation hotspot at " + x + " " + z);
}
}
if (WorldConfig.geyserChlorine > 0 && biome == BiomeGenBase.plains && rand.nextInt(WorldConfig.geyserWater) == 0) {
int x = i + rand.nextInt(16);
int z = j + rand.nextInt(16);
@ -545,36 +495,6 @@ public class HbmWorldGen implements IWorldGenerator {
}
}
if(WorldConfig.oilSpawn > 0 && rand.nextInt(WorldConfig.oilSpawn) == 0) {
int randPosX = i + rand.nextInt(16);
int randPosY = rand.nextInt(25);
int randPosZ = j + rand.nextInt(16);
OilBubble.spawnOil(world, randPosX, randPosY, randPosZ, 10 + rand.nextInt(7));
}
if(WorldConfig.bedrockOilSpawn > 0 && rand.nextInt(WorldConfig.bedrockOilSpawn) == 0) {
int randPosX = i + rand.nextInt(16);
int randPosZ = j + rand.nextInt(16);
for(int x = -4; x <= 4; x++) {
for(int y = 0; y <= 4; y++) {
for(int z = -4; z <= 4; z++) {
if(Math.abs(x) + Math.abs(y) + Math.abs(z) <= 6) {
Block b = world.getBlock(randPosX + x, y, randPosZ + z);
if(b.isReplaceableOreGen(world, randPosX + x, y, randPosZ + z, Blocks.stone) || b.isReplaceableOreGen(world, randPosX + x, y, randPosZ + z, Blocks.bedrock)) {
world.setBlock(randPosX + x, y, randPosZ + z, ModBlocks.ore_bedrock_oil);
}
}
}
}
}
DungeonToolbox.generateOre(world, rand, i, j, 16, 8, 10, 50, ModBlocks.stone_porous);
OilSpot.generateOilSpot(world, randPosX, randPosZ, 5, 50, true);
}
if(WorldConfig.meteoriteSpawn > 0 && rand.nextInt(WorldConfig.meteoriteSpawn) == 0) {
int x = i + rand.nextInt(16) + 8;
int z = j + rand.nextInt(16) + 8;

View File

@ -1,33 +0,0 @@
package com.hbm.world.feature;
import com.hbm.blocks.ModBlocks;
import net.minecraft.init.Blocks;
import net.minecraft.world.World;
public class OilBubble {
public static void spawnOil(World world, int x, int y, int z, int radius) {
int r = radius;
int r2 = r * r;
int r22 = r2 / 2;
for (int xx = -r; xx < r; xx++) {
int X = xx + x;
int XX = xx * xx;
for (int yy = -r; yy < r; yy++) {
int Y = yy + y;
int YY = XX + yy * yy * 3;
for (int zz = -r; zz < r; zz++) {
int Z = zz + z;
int ZZ = YY + zz * zz;
if (ZZ < r22) {
if(world.getBlock(X, Y, Z) == Blocks.stone)
world.setBlock(X, Y, Z, ModBlocks.ore_oil);
}
}
}
}
}
}

View File

@ -1,37 +0,0 @@
package com.hbm.world.feature;
import java.util.Random;
import com.hbm.blocks.ModBlocks;
import net.minecraft.init.Blocks;
import net.minecraft.world.World;
public class OilSandBubble {
private final static Random field_149933_a = new Random();
public static void spawnOil(World world, int x, int y, int z, int radius) {
int r = radius;
int r2 = r * r;
int r22 = r2 / 2;
for (int xx = -r; xx < r; xx++) {
int X = xx + x;
int XX = xx * xx;
for (int yy = -r; yy < r; yy++) {
int Y = yy + y;
int YY = XX + yy * yy * 3;
for (int zz = -r; zz < r; zz++) {
int Z = zz + z;
int ZZ = YY + zz * zz;
if (ZZ < r22 + field_149933_a.nextInt(r22 / 3)) {
if(world.getBlock(X, Y, Z) == Blocks.sand)
world.setBlock(X, Y, Z, ModBlocks.ore_oil_sand);
}
}
}
}
}
}

View File

@ -1,71 +0,0 @@
package com.hbm.world.feature;
import java.util.Random;
import com.hbm.blocks.ModBlocks;
import net.minecraft.block.Block;
import net.minecraft.world.World;
public class Sellafield {
private double depthFunc(double x, double rad, double depth) {
return -Math.pow(x, 2) / Math.pow(rad, 2) * depth + depth;
}
public void generate(World world, int x, int z, double radius, double depth) {
if(world.isRemote)
return;
Random rand = new Random();
int iRad = (int)Math.round(radius);
for(int a = -iRad - 5; a <= iRad + 5; a++) {
for(int b = -iRad - 5; b <= iRad + 5; b++) {
double r = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
if(r - rand.nextInt(3) <= radius) {
int dep = (int)depthFunc(r, radius, depth);
dig(world, x + a, z + b, dep);
if(r + rand.nextInt(3) <= radius / 3D) {
place(world, x + a, z + b, 3, ModBlocks.sellafield, 1);
} else if(r - rand.nextInt(3) <= radius / 3D * 2D) {
place(world, x + a, z + b, 3, ModBlocks.sellafield);
} else {
place(world, x + a, z + b, 3, ModBlocks.sellafield_slaked);
}
}
}
}
}
private void dig(World world, int x, int z, int depth) {
int y = world.getHeightValue(x, z) - 1;
if(y < depth * 2)
return;
for(int i = 0; i < depth; i++)
world.setBlockToAir(x, y - i, z);
}
private void place(World world, int x, int z, int depth, Block block) { place(world, x, z, depth, block, 0); }
private void place(World world, int x, int z, int depth, Block block, int meta) {
int y = world.getHeightValue(x, z) - 1;
for(int i = 0; i < depth; i++)
world.setBlock(x, y - i, z, block, meta, 2);
}
//private void placeCore(World world, int x, int z, double rad) { }
}

View File

@ -0,0 +1,13 @@
package com.hbm.world.gen;
import net.minecraft.world.gen.MapGenBase;
public class MapGenBaseMeta extends MapGenBase {
protected byte[] metas;
public void setMetas(byte[] metas) {
this.metas = metas;
}
}

View File

@ -0,0 +1,92 @@
package com.hbm.world.gen;
import java.util.ArrayList;
import java.util.List;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.minecraft.block.Block;
import net.minecraft.world.World;
import net.minecraft.world.chunk.IChunkProvider;
import net.minecraft.world.gen.MapGenBase;
import net.minecraftforge.event.terraingen.InitMapGenEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.terraingen.ChunkProviderEvent.ReplaceBiomeBlocks;
import net.minecraftforge.event.terraingen.InitMapGenEvent.EventType;
public class MapGenChainloader extends MapGenBase {
/**
* There is no way - with existing forge hooks - to add entirely new `MapGenBase` generators to existing dimensions (overworld, nether).
* So, in order to fix that (while not breaking mods like Greg's Caves), we chainload our own MapGenBase into the existing cave generator
*/
private MapGenBase parent;
private List<MapGenBase> generators = new ArrayList<>();
// These may be added to before OR after mapgen registration safely
private static List<MapGenBase> overworldGenerators = new ArrayList<>();
private static List<MapGenBase> netherGenerators = new ArrayList<>();
// Hack to provide the current generating chunk's block metas to the generation function
private static byte[] blockMetas;
// Executes our chainloaded parent, and all our child generators
@Override
public void func_151539_a(IChunkProvider chunk, World world, int chunkX, int chunkZ, Block[] blocks) {
parent.func_151539_a(chunk, world, chunkX, chunkZ, blocks);
// Some mods may use vanilla gen events for added dimensions, so we guard against that here
if(world.provider.dimensionId != 0 && world.provider.dimensionId != -1) return;
for(MapGenBase generator : generators) {
if(generator instanceof MapGenBaseMeta) ((MapGenBaseMeta)generator).setMetas(blockMetas);
generator.func_151539_a(chunk, world, chunkX, chunkZ, blocks);
}
}
public static void register() {
MapGenEventHandler handler = new MapGenEventHandler();
MinecraftForge.TERRAIN_GEN_BUS.register(handler);
MinecraftForge.EVENT_BUS.register(handler);
}
public static void addOverworldGenerator(MapGenBase generator) {
if(overworldGenerators.contains(generator)) return;
overworldGenerators.add(generator);
}
public static void addNetherGenerator(MapGenBase generator) {
if(netherGenerators.contains(generator)) return;
netherGenerators.add(generator);
}
public static class MapGenEventHandler {
// Register as late as possible to pick up any modded cave generators
@SubscribeEvent(priority = EventPriority.LOWEST)
public void addMapGenChainloader(InitMapGenEvent event) {
if(!(event.newGen instanceof MapGenChainloader)) {
if(event.type == EventType.CAVE) {
MapGenChainloader loader = new MapGenChainloader();
loader.parent = event.newGen;
loader.generators = overworldGenerators;
event.newGen = loader;
} else if(event.type == EventType.NETHER_CAVE) {
MapGenChainloader loader = new MapGenChainloader();
loader.parent = event.newGen;
loader.generators = netherGenerators;
event.newGen = loader;
}
}
}
@SubscribeEvent
public void storeLatestBlockMeta(ReplaceBiomeBlocks event) {
blockMetas = event.metaArray;
}
}
}

View File

@ -0,0 +1,110 @@
package com.hbm.world.gen.terrain;
import com.hbm.blocks.ModBlocks;
import com.hbm.blocks.generic.BlockDeadPlant.EnumDeadPlantType;
import com.hbm.blocks.generic.BlockNTMFlower.EnumFlowerType;
import com.hbm.world.gen.MapGenBaseMeta;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.world.World;
public class MapGenBedrockOil extends MapGenBaseMeta {
/**
* Similar to oil bubbles, but with a few more behaviours, like adding oily dirt
* no porous stone don't @ me
*/
private final int frequency;
public Block block = ModBlocks.ore_bedrock_oil;
public Block replace = Blocks.stone;
public int spotWidth = 5;
public int spotCount = 50;
public boolean addWillows = true;
public MapGenBedrockOil(int frequency) {
this.frequency = frequency;
this.range = 4;
}
@Override
protected void func_151538_a(World world, int offsetX, int offsetZ, int chunkX, int chunkZ, Block[] blocks) {
if(rand.nextInt(frequency) == frequency - 2) {
int xCoord = (chunkX - offsetX) * 16 + rand.nextInt(16);
int zCoord = (chunkZ - offsetZ) * 16 + rand.nextInt(16);
// Add the bedrock oil spot
for(int bx = 15; bx >= 0; bx--)
for(int bz = 15; bz >= 0; bz--)
for(int y = 0; y < 5; y++) {
int index = (bx * 16 + bz) * 256 + y;
if(blocks[index] == replace || blocks[index] == Blocks.bedrock) {
// x, z are the coordinates relative to the target virtual chunk origin
int x = xCoord + bx;
int z = zCoord + bz;
if(Math.abs(x) < 5 && Math.abs(z) < 5 && Math.abs(x) + Math.abs(y) + Math.abs(z) <= 6) {
blocks[index] = block;
}
}
}
int deadMetaCount = EnumDeadPlantType.values().length;
// Add oil spot damage
for(int i = 0; i < spotCount; i++) {
int rx = (int)(rand.nextGaussian() * spotWidth) - xCoord;
int rz = (int)(rand.nextGaussian() * spotWidth) - zCoord;
if(rx >= 0 && rx < 16 && rz >= 0 && rz < 16) {
// find ground level
for(int y = 127; y >= 0; y--) {
int index = (rx * 16 + rz) * 256 + y;
if(blocks[index] != null && blocks[index].isOpaqueCube()) {
for(int oy = 1; oy > -3; oy--) {
int subIndex = index + oy;
if(blocks[subIndex] == Blocks.grass || blocks[subIndex] == Blocks.dirt) {
blocks[subIndex] = rand.nextInt(10) == 0 ? ModBlocks.dirt_oily : ModBlocks.dirt_dead;
if(addWillows && oy == 0 && rand.nextInt(50) == 0) {
blocks[subIndex + 1] = ModBlocks.plant_flower;
metas[subIndex + 1] = (byte)EnumFlowerType.CD0.ordinal();
}
// this generation occurs BEFORE decoration, so we have no plants to modify
// so we'll instead just add some new ones right now
if(oy == 0 && rand.nextInt(20) == 0) {
blocks[subIndex + 1] = ModBlocks.plant_dead;
metas[subIndex + 1] = (byte)rand.nextInt(deadMetaCount);
}
break;
} else if(blocks[subIndex] == Blocks.sand || blocks[subIndex] == ModBlocks.ore_oil_sand) {
if(metas[subIndex] == 1) {
blocks[subIndex] = ModBlocks.sand_dirty_red;
} else {
blocks[subIndex] = ModBlocks.sand_dirty;
}
break;
} else if(blocks[subIndex] == Blocks.stone) {
blocks[subIndex] = ModBlocks.stone_cracked;
break;
}
}
break;
}
}
}
}
}
}
}

View File

@ -0,0 +1,79 @@
package com.hbm.world.gen.terrain;
import java.util.function.Predicate;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.world.gen.MapGenBase;
public class MapGenBubble extends MapGenBase {
/**
* Generates oil bubbles, which are generally wider than a chunk, in a safe + cascadeless manner
* Pretty much just an oblate sphere generator (dimensions: 3 x 1 x 3)
*/
private final int frequency;
private int minSize = 8;
private int maxSize = 64;
public int minY = 0;
public int rangeY = 25;
public boolean fuzzy;
public Block block;
public Block replace = Blocks.stone;
public Predicate<BiomeGenBase> canSpawn;
public MapGenBubble(int frequency) {
this.frequency = frequency;
}
public void setSize(int minSize, int maxSize) {
this.minSize = minSize;
this.maxSize = maxSize;
this.range = (maxSize / 8) + 1;
}
@Override
protected void func_151538_a(World world, int offsetX, int offsetZ, int chunkX, int chunkZ, Block[] blocks) {
if(rand.nextInt(frequency) == frequency - 1 && (canSpawn == null || canSpawn.test(world.getBiomeGenForCoords(offsetX * 16, offsetZ * 16)))) {
int xCoord = (chunkX - offsetX) * 16 + rand.nextInt(16);
int zCoord = (chunkZ - offsetZ) * 16 + rand.nextInt(16);
int yCoord = rand.nextInt(rangeY) + minY;
double radius = rand.nextInt(maxSize - minSize) + minSize;
double radiusSqr = (radius * radius) / 2; // original OilBubble implementation divided the square by 2 for some reason
int yMin = Math.max(1, MathHelper.floor_double(yCoord - radius));
int yMax = MathHelper.ceiling_double_int(yCoord + radius);
for(int bx = 15; bx >= 0; bx--) // bx, bz is the coordinate of the block we're modifying, relative to the generating chunk origin
for(int bz = 15; bz >= 0; bz--)
for(int by = yMin; by < yMax; by++) {
int index = (bx * 16 + bz) * 256 + by;
if(blocks[index] == replace) {
// x, z are the coordinates relative to the target virtual chunk origin
int x = xCoord + bx;
int z = zCoord + bz;
int y = yCoord - by;
double rSqr = x * x + z * z + y * y * 3;
if(fuzzy) rSqr -= rand.nextDouble() * radiusSqr / 3;
if(rSqr < radiusSqr) {
blocks[index] = block;
}
}
}
}
}
}

View File

@ -0,0 +1,90 @@
package com.hbm.world.gen.terrain;
import net.minecraft.block.Block;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.world.gen.MapGenBase;
public class MapGenCrater extends MapGenBase {
private int frequency = 100;
private int minSize = 8;
private int maxSize = 64;
public Block regolith;
public Block rock;
public BiomeGenBase targetBiome;
public MapGenCrater(int frequency) {
this.frequency = frequency;
}
public void setSize(int minSize, int maxSize) {
this.minSize = minSize;
this.maxSize = maxSize;
this.range = (maxSize / 8) + 1;
}
private double depthFunc(double x, double rad, double depth) {
return -Math.pow(x, 2) / Math.pow(rad, 2) * depth + depth;
}
// This function is looped over from -this.range to +this.range on both XZ axes.
@Override
protected void func_151538_a(World world, int offsetX, int offsetZ, int chunkX, int chunkZ, Block[] blocks) {
if(rand.nextInt(frequency) == 0 && (targetBiome == null || targetBiome == world.getBiomeGenForCoords(offsetX * 16, offsetZ * 16))) {
int xCoord = -offsetX + chunkX;
int zCoord = -offsetZ + chunkZ;
double radius = rand.nextInt(maxSize - minSize) + minSize;
double depth = radius * 0.35D;
for(int bx = 15; bx >= 0; bx--) { // bx, bz is the coordinate of the block we're modifying, relative to the generating chunk origin
for(int bz = 15; bz >= 0; bz--) {
for(int y = 127; y >= 0; y--) {
int index = (bx * 16 + bz) * 256 + y;
if(blocks[index] != null && (blocks[index].isOpaqueCube() || blocks[index].getMaterial().isLiquid())) {
// x, z are the coordinates relative to the target virtual chunk origin
int x = xCoord * 16 + bx;
int z = zCoord * 16 + bz;
// y is at the current height now
double r = Math.sqrt(x * x + z * z);
if(r - rand.nextInt(3) <= radius) {
// Carve out to intended depth
int dep = (int)MathHelper.clamp_double(depthFunc(r, radius, depth), 0, y - 1);
for(int i = 0; i < dep; i++) {
blocks[index - i] = null;
}
index -= dep;
y -= dep;
dep = Math.min(3, y - 1);
// Fill back in
if(r + rand.nextInt(3) <= radius / 3D) {
for(int i = 0; i < dep; i++) {
blocks[index - i] = regolith;
}
} else {
for(int i = 0; i < dep; i++) {
blocks[index - i] = rock;
}
}
}
break;
}
}
}
}
}
}
}