package net.tropicraft.world.mapgen; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Random; import net.minecraft.block.Block; import net.minecraft.init.Blocks; import net.minecraft.util.ChunkCoordinates; import net.minecraft.world.World; import net.minecraft.world.biome.BiomeGenBase; import net.tropicraft.registry.TCBlockRegistry; import net.tropicraft.world.biomes.BiomeGenTropicraft; import net.tropicraft.world.perlin.NoiseModule; import net.tropicraft.world.perlin.generator.Billowed; public class MapGenVolcano { protected HashMap coordMap = new HashMap(); public static List<BiomeGenBase> volcanoSpawnBiomesLand = Arrays.asList(new BiomeGenBase[] { BiomeGenTropicraft.tropics, BiomeGenTropicraft.rainforestPlains }); public static List<BiomeGenBase> volcanoSpawnBiomesOcean = Arrays.asList(new BiomeGenBase[] { BiomeGenTropicraft.tropicsOcean }); private World worldObj; private boolean useArrays; private final static int CHUNK_SIZE_X = 16; private final static int CHUNK_SIZE_Z = 16; private final static int CHUNK_SIZE_Y = 256; private final static int MAX_RADIUS = 65; private final static int MIN_RADIUS = 45; private final static int LAND_STEEPNESS_MOD = 4; private final static int OCEAN_STEEPNESS_MOD = 8; private final static int CALDERA_CUTOFF = 110; //The Y level where if the height of the volcano would pass becomes the caldera private final static int VOLCANO_TOP = 103; //The Y level cut off of the sides of the volcano private final static int VOLCANO_CRUST = 100; //The Y level where the crust of the volcano generates private final static int LAVA_LEVEL = 95; //The Y level where the top of the lava column is private final static int CRUST_HOLE_CHANCE = 15; //1 / x chance a certain block of the crust will be missing private final static Block VOLCANO_BLOCK = TCBlockRegistry.chunkOHead; private final static Block LAVA_BLOCK = Blocks.lava; public MapGenVolcano(World worldObj, boolean useArrays) { this.worldObj = worldObj; this.useArrays = useArrays; } public Block[] generate(int i, int k, Block[] blocks, byte[] metas) { ChunkCoordinates volcanoCoords = getVolcanoNear(worldObj, i, k); if (volcanoCoords == null) { return blocks; } int[] heightmap = new int[256]; for(int x = 0; x < 16; x++) { for(int z = 0; z < 16; z++) { for(int y = 0; y < 127; y++) { Block blockID = getBlock(x, y, z, blocks); if(blockID == Blocks.air || blockID == TCBlockRegistry.tropicsWater) { heightmap[x * 16 + z] = y; break; } if(y > 75) { heightmap[x * 16 + z] = y; break; } } } } i *= CHUNK_SIZE_X; k *= CHUNK_SIZE_Z; int volcCenterX = volcanoCoords.posX; int volcCenterZ = volcanoCoords.posZ; int steepnessMod = volcanoCoords.posY == 1 ? LAND_STEEPNESS_MOD : OCEAN_STEEPNESS_MOD; long seed = (long)volcCenterX * 341873128712L + (long)volcCenterZ * 132897987541L + worldObj.getWorldInfo().getSeed() + (long)4291726; Random rand = new Random(seed); int radiusX = rand.nextInt(MAX_RADIUS - MIN_RADIUS) + MIN_RADIUS; int radiusZ = rand.nextInt(MAX_RADIUS - MIN_RADIUS) + MIN_RADIUS; NoiseModule volcNoise = new Billowed(seed, 1, 1); volcNoise.amplitude = 0.45; for(int x = 0; x < CHUNK_SIZE_X; x++) { for(int z = 0; z < CHUNK_SIZE_Z; z++) { float relativeX = ((x + i) - volcCenterX); float relativeZ = ((z + k) - volcCenterZ); float distanceSquared = ((relativeX / radiusX) * (relativeX / radiusX) + (relativeZ / radiusZ) * (relativeZ / radiusZ)); float perlin = (float)volcNoise.getNoise(relativeX * 0.05 + 0.0001, relativeZ * 0.05 + 0.0001) + 1; double volcanoHeight = steepnessMod / (distanceSquared) * perlin - steepnessMod - 2; int groundHeight = heightmap[x * 16 + z]; if(distanceSquared < 1) { for(int y = CHUNK_SIZE_Y; y > 0; y--) { if(volcanoHeight + groundHeight < CALDERA_CUTOFF) { if(volcanoHeight + groundHeight <= VOLCANO_TOP) { if(y <= volcanoHeight + groundHeight && y >= groundHeight) { placeBlock(x, y, z, VOLCANO_BLOCK, blocks); } } else if(y <= VOLCANO_TOP) { placeBlock(x, y, z, VOLCANO_BLOCK, blocks); } } else { if(y == VOLCANO_CRUST && rand.nextInt(CRUST_HOLE_CHANCE) != 0) { placeBlock(x, y, z, VOLCANO_BLOCK, blocks); } if(y <= LAVA_LEVEL) { placeBlock(x, y, z, LAVA_BLOCK, blocks); } } } } } } return blocks; } public void placeBlock(int x, int y, int z, Block block, Block[] blocks) { blocks[x * CHUNK_SIZE_Y * 16 | z * CHUNK_SIZE_Y | y] = block; } public Block getBlock(int x, int y, int z, Block[] blocks) { return blocks[x * CHUNK_SIZE_Y * 16 | z * CHUNK_SIZE_Y | y]; } /** * Method to choose spawn locations for volcanos (borrowed from village gen) * Rarity is determined by the numChunks/offsetChunks vars (smaller numbers * mean more spawning) */ protected int canGenVolcanoAtCoords(World worldObj, int i, int j) { byte numChunks = 32; byte offsetChunks = 8; int oldi = i; int oldj = j; if (i < 0) { i -= numChunks - 1; } if (j < 0) { j -= numChunks - 1; } int randX = i / numChunks; int randZ = j / numChunks; long seed = (long)randX * 341873128712L + (long)randZ * 132897987541L + worldObj.getWorldInfo().getSeed() + (long)4291726; Random rand = new Random(seed); randX *= numChunks; randZ *= numChunks; randX += rand.nextInt(numChunks - offsetChunks); randZ += rand.nextInt(numChunks - offsetChunks); if (oldi == randX && oldj == randZ) { if(worldObj.getWorldChunkManager().areBiomesViable(oldi * 16 + 8, oldj * 16 + 8, 0, volcanoSpawnBiomesLand)) { return 1; } if(worldObj.getWorldChunkManager().areBiomesViable(oldi * 16 + 8, oldj * 16 + 8, 0, volcanoSpawnBiomesOcean)) { return 2; } } return 0; } /** * Returns the coordinates of a volcano if it should be spawned near * this chunk, otherwise returns null. * The posY of the returned object should be used as the volcano radius */ public ChunkCoordinates getVolcanoNear(World worldObj, int i, int j) { //Check 4 chunks in each direction (volcanoes are never more than 4 chunks wide) int range = 4; for(int x = i - range; x <= i + range; x++) { for(int z = j - range; z <= j + range; z++) { int biome = canGenVolcanoAtCoords(worldObj, x, z); if (biome != 0) { return new ChunkCoordinates(x * 16 + 8, biome, z * 16 + 8); } } } return null; } }