package com.bioxx.tfc2.world.generators; import java.util.Random; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.IChunkGenerator; import net.minecraft.world.chunk.IChunkProvider; import net.minecraftforge.fml.common.IWorldGenerator; import com.bioxx.jmapgen.IslandMap; import com.bioxx.jmapgen.Point; import com.bioxx.jmapgen.graph.Center; import com.bioxx.jmapgen.graph.Center.Marker; import com.bioxx.tfc2.Core; import com.bioxx.tfc2.api.Schematic; import com.bioxx.tfc2.api.Schematic.SchemBlock; import com.bioxx.tfc2.api.TFCOptions; import com.bioxx.tfc2.api.trees.TreeConfig; import com.bioxx.tfc2.api.trees.TreeRegistry; import com.bioxx.tfc2.api.trees.TreeSchemManager; import com.bioxx.tfc2.api.trees.TreeSchematic; import com.bioxx.tfc2.api.types.ClimateTemp; import com.bioxx.tfc2.api.types.Moisture; import com.bioxx.tfc2.api.types.WoodType; import com.bioxx.tfc2.world.WorldGen; public class WorldGenTrees implements IWorldGenerator { TreeSchemManager tsm; TreeConfig tc; TreeSchematic schem; @Override public void generate(Random random, int chunkX, int chunkZ, World world, IChunkGenerator chunkGen, IChunkProvider chunkProvider) { if(world.provider.getDimension() != 0) return; chunkX *= 16; chunkZ *= 16; int xM = (chunkX >> 12); int zM = (chunkZ >> 12); int xMLocal = chunkX & 4095; int zMLocal = chunkZ & 4095; IslandMap m = WorldGen.getInstance().getIslandMap(xM, zM); BlockPos chunkPos = new BlockPos(chunkX, 0, chunkZ); Center c = m.getClosestCenter(new Point(xMLocal+8, zMLocal+8)); if(c.hasMarker(Marker.Ocean) || !TFCOptions.shouldGenTrees) { return; } //The theoretical max number of trees per chunk is 8. //We mult this by whichever is lower, the hex moisture or the island moisture. //This way base dry islands still feature less trees overall. int baseTrees = 12; baseTrees = (int)(baseTrees * Math.min(c.getMoisture().getMoisture(), m.getParams().getIslandMoisture().getMoisture())); int numTrees = random.nextInt(baseTrees+1)+1; //numTrees = (int)(numTrees * c.getMoisture().getMoisture()); if(c.getMoisture() == Moisture.LOW) numTrees = random.nextDouble() < 0.25 ? 1 : 0; // Do palm tree gen on valid islands if(c.getElevation() < 0.2 && c.getMoisture().getMoisture() >= Moisture.HIGH.getMoisture() && m.getParams().getIslandTemp().getMapTemp() >= ClimateTemp.SUBTROPICAL.getMapTemp()) { for(int l = 0; l < 3; l++) { genPalm(random, chunkX, chunkZ, world, chunkPos, m); } } for(int l = 0; l < numTrees; l++) { double rarity = random.nextDouble(); TreeReturn out; if(rarity > 0.9) out = gen(random, chunkX, chunkZ, world, chunkPos, m, m.getParams().getRareTree()); else if(rarity > 0.6) out = gen(random, chunkX, chunkZ, world, chunkPos, m, m.getParams().getUncommonTree()); else out = gen(random, chunkX, chunkZ, world, chunkPos, m, m.getParams().getCommonTree()); if(out.baseCount > 4) { numTrees -= 1; } } } private TreeReturn gen(Random random, int chunkX, int chunkZ, World world, BlockPos chunkPos, IslandMap m, String wood) { tsm = TreeRegistry.instance.managerFromString(wood); tc = TreeRegistry.instance.treeFromString(wood); if(tsm == null || tc == null) return new TreeReturn(TreeReturnEnum.None, 0); BlockPos treePos = new BlockPos(chunkX + random.nextInt(16), 0, chunkZ + random.nextInt(16)); treePos = world.getTopSolidOrLiquidBlock(treePos); Point p = new Point(treePos.getX(), treePos.getZ()).toIslandCoord(); Center c = m.getClosestCenter(p); //Obviously we arent going to gen in an ocean hex so we can speed up some generation by skipping this location if(c.hasMarker(Marker.Ocean)) { return new TreeReturn(TreeReturnEnum.None, 0); } int growthStage = 0; if(c.getMoisture().isGreaterThan(Moisture.MEDIUM)) { growthStage = random.nextInt(3); } else if(c.getMoisture().isGreaterThan(Moisture.LOW)) { growthStage = random.nextInt(2); } TreeReturnEnum grown = TreeReturnEnum.None; for(;growthStage >= 0 && grown == TreeReturnEnum.None; growthStage--) { schem = tsm.getRandomSchematic(random, growthStage); if( schem != null && canGrowHere(world, treePos.down(), schem, Math.max(growthStage, 1))) { if(genTree(schem, tc, world, random, treePos)) { grown = TreeReturnEnum.fromSize(growthStage); } } } return new TreeReturn(grown, schem.getBaseCount()); } private TreeReturn genPalm(Random random, int chunkX, int chunkZ, World world, BlockPos chunkPos, IslandMap m) { tsm = TreeRegistry.instance.managerFromString(WoodType.Palm.getName()); tc = TreeRegistry.instance.treeFromString(WoodType.Palm.getName()); BlockPos genPos = new BlockPos(chunkX + random.nextInt(16), 0, chunkZ + random.nextInt(16)); BlockPos treePos; genPos = genPos.add(0, world.getHorizon(), 0); Center c = m.getClosestCenter(new Point(genPos.getX() % 4096, genPos.getZ() % 4096)); if(c.hasMarker(Marker.Ocean)) { return new TreeReturn(TreeReturnEnum.None, 0); } int growthStage = random.nextInt(3); TreeReturnEnum grown = TreeReturnEnum.None; for(;growthStage >= 0 && grown == TreeReturnEnum.None; growthStage--) { schem = tsm.getRandomSchematic(random, growthStage); treePos = genPos.add(-(schem.getCenterX()-1), 0, -(schem.getCenterZ()-1)); if( schem != null && canGrowHere(world, treePos.down(), schem, Math.max(growthStage, 1))) { if(genTree(schem, tc, world, random, treePos)) { grown = TreeReturnEnum.fromSize(growthStage); } } } return new TreeReturn(grown, schem.getBaseCount()); } //***************** // Private methods //***************** private boolean genTree(Schematic schem, TreeConfig tc, World world, Random rand, BlockPos pos) { int rot = rand.nextInt(4);//This causes world gen to change every other time we run the regen command. Not sure why. int index; int id; int meta; BlockPos treePos = pos.add(1, 0, 1); boolean capture = world.captureBlockSnapshots; world.captureBlockSnapshots = false; for(SchemBlock b : schem.getBlockMap()) { Process(world, tc, schem, this.rotateTree(treePos, b.pos, rot), b.state); } world.captureBlockSnapshots = capture; return true; } private void Process(World world, TreeConfig tc, Schematic schem, BlockPos blockPos, IBlockState state) { IBlockState block = tc.wood; Chunk chunk = world.getChunkFromBlockCoords(blockPos); IBlockState leaves = tc.leaves; if(state.getBlock().getMaterial(state) == Material.WOOD) { if(world.getBlockState(blockPos).getBlock().isReplaceable(world, blockPos)) //chunk.setBlockState(blockPos, block); world.setBlockState(blockPos, block, 2); } else if(state.getBlock().getMaterial(state) == Material.LEAVES) { if(world.getBlockState(blockPos).getBlock().isReplaceable(world, blockPos)) { //chunk.setBlockState(blockPos, leaves); world.setBlockState(blockPos, leaves, 2); } } else { //chunk.setBlockState(blockPos, state); world.setBlockState(blockPos, state, 2); } } private BlockPos rotateTree(BlockPos treePos, BlockPos localPos, int rot) { int localX = treePos.getX() + (localPos.getX() * -1) - 2; int localZ = treePos.getZ() + (localPos.getZ() * -1) - 2; int localY = treePos.getY() + localPos.getY(); if(rot == 0) { localX = treePos.getX() + localPos.getX() + 1; localZ = treePos.getZ() + localPos.getZ() + 1; } else if(rot == 1) { localX = treePos.getX() + localPos.getZ(); localZ = treePos.getZ() + (localPos.getX() * -1) - 2; } else if(rot == 2) { localX = treePos.getX() + (localPos.getZ() * -1) -2; localZ = treePos.getZ() + localPos.getX(); } return new BlockPos(localX, localY, localZ); } private boolean canGrowHere(World world, BlockPos pos, TreeSchematic schem, int growthStage) { IBlockState ground; IBlockState above; BlockPos gPos = pos; BlockPos aPos = pos.up(); int radius = Math.max(1, growthStage); int count = 0; //this should validate the ground for(int i = -radius; i <= radius; i++) { for(int k = -radius; k <= radius; k++) { ground = world.getBlockState(gPos.add(i, 0, k)); if(!world.canBlockSeeSky(gPos.add(i, 1, k))) return false; if(schem.getWoodType() != WoodType.Palm && !Core.isSoil(ground)) { return false; } else if(schem.getWoodType() == WoodType.Palm && !Core.isSoil(ground) && !Core.isSand(ground)) { return false; } } } //Scan to the tree height to make sure there is enough room for the tree /*for(int i = 0; i <= schem.getSizeY(); i++) { aPos = aPos.add(0, i, 0); above = world.getBlockState(aPos); if(!above.getBlock().isReplaceable(world, aPos)) { return false; } if(above.getBlock().isLeaves(above, world, aPos)) { count++; } //If we run into too many leaves, then don't place the tree here. This is not perfect as it //can not account for wide trees, but at least trees with a small radius will not stack. if(count > 2) return false; }*/ return true; } private class TreeReturn { public final TreeReturnEnum size; public final int baseCount; public TreeReturn(TreeReturnEnum e, int c) { size = e; baseCount = c; } } private enum TreeReturnEnum { None, Small, Normal, Large; public static TreeReturnEnum fromSize(int size) { if(size == 0) return Small; else if(size == 1) return Normal; else return Large; } } }