package ttftcuts.physis.common.worldgen.structure;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import ttftcuts.physis.common.file.IDataCallback;
import ttftcuts.physis.common.file.PhysisWorldSavedData;
import ttftcuts.physis.common.helper.WorldGenHelper;
import ttftcuts.physis.common.worldgen.structure.layout.LayoutNode;
import ttftcuts.physis.common.worldgen.structure.layout.StructureLayout;
import ttftcuts.physis.common.worldgen.structure.prop.Prop;
import net.minecraft.block.Block;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.World;
import net.minecraft.world.chunk.IChunkProvider;
import net.minecraft.world.chunk.storage.AnvilChunkLoader;
import net.minecraft.world.gen.ChunkProviderServer;
import net.minecraft.world.gen.structure.StructureBoundingBox;
import net.minecraftforge.common.DimensionManager;
public class StructureGenerator {
public static final String GENERATORTAG = "StrucutreGen";
public static Map<String, StructureGenerator> generators = new HashMap<String, StructureGenerator>();
public String savename;
protected Map<World, Map<Long, StructureData>> structureMap;
protected int radius;
public Set<Integer> allowedWorlds;
protected Random rand;
public static IDataCallback dataCallback = new IDataCallback() {
@Override
public void dataPacketSending() {}
@Override
public void dataPacketReceived() {}
@Override
public void dataSaving() {
saveGeneratorsToWorldData();
}
@Override
public void dataLoaded() {
loadFromWorldData();
}
};
public StructureGenerator(String id, int radius, int... dimensionids) {
this.savename = id;
this.radius = radius;
this.allowedWorlds = new HashSet<Integer>();
this.structureMap = new HashMap<World, Map<Long, StructureData>>();
this.rand = new Random();
for(int i : dimensionids) {
allowedWorlds.add(i);
}
generators.put(savename, this);
}
public boolean allowedInWorld(World world) {
return this.allowedWorlds.contains(world.provider.dimensionId);
}
public void plan(IChunkProvider chunkProvider, World world, int chunkX, int chunkZ, Block[] blocks) {
if (!this.allowedInWorld(world)) {
//Physis.logger.info("NOT VALID DIM");
return;
}
if (!(world.getChunkProvider() instanceof ChunkProviderServer)) {
//Physis.logger.info("NOT CPS");
return;
}
ChunkProviderServer cps = (ChunkProviderServer)world.getChunkProvider();
if (!(cps.currentChunkLoader instanceof AnvilChunkLoader)) {
//Physis.logger.info("NOT ACL");
return;
}
AnvilChunkLoader acl = (AnvilChunkLoader)cps.currentChunkLoader;
// check around this chunk
for (int rx=-this.radius; rx<=this.radius; rx++) {
scan:
for (int rz=-this.radius; rz<=this.radius; rz++) {
long coordhash = StructureData.idFromCoords(chunkX+rx, chunkZ+rz);
if (!this.structureMap.containsKey(world)) {
this.structureMap.put(world, new HashMap<Long, StructureData>());
}
if (this.structureMap.get(world).containsKey(coordhash)) {
//Physis.logger.info("Duplicate");
continue;
}
if (this.canGenerateInChunk(world, chunkX + rx, chunkZ + rz)) {
// ok, we can generate in the found chunk - find out if only the source chunk was generated
for (int sx=-this.radius; sx<=this.radius; sx++) {
for (int sz=-this.radius; sz<=this.radius; sz++) {
int checkX = chunkX + rx + sx;
int checkZ = chunkZ + rz + sz;
if (checkX == chunkX || checkZ == chunkZ) { continue; }
if (acl.chunkExists(world, checkX, checkZ)) {
//Physis.logger.info("Rejected structure at "+(chunkX + rx)+","+(chunkZ + rz)+" due to generated chunk at "+checkX+","+checkZ);
continue scan;
}
}
}
StructureData sdata = this.createStructureInChunk(world, chunkX + rx, chunkZ + rz);
if (!this.structureMap.containsKey(world)) {
this.structureMap.put(world, new HashMap<Long, StructureData>());
}
this.structureMap.get(world).put(sdata.id, sdata);
//Physis.logger.info("Generating a new structure at chunk "+sdata.x+","+sdata.z);
//saveGeneratorsToWorldData();
}
}
}
}
public void generateStructuresInChunk(World world, Random rand, int chunkX, int chunkZ) {
if (!this.allowedWorlds.contains(world.provider.dimensionId)) { return; }
if (!this.structureMap.containsKey(world)) { return; }
Map<Long, StructureData> map = this.structureMap.get(world);
//Physis.logger.info("gen coords: "+chunkX+","+chunkZ);
int minx = (chunkX << 4) + 8;
int maxx = minx + 15;
int minz = (chunkZ << 4) + 8;
int maxz = minz + 15;
StructureBoundingBox chunkBounds = new StructureBoundingBox(minx,minz,maxx,maxz);
for (int rx=-this.radius; rx<=this.radius; rx++) {
for (int rz=-this.radius; rz<=this.radius; rz++) {
long coordhash = StructureData.idFromCoords(chunkX+rx, chunkZ+rz);
if (map.containsKey(coordhash)) {
StructureData structure = map.get(coordhash);
if(structure.bounds.intersectsWith(minx, minz, maxx, maxz)) {
//Physis.logger.info("Structure bounds: "+structure.bounds);
for (StructurePiece piece : structure.pieces) {
//Physis.logger.info("Piece bounds: "+piece.bounds);
if (piece.bounds.intersectsWith(minx, minz, maxx, maxz)) {
piece.addComponentParts(world, rand, chunkBounds);
}
}
}
}
}
}
}
public List<StructureData> getStructuresAtPoint(World world, int x, int y, int z) {
if (!this.allowedWorlds.contains(world.provider.dimensionId)) { return null; }
if (!this.structureMap.containsKey(world)) { return null; }
Map<Long, StructureData> map = this.structureMap.get(world);
List<StructureData> out = new ArrayList<StructureData>();
int chunkX = x >> 4;
int chunkZ = z >> 4;
for (int rx=-this.radius; rx<=this.radius; rx++) {
for (int rz=-this.radius; rz<=this.radius; rz++) {
long coordhash = StructureData.idFromCoords(chunkX+rx, chunkZ+rz);
if (map.containsKey(coordhash)) {
StructureData structure = map.get(coordhash);
if(structure.bounds.isVecInside(x, y, z)) {
for (StructurePiece piece : structure.pieces) {
if (piece.bounds.isVecInside(x, y, z)) {
out.add(structure);
break;
}
}
}
}
}
}
return out;
}
public List<StructureData> getStructuresInChunk(World world, int chunkX, int chunkZ) {
if (!this.allowedWorlds.contains(world.provider.dimensionId)) { return null; }
if (!this.structureMap.containsKey(world)) { return null; }
Map<Long, StructureData> map = this.structureMap.get(world);
List<StructureData> out = new ArrayList<StructureData>();
int x = (chunkX << 4) + 8;
int z = (chunkZ << 4) + 8;
for (int rx=-this.radius; rx<=this.radius; rx++) {
for (int rz=-this.radius; rz<=this.radius; rz++) {
long coordhash = StructureData.idFromCoords(chunkX+rx, chunkZ+rz);
if (map.containsKey(coordhash)) {
StructureData structure = map.get(coordhash);
if(structure.bounds.intersectsWith(x, z, x+15, z+15)) {
for (StructurePiece piece : structure.pieces) {
if (piece.bounds.intersectsWith(x, z, x+15, z+15)) {
out.add(structure);
break;
}
}
}
}
}
}
return out;
}
public boolean areStructuresInChunk(World world, int chunkX, int chunkZ) {
if (!this.allowedWorlds.contains(world.provider.dimensionId)) { return false; }
if (!this.structureMap.containsKey(world)) { return false; }
Map<Long, StructureData> map = this.structureMap.get(world);
int x = (chunkX << 4) + 8;
int z = (chunkZ << 4) + 8;
for (int rx=-this.radius; rx<=this.radius; rx++) {
for (int rz=-this.radius; rz<=this.radius; rz++) {
long coordhash = StructureData.idFromCoords(chunkX+rx, chunkZ+rz);
if (map.containsKey(coordhash)) {
StructureData structure = map.get(coordhash);
if(structure.bounds.intersectsWith(x, z, x+15, z+15)) {
for (StructurePiece piece : structure.pieces) {
if (piece.bounds.intersectsWith(x, z, x+15, z+15)) {
return true;
}
}
}
}
}
}
return false;
}
public boolean canGenerateInChunk(World world, int chunkX, int chunkZ) {
int k = chunkX >> 4;
int l = chunkZ >> 4;
this.rand.setSeed((long)(k ^ l << 4) ^ world.getSeed());
this.rand.nextInt();
return (Math.abs(chunkX) % 20 == 0) && (Math.abs(chunkZ) % 20 == 0);
}
public StructureData createStructureInChunk(World world, int chunkX, int chunkZ) {
StructureData structure = new StructureData(world, chunkX, chunkZ);
StructureLayout layout = new StructureLayout((chunkX << 4) + 2, 90, (chunkZ << 4) + 2, this.rand);
List<StructurePiece> parts = layout.exportToStructurePieces(rand);
structure.pieces = parts;
structure.updateBounds();
return structure;
}
protected NBTTagCompound writeToNBT() {
NBTTagCompound tag = new NBTTagCompound();
NBTTagList list = new NBTTagList();
for(Entry<World,Map<Long, StructureData>> e : this.structureMap.entrySet()) {
World w = e.getKey();
Map<Long, StructureData> map = e.getValue();
NBTTagCompound worldtag = new NBTTagCompound();
NBTTagList worldlist = new NBTTagList();
for(StructureData sdata : map.values()) {
NBTTagCompound structuretag = sdata.writeToNBT();
NBTTagCompound savetag = new NBTTagCompound();
savetag.setLong("i", sdata.id);
savetag.setTag("d", structuretag);
worldlist.appendTag(savetag);
}
worldtag.setTag("l", worldlist);
worldtag.setInteger("w", w.provider.dimensionId);
list.appendTag(worldtag);
}
tag.setTag("l", list);
return tag;
}
protected void loadFromNBT(NBTTagCompound tag) {
NBTTagList list = tag.getTagList("l", 10);
for (int i=0; i<list.tagCount(); i++) {
NBTTagCompound worldtag = list.getCompoundTagAt(i);
int worldid = worldtag.getInteger("w");
NBTTagList worldlist = worldtag.getTagList("l", 10);
World world = DimensionManager.getWorld(worldid);
if (world == null) { continue; }
if (!this.structureMap.containsKey(world)) {
this.structureMap.put(world, new HashMap<Long, StructureData>());
}
for (int j=0; j<worldlist.tagCount(); j++) {
NBTTagCompound savetag = worldlist.getCompoundTagAt(j);
NBTTagCompound structuretag = savetag.getCompoundTag("d");
//long id = savetag.getLong("i");
StructureData data = StructureData.createFromNBT(structuretag);
if (data != null) {
this.structureMap.get(world).put(data.id, data);
}
}
}
//Physis.logger.info("Structuremap: "+this.structureMap.toString());
}
public static void loadFromWorldData() {
//Physis.logger.info("Loading Structure data from world data");
NBTTagCompound gens = PhysisWorldSavedData.getServerTag(GENERATORTAG);
for(Entry<String, StructureGenerator> e : generators.entrySet()) {
StructureGenerator gen = e.getValue();
gen.structureMap.clear();
if (gens.hasKey(gen.savename)) {
gen.loadFromNBT(gens.getCompoundTag(gen.savename));
}
}
}
public static void saveGeneratorsToWorldData() {
NBTTagCompound tag = new NBTTagCompound();
for(Entry<String, StructureGenerator> e : generators.entrySet()) {
StructureGenerator gen = e.getValue();
NBTTagCompound savetag = gen.writeToNBT();
if (savetag != null) {
tag.setTag(gen.savename, savetag);
}
}
PhysisWorldSavedData.setServerTag(GENERATORTAG, tag);
//Physis.logger.info("Saving Structure data");
}
public static void cleanUp() {
saveGeneratorsToWorldData();
for (StructureGenerator generator : generators.values()) {
generator.structureMap.clear();
}
}
public static class StructureData {
public Long id;
public int x;
public int z;
public World world;
public StructureBoundingBox bounds;
public List<StructurePiece> pieces;
public boolean warded = true;
public StructureData(World world, int x, int z) {
this.x = x;
this.z = z;
this.world = world;
this.id = idFromCoords(x, z);
this.bounds = new StructureBoundingBox(0,0,0,0,0,0);
this.pieces = new ArrayList<StructurePiece>();
}
public static long idFromCoords(int chunkX, int chunkZ) {
return Long.valueOf(ChunkCoordIntPair.chunkXZ2Int(chunkX, chunkZ));
}
public static StructureData createFromNBT(NBTTagCompound tag) {
//Physis.logger.info("StructureData: "+tag.toString());
int x = tag.getInteger("x");
int z = tag.getInteger("z");
int worldid = tag.getInteger("w");
World w = DimensionManager.getWorld(worldid);
boolean warded = tag.getBoolean("wa");
if (w == null) { return null; }
StructureData sdata = new StructureData(w, x,z);
sdata.warded = warded;
NBTTagList list = tag.getTagList("p", 10);
for (int i=0; i<list.tagCount(); i++) {
StructurePiece piece = StructurePiece.createFromNBT(list.getCompoundTagAt(i));
if (piece != null) {
sdata.pieces.add(piece);
}
}
if (sdata.pieces.size() > 0) {
sdata.bounds = WorldGenHelper.cloneBounds(sdata.pieces.get(0).bounds);
}
sdata.updateBounds();
return sdata;
}
public NBTTagCompound writeToNBT() {
NBTTagCompound tag = new NBTTagCompound();
tag.setInteger("x", this.x);
tag.setInteger("z", this.z);
tag.setInteger("w", this.world.provider.dimensionId);
tag.setBoolean("wa", this.warded);
NBTTagList list = new NBTTagList();
for (StructurePiece piece : this.pieces) {
list.appendTag(piece.writeToNBT());
}
tag.setTag("p", list);
return tag;
}
public void updateBounds() {
for (StructurePiece piece : this.pieces) {
this.bounds.expandTo(piece.bounds);
}
}
}
public static class StructurePiece {
public LayoutNode blueprintNode;
public StructureBoundingBox bounds;
public int layoutOffsetX;
public int layoutOffsetY;
public int layoutOffsetZ;
public StructurePiece(LayoutNode blueprint) {
this.blueprintNode = blueprint;
this.bounds = WorldGenHelper.cloneBounds(blueprint.bounds);
this.updateBoundingForProps();
}
public NBTTagCompound writeToNBT() {
NBTTagCompound tag = new NBTTagCompound();
tag.setInteger("xmin", this.bounds.minX);
tag.setInteger("xmax", this.bounds.maxX);
tag.setInteger("ymin", this.bounds.minY);
tag.setInteger("ymax", this.bounds.maxY);
tag.setInteger("zmin", this.bounds.minZ);
tag.setInteger("zmax", this.bounds.maxZ);
if (this.blueprintNode != null) {
tag.setTag("node", LayoutNode.writeToNBT(this.blueprintNode));
}
return tag;
}
public static StructurePiece createFromNBT(NBTTagCompound tag) {
int xmin = tag.getInteger("xmin");
int xmax = tag.getInteger("xmax");
int ymin = tag.getInteger("ymin");
int ymax = tag.getInteger("ymax");
int zmin = tag.getInteger("zmin");
int zmax = tag.getInteger("zmax");
LayoutNode node = LayoutNode.createFromNBT(tag.getCompoundTag("node"));
StructurePiece piece = new StructurePiece(node);
piece.bounds = new StructureBoundingBox(xmin,ymin,zmin,xmax,ymax,zmax);
return piece;
}
protected void updateBoundingForProps() {
if (this.blueprintNode.props.size() == 0) {return;}
int minX = Integer.MAX_VALUE;
int minY = Integer.MAX_VALUE;
int minZ = Integer.MAX_VALUE;
int maxX = Integer.MIN_VALUE;
int maxY = Integer.MIN_VALUE;
int maxZ = Integer.MIN_VALUE;
for (Prop prop : this.blueprintNode.props) {
minX = Math.min(minX, prop.bounds.minX);
minY = Math.min(minY, prop.bounds.minY);
minZ = Math.min(minZ, prop.bounds.minZ);
maxX = Math.max(maxX, prop.bounds.maxX);
maxY = Math.max(maxY, prop.bounds.maxY);
maxZ = Math.max(maxZ, prop.bounds.maxZ);
}
int bminX = this.bounds.minX;
int bminY = this.bounds.minY;
int bminZ = this.bounds.minZ;
this.bounds.minX = Math.min(this.bounds.minX, this.bounds.minX + minX);
this.bounds.minY = Math.min(this.bounds.minY, this.bounds.minY + minY);
this.bounds.minZ = Math.min(this.bounds.minZ, this.bounds.minZ + minZ);
this.bounds.maxX = Math.max(this.bounds.maxX, this.bounds.minX + maxX);
this.bounds.maxY = Math.max(this.bounds.maxY, this.bounds.minY + maxY);
this.bounds.maxZ = Math.max(this.bounds.maxZ, this.bounds.minZ + maxZ);
this.layoutOffsetX = bminX - this.bounds.minX;
this.layoutOffsetY = bminY - this.bounds.minY;
this.layoutOffsetZ = bminZ - this.bounds.minZ;
}
public void addComponentParts(World world, Random rand, StructureBoundingBox chunkBounds) {
if (this.blueprintNode != null) {
for(Prop prop : this.blueprintNode.props) {
prop.type.buildProp(this, prop, world, chunkBounds, rand);
}
}
/*int cx = this.bounds.getXSize()-1;
int cy = this.bounds.getYSize()-1;
int cz = this.bounds.getZSize()-1;
this.placeBlockAtCurrentPosition(world, Blocks.glowstone, 0, 0, 0, 0, chunkBounds);
this.placeBlockAtCurrentPosition(world, Blocks.glowstone, 0, 0, 0, cz, chunkBounds);
this.placeBlockAtCurrentPosition(world, Blocks.glowstone, 0, 0, cy, 0, chunkBounds);
this.placeBlockAtCurrentPosition(world, Blocks.glowstone, 0, 0, cy, cz, chunkBounds);
this.placeBlockAtCurrentPosition(world, Blocks.glowstone, 0, cx, 0, 0, chunkBounds);
this.placeBlockAtCurrentPosition(world, Blocks.glowstone, 0, cx, 0, cz, chunkBounds);
this.placeBlockAtCurrentPosition(world, Blocks.glowstone, 0, cx, cy, 0, chunkBounds);
this.placeBlockAtCurrentPosition(world, Blocks.glowstone, 0, cx, cy, cz, chunkBounds);*/
}
}
}