/** Copyright (C) <2015> <coolAlias> This file is part of coolAlias' Zelda Sword Skills Minecraft Mod; as such, you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package zeldaswordskills.world.gen.structure; import java.util.Arrays; import java.util.HashSet; import java.util.Random; import java.util.Set; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.BlockPos; import net.minecraft.util.EnumFacing; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.World; import net.minecraft.world.biome.BiomeGenBase; import net.minecraft.world.chunk.IChunkProvider; import net.minecraft.world.gen.structure.StructureBoundingBox; import zeldaswordskills.ZSSMain; import zeldaswordskills.block.IDungeonBlock; import zeldaswordskills.block.ZSSBlocks; import zeldaswordskills.block.tileentity.TileEntityInscription; import zeldaswordskills.ref.Config; import zeldaswordskills.songs.AbstractZeldaSong; import zeldaswordskills.songs.ZeldaSongs; import zeldaswordskills.util.BiomeType; import zeldaswordskills.util.StructureGenUtils; public class MapGenSongPillar extends ZSSMapGenBase { /** List of biomes in which ruined pillar generation is allowed */ private static final Set<String> allowedBiomes = new HashSet<String>(); /** Song inscription to place on top of the pillar for the current generation */ private AbstractZeldaSong song; /** Biome of currently generating chunk */ private BiomeGenBase biome; /** Temporary holding for chunk int coordinates as a Long value */ private long chunk; /** Radius (in chunks) within to search for other pillars */ private int range; public MapGenSongPillar() { range = Config.getPillarRange(); // TODO populate from Config if (allowedBiomes.isEmpty()) { allowedBiomes.addAll(Arrays.asList(BiomeType.COLD.defaultBiomes)); allowedBiomes.addAll(Arrays.asList(BiomeType.FOREST.defaultBiomes)); allowedBiomes.addAll(Arrays.asList(BiomeType.JUNGLE.defaultBiomes)); allowedBiomes.addAll(Arrays.asList(BiomeType.MOUNTAIN.defaultBiomes)); allowedBiomes.addAll(Arrays.asList(BiomeType.PLAINS.defaultBiomes)); allowedBiomes.addAll(Arrays.asList(BiomeType.TAIGA.defaultBiomes)); } } @Override public void generate(IChunkProvider provider, World world, Random rand, int chunkX, int chunkZ) { this.worldObj = world; loadOrCreateData(worldObj); int x = (chunkX << 4) + rand.nextInt(16); int z = (chunkZ << 4) + rand.nextInt(16); biome = world.getBiomeGenForCoords(new BlockPos(x, 64, z)); boolean flag = (biome == BiomeGenBase.swampland && rand.nextFloat() < 0.35F); boolean flag2 = (biome == BiomeGenBase.savanna && rand.nextFloat() < 0.35F); song = (flag ? ZeldaSongs.songSoaring : (flag2 ? ZeldaSongs.songSun : null)); if (song == null && biome != null && biome.biomeName != null) { flag = allowedBiomes.contains(biome.biomeName); } if (flag && generate2(rand, x, z)) { onPillarPlaced(chunkX, chunkZ); } song = null; } private boolean generate2(Random rand, int x, int z) { int y = worldObj.getHeight(new BlockPos(x, 64, z)).down().getY(); if (rand.nextFloat() < (rand.nextFloat() * 0.2F) && canGenerateAt(worldObj, x, y, z)) { doGenerate(worldObj, rand, x, y, z); return true; } return false; } @Override public String getTagName() { return "zssPillars"; } /** * Call after a pillar is placed to store it into the map / world data */ private void onPillarPlaced(int chunkX, int chunkZ) { structureMap.put(Long.valueOf(ChunkCoordIntPair.chunkXZ2Int(chunkX, chunkZ)), song); NBTTagCompound compound = new NBTTagCompound(); compound.setString("song", (song == null ? "NULL" : song.getUnlocalizedName())); addRoomTag(compound, chunkX, chunkZ); } @Override protected void translateNbtIntoMap(NBTTagCompound compound) { if (compound.hasKey("chunkX") && compound.hasKey("chunkZ") && compound.hasKey("song")) { int i = compound.getInteger("chunkX"); int j = compound.getInteger("chunkZ"); AbstractZeldaSong song = ZeldaSongs.getSongByName(compound.getString("song")); structureMap.put(Long.valueOf(ChunkCoordIntPair.chunkXZ2Int(i, j)), song); } else { ZSSMain.logger.warn("Failed to translate Song Pillar NBT compound into structure map"); } } /** * Not using this method either */ @Override protected StructureBoundingBox getStructureBBAt(int x, int y, int z) { return null; } /** * Not using this method, since no RoomBase */ @Override public boolean areStructuresWithinRange(RoomBase room, int range) { return false; } /** * Actually generates the pillar */ private void doGenerate(World world, Random rand, int x, int y, int z) { int meta = 0; // 0 - stone, 1 - mossy stone, 2 - cracked stone, 3 - chiseled stone brick for (int i = x - 1; i <= x + 1; ++i) { for (int k = z - 1; k <= z + 1; ++k) { meta = (rand.nextFloat() < 0.2F ? 1 : rand.nextFloat() < 0.2F ? 2 : 0); world.setBlockState(new BlockPos(i, y, k), Blocks.stonebrick.getStateFromMeta(meta), 2); } } world.setBlockState(new BlockPos(x, y, z + 2), Blocks.stonebrick.getStateFromMeta(rand.nextFloat() < 0.4F ? 1 : 0), 2); world.setBlockState(new BlockPos(x, y, z - 2), Blocks.stonebrick.getStateFromMeta(rand.nextFloat() < 0.4F ? 1 : 0), 2); world.setBlockState(new BlockPos(x + 2, y, z), Blocks.stonebrick.getStateFromMeta(rand.nextFloat() < 0.4F ? 1 : 0), 2); world.setBlockState(new BlockPos(x - 2, y, z), Blocks.stonebrick.getStateFromMeta(rand.nextFloat() < 0.4F ? 1 : 0), 2); if (song == null) { if (world.isSideSolid(new BlockPos(x + 2, y, z + 2), EnumFacing.UP) && rand.nextFloat() < 0.35F) { StructureGenUtils.setBlockIfReplaceable(world, new BlockPos(x + 2, y + 1, z + 2), Blocks.stonebrick.getStateFromMeta(3)); } if (world.isSideSolid(new BlockPos(x - 2, y, z + 2), EnumFacing.UP) && rand.nextFloat() < 0.35F) { StructureGenUtils.setBlockIfReplaceable(world, new BlockPos(x - 2, y + 1, z + 2), Blocks.stonebrick.getStateFromMeta(3)); } if (world.isSideSolid(new BlockPos(x + 2, y , z - 2), EnumFacing.UP) && rand.nextFloat() < 0.35F) { StructureGenUtils.setBlockIfReplaceable(world, new BlockPos(x + 2, y + 1, z - 2), Blocks.stonebrick.getStateFromMeta(3)); } if (world.isSideSolid(new BlockPos(x - 2, y, z - 2), EnumFacing.UP) && rand.nextFloat() < 0.35F) { StructureGenUtils.setBlockIfReplaceable(world, new BlockPos(x - 2, y + 1, z - 2), Blocks.stonebrick.getStateFromMeta(3)); } world.setBlockState(new BlockPos(x, y + 1, z), Blocks.stonebrick.getStateFromMeta(2), 2); world.setBlockState(new BlockPos(x, y + 2, z), Blocks.stonebrick.getStateFromMeta(1), 2); if (rand.nextFloat() < 0.5F) { world.setBlockState(new BlockPos(x, y + 3, z), Blocks.stonebrick.getStateFromMeta(1), 2); world.setBlockState(new BlockPos(x, y + 4, z), Blocks.stonebrick.getStateFromMeta(1), 2); if (rand.nextFloat() < 0.5F) { world.setBlockState(new BlockPos(x + 1, y + 4, z), Blocks.stone_brick_stairs.getStateFromMeta(5), 2); } if (rand.nextFloat() < 0.5F) { world.setBlockState(new BlockPos(x - 1, y + 4, z), Blocks.stone_brick_stairs.getStateFromMeta(4), 2); } if (rand.nextFloat() < 0.5F) { world.setBlockState(new BlockPos(x, y + 4, z + 1), Blocks.stone_brick_stairs.getStateFromMeta(7), 2); } if (rand.nextFloat() < 0.5F) { world.setBlockState(new BlockPos(x, y + 4, z - 1), Blocks.stone_brick_stairs.getStateFromMeta(6), 2); } } } else { if (world.isSideSolid(new BlockPos(x + 2, y, z + 2), EnumFacing.UP)) { StructureGenUtils.setBlockIfReplaceable(world, new BlockPos(x + 2, y + 1, z + 2), Blocks.stonebrick.getStateFromMeta(3)); } if (world.isSideSolid(new BlockPos(x - 2, y, z + 2), EnumFacing.UP)) { StructureGenUtils.setBlockIfReplaceable(world, new BlockPos(x - 2, y + 1, z + 2), Blocks.stonebrick.getStateFromMeta(3)); } if (world.isSideSolid(new BlockPos(x + 2, y , z - 2), EnumFacing.UP)) { StructureGenUtils.setBlockIfReplaceable(world, new BlockPos(x + 2, y + 1, z - 2), Blocks.stonebrick.getStateFromMeta(3)); } if (world.isSideSolid(new BlockPos(x - 2, y, z - 2), EnumFacing.UP)) { StructureGenUtils.setBlockIfReplaceable(world, new BlockPos(x - 2, y + 1, z - 2), Blocks.stonebrick.getStateFromMeta(3)); } world.setBlockState(new BlockPos(x, y + 1, z), Blocks.stonebrick.getStateFromMeta(2), 2); world.setBlockState(new BlockPos(x, y + 2, z), Blocks.stonebrick.getStateFromMeta(1), 2); world.setBlockState(new BlockPos(x, y + 3, z), Blocks.stonebrick.getStateFromMeta(1), 2); world.setBlockState(new BlockPos(x, y + 4, z), ZSSBlocks.secretStone.getStateFromMeta(3), 2); // stonebrick secret stone world.setBlockState(new BlockPos(x + 1, y + 4, z), Blocks.stone_brick_stairs.getStateFromMeta(5), 2); world.setBlockState(new BlockPos(x - 1, y + 4, z), Blocks.stone_brick_stairs.getStateFromMeta(4), 2); world.setBlockState(new BlockPos(x, y + 4, z + 1), Blocks.stone_brick_stairs.getStateFromMeta(7), 2); world.setBlockState(new BlockPos(x, y + 4, z - 1), Blocks.stone_brick_stairs.getStateFromMeta(6), 2); world.setBlockState(new BlockPos(x, y + 5, z), ZSSBlocks.inscription.getDefaultState(), 2); TileEntity te = world.getTileEntity(new BlockPos(x, y + 5, z)); if (te instanceof TileEntityInscription) { ((TileEntityInscription) te).setSong(song); } } } /** * Returns true if all blocks underneath are solid and the blocks to replace * are comprised of non-solid blocks and / or all the same block */ private boolean canGenerateAt(World world, int x, int y, int z) { Block block = world.getBlockState(new BlockPos(x, y, z)).getBlock(); Block block2; for (int i = x - 1; i <= x + 1; ++i) { for (int j = y - 1; j <= y; ++j) { for (int k = z - 1; k <= z + 1; ++k) { block2 = world.getBlockState(new BlockPos(i, j, k)).getBlock(); if (block2 instanceof IDungeonBlock || world.getBlockState(new BlockPos(i, j + 4, k)).getBlock() instanceof IDungeonBlock) { return false; } else if (j < y) { if (!block2.getMaterial().isSolid() || block2.getMaterial() == Material.leaves) { return false; } } else if (block2.getMaterial().isSolid()) { if (!block.getMaterial().isSolid()) { block = block2; } else if (block != block2) { return false; } } } } } int min = (song == null ? Config.getBrokenPillarMin() : Config.getSongPillarMin()); int d = findNearestPillar(world, x, z, min / 4); if (d < 0) { return true; } float f = 0.75F - ((float)(min - d) / (float) min); return (world.rand.nextFloat() < f); } /** * Returns the distance (in chunks) to the nearest song pillar, or -1 if none were found. * Pillars with a different or null song double the distance (meaning they can be closer). */ private int findNearestPillar(World world, int x, int z, int min) { int d1 = -1; double d2 = 0; x = (x >> 4); z = (z >> 4); boolean flag = song != null; // true as long as no other song pillars within radius boolean cont = true; for (int i = x - range; cont && i <= x + range; ++i) { for (int k = z - range; cont && k <= z + range; ++k) { chunk = Long.valueOf(ChunkCoordIntPair.chunkXZ2Int(i, k)); if (structureMap.containsKey(chunk)) { AbstractZeldaSong zs = (AbstractZeldaSong) structureMap.get(chunk); d2 = Math.ceil(Math.sqrt(((i - x) * (i - x)) + ((k - z) * (k - z)))); if (flag && zs != null) { flag = false; } if (zs == null) { // null pillars can generate closer together d2 *= 3; } if (song == null && world.rand.nextFloat() < 0.35F) { d2 *= 2; } if (d2 < d1 || d1 < 0) { d1 = (int) d2; } cont = d1 > min; // stop if no chance of generating } } } return (flag && d1 > (min / 2) && world.rand.nextFloat() < 0.2F ? -1 : d1); } }