package hunternif.mc.atlas.ext;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import hunternif.mc.atlas.util.MathUtil;
import net.minecraftforge.event.terraingen.PopulateChunkEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.World;
import net.minecraft.world.gen.structure.MapGenStructureData;
import net.minecraft.world.gen.structure.StructureBoundingBox;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import hunternif.mc.atlas.AntiqueAtlasMod;
import hunternif.mc.atlas.api.AtlasAPI;
import hunternif.mc.atlas.marker.Marker;
import hunternif.mc.atlas.marker.MarkersData;
import hunternif.mc.atlas.registry.MarkerTypes;
import hunternif.mc.atlas.util.Log;
public class VillageWatcher {
public static final String MARKER = "village";
/** Set of tag names for every village, in the format "[x, y]" */
//TODO: list of visited villages must be reset when changing worlds!
// And the same goes for Nether Fortress Watcher
private final Set<String> visited = new HashSet<>();
private static final String LIBRARY= "ViBH"; // Big-ish house with a high slanted roof, large windows; books and couches inside.
private static final String SMITHY = "ViS"; // The smithy.
private static final String L_HOUSE = "ViTRH"; // Medium-sized, L-shaped houses with slanted roof.
private static final String FARMLAND_LARGE = "ViDF"; // Large field, basically 2 times larger than FIELD2
private static final String FARMLAND_SMALL = "ViF"; // Smaller field.
@SuppressWarnings("unused")
private static final String PATH = "ViSR"; // Usually too narrow to be seen between the houses...
private static final String TORCH = "ViL";
private static final String WELL = "ViW";
private static final String START = "ViStart"; // Same as WELL
private static final String BUTCHERS = "ViPH"; // Medium house with a low slanted roof and a dirt porch with a fence.
private static final String HUT = "ViSmH"; // Tiniest hut with a flat roof.
private static final String HOUSE_SMALL = "ViSH"; // Slightly larger than huts, sometimes with a fenced balcony on the roof and a ladder.
private static final String CHURCH = "ViST"; // The church.
private static final Map<String, String> partToTileMap;
static {
ImmutableMap.Builder<String, String> builder = new Builder<>();
builder.put(LIBRARY, ExtTileIdMap.TILE_VILLAGE_LIBRARY);
builder.put(SMITHY, ExtTileIdMap.TILE_VILLAGE_SMITHY);
builder.put(L_HOUSE, ExtTileIdMap.TILE_VILLAGE_L_HOUSE);
builder.put(FARMLAND_LARGE, ExtTileIdMap.TILE_VILLAGE_FARMLAND_LARGE);
builder.put(FARMLAND_SMALL, ExtTileIdMap.TILE_VILLAGE_FARMLAND_SMALL);
// builder.put(PATH, ExtTileIdMap.TILE_VILLAGE_PATH_X); // Handled separately
builder.put(TORCH, ExtTileIdMap.TILE_VILLAGE_TORCH);
builder.put(WELL, ExtTileIdMap.TILE_VILLAGE_WELL);
builder.put(START, ExtTileIdMap.TILE_VILLAGE_WELL);
builder.put(BUTCHERS, ExtTileIdMap.TILE_VILLAGE_BUTCHERS_SHOP);
builder.put(HOUSE_SMALL, ExtTileIdMap.TILE_VILLAGE_SMALL_HOUSE);
builder.put(HUT, ExtTileIdMap.TILE_VILLAGE_HUT);
builder.put(CHURCH, ExtTileIdMap.TILE_VILLAGE_CHURCH);
partToTileMap = builder.build();
}
/** Tiles with the higher priority override tiles with lower priority at the same chunk. */
private static final Map<String, Integer> tilePriority;
static {
ImmutableMap.Builder<String, Integer> builder = new Builder<>();
builder.put(ExtTileIdMap.TILE_VILLAGE_LIBRARY, 5);
builder.put(ExtTileIdMap.TILE_VILLAGE_SMITHY, 6);
builder.put(ExtTileIdMap.TILE_VILLAGE_L_HOUSE, 5);
builder.put(ExtTileIdMap.TILE_VILLAGE_FARMLAND_LARGE, 3);
builder.put(ExtTileIdMap.TILE_VILLAGE_FARMLAND_SMALL, 3);
// builder.put(ExtTileIdMap.TILE_VILLAGE_PATH_X, 0);
// builder.put(ExtTileIdMap.TILE_VILLAGE_PATH_Z, 0);
builder.put(ExtTileIdMap.TILE_VILLAGE_TORCH, 1);
builder.put(ExtTileIdMap.TILE_VILLAGE_WELL, 7);
builder.put(ExtTileIdMap.TILE_VILLAGE_BUTCHERS_SHOP, 4);
builder.put(ExtTileIdMap.TILE_VILLAGE_SMALL_HOUSE, 4);
builder.put(ExtTileIdMap.TILE_VILLAGE_HUT, 3);
builder.put(ExtTileIdMap.TILE_VILLAGE_CHURCH, 6);
tilePriority = builder.build();
}
@SubscribeEvent(priority=EventPriority.LOWEST)
public void onWorldLoad(WorldEvent.Load event) {
if (!event.getWorld().isRemote && event.getWorld().provider.getDimension() == 0) {
visitAllUnvisitedVillages(event.getWorld());
}
}
@SubscribeEvent
public void onPopulateChunk(PopulateChunkEvent.Post event) {
if (!event.getWorld().isRemote && event.getWorld().provider.getDimension() == 0) {
visitAllUnvisitedVillages(event.getWorld());
}
}
private void visitAllUnvisitedVillages(World world) {
MapGenStructureData data = (MapGenStructureData)world.getPerWorldStorage().getOrLoadData(MapGenStructureData.class, "Village");
if (data == null) return;
NBTTagCompound villageNBTData = data.getTagCompound();
Set<String> tagSet = villageNBTData.getKeySet();
for (String coords : tagSet) {
if (!visited.contains(coords)) {
NBTBase tag = villageNBTData.getTag(coords);
if (tag.getId() == 10) { // is NBTTagCompound
visitVillage(world, (NBTTagCompound) tag);
visited.add(coords);
}
}
}
}
/** Put all child parts of the fortress on the map as global custom tiles. */
private void visitVillage(World world, NBTTagCompound tag) {
if (!tag.getBoolean("Valid")) {
// The village was not actually generated and should not be mapped.
// Remove legacy marker and custom tile:
removeVillage(world, tag);
return;
}
int startChunkX = tag.getInteger("ChunkX");
int startChunkZ = tag.getInteger("ChunkZ");
Log.info("Visiting NPC Village in dimension #%d \"%s\" at chunk (%d, %d) ~ blocks (%d, %d)",
world.provider.getDimension(), world.provider.getDimensionType().getName(),
startChunkX, startChunkZ, startChunkX << 4, startChunkZ << 4);
NBTTagList children = tag.getTagList("Children", 10);
for (int i = 0; i < children.tagCount(); i++) {
NBTTagCompound child = children.getCompoundTagAt(i);
String childID = child.getString("id");
StructureBoundingBox boundingBox = new StructureBoundingBox(child.getIntArray("BB"));
int x = MathUtil.getCenter(boundingBox).getX();
int z = MathUtil.getCenter(boundingBox).getZ();
int chunkX = x >> 4;
int chunkZ = z >> 4;
if (START.equals(childID)) {
// Put marker at Start.
// Check if the marker already exists:
boolean foundMarker = false;
// Legacy support: don't place new marker if there's already one in a wider area.
for (int j = -1; j <= 1; j++) {
for (int k = -1; k <= 1; k++) {
List<Marker> markers = AntiqueAtlasMod.globalMarkersData.getData()
.getMarkersAtChunk(world.provider.getDimension(), j + chunkX / MarkersData.CHUNK_STEP, k + chunkZ / MarkersData.CHUNK_STEP);
if (markers != null) {
for (Marker marker : markers) {
if (marker.getType().equals(MarkerTypes.VILLAGE)) {
foundMarker = true;
break;
}
}
}
}
}
if (!foundMarker && AntiqueAtlasMod.settings.autoVillageMarkers) {
AtlasAPI.markers.putGlobalMarker(world, false, MarkerTypes.VILLAGE, "gui.antiqueatlas.marker.village", x, z);
}
}
// String tileName = null;
// if (PATH.equals(childID)) {
// int orientation = child.getInteger("O");
// switch (orientation) {
// case 0:
// case 2: tileName = ExtTileIdMap.TILE_VILLAGE_PATH_Z; break;
// case 1:
// case 3: tileName = ExtTileIdMap.TILE_VILLAGE_PATH_X; break;
// }
// } else {
// }
String tileName = partToTileMap.get(childID);
if (tileName != null) {
Integer curTilePriority = tilePriority.get(tileName);
Integer prevTilePriority = tilePriority.get(tileAt(chunkX, chunkZ));
if (curTilePriority != null && prevTilePriority != null) {
if (curTilePriority >= prevTilePriority) {
AtlasAPI.tiles.putCustomGlobalTile(world, tileName, chunkX, chunkZ);
}
} else {
AtlasAPI.tiles.putCustomGlobalTile(world, tileName, chunkX, chunkZ);
}
}
}
}
private static String tileAt(int chunkX, int chunkZ) {
int biomeID = AntiqueAtlasMod.extBiomeData.getData().getBiomeIdAt(0, chunkX, chunkZ);
return ExtTileIdMap.instance().getPseudoBiomeName(biomeID);
}
/** Delete the marker and custom tile data about the village. */
private static void removeVillage(World world, NBTTagCompound tag) {
NBTTagList children = tag.getTagList("Children", 10);
for (int i = 0; i < children.tagCount(); i++) {
NBTTagCompound child = children.getCompoundTagAt(i);
String childID = child.getString("id");
StructureBoundingBox boundingBox = new StructureBoundingBox(child.getIntArray("BB"));
int x = MathUtil.getCenter(boundingBox).getX();
int z = MathUtil.getCenter(boundingBox).getZ();
int chunkX = x >> 4;
int chunkZ = z >> 4;
if (START.equals(childID)) {
List<Marker> markers = AntiqueAtlasMod.globalMarkersData.getData()
.getMarkersAtChunk(world.provider.getDimension(), chunkX / MarkersData.CHUNK_STEP, chunkZ / MarkersData.CHUNK_STEP);
if (markers != null) {
for (Marker marker : markers) {
if (marker.getType().equals(MarkerTypes.VILLAGE)) {
AtlasAPI.markers.deleteGlobalMarker(world, marker.getId());
Log.info("Removed faux village marker");
break;
}
}
}
}
AtlasAPI.tiles.deleteCustomGlobalTile(world, chunkX, chunkZ);
Log.info("Removed faux village tile");
}
}
}