diff --git a/src/main/java/ca/recrown/islandsurvivalcraft/worldgen/IslandMapGenerator.java b/src/main/java/ca/recrown/islandsurvivalcraft/worldgen/IslandMapGenerator.java index b27cfc1..7b1ee4a 100644 --- a/src/main/java/ca/recrown/islandsurvivalcraft/worldgen/IslandMapGenerator.java +++ b/src/main/java/ca/recrown/islandsurvivalcraft/worldgen/IslandMapGenerator.java @@ -4,53 +4,81 @@ import java.util.Random; import org.bukkit.util.noise.SimplexOctaveGenerator; -public class IslandMapGenerator { +import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateValidatable; +import ca.recrown.islandsurvivalcraft.pathfinding.DepthFirstSearch; + +public class IslandMapGenerator implements CoordinateValidatable { private SimplexOctaveGenerator noiseGenerator; private final int noiseOctaves = 8; private final int islandGenerationPercent = 47; private final float exaggerationFactor = 1.5f; private final double noiseFrequency = 0.5D; private final double noiseAmplitude = 0.5D; - private final int shoreSize = 4; - private final int maxHeightAboveWater = 8; + private final float shoreFactor = 0.065f; + private final int maxHeightAboveSea = 8; + private final int lowestSeaFloor = 16; private final int seaLevel; private final int shallowDepth; + private final DepthFirstSearch dfs; public IslandMapGenerator(Random random, int seaLevel, int shallowDepth) { this.noiseGenerator = new SimplexOctaveGenerator(random, noiseOctaves); this.seaLevel = seaLevel; this.shallowDepth = shallowDepth; + dfs = new DepthFirstSearch(this); } + /** + * Considers this land if it is greater than sea level. + * @param worldX the X coordinate of location in world. + * @param worldZ the Z coordinate of location in world. + * @return + */ public boolean isLand(int worldX, int worldZ) { - if (getBaseBlockHeight(worldX, worldZ) >= seaLevel) { + if (getIslandValue(worldX, worldZ) >= 0) { return true; } return false; } + /** + * Calculates if this is the edge of the island by checking if it's surrounding blocks are part of this island. + * @param worldX The world X coordinate. + * @param worldZ The world Z coordinate. + * @return true if it is the edge of the island and false if it isn't. + */ public boolean isEdgeOfIsland(int worldX, int worldZ) { return isIsland(worldX, worldZ) && - (!isSameIsland(worldX, worldZ, worldX + 1, worldZ) || - isSameIsland(worldX, worldZ, worldX - 1, worldZ) || - isSameIsland(worldX, worldZ, worldX , worldZ + 1) || + !(isSameIsland(worldX, worldZ, worldX + 1, worldZ) && + isSameIsland(worldX, worldZ, worldX - 1, worldZ) && + isSameIsland(worldX, worldZ, worldX , worldZ + 1) && isSameIsland(worldX, worldZ, worldX, worldZ - 1)); } + /** + * These coordinates are considered to be the shore if: + * It is a part of an island, + * within a square radius of shoreSize, there is an edge of the island. + * @param worldX the X coordinate of location in world. + * @param worldZ the Z coordinate of location in world. + * @return + */ public boolean isShore(int worldX, int worldZ) { if (!isIsland(worldX, worldZ)) return false; - - for (int xPos = 0; xPos < shoreSize; xPos++) { - for (int zPos = 0; zPos < shoreSize; zPos++) { - if (isEdgeOfIsland(worldX + xPos, worldZ + zPos) || isEdgeOfIsland(worldX - xPos, worldZ - zPos)) { - return true; - } - } + if (isLand(worldX, worldZ) && getIslandValue(worldX, worldZ) <= shoreFactor) { + return true; } return false; } + /** + * Considers these coordinates to be part of an island if: + * It is land, or if it is a shallow portion of land. + * @param worldX the X coordinate of location in world. + * @param worldZ the Y coordinate of location in world. + * @return true if these coordinates represent an island. + */ public boolean isIsland(int worldX, int worldZ) { if (isLand(worldX, worldZ) || isShallowPortion(worldX, worldZ)) { return true; @@ -58,9 +86,16 @@ public class IslandMapGenerator { return false; } + /** + * This block is considered a shallow portion of the island: + * The block height is less than the sea level, + * and that the block height is greater than the sea level - the shallow depth. + * @param worldX the X coordinate of location in world. + * @param worldZ the Z coordinate of location in world. + * @return true if it is considered the shallow portion. + */ public boolean isShallowPortion(int worldX, int worldZ) { - int blockHeight = getBaseBlockHeight(worldX, worldZ); - if (blockHeight < seaLevel && blockHeight >= (seaLevel - shallowDepth)) { + if (getIslandValue(worldX, worldZ) >= - (float) shallowDepth / (float) seaLevel) { return true; } return true; @@ -68,34 +103,51 @@ public class IslandMapGenerator { private double getTerrainValue(int worldX, int worldZ) { double normalized = getNoiseValue(worldX, worldZ); - return Math.pow(normalized, exaggerationFactor); + return Math.pow(normalized, exaggerationFactor) * (islandGenerationPercent + seaLevel); } public double getNoiseValue(int worldX, int worldZ) { return (noiseGenerator.noise(worldX, worldZ, noiseFrequency, noiseAmplitude, true) + 1D) / 2D; } - public int getBaseBlockHeight(int worldX, int worldZ) { - return Math.min((int) getTerrainValue(worldX, worldZ) * (islandGenerationPercent + seaLevel), maxHeightAboveWater + seaLevel); + /*** + * Island value will be 0 or positive if it is a part of an island. + * If less than, it is considered under the sea. + * Does not factor in a shallow depth. + * The value above zero is normalized to [0, 1]. + * @param worldX the x world coordinate to obtain this value for. + * @param worldZ the z world coordinate to obtain this value for. + * @return a value representing the island at the given point. + */ + public float getIslandValue(int worldX, int worldZ) { + return (float) ((getTerrainValue(worldX, worldZ) - seaLevel) / (islandGenerationPercent)); } + public int getBaseTerrainHeight(int worldX, int worldZ) { + double terrainVal = getTerrainValue(worldX, worldZ); + return (int) Math.max(Math.min(terrainVal, maxHeightAboveSea + seaLevel), lowestSeaFloor); + } + + /** + * Using DFS, determines if two given blocks are from the same island. + * Works by checking if a path can be drawn between these two. + * @param firstBlockX The x coord of the first block. + * @param firstBlockZ The z coord of the first block. + * @param secondBlockX The X coord of the first block. + * @param secondBlockZ The Z coord of the first block. + * @return true if the two blocks are on the same island, and false otherwise. + */ public boolean isSameIsland(int firstBlockX, int firstBlockZ, int secondBlockX, int secondBlockZ) { if (!isIsland(firstBlockX, firstBlockZ)) return false; if (!isIsland(secondBlockX, secondBlockZ)) return false; + dfs.setStartNode(secondBlockX, secondBlockZ, firstBlockX, firstBlockZ); + boolean res = dfs.isConnected(); + dfs.deleteTree(); + return res; + } - int xDirection = (secondBlockX - firstBlockX) / Math.abs(secondBlockX - firstBlockX); - for (int xPos = firstBlockX; xPos != secondBlockX; xPos += xDirection) { - if (!isIsland(xPos, firstBlockZ)) { - return false; - } - } - - int zDirection = (secondBlockZ - secondBlockZ) / Math.abs(secondBlockZ - secondBlockZ); - for (int zPos = firstBlockZ; zPos != secondBlockZ; zPos += zDirection) { - if (!isIsland(secondBlockX, zPos)) { - return false; - } - } - return true; + @Override + public boolean validate(int x, int y) { + return isIsland(x, x); } } \ No newline at end of file