/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.pepsoft.worldpainter.layers.exporters; import org.pepsoft.util.CollectionUtils; import org.pepsoft.worldpainter.Dimension; import org.pepsoft.worldpainter.exporting.AbstractLayerExporter; import org.pepsoft.worldpainter.exporting.Fixup; import org.pepsoft.worldpainter.exporting.MinecraftWorld; import org.pepsoft.worldpainter.exporting.SecondPassLayerExporter; import org.pepsoft.worldpainter.layers.Frost; import java.awt.*; import java.util.BitSet; import java.util.List; import java.util.Random; import static org.pepsoft.minecraft.Constants.*; import static org.pepsoft.minecraft.Material.SNOW; /** * * @author pepijn */ public class FrostExporter extends AbstractLayerExporter<Frost> implements SecondPassLayerExporter { public FrostExporter() { super(Frost.INSTANCE, new FrostSettings()); } @Override public List<Fixup> render(final Dimension dimension, final Rectangle area, final Rectangle exportedArea, final MinecraftWorld minecraftWorld) { final FrostSettings settings = (FrostSettings) getSettings(); final boolean frostEverywhere = settings.isFrostEverywhere(); final int mode = settings.getMode(); final boolean snowUnderTrees = settings.isSnowUnderTrees(); final int maxHeight = dimension.getMaxHeight(); final Random random = new Random(); // Only used for random snow height, so it's not a big deal if it's different every time final BitSet noSnowOn = (BitSet) NO_SNOW_ON.clone(); String customNoSnowOnIds = System.getProperty("org.pepsoft.worldpainter.noSnowOn"); if ((customNoSnowOnIds != null) && (! customNoSnowOnIds.trim().isEmpty())) { if (logger.isDebugEnabled()) { logger.debug("Not placing snow on the following additional block IDs: \"" + customNoSnowOnIds + "\""); } for (String id: customNoSnowOnIds.split("[,;]")) { noSnowOn.set(Integer.parseInt(id.trim())); } } for (int x = area.x; x < area.x + area.width; x++) { for (int y = area.y; y < area.y + area.height; y++) { if (frostEverywhere || dimension.getBitLayerValueAt(Frost.INSTANCE, x, y)) { int previousBlockType = minecraftWorld.getBlockTypeAt(x, y, maxHeight - 1); int leafBlocksEncountered = 0; for (int height = (maxHeight - 2); height >= 0; height--) { int blockType = minecraftWorld.getBlockTypeAt(x, y, height); if (noSnowOn.get(blockType)) { previousBlockType = blockType; continue; } else { if (blockType == BLK_STATIONARY_WATER) { minecraftWorld.setBlockTypeAt(x, y, height, BLK_ICE); break; } else if ((blockType == BLK_LEAVES) || (blockType == BLK_LEAVES2) || (blockType == BLK_WOOD) || (blockType == BLK_WOOD2)) { if (previousBlockType == BLK_AIR) { minecraftWorld.setBlockTypeAt(x, y, height + 1, BLK_SNOW); } leafBlocksEncountered++; if ((! snowUnderTrees) && (leafBlocksEncountered > 1)) { break; } } else { // Obliterate tall grass, 'cause there is too // much of it, and leaving it in would look // strange. Also replace existing snow, as we // might want to place thicker snow if ((previousBlockType == BLK_AIR) || (previousBlockType == BLK_TALL_GRASS) || (previousBlockType == BLK_SNOW)) { if ((mode == FrostSettings.MODE_SMOOTH_AT_ALL_ELEVATIONS) || (height == dimension.getIntHeightAt(x, y)) || (blockType == BLK_SNOW_BLOCK)) { // Only vary the snow tickness if we're // at surface height, otherwise it looks // odd switch (mode) { case FrostSettings.MODE_FLAT: minecraftWorld.setMaterialAt(x, y, height + 1, SNOW); break; case FrostSettings.MODE_RANDOM: minecraftWorld.setBlockTypeAt(x, y, height + 1, BLK_SNOW); minecraftWorld.setDataAt( x, y, height + 1, random.nextInt(3)); break; case FrostSettings.MODE_SMOOTH: case FrostSettings.MODE_SMOOTH_AT_ALL_ELEVATIONS: int snowHeight = (int) ((dimension.getHeightAt(x, y) + 0.5f - dimension.getIntHeightAt(x, y)) / 0.125f); if ((snowHeight > 0) && (! frostEverywhere)) { snowHeight = Math.max(Math.min(snowHeight, dimension.getBitLayerCount(Frost.INSTANCE, x, y, 1) - 2), 0); } minecraftWorld.setBlockTypeAt(x, y, height + 1, BLK_SNOW); minecraftWorld.setDataAt( x, y, height + 1, snowHeight); break; } } else { // At other elevations just place a // regular thin snow block minecraftWorld.setMaterialAt(x, y, height + 1, SNOW); } } break; } } previousBlockType = blockType; } } } } return null; } private static final BitSet NO_SNOW_ON = CollectionUtils.bitSetOf( BLK_AIR, BLK_ICE, BLK_LAVA, BLK_STATIONARY_LAVA, BLK_TORCH, BLK_DANDELION, BLK_ROSE, BLK_BROWN_MUSHROOM, BLK_RED_MUSHROOM, BLK_FIRE, BLK_TALL_GRASS, BLK_DEAD_SHRUBS, BLK_WOODEN_STAIRS, BLK_COBBLESTONE_STAIRS, BLK_BRICK_STAIRS, BLK_NETHER_BRICK_STAIRS, BLK_STONE_BRICK_STAIRS, BLK_SLAB, BLK_FENCE, BLK_FENCE_GATE, BLK_NETHER_BRICK_FENCE, BLK_WALL_SIGN, BLK_SIGN, BLK_VINES, BLK_SAPLING, BLK_WATER, BLK_BED, BLK_POWERED_RAILS, BLK_RAILS, BLK_DETECTOR_RAILS, BLK_COBWEB, BLK_PISTON_HEAD, BLK_CHEST, BLK_REDSTONE_WIRE, BLK_WHEAT, BLK_BURNING_FURNACE, BLK_WOODEN_DOOR, BLK_IRON_DOOR, BLK_IRON_BARS, BLK_LADDER, BLK_LEVER, BLK_STONE_PRESSURE_PLATE, BLK_WOODEN_PRESSURE_PLATE, BLK_REDSTONE_TORCH_OFF, BLK_REDSTONE_TORCH_ON, BLK_STONE_BUTTON, BLK_SNOW, BLK_CACTUS, BLK_SUGAR_CANE, BLK_CAKE, BLK_REDSTONE_REPEATER_OFF, BLK_REDSTONE_REPEATER_ON, BLK_TRAPDOOR, BLK_GLASS_PANE, BLK_PUMPKIN_STEM, BLK_MELON_STEM, BLK_LILY_PAD, BLK_NETHER_WART, BLK_ENCHANTMENT_TABLE, BLK_BREWING_STAND, BLK_END_PORTAL, BLK_END_PORTAL_FRAME, BLK_DRAGON_EGG, BLK_WOODEN_SLAB, BLK_COCOA_PLANT, BLK_SANDSTONE_STAIRS, BLK_ENDER_CHEST, BLK_TRIPWIRE_HOOK, BLK_TRIPWIRE, BLK_PINE_WOOD_STAIRS, BLK_BIRCH_WOOD_STAIRS, BLK_JUNGLE_WOOD_STAIRS, BLK_COBBLESTONE_WALL, BLK_FLOWER_POT, BLK_CARROTS, BLK_POTATOES, BLK_WOODEN_BUTTON, BLK_HEAD, BLK_ANVIL, BLK_TRAPPED_CHEST, BLK_WEIGHTED_PRESSURE_PLATE_HEAVY, BLK_WEIGHTED_PRESSURE_PLATE_LIGHT, BLK_REDSTONE_COMPARATOR_UNPOWERED, BLK_DAYLIGHT_SENSOR, BLK_ACTIVATOR_RAIL, BLK_STAINED_GLASS_PANE, BLK_ACACIA_WOOD_STAIRS, BLK_DARK_OAK_WOOD_STAIRS, BLK_CARPET, BLK_LARGE_FLOWERS, BLK_PACKED_ICE); private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(FrostExporter.class); public static class FrostSettings implements ExporterSettings { @Override public boolean isApplyEverywhere() { return frostEverywhere; } @Override public Frost getLayer() { return Frost.INSTANCE; } public boolean isFrostEverywhere() { return frostEverywhere; } public void setFrostEverywhere(boolean frostEverywhere) { this.frostEverywhere = frostEverywhere; } public int getMode() { return mode; } public void setMode(int mode) { this.mode = mode; } public boolean isSnowUnderTrees() { return snowUnderTrees; } public void setSnowUnderTrees(boolean snowUnderTrees) { this.snowUnderTrees = snowUnderTrees; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final FrostSettings other = (FrostSettings) obj; if (this.frostEverywhere != other.frostEverywhere) { return false; } if (this.mode != other.mode) { return false; } if (this.snowUnderTrees != other.snowUnderTrees) { return false; } return true; } @Override public int hashCode() { int hash = 3; hash = 23 * hash + (this.frostEverywhere ? 1 : 0); hash = 23 * hash + mode; hash = 23 * hash + (this.snowUnderTrees ? 1 : 0); return hash; } @Override public FrostSettings clone() { try { return (FrostSettings) super.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } private boolean frostEverywhere; private int mode = MODE_SMOOTH; private boolean snowUnderTrees = true; public static final int MODE_FLAT = 0; // Always place thin snow blocks public static final int MODE_RANDOM = 1; // Place random height snow blocks on the surface public static final int MODE_SMOOTH = 2; // Place smooth snow blocks on the surface public static final int MODE_SMOOTH_AT_ALL_ELEVATIONS = 3; // Place smooth snow blocks at any elevation private static final long serialVersionUID = 2011060801L; } }