package hunternif.mc.atlas.core; import hunternif.mc.atlas.ext.ExtTileIdMap; import hunternif.mc.atlas.util.ByteUtil; import net.minecraft.block.Block; import net.minecraft.init.Biomes; import net.minecraft.init.Blocks; import net.minecraft.world.biome.Biome; import net.minecraft.world.chunk.Chunk; import net.minecraftforge.common.BiomeDictionary; import net.minecraftforge.common.BiomeDictionary.Type; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Map; /** * Detects the 256 vanilla biomes, water pools and lava pools. * Water and beach biomes are given priority because shore line is the defining * feature of the map, and so that rivers are more connected. * @author Hunternif */ public class BiomeDetectorBase implements IBiomeDetector { private boolean doScanPonds = true; /** Biome used for occasional pools of water. */ private static final int waterPoolBiomeID = Biome.getIdForBiome(Biomes.RIVER); /** Increment the counter for water biomes by this much during iteration. * This is done so that water pools are more visible. */ private static final int priorityWaterPool = 3, prioritylavaPool = 6; /** Set to true for biome IDs that return true for BiomeDictionary.isBiomeOfType(WATER) */ private static final boolean[] waterBiomes = new boolean[256]; /** Set to true for biome IDs that return true for BiomeDictionary.isBiomeOfType(BEACH) */ private static final boolean[] beachBiomes = new boolean[256]; /** Scan all registered biomes to mark biomes of certain types that will be * given higher priority when identifying mean biome ID for a chunk. * (Currently WATER and BEACH) */ public static void scanBiomeTypes() { for (Biome biome : BiomeDictionary.getBiomes(Type.WATER)) { waterBiomes[Biome.getIdForBiome(biome)] = true; } for (Biome biome : BiomeDictionary.getBiomes(Type.BEACH)) { beachBiomes[Biome.getIdForBiome(biome)] = true; } } public void setScanPonds(boolean value) { this.doScanPonds = value; } int priorityForBiome(Biome biome) { if (waterBiomes[Biome.getIdForBiome(biome)]) { return 4; } else if (beachBiomes[Biome.getIdForBiome(biome)]) { return 3; } else { return 1; } } /** If no valid biome ID is found, returns {@link IBiomeDetector#NOT_FOUND}. */ @Override public int getBiomeID(Chunk chunk) { int biomeCount = Biome.REGISTRY.getKeys().size(); int[] chunkBiomes = ByteUtil.unsignedByteToIntArray(chunk.getBiomeArray()); Map<Integer, Integer> biomeOccurrences = new HashMap<>(biomeCount); // The following important pseudo-biomes don't have IDs: int lavaOccurrences = 0; for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { int biomeID = chunkBiomes[x << 4 | z]; if (doScanPonds) { int y = chunk.getHeightValue(x, z); if (y > 0) { Block topBlock = chunk.getBlockState(x, y-1, z).getBlock(); // For some reason lava doesn't count in height value // TODO: check if 1.8 fixes this! Block topBlock2 = chunk.getBlockState(x, y, z).getBlock(); // Check if there's surface of water at (x, z), but not swamp if (topBlock == Blocks.WATER && biomeID != Biome.getIdForBiome(Biomes.SWAMPLAND) && biomeID != Biome.getIdForBiome(Biomes.MUTATED_SWAMPLAND)) { int occurrence = biomeOccurrences.getOrDefault(waterPoolBiomeID, 0) + priorityWaterPool; biomeOccurrences.put(waterPoolBiomeID, occurrence); } else if (topBlock2 == Blocks.LAVA) { lavaOccurrences += prioritylavaPool; } } } if (biomeID >= 0 && Biome.getBiomeForId(biomeID) != null) { int occurrence = biomeOccurrences.getOrDefault(biomeID, 0) + priorityForBiome(Biome.getBiomeForId(biomeID)); biomeOccurrences.put(biomeID, occurrence); } } } Map.Entry<Integer, Integer> meanBiome = Collections.max(biomeOccurrences.entrySet(), Comparator.comparingInt(Map.Entry::getValue)); int meanBiomeId = meanBiome.getKey(); int meanBiomeOccurrences = meanBiome.getValue(); // The following important pseudo-biomes don't have IDs: if (meanBiomeOccurrences < lavaOccurrences) { return ExtTileIdMap.instance().getPseudoBiomeID(ExtTileIdMap.TILE_LAVA); } return meanBiomeId; } }