Improved island map generator.
Added documentation as well. Untested.
This commit is contained in:
parent
6cf1f6dcc1
commit
44f183cca0
@ -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)) {
|
||||
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;
|
||||
|
||||
int xDirection = (secondBlockX - firstBlockX) / Math.abs(secondBlockX - firstBlockX);
|
||||
for (int xPos = firstBlockX; xPos != secondBlockX; xPos += xDirection) {
|
||||
if (!isIsland(xPos, firstBlockZ)) {
|
||||
return false;
|
||||
}
|
||||
dfs.setStartNode(secondBlockX, secondBlockZ, firstBlockX, firstBlockZ);
|
||||
boolean res = dfs.isConnected();
|
||||
dfs.deleteTree();
|
||||
return res;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user