package com.bioxx.tfc2.world;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
import java.util.Vector;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.IEntityLivingData;
import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Vec3i;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkPrimer;
import net.minecraft.world.gen.ChunkProviderOverworld;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.event.terraingen.PopulateChunkEvent;
import net.minecraftforge.event.terraingen.TerrainGen;
import com.bioxx.jmapgen.*;
import com.bioxx.jmapgen.IslandParameters.Feature;
import com.bioxx.jmapgen.attributes.*;
import com.bioxx.jmapgen.dungeon.*;
import com.bioxx.jmapgen.graph.Center;
import com.bioxx.jmapgen.graph.Center.Marker;
import com.bioxx.jmapgen.processing.CaveAttrNode;
import com.bioxx.jmapgen.processing.OreAttrNode;
import com.bioxx.libnoise.model.Plane;
import com.bioxx.libnoise.model.Segment;
import com.bioxx.libnoise.module.modifier.Clamp;
import com.bioxx.libnoise.module.modifier.Curve;
import com.bioxx.libnoise.module.modifier.ScaleBias;
import com.bioxx.libnoise.module.source.Perlin;
import com.bioxx.tfc2.Core;
import com.bioxx.tfc2.TFCBlocks;
import com.bioxx.tfc2.api.AnimalSpawnRegistry;
import com.bioxx.tfc2.api.AnimalSpawnRegistry.SpawnGroup;
import com.bioxx.tfc2.api.Global;
import com.bioxx.tfc2.api.Schematic.SchemBlock;
import com.bioxx.tfc2.api.TFCOptions;
import com.bioxx.tfc2.api.ore.OreConfig;
import com.bioxx.tfc2.api.ore.OreConfig.VeinType;
import com.bioxx.tfc2.api.ore.OreRegistry;
import com.bioxx.tfc2.api.util.Helper;
import com.bioxx.tfc2.blocks.terrain.BlockDirt;
import com.bioxx.tfc2.blocks.terrain.BlockGrass;
import com.bioxx.tfc2.blocks.terrain.BlockGravel;
import com.bioxx.tfc2.blocks.terrain.BlockStone;
public class ChunkProviderSurface extends ChunkProviderOverworld
{
private World worldObj;
private Random rand;
private static final int MAP_SIZE = 4096;
int worldX;//This is the x coordinate of the chunk using world coords.
int worldZ;//This is the z coordinate of the chunk using world coords.
int islandChunkX;//This is the x coordinate of the chunk within the bounds of the island (0 - MAP_SIZE)
int islandChunkZ;//This is the z coordinate of the chunk within the bounds of the island (0 - MAP_SIZE)
int mapX;//This is the x coordinate of the chunk using world coords.
int mapZ;//This is the z coordinate of the chunk using world coords.
int chunkX, chunkZ;
Plane turbMap;
Plane turbMap1_4;
IslandMap islandMap;
Vector<Center> centersInChunk;
int[] elevationMap;
/**
* Cache for Hex lookup.
*/
private Center[][] centerCache;
/**
* A static array of sample points for performing our hex smoothing
*/
private static Point[][] hexSamplePoints;
public ChunkProviderSurface(World worldIn, long seed, boolean enableMapFeatures, String rules)
{
super(worldIn, seed, false, rules);
worldObj = worldIn;
rand = worldObj.rand;
hexSamplePoints = new Point[11][6];
//Setup the sampling hexagon for hex smoothing
for(int i = 0; i < 11; i++)
{
double c = i;
double a = 0.5*c;
double b = Math.sin(60)*c;
for(int j = 0; j < 6; j++)
{
hexSamplePoints[i][j] = hex_corner(i, j);
}
}
/**
* Setup our turbulence Module
*/
Perlin pe = new Perlin();
pe.setSeed (seed);
pe.setFrequency (1f/16f);
pe.setLacunarity(1.5);
pe.setOctaveCount(4);
pe.setNoiseQuality (com.bioxx.libnoise.NoiseQuality.BEST);
//The scalebias makes our noise fit the range 0-1
ScaleBias sb2 = new ScaleBias();
sb2.setSourceModule(0, pe);
//Noise is normally +-2 so we scale by 0.5 to make it +-1.0
//sb2.setScale(0.5);
turbMap = new Plane(sb2);
turbMap1_4 = createPondTurbMap(seed);
}
private Plane createPondTurbMap(long seed)
{
Perlin pe = new Perlin();
pe.setSeed(seed);
pe.setFrequency (1f/4f);
pe.setLacunarity(5);
pe.setOctaveCount(4);
pe.setPersistence(1f/4f);
pe.setNoiseQuality (com.bioxx.libnoise.NoiseQuality.BEST);
ScaleBias sb2 = new ScaleBias();
sb2.setSourceModule(0, pe);
//Noise is normally +-2 so we scale by 0.5 to make it +-1.0
sb2.setBias(0.5);
sb2.setScale(0.25);
return new Plane(sb2);
}
@Override
public void recreateStructures(Chunk chunkIn, int x, int z)
{
}
private Point hex_corner(double size, int i)
{
double angle_deg = 60 * i + 30;
double angle_rad = Math.PI / 180 * angle_deg;
return new Point(size * Math.cos(angle_rad),
size * Math.sin(angle_rad));
}
@Override
public Chunk provideChunk(int chunkX, int chunkZ)
{
centerCache = new Center[48][48];
elevationMap = new int[256];
this.chunkX = chunkX;
this.chunkZ = chunkZ;
worldX = chunkX * 16;
worldZ = chunkZ * 16;
islandChunkX = worldX % MAP_SIZE;
islandChunkZ = worldZ % MAP_SIZE;
mapX = (chunkX >> 8);
mapZ = (chunkZ >> 8);
islandMap = WorldGen.getInstance().getIslandMap(mapX, mapZ);
centersInChunk = new Vector<Center>();
for(int x = -16; x < 32; x++)
{
for(int z = -16; z < 32; z++)
{
getHex(new Point(x,z));
}
}
this.rand.setSeed((long)chunkX * 341873128712L + (long)chunkZ * 132897987541L);
ChunkPrimer chunkprimer = new ChunkPrimer();
generateTerrain(chunkprimer, chunkX, chunkZ);
decorate(chunkprimer, chunkX, chunkZ);
carveRiverSpline(chunkprimer);
carveCaves(chunkprimer);
placeOreSeams(chunkprimer);
placeOreLayers(chunkprimer);
createSpires(chunkprimer);
createDungeons(chunkprimer);
if(TFCOptions.shouldStripChunks)
stripChunk(chunkprimer);
Chunk chunk = new Chunk(this.worldObj, chunkprimer, chunkX, chunkZ);
chunk.setHeightMap(elevationMap);
byte[] biomeArray = chunk.getBiomeArray();
Point p = new Point(0, 0);
for (int x = 0; x < 16; x++)
{
for (int z = 0; z < 16; z++)
{
biomeArray[z << 4 | x] = (byte)(getHex(p.plus(x, z)).getMoistureRaw() * 255f);
}
}
chunk.generateSkylightMap();
return chunk;
}
@Override
public void populate(int x, int z)
{
net.minecraft.block.BlockFalling.fallInstantly = true;
BlockPos blockpos = new BlockPos(x * 16, 0, z * 16);
Biome Biome = this.worldObj.getBiome(blockpos.add(16, 0, 16));
this.rand.setSeed(this.worldObj.getSeed());
long k = this.rand.nextLong() / 2L * 2L + 1L;
long l = this.rand.nextLong() / 2L * 2L + 1L;
this.rand.setSeed(x * k + z * l ^ this.worldObj.getSeed());
boolean flag = false;
ChunkPos ChunkPos = new ChunkPos(x, z);
ForgeEventFactory.onChunkPopulate(true, this, this.worldObj, x, z, flag);
TerrainGen.populate(this, this.worldObj, this.rand, x, z, flag, PopulateChunkEvent.Populate.EventType.LAKE);
TerrainGen.populate(this, this.worldObj, this.rand, x, z, flag, PopulateChunkEvent.Populate.EventType.LAVA);
Biome.decorate(this.worldObj, this.rand, new BlockPos(x * 16, 0, z * 16));
if(TerrainGen.populate(this, this.worldObj, this.rand, x, z, flag, PopulateChunkEvent.Populate.EventType.ANIMALS))
{
BlockPos chunkWorldPos = new BlockPos(x * 16, 0, z * 16);
worldX = x * 16;
worldZ = z * 16;
islandChunkX = worldX % MAP_SIZE;
islandChunkZ = worldZ % MAP_SIZE;
Point islandPos = new Point(islandChunkX, islandChunkZ).toIslandCoord();
IslandMap map = Core.getMapForWorld(worldObj, chunkWorldPos);
Center centerInChunk = null;
Center temp = map.getClosestCenter(islandPos);
if(Core.isCenterInRect(temp, (int)islandPos.x, (int)islandPos.y, 16, 16))
centerInChunk = temp;
else
{
temp = map.getClosestCenter(islandPos.plus(15, 0));
if(Core.isCenterInRect(temp, (int)islandPos.x, (int)islandPos.y, 16, 16))
centerInChunk = temp;
else
{
temp = map.getClosestCenter(islandPos.plus(0, 15));
if(Core.isCenterInRect(temp, (int)islandPos.x, (int)islandPos.y, 16, 16))
centerInChunk = temp;
else
{
temp = map.getClosestCenter(islandPos.plus(15, 15));
if(Core.isCenterInRect(temp, (int)islandPos.x, (int)islandPos.y, 16, 16))
centerInChunk = temp;
}
}
}
if(centerInChunk != null && centerInChunk.getCustomNBT().hasKey("animalsToSpawn"))
{
NBTTagList tag = centerInChunk.getCustomNBT().getTagList("animalsToSpawn", 8);
IEntityLivingData ientitylivingdata = null;
/*Iterator iter = tag.getKeySet().iterator();
while(iter.hasNext())
{
String key = (String)iter.next();
String groupName = tag.getString(key);
SpawnGroup group = AnimalSpawnRegistry.getInstance().getGroupFromName(groupName);
AnimalSpawner.SpawnAnimalGroup(worldObj, group, worldObj.getChunkFromChunkCoords(x, z));
tag.removeTag(key);
}*/
for(int i = 0; i < tag.tagCount(); i++)
{
String groupName = tag.getStringTagAt(i);
SpawnGroup group = AnimalSpawnRegistry.getInstance().getGroupFromName(groupName);
AnimalSpawner.SpawnAnimalGroup(worldObj, group, worldObj.getChunkFromChunkCoords(x, z));
}
centerInChunk.getCustomNBT().removeTag("animalsToSpawn");
}
}
blockpos = blockpos.add(8, 0, 8);
if (TerrainGen.populate(this, this.worldObj, this.rand, x, z, flag, PopulateChunkEvent.Populate.EventType.ICE))
{
for (int k2 = 0; k2 < 16; k2++)
{
for (int j3 = 0; j3 < 16; j3++)
{
BlockPos blockpos1 = this.worldObj.getPrecipitationHeight(blockpos.add(k2, 0, j3));
BlockPos blockpos2 = blockpos1.down();
if (this.worldObj.canBlockFreezeWater(blockpos2))
{
this.worldObj.setBlockState(blockpos2, Blocks.ICE.getDefaultState(), 2);
}
if (this.worldObj.canSnowAt(blockpos1, true))
{
this.worldObj.setBlockState(blockpos1, Blocks.SNOW_LAYER.getDefaultState(), 2);
}
}
}
}
ForgeEventFactory.onChunkPopulate(false, this, this.worldObj, x, z, flag);
net.minecraft.block.BlockFalling.fallInstantly = false;
}
/**
* This is for stripping a chunk of all but ore and BEDROCK for easier testing.
*/
protected void stripChunk(ChunkPrimer primer)
{
Point p;
Center closestCenter;
IBlockState state;
for(int x = 0; x < 16; x++)
{
for(int z = 0; z < 16; z++)
{
p = new Point(x, z);
closestCenter = this.getHex(p);
int hexElev = this.getHexElevation(closestCenter, p);
if(closestCenter.hasAnyMarkersOf(Marker.Coast, Marker.Ocean))
continue;
for(int y = hexElev; y >= 0; y--)
{
state = primer.getBlockState(x, y, z);
if(state.getBlock() != TFCBlocks.Ore && state.getBlock() != Blocks.BEDROCK && state.getBlock() != Blocks.WOOL)
{
primer.setBlockState(x, y, z, Blocks.AIR.getDefaultState());
}
}
}
}
}
/**
* @param p this Point should always be using local chunk coordinates
* @return Returns the nearest Hex for this map
*/
protected Center getHex(Point p)
{
int x = (int)p.x;
int y = (int)p.y;
int x16 = (int)p.x + 16;
int y16 = (int)p.y + 16;
if(centerCache[x16][y16] == null)
{
centerCache[x16][y16] = islandMap.getClosestCenter(p.plus(islandChunkX, islandChunkZ).toIslandCoord());
}
if(!centersInChunk.contains(centerCache[x16][y16]))
{
centersInChunk.add(centerCache[x16][y16]);
for(Center n : centerCache[x16][y16].neighbors)
{
if(!centersInChunk.contains(n))
centersInChunk.add(n);
}
}
return centerCache[x16][y16];
}
protected void decorate(ChunkPrimer chunkprimer, int chunkX, int chunkZ)
{
Point p;
Center closestCenter;
IBlockState grass = TFCBlocks.Grass.getStateFromMeta(this.islandMap.getParams().getSurfaceRock().getMeta());
IBlockState dirt = TFCBlocks.Dirt.getStateFromMeta(this.islandMap.getParams().getSurfaceRock().getMeta());
IBlockState stone = TFCBlocks.Stone.getStateFromMeta(this.islandMap.getParams().getSurfaceRock().getMeta());
IBlockState sand = TFCBlocks.Sand.getStateFromMeta(this.islandMap.getParams().getSurfaceRock().getMeta());
IBlockState freshwater = Blocks.WATER.getDefaultState();//TFCBlocks.FreshWaterStatic.getDefaultState();
IBlockState saltwater = Blocks.WATER.getDefaultState();//TFCBlocks.SaltWaterStatic.getDefaultState();
IBlockState top = grass;
IBlockState fill = dirt;
int closestElev;
for(int x = 0; x < 16; x++)
{
for(int z = 0; z < 16; z++)
{
p = new Point(x, z);
closestCenter = this.getHex(p);
closestElev = convertElevation(closestCenter.getElevation());
if(islandMap.getParams().hasFeature(Feature.Desert) && !closestCenter.hasAttribute(Attribute.River) && closestCenter.getMoistureRaw() < 0.25)
{
top = sand;
fill = sand;
}
else
{
top = grass;
fill = dirt;
}
int hexElev = elevationMap[z << 4 | x];
boolean isCliff = false;
int h0 = x+1 < 16 ? elevationMap[z << 4 | (x+1)] : getHexElevation(getHex(p.plus(1,0)), p.plus(1,0));
int h1 = z-1 > 0 ? elevationMap[(z-1) << 4 | x] : getHexElevation(getHex(p.plus(0,-1)), p.plus(0,-1));
int h2 = x-1 > 0 ? elevationMap[z << 4 | (x-1)] : getHexElevation(getHex(p.plus(-1,0)), p.plus(-1,0));
int h3 = z + 1 < 16 ? elevationMap[(z+1) << 4 | x] : getHexElevation(getHex(p.plus(0,1)), p.plus(0,1));
if(hexElev - h0 > 2 ||
hexElev - h1 > 2 ||
hexElev - h2 > 2 ||
hexElev - h3 > 2)
{
isCliff = true;
}
for(int y = hexElev; y >= 0; y--)
{
IBlockState block = chunkprimer.getBlockState(x, y, z);
IBlockState blockUp = chunkprimer.getBlockState(x, y+1, z);
if(block == Blocks.STONE.getDefaultState() && blockUp == Blocks.AIR.getDefaultState())
{
if(!isCliff || hexElev == closestElev)
{
chunkprimer.setBlockState(x, y, z, top);
if(!isCliff)
{
chunkprimer.setBlockState(x, y-1, z, fill);
chunkprimer.setBlockState(x, y-2, z, fill);
}
}
if((closestCenter.biome == BiomeType.BEACH || closestCenter.biome == BiomeType.OCEAN) && y <= Global.SEALEVEL + 3)
{
chunkprimer.setBlockState(x, y, z, sand);
chunkprimer.setBlockState(x, y-1, z, sand);
chunkprimer.setBlockState(x, y-2, z, sand);
}
}
if(chunkprimer.getBlockState(x, y, z) == Blocks.STONE.getDefaultState())
{
chunkprimer.setBlockState(x, y, z, stone);
}
if(closestCenter.hasAttribute(Attribute.River) && closestCenter.hasAnyMarkersOf(Marker.Pond))
{
RiverAttribute attrib = (RiverAttribute)closestCenter.getAttribute(Attribute.River);
if(attrib.upriver == null || attrib.upriver.size() == 0)
{
boolean border = isLakeBorder(p, closestCenter, 7);
h0 = this.getPondTurbulence(closestCenter, p, 2);
if(!border && y < closestElev && y >= closestElev-1-h0)
{
chunkprimer.setBlockState(x, y, z, freshwater);
chunkprimer.setBlockState(x, y-1, z, dirt);
}
}
}
if(closestCenter.biome == BiomeType.LAKE && closestCenter.hasAttribute(Attribute.Lake))
{
LakeAttribute attrib = (LakeAttribute)closestCenter.getAttribute(Attribute.Lake);
//Not a border area, elev less than the water height, elev greater than the ground height beneath the water
if(!isLakeBorder(p, closestCenter) && y < convertElevation(attrib.getLakeElev()) && y >= closestElev-this.getTurbulence(closestCenter, p, 4)-1)
chunkprimer.setBlockState(x, y, z, freshwater);
if(getBlock(chunkprimer, x, y, z).isFullCube(getBlock(chunkprimer, x, y, z).getDefaultState()) && blockUp == freshwater)
{
chunkprimer.setBlockState(x, y, z, sand);
}
}
else if(closestCenter.biome == BiomeType.MARSH && closestCenter.hasAttribute(Attribute.Lake))
{
LakeAttribute attrib = (LakeAttribute)closestCenter.getAttribute(Attribute.Lake);
if(!isLakeBorder(p, closestCenter) && y < convertElevation(attrib.getLakeElev()) && y >= closestElev-this.getTurbulence(closestCenter, p, 2)-1 && this.rand.nextInt(100) < 70)
chunkprimer.setBlockState(x, y, z, freshwater);
}
if(closestCenter.hasMarker(Marker.Ocean) && block.getBlock().getMaterial(block) == Material.ROCK && blockUp == saltwater)
{
chunkprimer.setBlockState(x, y, z, sand);
}
}
}
}
}
protected boolean isLakeBorder(Point p, Center c, double width)
{
Point pt = p.plus(0, width);
Center c2 = getHex(pt);
if(c2 != c && !c2.hasMarker(Marker.Water))
return true;
pt = p.plus(0, -width);
c2 = getHex(pt);
if(c2 != c && !c2.hasMarker(Marker.Water))
return true;
pt = p.plus(width, 0);
c2 = getHex(pt);
if(c2 != c && !c2.hasMarker(Marker.Water))
return true;
pt = p.plus(-width, width);
c2 = getHex(pt);
if(c2 != c && !c2.hasMarker(Marker.Water))
return true;
pt = p.plus(-width, -width);
c2 = getHex(pt);
if(c2 != c && !c2.hasMarker(Marker.Water))
return true;
pt = p.plus(width, width);
c2 = getHex(pt);
if(c2 != c && !c2.hasMarker(Marker.Water))
return true;
pt = p.plus(width, -width);
c2 = getHex(pt);
if(c2 != c && !c2.hasMarker(Marker.Water))
return true;
return false;
}
protected boolean isLakeBorder(Point p, Center c)
{
return isLakeBorder(p, c, 3);
}
protected int getTurbulence(Center c, Point p, double scale)
{
Point p2 = p.plus(islandChunkX, islandChunkZ);
double turb = Math.max(turbMap.GetValue(p2.x, p2.y), 0);
return (int)(turb * scale);
}
protected int getPondTurbulence(Center c, Point p, double scale)
{
Point p2 = p.plus(islandChunkX, islandChunkZ);
double turb = Math.max(createPondTurbMap(0).GetValue(p2.x, p2.y), 0);
return (int)(turb * scale);
}
protected int getHexElevation(Center c, Point p)
{
return convertElevation(getSmoothHeightHex(c, p));
}
protected int convertElevation(double height)
{
return (int)(Global.SEALEVEL+height * islandMap.getParams().islandMaxHeight);
}
protected void generateTerrain(ChunkPrimer chunkprimer, int chunkX, int chunkZ)
{
Point p;
Center closestCenter;
double[] dts = new double[] {0,0};
double dist = 0;
double loc = 0;
for(int x = 0; x < 16; x++)
{
for(int z = 0; z < 16; z++)
{
p = new Point(x, z);
closestCenter = this.getHex(p);
int hexElev = 0;
if(!closestCenter.hasAttribute(Attribute.River) && !closestCenter.hasMarker(Marker.Coast) && !closestCenter.hasMarker(Marker.CoastWater) && !closestCenter.hasAttribute(Attribute.Lake))
{
//hexElev = convertElevation(getSmoothHeightHex(closestCenter, p));
hexElev = convertElevation(getSmoothHeightHex(closestCenter, p)) + (int)Math.ceil(turbMap.GetValue(worldX+p.x, worldZ+p.y));
}
else
{
hexElev = convertElevation(getSmoothHeightHex(closestCenter, p));
}
elevationMap[z << 4 | x] = hexElev;
for(int y = Math.min(Math.max(hexElev, Global.SEALEVEL), 255); y >= 0; y--)
{
Block b = Blocks.AIR;
if(y < hexElev)
{
b = Blocks.STONE;
}
else if(y < Global.SEALEVEL)
{
b = Blocks.WATER;
}
if(y <= hexElev * 0.2)
b = Blocks.BEDROCK;
chunkprimer.setBlockState(x, y, z, b.getDefaultState());
}
}
}
}
protected void carveRiverSpline(ChunkPrimer chunkprimer)
{
ArrayList riverPoints = new ArrayList<Point>();
/*if(this.islandMap.islandParams.shouldGenVolcano())
riverStates = new IBlockState[] {Blocks.AIR.getDefaultState(), Blocks.flowing_lava.getDefaultState(), Blocks.gravel.getDefaultState()};*/
for(Center c : centersInChunk)
{
RiverAttribute attrib = ((RiverAttribute)c.getAttribute(Attribute.River));
if(attrib != null && attrib.getRiver() > 0)
{
//If the river has multiple Up River locations then we need to handle the splines in two parts.
if(attrib.upriver != null && attrib.upriver.size() > 1)
{
for(Center u : attrib.upriver)
{
RiverAttribute uAttrib = ((RiverAttribute)u.getAttribute(Attribute.River));
riverPoints.clear();
riverPoints.add(u.point.midpoint(c.point));
riverPoints.add(c.point);
carveRiver(chunkprimer, null, c, u, new Spline2D(riverPoints.toArray()), uAttrib);
}
//Now do the downriver side
if(attrib.getDownRiver() != null)
{
riverPoints.clear();
riverPoints.add(c.point);
riverPoints.add(attrib.getDownRiver().point.midpoint(c.point));
carveRiver(chunkprimer, attrib.getDownRiver(), c, null, new Spline2D(riverPoints.toArray()), attrib);
}
}
else if(attrib.upriver != null && attrib.upriver.size() == 1)
{
riverPoints.clear();
riverPoints.add(attrib.upriver.get(0).point.midpoint(c.point));
riverPoints.add(attrib.getRiverMidpoint());
if(attrib.getDownRiver() != null)
riverPoints.add(attrib.getDownRiver().point.midpoint(c.point));
carveRiver(chunkprimer, attrib.getDownRiver(), c, attrib.upriver.get(0), new Spline2D(riverPoints.toArray()), attrib);
}
else if(attrib.getDownRiver() != null)
{
riverPoints.clear();
riverPoints.add(c.point);
riverPoints.add(attrib.getDownRiver().point.midpoint(c.point));
carveRiver(chunkprimer, attrib.getDownRiver(), c, null, new Spline2D(riverPoints.toArray()), attrib);
}
}
}
}
protected void carveRiver(ChunkPrimer chunkprimer, Center dnCenter, Center center, Center upCenter, Spline2D spline, RiverAttribute attrib)
{
ArrayList<BlockPos> points = new ArrayList<BlockPos>();
Point splinePos;
BlockPos pos, pos2, pos3, pos4;
Point iPoint = new Point(islandChunkX, islandChunkZ).toIslandCoord();
Center closest;
IBlockState b;
double wSq = 2;
int min = 0, max = 1, rDepth = Math.min((int)Math.ceil(attrib.getRiver()), 3);
boolean doAir = false;
int waterLevel, hexElev, terrainElev = 0;
if(attrib != null)
{
wSq = Math.max(rDepth * rDepth*2, 1);
for(double i = 0; i < 1; i+= 0.05)
{
//Get the spline point for this iteration
splinePos = spline.getPoint(i);
//Get the closest hex to this spline point
closest = islandMap.getClosestCenter(splinePos);
splinePos = splinePos.minus(iPoint);
//If the spline point is outside chunk boundary than we skip it
if(splinePos.x < -16 || splinePos.y < -16 || splinePos.x >= 32 || splinePos.y >= 32)
continue;
//Setup our base position that we'll be carving around
pos = new BlockPos((int)Math.floor(splinePos.x), 0, (int)Math.floor(splinePos.y));
min = (int)Math.floor(-attrib.getRiver());
max = (int)attrib.getRiver();
//Begin X/Z iteration around the base point
for(double x = min; x <= max; x++)
{
for(double z = min; z <= max; z++)
{
//Add x and z to our base point to get the local position
pos2 = pos.add(x, 0, z);
//If this local position is outside of the chunk, end here so we don't crash
if(pos2.getX() < 0 || pos2.getY() < 0 || pos2.getZ() < 0 || pos2.getX() >= 16 || pos2.getY() >= 256 || pos2.getZ() >= 16)
{
continue;
}
//Get the water level for this location. This is the local terrain elevation - 1
hexElev = convertElevation(center.getElevation());
terrainElev = this.elevationMap[(pos2.getZ() << 4) | pos2.getX()];
waterLevel = Math.max(terrainElev-1, Global.SEALEVEL);
pos2 = pos2.add(0, waterLevel, 0);
pos4 = pos.add(0, waterLevel, 0);
int rd = -rDepth;
if(terrainElev != hexElev)
{
rd = (int)(-rDepth/1.5f);
}
for(int depth = rDepth; depth >= rd; depth--)
{
pos3 = pos2.add(0, depth, 0);
doAir = false;
IBlockState s = getState(chunkprimer, pos3);
if(depth >= 0 && pos3.distanceSq(pos4.getX(), pos4.getY(), pos4.getZ()) < wSq)
{
if(!Core.isWater(s))
{
setState(chunkprimer, pos3, Blocks.AIR.getDefaultState());
}
//continue;
}
else if(depth < 0 && pos3.distanceSq(pos4.getX(), pos4.getY(), pos4.getZ()) <= wSq)
{
IBlockState fillState = Blocks.WATER.getDefaultState();
//If we're moving up or down a slope then don't place water
if(terrainElev != hexElev /* pos3.getY() >= waterLevel*/)
{
fillState = Blocks.AIR.getDefaultState();
}
if(!Core.isWater(s))
{
setState(chunkprimer, pos3, fillState);
convertRiverBank(chunkprimer, pos3.down());
}
if(pos3.getY() == waterLevel || pos3.getY() == waterLevel-1)
{
doAir = true;
convertRiverBank(chunkprimer, pos3.north(), doAir);
convertRiverBank(chunkprimer, pos3.south(), doAir);
convertRiverBank(chunkprimer, pos3.east(), doAir);
convertRiverBank(chunkprimer, pos3.west(), doAir);
}
}
}
}
}
}
}
}
protected void convertRiverBank(ChunkPrimer chunkprimer, BlockPos pos)
{
convertRiverBank(chunkprimer,pos, true);
}
protected void convertRiverBank(ChunkPrimer chunkprimer, BlockPos pos, boolean doAir)
{
if(pos.getX() >= 0 && pos.getY() >= 0 && pos.getZ() >= 0 && pos.getX() < 16 && pos.getY() < 256 && pos.getZ() < 16)
{
IBlockState b = getState(chunkprimer, pos);
if(Core.isTerrain(b))
{
Center closest = islandMap.getClosestCenter(pos.add(this.islandChunkX, 0, this.islandChunkZ));
int elev = this.convertElevation(closest.getElevation());
if(elevationMap[pos.getZ() << 4 | pos.getX()] != elev)
{
setState(chunkprimer, pos, TFCBlocks.Stone.getDefaultState().withProperty(BlockGravel.META_PROPERTY, islandMap.getParams().getSurfaceRock()));
if(!Core.isStone(this.getState(chunkprimer, pos.down())))
{
setState(chunkprimer, pos.down(), TFCBlocks.Stone.getDefaultState().withProperty(BlockGravel.META_PROPERTY, islandMap.getParams().getSurfaceRock()));
if(!Core.isStone(this.getState(chunkprimer, pos.down())))
{
setState(chunkprimer, pos.down(), TFCBlocks.Stone.getDefaultState().withProperty(BlockGravel.META_PROPERTY, islandMap.getParams().getSurfaceRock()));
}
}
}
else
{
setState(chunkprimer, pos, TFCBlocks.Gravel.getDefaultState().withProperty(BlockGravel.META_PROPERTY, islandMap.getParams().getSurfaceRock()));
}
if(Core.isTerrain(getState(chunkprimer, pos.up(1))))
{
if(doAir && Core.isGravel(getState(chunkprimer, pos.up(1))))
{
convertRiverBank(chunkprimer, pos.up(1).north(), true);
convertRiverBank(chunkprimer, pos.up(1).south(), true);
convertRiverBank(chunkprimer, pos.up(1).east(), true);
convertRiverBank(chunkprimer, pos.up(1).west(), true);
}
setState(chunkprimer, pos.up(1), Blocks.AIR.getDefaultState());
}
}
}
}
protected double getSmoothHeightHex(Center c, Point p, int range)
{
double h = c.elevation;
boolean isLakeBorder = false;
boolean isLake = c.hasMarker(Marker.Water) && !c.hasMarker(Marker.Ocean);
if(isLake)
isLakeBorder = isLakeBorder(p, c);
if(!(isLake && !isLakeBorder) && (getHex(hexSamplePoints[range][0].plus(p)) != c || getHex(hexSamplePoints[range][1].plus(p)) != c ||
getHex(hexSamplePoints[range][2].plus(p)) != c || getHex(hexSamplePoints[range][3].plus(p)) != c ||
getHex(hexSamplePoints[range][4].plus(p)) != c || getHex(hexSamplePoints[range][5].plus(p)) != c))
{
for(int i = 0; i < 6; i++)
{
h += getHex(hexSamplePoints[range][i].plus(p)).elevation;
}
h /= 7;
}
double outH = c.elevation - (c.elevation - h);
//If this hex is a water hex and the smoothed elevation is lower than the hex elevation than we do not want to lower this cell
if(c.hasMarker(Marker.Water) && isLakeBorder && outH < c.elevation)
return c.elevation;
return outH;
}
protected double getSmoothHeightHex(Center c, Point p)
{
if(this.islandMap.getParams().hasFeature(Feature.Cliffs))
return getSmoothHeightHex(c, p, 2);
if(islandMap.getParams().hasFeature(Feature.Canyons) || islandMap.getParams().hasFeature(Feature.Gorges))
{
//If this block is in a gorge hex
if(c.hasAttribute(Attribute.Gorge))
{
return getSmoothHeightHex(c, p, 2);
}
//If this block is in a canyon hex
if(c.hasAttribute(Attribute.Canyon))
{
CanyonAttribute a = (CanyonAttribute) c.getAttribute(Attribute.Canyon);
if(a.nodeNum < 10)
return getSmoothHeightHex(c, p, 5);
return getSmoothHeightHex(c, p, 2);
}
//If we're in any other hex then we need to see if we are smoothing into a gorge or canyon hex. If so,
//we want to limit the smoothing, otherwise we smooth this block like normal.
Vector<Center> nearList = getCentersNear(p, 5);
for(Center n : nearList)
{
if(n.hasAttribute(Attribute.Canyon) || n.hasAttribute(Attribute.Gorge))
return getSmoothHeightHex(c, p, 2);
}
}
if(c.hasAnyMarkersOf(Marker.Pond))
{
getSmoothHeightHex(c, p, 3);
}
return getSmoothHeightHex(c, p, 5);
}
private Vector<Center> getCentersNear(Point p, int range)
{
Vector<Center> outList = new Vector<Center>();
for(int i = 0; i < 6; i++)
{
Center c = getHex(hexSamplePoints[range][i].plus(p));
if(!outList.contains(c))
outList.add(c);
}
return outList;
}
private Block getBlock(ChunkPrimer chunkprimer, int x, int y, int z)
{
return chunkprimer.getBlockState(x, y, z).getBlock();
}
protected void carveCaves(ChunkPrimer chunkprimer)
{
ArrayList<BlockPos> points = new ArrayList<BlockPos>();
BlockPos pos, pos2;
Spline3D spline;
Point iPoint = new Point(islandChunkX, islandChunkZ).toIslandCoord();
Vec3i islandOffset = new Vec3i(iPoint.x, 0, iPoint.y);
double wSq = 4;
for(Center c : centersInChunk)
{
CaveAttribute attrib = ((CaveAttribute)c.getAttribute(Attribute.Cave));
if(attrib != null)
{
for(CaveAttrNode n : attrib.nodes)
{
wSq = n.getNodeWidth() * n.getNodeWidth();
points.clear();
if(n.getPrev() != null)
points.add(n.getPrevOffset());
points.add(n.getOffset());
if(n.getNext() != null)
points.add(n.getNextOffset());
spline = new Spline3D(points);
//Iterate down the spline and carve the cave
for(double i = 0; i < 1; i+= 0.05)
{
pos = spline.getPoint(i);
ArrayList<BlockPos> list = new ArrayList<BlockPos>();
for(int y = -n.getNodeHeight(); y < n.getNodeHeight(); y++)
{
for(int x = -n.getNodeWidth(); x < n.getNodeWidth(); x++)
{
for(int z = -n.getNodeWidth(); z < n.getNodeWidth(); z++)
{
if(pos.add(x, y, z).distanceSq(pos.getX(), pos.getY(), pos.getZ()) <= wSq)
list.add(pos.add(x, y, z));
}
}
}
IBlockState down, up, fillBlock, state;
Iterator it = list.iterator();
while(it.hasNext())
{
fillBlock = Blocks.AIR.getDefaultState();
pos2 = (BlockPos) it.next();
if(pos.distanceSqToCenter(pos2.getX(), pos2.getY(), pos2.getZ()) <= wSq)
{
BlockPos pos3 = pos2.subtract(islandOffset);
if(pos3.getX() >= 0 && pos3.getY() >= 0 && pos3.getZ() >= 0 && pos3.getX() < 16 && pos3.getY() < 256 && pos3.getZ() < 16)
{
state = getState(chunkprimer, pos3);
Block b = state.getBlock();
if(b != Blocks.BEDROCK && b.getMaterial(state) != Material.WATER)
{
down = getState(chunkprimer, pos3.down());
up = getState(chunkprimer, pos3.up());
if(Core.isDirt(down))
{
setState(chunkprimer, pos3.down(), TFCBlocks.Grass.getDefaultState().withProperty(BlockGrass.META_PROPERTY, down.getValue(BlockDirt.META_PROPERTY)));
}
if(down.getBlock().getMaterial(down) == Material.WATER)
continue;
if(up.getBlock().getMaterial(up) == Material.WATER)
continue;
if(n.isSeaCave() && pos3.getY() < Global.SEALEVEL)
fillBlock = Blocks.WATER.getDefaultState();
else if(c.hasAttribute(Attribute.River))
{
if(up.getBlock() != TFCBlocks.Gravel && b != TFCBlocks.Gravel)
fillBlock = state;
}
if(TFCOptions.shouldStripChunks)
fillBlock = Blocks.WOOL.getDefaultState();
setState(chunkprimer, pos3, fillBlock);
if(Core.isSoil(up) && !Core.isGrass(up))
{
setState(chunkprimer, pos3.up(), TFCBlocks.Stone.getDefaultState().withProperty(BlockStone.META_PROPERTY, islandMap.getParams().getSurfaceRock()));
}
}
}
}
}
}
//After the main iteration, we'll go down the spline one more and add natural columns for cave support
/*if(n.getOffset().getY() < 64+islandMap.convertHeightToMC(c.getElevation()))
{
float width = n.getNodeWidth()/3;
pos = spline.getPoint(0.4+this.rand.nextDouble()*0.2);
pos = pos.add(-2+rand.nextInt(2), -n.getNodeHeight(), -2+rand.nextInt(2)).subtract(islandOffset);
IBlockState fillBlock = TFCBlocks.Stone.getDefaultState().withProperty(BlockStone.META_PROPERTY, islandMap.getParams().getSurfaceRock());
for(double i = 0; i < 1; i+= 0.1)
{
for(float x = -width; x <= width; x++)
{
for(float z = -width; z <= width; z++)
{
pos2 = pos.add(x, i * (n.getNodeHeight() * 2), z);
if(Helper.dist2dSq(pos, pos2) < width*width)
setState(chunkprimer, pos2, fillBlock);
}
}
}
}*/
}
}
}
}
protected void placeOreSeams(ChunkPrimer chunkprimer)
{
ArrayList<BlockPos> points = new ArrayList<BlockPos>();
BlockPos pos, pos2;
Spline3D spline;
Point iPoint = new Point(islandChunkX, islandChunkZ).toIslandCoord();
Vec3i islandOffset = new Vec3i(iPoint.x, 0, iPoint.y);
double wSq = 4;
for(Center c : centersInChunk)
{
OreAttribute attrib = ((OreAttribute)c.getAttribute(Attribute.Ore));
if(attrib != null)
{
for(OreAttrNode n : attrib.nodes)
{
OreConfig oc = OreRegistry.getInstance().getConfig(n.getOreType(), islandMap.getParams().getSurfaceRock());
if(oc.getVeinType() != VeinType.Seam)
continue;
wSq = n.getNodeWidth() * n.getNodeWidth();
points.clear();
if(n.getPrev() != null)
points.add(n.getPrevOffset().subtract(islandOffset));
points.add(n.getOffset().subtract(islandOffset));
if(n.getNext() != null)
points.add(n.getNextOffset().subtract(islandOffset));
spline = new Spline3D(points);
for(double i = 0; i < 1; i+= 0.03)
{
pos = spline.getPoint(i);
ArrayList<BlockPos> list = new ArrayList<BlockPos>();
for(int y = -n.getNodeHeight(); y < n.getNodeHeight(); y++)
{
for(int x = -n.getNodeWidth(); x < n.getNodeWidth(); x++)
{
for(int z = -n.getNodeWidth(); z < n.getNodeWidth(); z++)
{
if(this.rand.nextDouble() < 0.75)
list.add(pos.add(x, y, z));
}
}
}
IBlockState down, up, fillBlock, state;
Iterator it = list.iterator();
while(it.hasNext())
{
fillBlock = oc.getOreBlockState();
pos2 = (BlockPos) it.next();
{
if(pos2.getX() >= 0 && pos2.getY() >= 0 && pos2.getZ() >= 0 && pos2.getX() < 16 && pos2.getY() < 256 && pos2.getZ() < 16)
{
state = getState(chunkprimer, pos2);
if(Core.isStone(state))
{
down = getState(chunkprimer, pos2.down());
up = getState(chunkprimer, pos2.up());
setState(chunkprimer, pos2, fillBlock);
}
}
}
}
}
}
}
}
}
protected void placeOreLayers(ChunkPrimer chunkprimer)
{
Point p;
BlockPos pos = new BlockPos(0,0,0);
BlockPos pos2;
double wSq = 4;
IBlockState state;
for(Center c : centersInChunk)
{
OreAttribute attrib = ((OreAttribute)c.getAttribute(Attribute.Ore));
if(attrib != null)
{
for(OreAttrNode n : attrib.nodes)
{
OreConfig oc = OreRegistry.getInstance().getConfig(n.getOreType(), islandMap.getParams().getSurfaceRock());
if(oc.getVeinType() != VeinType.Layer)
continue;
for(int x = 0; x < 16; x++)
{
for(int z = 0; z < 16; z++)
{
for(int y = n.getOffset().getY(); y < n.getOffset().getY() + n.getNodeHeight(); y++)
{
p = new Point(x, z);
pos2 = pos.add(x, y, z);
state = getState(chunkprimer, pos2);
if(this.getHex(p) == c && Core.isStone(state))
{
//Add air check
this.setState(chunkprimer, pos2, oc.getOreBlockState());
}
}
}
}
}
}
}
}
public IBlockState getState(ChunkPrimer primer, BlockPos pos)
{
if(pos.getX() >= 0 && pos.getY() >= 0 && pos.getZ() >= 0 && pos.getX() < 16 && pos.getY() < 256 && pos.getZ() < 16)
return primer.getBlockState(pos.getX(), pos.getY(), pos.getZ());
return Blocks.AIR.getDefaultState();
}
public void setState(ChunkPrimer primer, BlockPos pos, IBlockState state)
{
if(pos.getX() >= 0 && pos.getY() >= 0 && pos.getZ() >= 0 && pos.getX() < 16 && pos.getY() < 256 && pos.getZ() < 16)
primer.setBlockState(pos.getX(), pos.getY(), pos.getZ(), state);
}
public void createSpires(ChunkPrimer primer)
{
//I want to expand on these in the future but for now the concept is sound.
double radiusSq = 10 * 10;
IBlockState stone = TFCBlocks.Stone.getStateFromMeta(this.islandMap.getParams().getSurfaceRock().getMeta());
for(Center c : this.centersInChunk)
{
if(c.hasAnyMarkersOf(Marker.Spire))
{
Perlin perlin = new Perlin();
perlin.setSeed((long)c.index+Helper.combineCoords(mapX, mapZ));
perlin.setFrequency(4);
perlin.setLacunarity(4);
perlin.setOctaveCount(2);
perlin.setPersistence(0.2);
Clamp clamp = new Clamp(perlin, 0, 1);
Curve cu = new Curve();
cu.setSourceModule(0, clamp);
cu.AddControlPoint(0.0, 0.2);
cu.AddControlPoint(0.5, 0.5);
cu.AddControlPoint(1, 0.9);
Segment s = new Segment(cu);
s.setAttenuate(true);
Point p0 = c.point.minus(new Point(islandChunkX, islandChunkZ).toIslandCoord());
Random rand = new Random(c.index);
int elev = 35 + rand.nextInt(20);
for(int x = -10; x <= 10; x++)
{
for(int z = -10; z <= 10; z++)
{
Point p1 = p0.plus(x, z);
//Validation
if(p1.x < 0 || p1.x > 15 || p1.y < 0 || p1.y > 15)
continue;
//Get the inCircle radius
double dist = p1.distanceSq(p0);
//Perform a quick test to see if the point is within the inCircle.
if(dist == radiusSq && rand.nextBoolean())
continue;
if(dist <= radiusSq)
{
int el = elevationMap[(int)p1.y << 4 | (int)p1.x];
for(int y = 0; y < elev; y++)
{
double width = s.getValue((double)y/(double)elev) * 7D;
if(y < 9)
{
width = (4-Math.max(y-5, 0));
}
width *= width;
if(p0.distanceSq(p1) < 1+width)
this.setState(primer, new BlockPos((int)p1.x, y+el-5, (int)p1.y), stone);
}
elevationMap[(int)p1.y << 4 | (int)p1.x] += elev;
}
}
}
}
}
}
public void createDungeons(ChunkPrimer primer)
{
Point p = new Point(islandChunkX, islandChunkZ).toIslandCoord();
int iChunkX = ((int)p.x >> 4);
int iChunkZ = ((int)p.y >> 4);
//world chunk coord for the top left of this island
int islandStartChunkX = (islandMap.getParams().getXCoord() << 8);
int islandStartChunkZ = (islandMap.getParams().getZCoord() << 8);
for(Dungeon d : islandMap.dungeons)
{
int cX = iChunkX;
int cZ = iChunkZ;
DungeonChunk dc = d.getChunk(cX, cZ);
if(dc != null)
{
Iterator<DungeonRoom> iter = dc.getRoomMap().values().iterator();
while(iter.hasNext())
{
DungeonRoom dr = iter.next();
if(dr != null)
{
genRoom(primer, d, dr);
}
}
}
}
}
protected void genRoom(ChunkPrimer primer, Dungeon dungeon, DungeonRoom room)
{
RoomSchematic schem = room.getSchematic();
for(SchemBlock b : schem.getProcessedBlockList(dungeon))
{
DungeonDirection borderFacing = isOnBorder(b);
if(borderFacing != null && b.state.getBlock() == Blocks.OAK_DOOR)
{
if(!room.hasConnection(borderFacing))
{
primer.setBlockState(8+b.pos.getX(), room.getPosition().getY() + b.pos.getY(), 8+b.pos.getZ(), dungeon.blockMap.get("dungeon_wall"));
continue;
}
else if(room.hasConnection(borderFacing) && !room.getConnection(borderFacing).placeDoor)
{
primer.setBlockState(8+b.pos.getX(), room.getPosition().getY() + b.pos.getY(), 8+b.pos.getZ(), Blocks.AIR.getDefaultState());
continue;
}
}
else if(borderFacing != null && b.state.getBlock() == Blocks.AIR)
{
if(!room.hasConnection(borderFacing) && b.pos.getY() < 10)//the <10 check here makes sure that the surface sections of entrances
{
primer.setBlockState(8+b.pos.getX(), room.getPosition().getY() + b.pos.getY(), 8+b.pos.getZ(), dungeon.blockMap.get("dungeon_wall"));
continue;
}
}
primer.setBlockState(8+b.pos.getX(), room.getPosition().getY() + b.pos.getY(), 8+b.pos.getZ(), b.state);
}
}
private DungeonDirection isOnBorder(SchemBlock b)
{
if(b.pos.getX() == -8)
return DungeonDirection.WEST;
else if(b.pos.getX() == 7)
return DungeonDirection.EAST;
else if(b.pos.getZ() == -8)
return DungeonDirection.NORTH;
else if(b.pos.getZ() == 7)
return DungeonDirection.SOUTH;
return null;
}
}