package org.pepsoft.worldpainter.layers.tunnel; import org.pepsoft.worldpainter.*; import org.pepsoft.worldpainter.Dimension; import org.pepsoft.worldpainter.heightMaps.NoiseHeightMap; import java.awt.*; import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.util.*; /** * A {@link Dimension} of which the terrain height follows the floor of a * particular Custom Cave/Tunnel Layer. * * Created by pepijn on 31-7-15. */ public class TunnelFloorDimension extends RODelegatingDimension { public TunnelFloorDimension(Dimension dimension, TunnelLayer layer) { super(dimension); this.layer = layer; floorMode = layer.getFloorMode(); roofMode = layer.getRoofMode(); floorWallDepth = layer.getFloorWallDepth(); roofWallDepth = layer.getRoofWallDepth(); floorLevel = layer.getFloorLevel(); roofLevel = layer.getRoofLevel(); maxWallDepth = Math.max(floorWallDepth, roofWallDepth) + 1; floorMin = layer.getFloorMin(); floorMax = layer.getFloorMax(); roofMin = layer.getRoofMin(); roofMax = layer.getRoofMax(); minZ = dimension.isBottomless() ? 0 : 1; maxZ = dimension.getMaxHeight() - 1; if (layer.getFloorNoise() != null) { floorNoise = new NoiseHeightMap(layer.getFloorNoise(), TunnelLayerExporter.FLOOR_NOISE_SEED_OFFSET); floorNoiseOffset = layer.getFloorNoise().getRange(); } else { floorNoise = null; floorNoiseOffset = 0; } if (layer.getRoofNoise()!= null) { roofNoise = new NoiseHeightMap(layer.getRoofNoise(), TunnelLayerExporter.ROOF_NOISE_SEED_OFFSET); roofNoiseOffset = layer.getRoofNoise().getRange(); } else { roofNoise = null; roofNoiseOffset = 0; } } @Override public float getHeightAt(int x, int y) { if (dimension.getBitLayerValueAt(layer, x, y)) { // Potentially in cave/tunnel final int terrainHeight = dimension.getIntHeightAt(x, y); int actualFloorLevel = TunnelLayerExporter.calculateLevel(floorMode, floorLevel, terrainHeight, floorMin, floorMax, minZ, maxZ, (floorNoise != null) ? ((int) floorNoise.getHeight(x, y) - floorNoiseOffset) : 0); int actualRoofLevel = TunnelLayerExporter.calculateLevel(roofMode, roofLevel, terrainHeight, roofMin, roofMax, minZ, maxZ, (roofNoise != null) ? ((int) roofNoise.getHeight(x, y) - roofNoiseOffset) : 0); if (actualRoofLevel <= actualFloorLevel) { return dimension.getHeightAt(x, y); } final float distanceToWall = dimension.getDistanceToEdge(layer, x, y, maxWallDepth) - 1; final int floorLedgeHeight = TunnelLayerExporter.calculateLedgeHeight(floorWallDepth, distanceToWall); final int roofLedgeHeight = TunnelLayerExporter.calculateLedgeHeight(roofWallDepth, distanceToWall); actualFloorLevel += floorLedgeHeight; actualRoofLevel -= roofLedgeHeight; if (actualRoofLevel <= actualFloorLevel) { return dimension.getHeightAt(x, y); } else { return actualFloorLevel; } } else { // Definitely outside cave/tunnel return dimension.getHeightAt(x, y); } } @Override public int getIntHeightAt(int x, int y, int defaultValue) { float height = getHeightAt(x, y); return (height == Float.MIN_VALUE) ? -1 : (int) (height + 0.5f); } @Override public Tile getTile(Point coords) { Reference<TunnelFloorTile> cachedTileRef = tileCache.get(coords); TunnelFloorTile cachedTile = (cachedTileRef != null) ? cachedTileRef.get() : null; if (cachedTile == null) { Tile tile = dimension.getTile(coords); if (tile != null) { cachedTile = new TunnelFloorTile(tile); tileCache.put(coords, new SoftReference<>(cachedTile)); } } return cachedTile; } @Override public Collection<? extends Tile> getTiles() { Collection<? extends Tile> tiles = dimension.getTiles(); java.util.List<Tile> wrappedTiles = new ArrayList<>(tiles.size()); for (Tile tile: tiles) { Reference<TunnelFloorTile> cachedTileRef = tileCache.get(new Point(tile.getX(), tile.getY())); TunnelFloorTile cachedTile = (cachedTileRef != null) ? cachedTileRef.get() : null; if (cachedTile != null) { wrappedTiles.add(cachedTile); } else { wrappedTiles.add(new TunnelFloorTile(tile)); } } return wrappedTiles; } final TunnelLayer layer; final TunnelLayer.Mode floorMode, roofMode; final int floorLevel, floorMin, floorMax, minZ, maxZ, roofLevel, roofMin, roofMax, floorWallDepth, roofWallDepth, maxWallDepth; final NoiseHeightMap floorNoise, roofNoise; final int floorNoiseOffset, roofNoiseOffset; private final Map<Point, Reference<TunnelFloorTile>> tileCache = new HashMap<>(); /** * A {@link Tile} of which the terrain height follows the floor of a particular * Custom Cave/Tunnel Layer. * * Created by pepijn on 31-7-15. */ public class TunnelFloorTile extends RODelegatingTile { public TunnelFloorTile(Tile tile) { super(tile); xOffset = tile.getX() << Constants.TILE_SIZE_BITS; yOffset = tile.getY() << Constants.TILE_SIZE_BITS; } @Override public float getHeight(int x, int y) { if (tile.getBitLayerValue(layer, x, y)) { // Potentially in cave/tunnel final int terrainHeight = tile.getIntHeight(x, y); int actualFloorLevel = TunnelLayerExporter.calculateLevel(floorMode, floorLevel, terrainHeight, floorMin, floorMax, minZ, maxZ, (floorNoise != null) ? ((int) floorNoise.getHeight(xOffset | x, yOffset | y) - floorNoiseOffset) : 0); int actualRoofLevel = TunnelLayerExporter.calculateLevel(roofMode, roofLevel, terrainHeight, roofMin, roofMax, minZ, maxZ, (roofNoise != null) ? ((int) roofNoise.getHeight(xOffset | x, yOffset | y) - roofNoiseOffset) : 0); if (actualRoofLevel <= actualFloorLevel) { return tile.getHeight(x, y); } final float distanceToWall = dimension.getDistanceToEdge(layer, xOffset | x, yOffset | y, maxWallDepth) - 1; final int floorLedgeHeight = TunnelLayerExporter.calculateLedgeHeight(floorWallDepth, distanceToWall); final int roofLedgeHeight = TunnelLayerExporter.calculateLedgeHeight(roofWallDepth, distanceToWall); actualFloorLevel += floorLedgeHeight; actualRoofLevel -= roofLedgeHeight; if (actualRoofLevel <= actualFloorLevel) { return tile.getHeight(x, y); } else { return actualFloorLevel; } } else { // Definitely outside cave/tunnel return tile.getHeight(x, y); } } @Override public int getIntHeight(int x, int y) { return (int) (getHeight(x, y) + 0.5f); } final int xOffset, yOffset; } }