/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.pepsoft.worldpainter.layers.exporters; import org.pepsoft.minecraft.Chunk; import org.pepsoft.util.PerlinNoise; import org.pepsoft.worldpainter.Dimension; import org.pepsoft.worldpainter.Tile; import org.pepsoft.worldpainter.exporting.AbstractLayerExporter; import org.pepsoft.worldpainter.exporting.FirstPassLayerExporter; import org.pepsoft.worldpainter.layers.Resources; import java.io.IOException; import java.io.ObjectInputStream; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.Set; import static org.pepsoft.minecraft.Constants.*; import static org.pepsoft.worldpainter.Constants.*; /** * * @author pepijn */ public class ResourcesExporter extends AbstractLayerExporter<Resources> implements FirstPassLayerExporter { public ResourcesExporter() { super(Resources.INSTANCE); } @Override public void setSettings(ExporterSettings settings) { super.setSettings(settings); ResourcesExporterSettings resourcesSettings = (ResourcesExporterSettings) getSettings(); if (resourcesSettings != null) { noiseGenerators = new PerlinNoise[256]; seedOffsets = new long[256]; minLevels = new int[256]; maxLevels = new int[256]; chances = new float[256][16]; activeOreCount = 0; for (int blockType: resourcesSettings.getBlockTypes()) { if (resourcesSettings.getChance(blockType) == 0) { continue; } activeOreCount++; noiseGenerators[blockType] = new PerlinNoise(0); seedOffsets[blockType] = resourcesSettings.getSeedOffset(blockType); minLevels[blockType] = resourcesSettings.getMinLevel(blockType); maxLevels[blockType] = resourcesSettings.getMaxLevel(blockType); chances[blockType] = new float[16]; for (int i = 0; i < 16; i++) { chances[blockType][i] = PerlinNoise.getLevelForPromillage(Math.min(resourcesSettings.getChance(blockType) * i / 8f, 1000f)); } } } } @Override public void render(Dimension dimension, Tile tile, Chunk chunk) { ResourcesExporterSettings settings = (ResourcesExporterSettings) getSettings(); if (settings == null) { settings = new ResourcesExporterSettings(dimension.getMaxHeight()); setSettings(settings); } final int minimumLevel = settings.getMinimumLevel(); final int xOffset = (chunk.getxPos() & 7) << 4; final int zOffset = (chunk.getzPos() & 7) << 4; final long seed = dimension.getSeed(); final int[] oreTypes = new int[activeOreCount]; final int maxY = dimension.getMaxHeight() - 1; int i = 0; for (int oreType: settings.getBlockTypes()) { if (settings.getChance(oreType) == 0) { continue; } oreTypes[i++] = oreType; } if ((currentSeed == 0) || (currentSeed != seed)) { for (int blockType: oreTypes) { if (noiseGenerators[blockType].getSeed() != (seed + seedOffsets[blockType])) { noiseGenerators[blockType].setSeed(seed + seedOffsets[blockType]); } } } // int[] counts = new int[256]; for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { final int localX = xOffset + x, localY = zOffset + z; final int worldX = tile.getX() * TILE_SIZE + localX, worldY = tile.getY() * TILE_SIZE + localY; if (tile.getBitLayerValue(org.pepsoft.worldpainter.layers.Void.INSTANCE, localX, localY)) { continue; } final int resourcesValue = Math.max(minimumLevel, tile.getLayerValue(Resources.INSTANCE, localX, localY)); if (resourcesValue > 0) { final int terrainheight = tile.getIntHeight(localX, localY); final double dx = worldX / TINY_BLOBS, dy = worldY / TINY_BLOBS; final double dirtX = worldX / SMALL_BLOBS, dirtY = worldY / SMALL_BLOBS; // Capping to maxY really shouldn't be necessary, but we've // had several reports from the wild of this going higher // than maxHeight, so there must be some obscure way in // which the terrainHeight can be raised too high for (int y = Math.min(terrainheight - dimension.getTopLayerDepth(worldX, worldY, terrainheight), maxY); y > 0; y--) { final double dz = y / TINY_BLOBS; final double dirtZ = y / SMALL_BLOBS; for (int oreType: oreTypes) { final float chance = chances[oreType][resourcesValue]; if ((chance <= 0.5f) && (y >= minLevels[oreType]) && (y <= maxLevels[oreType]) && (((oreType == BLK_DIRT) || (oreType == BLK_GRAVEL)) ? (noiseGenerators[oreType].getPerlinNoise(dirtX, dirtY, dirtZ) >= chance) : (noiseGenerators[oreType].getPerlinNoise(dx, dy, dz) >= chance))) { // counts[oreType]++; chunk.setBlockType(x, y, z, oreType); chunk.setDataValue(x, y, z, 0); break; } } } } } } // System.out.println("Tile " + tile.getX() + "," + tile.getY()); // for (i = 0; i < 256; i++) { // if (counts[i] > 0) { // System.out.printf("Exported %6d of ore type %3d%n", counts[i], i); // } // } // System.out.println(); } // TODO: resource frequenties onderzoeken met Statistics tool! private PerlinNoise[] noiseGenerators; private long[] seedOffsets; private int[] minLevels, maxLevels; private float[][] chances; private int activeOreCount; private long currentSeed; public static class ResourcesExporterSettings implements ExporterSettings { public ResourcesExporterSettings(int maxHeight) { this(maxHeight, false); } public ResourcesExporterSettings(int maxHeight, boolean nether) { minLevels.put(BLK_GOLD_ORE, 0); minLevels.put(BLK_IRON_ORE, 0); minLevels.put(BLK_COAL, 0); minLevels.put(BLK_LAPIS_LAZULI_ORE, 0); minLevels.put(BLK_DIAMOND_ORE, 0); minLevels.put(BLK_REDSTONE_ORE, 0); minLevels.put(BLK_WATER, 0); minLevels.put(BLK_LAVA, 0); minLevels.put(BLK_DIRT, 0); minLevels.put(BLK_GRAVEL, 0); minLevels.put(BLK_EMERALD_ORE, 0); minLevels.put(BLK_QUARTZ_ORE, 0); maxLevels.put(BLK_GOLD_ORE, 31); maxLevels.put(BLK_IRON_ORE, 63); maxLevels.put(BLK_COAL, maxHeight - 1); maxLevels.put(BLK_LAPIS_LAZULI_ORE, 31); maxLevels.put(BLK_DIAMOND_ORE, 15); maxLevels.put(BLK_REDSTONE_ORE, 15); maxLevels.put(BLK_WATER, maxHeight - 1); maxLevels.put(BLK_LAVA, 15); maxLevels.put(BLK_DIRT, maxHeight - 1); maxLevels.put(BLK_GRAVEL, maxHeight - 1); maxLevels.put(BLK_EMERALD_ORE, 31); maxLevels.put(BLK_QUARTZ_ORE, maxHeight - 1); if (nether) { chances.put(BLK_GOLD_ORE, 0); chances.put(BLK_IRON_ORE, 0); chances.put(BLK_COAL, 0); chances.put(BLK_LAPIS_LAZULI_ORE, 0); chances.put(BLK_DIAMOND_ORE, 0); chances.put(BLK_REDSTONE_ORE, 0); chances.put(BLK_WATER, 0); chances.put(BLK_LAVA, 0); chances.put(BLK_DIRT, 0); chances.put(BLK_GRAVEL, 0); chances.put(BLK_EMERALD_ORE, 0); if (maxHeight != DEFAULT_MAX_HEIGHT_2) { chances.put(BLK_QUARTZ_ORE, 0); } else { chances.put(BLK_QUARTZ_ORE, 6); } } else { chances.put(BLK_GOLD_ORE, 1); chances.put(BLK_IRON_ORE, 6); chances.put(BLK_COAL, 10); chances.put(BLK_LAPIS_LAZULI_ORE, 1); chances.put(BLK_DIAMOND_ORE, 1); chances.put(BLK_REDSTONE_ORE, 8); chances.put(BLK_WATER, 1); chances.put(BLK_LAVA, 2); chances.put(BLK_DIRT, 57); chances.put(BLK_GRAVEL, 28); if (maxHeight != DEFAULT_MAX_HEIGHT_2) { chances.put(BLK_EMERALD_ORE, 0); } else { chances.put(BLK_EMERALD_ORE, 1); } chances.put(BLK_QUARTZ_ORE, 0); } Random random = new Random(); for (int blockType: maxLevels.keySet()) { seedOffsets.put(blockType, random.nextLong()); } } private ResourcesExporterSettings(int minimumLevel, Map<Integer, Integer> minLevels, Map<Integer, Integer> maxLevels, Map<Integer, Integer> chances, Map<Integer, Long> seedOffsets) { this.minimumLevel = minimumLevel; this.minLevels.putAll(minLevels); this.maxLevels.putAll(maxLevels); this.chances.putAll(chances); this.seedOffsets.putAll(seedOffsets); } @Override public boolean isApplyEverywhere() { return minimumLevel > 0; } public int getMinimumLevel() { return minimumLevel; } public void setMinimumLevel(int minimumLevel) { this.minimumLevel = minimumLevel; } public Set<Integer> getBlockTypes() { return maxLevels.keySet(); } public int getMinLevel(int blockType) { return minLevels.get(blockType); } public void setMinLevel(int blockType, int minLevel) { minLevels.put(blockType, minLevel); } public int getMaxLevel(int blockType) { return maxLevels.get(blockType); } public void setMaxLevel(int blockType, int maxLevel) { maxLevels.put(blockType, maxLevel); } public int getChance(int blockType) { return chances.get(blockType); } public void setChance(int blockType, int chance) { chances.put(blockType, chance); } public long getSeedOffset(int blockType) { return seedOffsets.get(blockType); } @Override public Resources getLayer() { return Resources.INSTANCE; } @Override public ResourcesExporterSettings clone() { return new ResourcesExporterSettings(minimumLevel, minLevels, maxLevels, chances, seedOffsets); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); // Fix static water and lava if (! maxLevels.containsKey(BLK_WATER)) { logger.warn("Fixing water and lava settings"); maxLevels.put(BLK_WATER, maxLevels.get(BLK_STATIONARY_WATER)); chances.put(BLK_WATER, chances.get(BLK_STATIONARY_WATER)); seedOffsets.put(BLK_WATER, seedOffsets.get(BLK_STATIONARY_WATER)); maxLevels.put(BLK_LAVA, maxLevels.get(BLK_STATIONARY_LAVA)); chances.put(BLK_LAVA, chances.get(BLK_STATIONARY_LAVA)); seedOffsets.put(BLK_LAVA, seedOffsets.get(BLK_STATIONARY_LAVA)); maxLevels.remove(BLK_STATIONARY_WATER); chances.remove(BLK_STATIONARY_WATER); seedOffsets.remove(BLK_STATIONARY_WATER); maxLevels.remove(BLK_STATIONARY_LAVA); chances.remove(BLK_STATIONARY_LAVA); seedOffsets.remove(BLK_STATIONARY_LAVA); } if (! maxLevels.containsKey(BLK_EMERALD_ORE)) { maxLevels.put(BLK_EMERALD_ORE, 31); chances.put(BLK_EMERALD_ORE, 0); } Random random = new Random(); if (! seedOffsets.containsKey(BLK_EMERALD_ORE)) { seedOffsets.put(BLK_EMERALD_ORE, random.nextLong()); } if (minLevels == null) { minLevels = new HashMap<>(); for (int blockType: maxLevels.keySet()) { minLevels.put(blockType, 0); } } if (! minLevels.containsKey(BLK_QUARTZ_ORE)) { minLevels.put(BLK_QUARTZ_ORE, 0); maxLevels.put(BLK_QUARTZ_ORE, 255); chances.put(BLK_QUARTZ_ORE, 0); seedOffsets.put(BLK_QUARTZ_ORE, random.nextLong()); } } private int minimumLevel = 8; private final Map<Integer, Integer> maxLevels = new HashMap<>(); private final Map<Integer, Integer> chances = new HashMap<>(); private final Map<Integer, Long> seedOffsets = new HashMap<>(); private Map<Integer, Integer> minLevels = new HashMap<>(); private static final long serialVersionUID = 1L; private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ResourcesExporter.class); } }