package slimeknights.tconstruct.world.worldgen;
import gnu.trove.map.hash.TIntObjectHashMap;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.WorldProvider;
import net.minecraft.world.WorldType;
import net.minecraft.world.chunk.IChunkGenerator;
import net.minecraft.world.chunk.IChunkProvider;
import net.minecraft.world.gen.structure.StructureBoundingBox;
import net.minecraftforge.fml.common.IWorldGenerator;
import java.awt.geom.Ellipse2D;
import java.util.Random;
import slimeknights.tconstruct.common.config.Config;
import slimeknights.tconstruct.shared.TinkerCommons;
import slimeknights.tconstruct.shared.TinkerFluids;
import slimeknights.tconstruct.shared.block.BlockSlime;
import slimeknights.tconstruct.world.TinkerWorld;
import slimeknights.tconstruct.world.block.BlockSlimeDirt;
import slimeknights.tconstruct.world.block.BlockSlimeGrass;
import slimeknights.tconstruct.world.block.BlockSlimeVine;
public class SlimeIslandGenerator implements IWorldGenerator {
public static SlimeIslandGenerator INSTANCE = new SlimeIslandGenerator();
// defines the jaggedness of the surface/bottom
protected int randomness = 1; // 2% chance to have an abnormality in the surface
protected SlimeLakeGenerator lakeGenGreen;
protected SlimeLakeGenerator lakeGenBlue;
protected SlimeLakeGenerator lakeGenPurple;
protected SlimePlantGenerator plantGenBlue;
protected SlimePlantGenerator plantGenPurple;
protected SlimeTreeGenerator treeGenBlue;
protected SlimeTreeGenerator treeGenPurple;
protected IBlockState air;
protected TIntObjectHashMap<SlimeIslandData> islandData = new TIntObjectHashMap<SlimeIslandData>();
public SlimeIslandGenerator() {
air = Blocks.AIR.getDefaultState();
IBlockState slimeGreen = TinkerCommons.blockSlimeCongealed.getDefaultState().withProperty(BlockSlime.TYPE, BlockSlime.SlimeType.GREEN);
IBlockState slimeBlue = TinkerCommons.blockSlimeCongealed.getDefaultState().withProperty(BlockSlime.TYPE, BlockSlime.SlimeType.BLUE);
IBlockState slimePurple = TinkerCommons.blockSlimeCongealed.getDefaultState().withProperty(BlockSlime.TYPE, BlockSlime.SlimeType.PURPLE);
IBlockState leaves = TinkerWorld.slimeLeaves.getDefaultState();
IBlockState slimeFLuidBlue = Blocks.WATER.getDefaultState();
IBlockState slimeFLuidPurple = Blocks.WATER.getDefaultState();
if(TinkerFluids.blueslime != null) {
slimeFLuidBlue = TinkerFluids.blueslime.getBlock().getDefaultState();
slimeFLuidPurple = slimeFLuidBlue; // just in case, will never be used with how the mod is set up
}
if(TinkerFluids.purpleSlime != null) {
slimeFLuidPurple = TinkerFluids.purpleSlime.getBlock().getDefaultState();
}
lakeGenGreen = new SlimeLakeGenerator(slimeFLuidBlue, slimeGreen, slimeGreen, slimeBlue);
lakeGenBlue = new SlimeLakeGenerator(slimeFLuidBlue, slimeBlue, slimeGreen, slimeBlue);
lakeGenPurple = new SlimeLakeGenerator(slimeFLuidPurple, slimePurple, slimePurple);
treeGenBlue = new SlimeTreeGenerator(5, 4, slimeGreen, leaves.withProperty(BlockSlimeGrass.FOLIAGE, BlockSlimeGrass.FoliageType.BLUE), TinkerWorld.slimeVineBlue3.getDefaultState());
treeGenPurple = new SlimeTreeGenerator(5, 4, slimeGreen, leaves.withProperty(BlockSlimeGrass.FOLIAGE, BlockSlimeGrass.FoliageType.PURPLE), TinkerWorld.slimeVinePurple3.getDefaultState());
plantGenBlue = new SlimePlantGenerator(BlockSlimeGrass.FoliageType.BLUE, false);
plantGenPurple = new SlimePlantGenerator(BlockSlimeGrass.FoliageType.PURPLE, false);
}
public boolean isSlimeIslandAt(World world, BlockPos pos) {
for(StructureBoundingBox data : getIslandData(world).islands) {
if(data.isVecInside(pos)) {
return true;
}
}
return false;
}
protected String getDataName() {
return "SlimeIslands";
}
protected SlimeIslandData getIslandData(World world) {
int dimensionId = world.provider.getDimension();
if(!islandData.containsKey(dimensionId)) {
SlimeIslandData data = (SlimeIslandData) world.getPerWorldStorage().getOrLoadData(SlimeIslandData.class, getDataName());
if(data == null) {
data = new SlimeIslandData(getDataName());
world.getPerWorldStorage().setData(getDataName(), data);
}
islandData.put(dimensionId, data);
}
return islandData.get(dimensionId);
}
protected boolean shouldGenerateInDimension(int id) {
for(int dim : Config.slimeIslandBlacklist) {
if(dim == id) {
return false;
}
}
return true;
}
@Override
public void generate(Random random, int chunkX, int chunkZ, World world, IChunkGenerator chunkGenerator, IChunkProvider chunkProvider) {
if(!Config.genSlimeIslands) {
return;
}
// do we generate in superflat?
if(world.getWorldType() == WorldType.FLAT && !Config.genIslandsInSuperflat) {
return;
}
if(!Config.slimeIslandsOnlyGenerateInSurfaceWorlds && !world.provider.isSurfaceWorld()) {
return;
}
// should generate in this dimension?
if(!shouldGenerateInDimension(world.provider.getDimension())) {
return;
}
// do we generate in this chunk?
if(random.nextInt(Config.slimeIslandsRate) > 0) {
return;
}
// We do. determine parameters of the slime island!
// default is a blue island
BlockSlimeGrass.FoliageType grass = BlockSlimeGrass.FoliageType.BLUE;
BlockSlimeDirt.DirtType dirt = BlockSlimeDirt.DirtType.BLUE;
SlimeLakeGenerator lakeGen = lakeGenBlue;
SlimePlantGenerator plantGen = plantGenPurple;
SlimeTreeGenerator treeGen = treeGenPurple; // purple trees on blue/green islands
IBlockState vine = TinkerWorld.slimeVineBlue1.getDefaultState();
int rnr = random.nextInt(10);
// purple island.. rare!
if(rnr <= 1) {
grass = BlockSlimeGrass.FoliageType.PURPLE;
dirt = BlockSlimeDirt.DirtType.PURPLE;
lakeGen = lakeGenPurple;
treeGen = treeGenBlue; // blue trees on purple grass. yay
plantGen = plantGenBlue;
vine = TinkerWorld.slimeVinePurple1.getDefaultState();
}
// green island.. not so rare
else if(rnr < 6) {
dirt = BlockSlimeDirt.DirtType.GREEN;
lakeGen = lakeGenGreen;
}
IBlockState dirtState = TinkerWorld.slimeDirt.getDefaultState().withProperty(BlockSlimeDirt.TYPE, dirt);
IBlockState grassState = TinkerWorld.slimeGrass.getStateFromDirt(dirtState).withProperty(BlockSlimeGrass.FOLIAGE, grass);
int x = chunkX * 16 + 7 + random.nextInt(6) - 3;
int z = chunkZ * 16 + 7 + random.nextInt(6) - 3;
int y = world.getHeight(new BlockPos(x, 0, z)).getY() + 50 + random.nextInt(50) + 11;
generateIsland(random, world, x, z, y, dirtState, grassState, vine, lakeGen, treeGen, plantGen);
}
public void generateIsland(Random random, World world, int xPos, int zPos, int ySurfacePos, IBlockState dirt, IBlockState grass, IBlockState vine, SlimeLakeGenerator lakeGenerator, SlimeTreeGenerator treeGenerator, SlimePlantGenerator plantGen) {
int xRange = 20 + random.nextInt(13);
int zRange = 20 + random.nextInt(13);
int yRange = 11 + random.nextInt(3);
int height = yRange;
//int top = height;
int yBottom = ySurfacePos - yRange;
BlockPos center = new BlockPos(xPos, yBottom + height, zPos);
BlockPos start = new BlockPos(xPos - xRange / 2, yBottom, zPos - zRange / 2);
// the elliptic shape
Ellipse2D.Double ellipse = new Ellipse2D.Double(0, 0, xRange, zRange);
// Basic shape
for(int x = 0; x <= xRange; x++) {
for(int z = 0; z <= zRange; z++) {
for(int y = 0; y <= yRange; y++) {
if(ellipse.contains(x, z)) {
world.setBlockState(start.add(x, y, z), dirt, 2);
}
}
}
}
// now we have a cylindric-elliptic shape floating 50+ blocks above the ground. yaaaaay
// Erode bottom
int erode_height = 8;
for(int x = 0; x <= xRange; x++) {
for(int z = 0; z <= zRange; z++) {
for(int y = 0; y <= erode_height; y++) {
// we go top down
BlockPos pos1 = start.add(x, erode_height - y, z);
BlockPos pos2 = start.add(xRange - x, erode_height - y, zRange - z);
for(BlockPos pos : new BlockPos[]{pos1, pos2}) {
if(world.getBlockState(pos) == dirt) {
if(world.getBlockState(pos.add(-1, +1, 0)) != dirt ||
world.getBlockState(pos.add(+1, +1, 0)) != dirt ||
world.getBlockState(pos.add(0, +1, -1)) != dirt ||
world.getBlockState(pos.add(-1, +1, +1)) != dirt ||
random.nextInt(100) <= randomness) {
world.setBlockState(pos, air, 2);
}
}
}
}
}
}
// Erode top
erode_height = 2;
for(int x = 0; x <= xRange; x++) {
for(int z = 0; z <= zRange; z++) {
for(int y = 0; y <= erode_height; y++) {
// bottom up, starting with top - erosion layers
BlockPos pos1 = start.add(x, y + height - erode_height + 2, z);
BlockPos pos2 = start.add(xRange - x, y + height - erode_height + 2, zRange - z);
for(BlockPos pos : new BlockPos[]{pos1, pos2}) {
BlockPos below = pos.down();
if(world.getBlockState(below.north()) != dirt
|| world.getBlockState(below.east()) != dirt
|| world.getBlockState(below.south()) != dirt
|| world.getBlockState(below.west()) != dirt) {
world.setBlockState(pos, Blocks.AIR.getDefaultState(), 2);
}
}
}
}
}
// make surface grass
for(int x = 0; x <= xRange; x++) {
for(int z = 0; z <= zRange; z++) {
BlockPos top = start.add(x, height, z);
for(int y = 0; y <= height; y++) {
BlockPos pos = top.down(y);
if(world.getBlockState(pos) == dirt && world.isAirBlock(pos.up())) {
world.setBlockState(pos, grass, 2);
break;
}
}
}
}
// lake
if(lakeGenerator != null) {
//System.out.println(center.toString());
lakeGenerator.generateLake(random, world, center);
}
// plants
if(plantGen != null) {
plantGen.generatePlants(random, world, start.up(height + 1), start.add(xRange, height - 3, zRange), 128);
}
if(treeGenerator != null) {
// trees
for(int i = 0; i < 3; i++) {
BlockPos pos = start.add(random.nextInt(xRange), height, random.nextInt(zRange));
treeGenerator.generateTree(random, world, pos);
}
}
if(vine != null) {
for(int i = 0; i < 30; i++) {
BlockPos pos = start.add(-1 + random.nextInt(xRange + 2), 0, -1 + random.nextInt(zRange + 2));
tryPlacingVine(random, world, pos, height, vine);
}
}
// save it
SlimeIslandData data = getIslandData(world);
data.islands.add(new StructureBoundingBox(start.getX(), start.getY(), start.getZ(),
start.getX() + xRange,
start.getY() + yRange,
start.getZ() + zRange));
data.markDirty();
}
// takse the position and goes up until it finds a block. if it doesn't find a block directly above it'll check if it has side blocks on the way up to attach to.
public void tryPlacingVine(Random random, World world, BlockPos below, int limit, IBlockState vine) {
BlockPos pos = below;
BlockPos candidate = null;
// check straight up first
for(int i = 0; i < limit; i++) {
// check around for a possible block
if(vine.getBlock().canPlaceBlockOnSide(world, pos, EnumFacing.NORTH)
|| vine.getBlock().canPlaceBlockOnSide(world, pos, EnumFacing.EAST)
|| vine.getBlock().canPlaceBlockOnSide(world, pos, EnumFacing.SOUTH)
|| vine.getBlock().canPlaceBlockOnSide(world, pos, EnumFacing.WEST)) {
if(candidate == null || random.nextInt(10) == 0) {
candidate = pos;
}
}
pos = pos.up();
}
if(candidate != null) {
// place the vine
world.setBlockState(candidate, vine.getBlock().getStateForPlacement(world, candidate, EnumFacing.UP, 0, 0, 0, 0, null, null), 2);
// and let it grow, let it grow, let it groooooow!
pos = candidate;
for(int size = random.nextInt(8); size >= 0; size++) {
if(!(world.getBlockState(pos).getBlock() instanceof BlockSlimeVine)) {
break;
}
((BlockSlimeVine) world.getBlockState(pos).getBlock()).grow(world, random, pos, world.getBlockState(pos));
pos = pos.down();
}
}
}
}