Island highlighting debug now works, albeit, not favorable.

Reworked DFS as well to suit a broader spectrum of needs.

Reworked other classes for easier use.
This commit is contained in:
Harrison Deng 2020-05-09 21:56:27 -05:00
parent 19f387def9
commit 4f18f25c01
15 changed files with 365 additions and 330 deletions

View File

@ -5,6 +5,7 @@ import java.util.LinkedList;
import java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.World;
@ -12,16 +13,18 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import ca.recrown.islandsurvivalcraft.IslandSurvivalCraft;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.DepthFirstSearch;
import ca.recrown.islandsurvivalcraft.world.IslandWorldMap;
import ca.recrown.islandsurvivalcraft.world.WorldInfo;
public class HighlightIslandCommand implements CommandRunnable {
private final int CHECK_RADIUS = 3;
private final int PARTICLE_AMOUNT = 2;
private final Particle PARTICLE_TYPE = Particle.FIREWORKS_SPARK;
private final double OFFSET_X = 0.0d, OFFSET_Y = 0.0d, OFFSET_Z = 0.0d;
private final Particle.DustOptions OPTIONS = new Particle.DustOptions(Color.RED, 1f);
private final int PARTICLE_AMOUNT = 3;
private final double OFFSET_X = 0.0d, OFFSET_Y = 6.0d, OFFSET_Z = 0.0d;
private final double EXTRA = 0d;
HashSet<Player> playersHighlighting = new HashSet<>();
LinkedList<Player> waitingList = new LinkedList<>();
LinkedList<PlayerIslandHighlighter> waitingList = new LinkedList<>();
@Override
public String getDescription() {
@ -42,7 +45,7 @@ public class HighlightIslandCommand implements CommandRunnable {
}
if (args[0].toLowerCase().equals("start")) {
if (playersHighlighting.add(player)) {
waitingList.add(player);
waitingList.add(new PlayerIslandHighlighter(player));
completableFuture.complete("You are now highlighting islands.");
} else {
completableFuture.complete("You are already highlighting islands.");
@ -54,17 +57,19 @@ public class HighlightIslandCommand implements CommandRunnable {
completableFuture.complete("You weren't highlighting islands.");
}
} else {
completableFuture.complete(String.format("Argument \"%d\" was not understood. Please refer to \"/IslandSurvivalCraft help\" for more info.", args[0]));
completableFuture.complete(String.format("Argument \"%s\" was not understood. Please refer to \"/IslandSurvivalCraft help\" for more info.", args[0]));
}
return completableFuture;
}
@Override
public void initialize(IslandSurvivalCraft islandsurvivalcraft) {
public void initialize(IslandSurvivalCraft islandsurvivalcraft) {
Bukkit.getScheduler().scheduleSyncRepeatingTask(islandsurvivalcraft, new Runnable() {
@Override
public void run() {
Player player = waitingList.poll();
PlayerIslandHighlighter highlighter = waitingList.poll();
if (highlighter == null) return;
Player player = highlighter.player;
if (player == null || !player.isOnline() || !playersHighlighting.contains(player)) {
playersHighlighting.remove(player);
return;
@ -73,25 +78,74 @@ public class HighlightIslandCommand implements CommandRunnable {
WorldInfo worldInfo = islandsurvivalcraft.getWorldInfoManager().retrieve(world);
Location playerLocation = player.getLocation();
int playerX = playerLocation.getBlockX(), playerY = playerLocation.getBlockY(), playerZ = playerLocation.getBlockZ();
for (int x = 1; x < CHECK_RADIUS; x++) {
for (int z = 1; z < CHECK_RADIUS; z++) {
if (worldInfo.getIslandMap().isIsland(playerX + x, playerZ + z)) {
world.spawnParticle(PARTICLE_TYPE, x + playerX, playerY, z + playerZ, PARTICLE_AMOUNT, OFFSET_X, OFFSET_Y, OFFSET_Z);
}
if (worldInfo.getIslandMap().isIsland(playerX - x, playerZ - z)) {
world.spawnParticle(PARTICLE_TYPE, playerX - x, playerY, playerZ - z, PARTICLE_AMOUNT, OFFSET_X, OFFSET_Y, OFFSET_Z);
}
IslandWorldMap map = worldInfo.getIslandMap();
highlighter.update(new Point2(playerX, playerZ), map);
LinkedList<Point2> highlighted = highlighter.getHighlighted();
for (Point2 point2 : highlighted) {
if (point2 != null) {
spawnParticle(world, point2.x, playerY, point2.y);
}
}
waitingList.add(player);
waitingList.add(highlighter);
}
}, 0, 5);
}, 0, 4);
}
private void spawnParticle(World world, double x, double y, double z) {
world.spawnParticle(Particle.REDSTONE, x, y, z, PARTICLE_AMOUNT, OFFSET_X, OFFSET_Y, OFFSET_Z, EXTRA, OPTIONS, true);
}
@Override
public String getDetailedDescription() {
return "A helpful debugging command to analyze what the plugin considers an island by displaying particle effects around the player that is a part of the island. The <start | stop> argument is required to dicate whether or not to start the highlighting or stop it.";
}
private class PlayerIslandHighlighter {
public final int TRAIL_LENGTH = 64;
private final int MAX_SEARCH = 24;
private final int MAX_SEARCH_DIST = 8;
private final Player player;
private final LinkedList<Point2> highlighted = new LinkedList<>();
private DepthFirstSearch dfs;
public PlayerIslandHighlighter(Player player) {
this.player = player;
}
private void addHighlight(Point2 point) {
highlighted.add(point);
if (highlighted.size() > TRAIL_LENGTH) {
highlighted.poll();
}
}
public void update(Point2 playerLocation, IslandWorldMap islandWorldMap) {
if (dfs == null || dfs.step(islandWorldMap.islandValidator, (p) -> p.distance(playerLocation) > MAX_SEARCH_DIST)) {
dfs = new DepthFirstSearch(playerLocation, null, (p1, p2) -> {
double diff = islandWorldMap.getWorldValue(p1.x, p1.y) - islandWorldMap.getWorldValue(p2.x, p2.y);
if (diff == 0) {
return 0;
} else if (diff > 0) {
return 1;
} else {
return -1;
}
}, MAX_SEARCH);
}
Point2 currentPoint = dfs.getCurrentNode();
if (currentPoint != null && islandWorldMap.isEdgeOfIsland(currentPoint.x, currentPoint.y)) {
addHighlight(currentPoint);
}
}
/**
* @return the highlighted
*/
public LinkedList<Point2> getHighlighted() {
return highlighted;
}
}
}

View File

@ -20,15 +20,7 @@ public class OceanBiomeInfo implements BiomeInfo {
public Biome getOcean() {
return ocean;
}
/**
* @return the transition ocean. Will return the normal biome if no transitional biome is designated.
*/
public Biome getTransitionOcean() {
if (transitionOcean == null) return getOcean();
return transitionOcean;
}
/**
* @return the deep version of this ocean.
*/
@ -43,6 +35,7 @@ public class OceanBiomeInfo implements BiomeInfo {
@Override
public Biome getTransitionBiome() {
return getTransitionBiome();
if (transitionOcean == null) return getOcean();
return transitionOcean;
}
}

View File

@ -12,6 +12,10 @@ public class Point2 {
this.hash = Objects.hash(x, y);
}
public Point2(Point2 point) {
this(point.x, point.y);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Point2) {

View File

@ -1,5 +1,7 @@
package ca.recrown.islandsurvivalcraft.utilities.pathfinding;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
public interface CoordinateTargetValidatable {
/**
* Is the current coordinate the objective?
@ -7,5 +9,5 @@ public interface CoordinateTargetValidatable {
* @param y the y of the coordinate.
* @return true if this is the objective coordinate.
*/
public boolean isCoordinateTarget(int x, int y);
public boolean isCoordinateTarget(Point2 point);
}

View File

@ -1,5 +1,7 @@
package ca.recrown.islandsurvivalcraft.utilities.pathfinding;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
public interface CoordinateValidatable {
public boolean validate(int x, int y);
public boolean validate(Point2 point);
}

View File

@ -1,67 +0,0 @@
package ca.recrown.islandsurvivalcraft.utilities.pathfinding;
import java.util.Objects;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
public class DFSNode extends Point2 implements Comparable<DFSNode> {
private DFSNode goal;
public DFSNode[] child;
private DFSNode parent;
public DFSNode(DFSNode parent, int x, int y, DFSNode goal) {
super(x, y);
child = new DFSNode[4];
this.parent = parent;
this.goal = goal;
}
public DFSNode(DFSNode parent, Point2 coords, DFSNode goal) {
this(parent, coords.x, coords.y, goal);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public DFSNode getGoal() {
return goal;
}
@Override
public int compareTo(DFSNode o) {
if (goal == null) return 0;
return Math.round(distanceToGoal(goal) - o.distanceToGoal(goal));
}
public DFSNode getParent() {
return parent;
}
public float distanceToGoal(DFSNode goal) {
float distanceX = goal.x - this.x;
float distanceY = goal.y - this.y;
float distance = (float) Math.sqrt(distanceX * distanceX + distanceY * distanceY);
return distance;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DFSNode) {
DFSNode comp = (DFSNode) obj;
if (comp.x == this.x && comp.y == this.y) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}

View File

@ -4,127 +4,108 @@ import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Queue;
import org.apache.commons.lang.NullArgumentException;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
public class DepthFirstSearch {
private Queue<DFSNode> queue;
private CoordinateValidatable coordValidatable;
private DFSNode startNode;
private DFSNode endNode;
private HashSet<DFSNode> checkedNodes;
private int maxNodesSearched = -1;
private DFSNode foundNode;
private final HashSet<Node> checkedNodes;
private final NodeComparable comparable;
private final Queue<Node> queue;
private final Node startNode;
private final Point2 endGoal;
private final int maxNodesSearched;
private Node currentValidNode;
private Node results;
public DepthFirstSearch() {
/**
* Instantiates a DFS search object.
* @param start The starting point for this search.
* @param endGoal The objective. May be null, but a target validator will then be required.
* @param customComparable The comparable to use to compare points. May be null, where then there will be no prioritization of direction.
* @param maxNodesSearched The maximum amount of allowed searched nodes. set to 0 for infinite.
*/
public DepthFirstSearch(Point2 start, Point2 endGoal, NodeComparable customComparable, int maxNodesSearched) {
if (start == null) throw new NullPointerException("Start point cannot be null.");
checkedNodes = new HashSet<>();
queue = new PriorityQueue<>();
}
public DepthFirstSearch(int maxNodesSearched) {
this();
this.comparable = customComparable;
this.endGoal = endGoal;
this.startNode = new Node(null, start, endGoal, customComparable);
queue.add(startNode);
this.maxNodesSearched = maxNodesSearched;
}
public DepthFirstSearch(int searchRadius, CoordinateValidatable validatable) {
this(searchRadius);
setValidatable(validatable);
/**
* Instantiates a DFS search object.
* Where there is no comparable set and distance to goal will be used if there is one,
* and where there is no limit on amount of searched nodes.
* @param start The starting point of the search.
* @param endGoal The objective of the search, may be null.
*/
public DepthFirstSearch(Point2 start, Point2 endGoal) {
this(start, endGoal, null, 0);
}
public DepthFirstSearch(CoordinateValidatable validatable) {
this();
setValidatable(validatable);
}
public void setValidatable(CoordinateValidatable validatable) {
this.coordValidatable = validatable;
}
public void setStartPosition(int x, int y) {
startNode = new DFSNode(null, x, y, endNode);
}
public void setStartPosition(Point2 start) {
startNode = new DFSNode(null, start, endNode);
}
public void setEndPosition(int x, int y) {
endNode = new DFSNode(null, x, y, endNode);
}
public void setEndPosition(Point2 goal) {
endNode = new DFSNode(null, goal, endNode);
}
public DFSNode getFoundNode() {
return foundNode;
}
public void deleteTree() {
startNode = null;
endNode = null;
}
public boolean buildPathToEndNode() {
if (coordValidatable == null)
throw new IllegalStateException("Need to set a validator.");
if (startNode == null)
throw new IllegalStateException("Need to set the starting position.");
return findTarget(new CoordinateTargetValidatable() {
@Override
public boolean isCoordinateTarget(int x, int y) {
return endNode.getX() == x && endNode.getY() == y;
}
});
public Node getResult() {
return results;
}
/**
* Finds a coordinate determined by a target validator. If an end node is set,
* it will try going in that direction first.
*
* @param targetValidator
* @return If it succeeded in finding an end node.
* @return the currentNode
*/
public boolean findTarget(CoordinateTargetValidatable targetValidator) {
if (coordValidatable == null)
throw new IllegalStateException("Need to set a validator.");
if (targetValidator == null)
throw new NullArgumentException("targetValidator");
if (startNode == null)
throw new IllegalStateException("Need to set the starting position.");
public Node getCurrentNode() {
return currentValidNode;
}
/**
* Makes a search step.
* @param validity the validator.
* @param objective used to determine whether or not this DFS has founds its objective. Will use this over using the end goal.
* @return true if done, whether or not it found the objective is determined by the result.
*/
public boolean step(CoordinateValidatable validity, CoordinateTargetValidatable objective) {
if (validity == null) throw new NullPointerException("The validator cannot be null.");
Node node = queue.poll();
if (node == null) return true;
queue.clear();
checkedNodes.clear();
DFSNode begin = startNode;
if (!coordValidatable.validate(begin.x, begin.y)) return false;
if (targetValidator.isCoordinateTarget(begin.x, begin.y)) return true;
checkedNodes.add(begin);
queue.add(begin);
while (!queue.isEmpty()) {
DFSNode n = queue.poll();
if (maxNodesSearched != -1 && checkedNodes.size() > maxNodesSearched) return false;
n.child[0] = new DFSNode(n, n.getX() + 1, n.getY(), endNode);
n.child[1] = new DFSNode(n, n.getX() - 1, n.getY(), endNode);
n.child[2] = new DFSNode(n, n.getX(), n.getY() + 1, endNode);
n.child[3] = new DFSNode(n, n.getX(), n.getY() - 1, endNode);
for (int c = 0; c < n.child.length; c++) {
if (checkedNodes.add(n.child[c]) && coordValidatable.validate(n.child[c].x, n.child[c].y)) {
if (targetValidator.isCoordinateTarget(n.child[c].x, n.child[c].y)) {
foundNode = n.child[c];
return true;
}
queue.add(n.child[c]);
if (checkedNodes.add(node) && validity.validate(node)) {
currentValidNode = node;
if (objective != null) {
if (objective.isCoordinateTarget(currentValidNode)) {
results = currentValidNode;
return true;
}
} else {
if (endGoal == null) throw new IllegalStateException("Cannot build path without a target or goal.");
if (endGoal.fastEquals(currentValidNode)) {
results = currentValidNode;
return true;
}
}
if (maxNodesSearched > 0 && getSearchedCount() >= maxNodesSearched) return true;
Node north = new Node(currentValidNode, currentValidNode.x, currentValidNode.y + 1, endGoal, comparable);
queue.add(north);
Node south = new Node(currentValidNode, currentValidNode.x, currentValidNode.y - 1, endGoal, comparable);
queue.add(south);
Node west = new Node(currentValidNode, currentValidNode.x - 1, currentValidNode.y, endGoal, comparable);
queue.add(west);
Node east = new Node(currentValidNode, currentValidNode.x + 1, currentValidNode.y, endGoal, comparable);
queue.add(east);
}
return false;
}
/**
* Searches until complete. Completion is defined such that when a call to the step function does not return true.
* @param validator The validator to use to check if a node is considered allowed to be used.
* @param targetValidatable The validator to be used to determine whether or not the current node is the objective. May be null, however, the end goal must not be null in such a case.
* @return
*/
public boolean buildToGoal(CoordinateValidatable validator, CoordinateTargetValidatable targetValidatable) {
while (!step(validator, targetValidatable));
return results != null;
}
public int getSearchedCount() {
return checkedNodes.size();
}

View File

@ -0,0 +1,61 @@
package ca.recrown.islandsurvivalcraft.utilities.pathfinding;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
public class Node extends Point2 implements Comparable<Point2> {
private final NodeComparable nodeComparable;
private final Point2 parent;
private final Point2 goal;
private Point2 child;
public Node(Point2 parent, int x, int y, Point2 goal, NodeComparable nodeComparable) {
super(x, y);
this.parent = parent;
this.goal = goal;
this.nodeComparable = nodeComparable;
}
public Node(Point2 parent, Point2 location, Point2 goal, NodeComparable nodeComparable) {
this(parent, location.x, location.y, goal, nodeComparable);
}
/**
* Sets the child of this node. Can only be set once.
* @param child the child to set.
*/
public void setChild(Point2 child) {
if (this.child != null) throw new IllegalStateException("Child has already been set.");
this.child = child;
}
@Override
public int compareTo(Point2 o) {
if (nodeComparable != null) return nodeComparable.compare(this, o);
if (goal == null) return 0;
double distDiff = distance(goal) - distance(o);
if (distDiff == 0) {
return 0;
} else if (distDiff < 0) {
return -1;
} else {
return 1;
}
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
/**
* @return the parent
*/
public Point2 getParent() {
return parent;
}
}

View File

@ -0,0 +1,7 @@
package ca.recrown.islandsurvivalcraft.utilities.pathfinding;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
public interface NodeComparable {
public int compare(Point2 current, Point2 other);
}

View File

@ -12,25 +12,22 @@ import ca.recrown.islandsurvivalcraft.utilities.pathfinding.CoordinateValidatabl
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.DepthFirstSearch;
public class IslandWorldMap {
private final Cache<Point2, Double> blockValueCache;
private final float TRANSITION_DEPTH_PORTION = 0.1f;
private final float DEEP_OCEAN_PORTION = 0.5f;
private final float HILL_PORTION = 0.4f;
private final float SHORE_PORTION = 0.095f;
private final Cache<Point2, Double> blockValueCache;
private final SimplexOctaveGenerator noiseGenerator;
public final islandValidator islandValidator = new islandValidator();
private final int NOISE_OCTAVES = 4;
private final float ISLAND_PERCENT = 0.36f;
private final double NOISE_FREQ = 1.78D;
private final double NOISE_AMP = 0.47D;
private final float SHALLOW_PORTION = 0.06f;
private final double SCALE = 0.005D;
private final DepthFirstSearch dfs;
public IslandWorldMap(Random random) {
dfs = new DepthFirstSearch(new CoordinateValidatable(){
@Override
public boolean validate(int x, int y) {
return isIsland(x, y);
}
});
this.noiseGenerator = new SimplexOctaveGenerator(random, NOISE_OCTAVES);
noiseGenerator.setScale(SCALE);
this.blockValueCache = new Cache<>(131072);
@ -57,10 +54,10 @@ public class IslandWorldMap {
*/
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, worldZ - 1));
!(isIsland(worldX + 1, worldZ) &&
isIsland(worldX - 1, worldZ) &&
isIsland(worldX , worldZ + 1) &&
isIsland(worldX, worldZ - 1));
}
/**
@ -74,6 +71,22 @@ public class IslandWorldMap {
return isLand(worldX, worldZ) || isShallowPortion(worldX, worldZ);
}
public boolean isHill(int worldX, int worldZ) {
return getWorldValue(worldX, worldZ) >= HILL_PORTION;
}
public boolean isShore(int worldX, int worldZ) {
return isLand(worldX, worldZ) && getWorldValue(worldX, worldZ) <= SHORE_PORTION;
}
public boolean isTransitional(int worldX, int worldZ) {
return !isIsland(worldX, worldZ) && getWorldValue(worldX, worldZ) >= -TRANSITION_DEPTH_PORTION;
}
public boolean isDeep(int worldX, int worldZ) {
return getWorldValue(worldX, worldZ) <= -DEEP_OCEAN_PORTION;
}
/**
* This block is considered a shallow portion of the island:
* The block height is less than the sea level,
@ -123,22 +136,16 @@ public class IslandWorldMap {
}
/**
* 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.
* Determines of the two columns are part of the same island.
* If both points are connected consistently by island, than both are considered part of the same island.
* @param firstColumn The column of the first island.
* @param secondColumn The column of the second island.
* @return If the two islands are connected.
*/
public boolean isSameIsland(int firstBlockX, int firstBlockZ, int secondBlockX, int secondBlockZ) {
if (!isIsland(firstBlockX, firstBlockZ)) return false;
if (!isIsland(secondBlockX, secondBlockZ)) return false;
dfs.setStartPosition(firstBlockX, firstBlockZ);
dfs.setEndPosition(secondBlockX, secondBlockZ);
boolean res = dfs.buildPathToEndNode();
dfs.deleteTree();
return res;
public boolean isSameIsland(Point2 firstColumn, Point2 secondColumn) {
DepthFirstSearch dfs = new DepthFirstSearch(firstColumn, secondColumn);
boolean same = dfs.buildToGoal(islandValidator, null);
return same;
}
/**
@ -150,22 +157,21 @@ public class IslandWorldMap {
*/
public Point2 findIslandOrigin(int worldX, int worldZ, CoordinateTargetValidatable coordinateTargetValidatable) {
if (!isIsland(worldX, worldZ)) throw new IllegalArgumentException("The given coordinates are not part is an island.");
dfs.setStartPosition(worldX, worldZ);
final Point2 goal = new Point2(0, 0);
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(worldX, worldZ), goal);
Reference<Point2> closest = new Reference<>();
dfs.findTarget(new CoordinateTargetValidatable(){
dfs.buildToGoal(islandValidator, new CoordinateTargetValidatable(){
Double closestDistance = null;
@Override
public boolean isCoordinateTarget(int x, int y) {
if (coordinateTargetValidatable != null && coordinateTargetValidatable.isCoordinateTarget(x, y)) {
public boolean isCoordinateTarget(Point2 point) {
if (coordinateTargetValidatable != null && coordinateTargetValidatable.isCoordinateTarget(point)) {
closest.value = null;
return true;
}
Point2 current = new Point2(x, y);
double currentDistance = current.distance(goal);
double currentDistance = point.distance(goal);
if (closestDistance == null || (currentDistance) < closestDistance) {
closestDistance = currentDistance;
closest.value = current;
closest.value = point;
}
return false;
}
@ -181,4 +187,13 @@ public class IslandWorldMap {
public int calculateIslandHash(int worldX, int worldZ) {
return findIslandOrigin(worldX, worldZ).hashCode();
}
private class islandValidator implements CoordinateValidatable {
@Override
public boolean validate(Point2 point) {
return isIsland(point.x, point.y);
}
}
}

View File

@ -36,8 +36,7 @@ public class UniqueBiomeGenerator implements BiomeGenerator {
Reference<BiomeInfo> currentIslandInfo = new Reference<>();
Point2 islandOrigin = islandMap.findIslandOrigin(worldX, worldZ, new CoordinateTargetValidatable() {
@Override
public boolean isCoordinateTarget(int x, int y) {
Point2 coord = new Point2(x, y);
public boolean isCoordinateTarget(Point2 coord) {
BiomeInfo info = biomeCache.get(coord);
if (info == null) return false;
currentIslandInfo.value = info;

View File

@ -30,10 +30,6 @@ import ca.recrown.islandsurvivalcraft.world.generation.shaders.WorldHeightShader
import ca.recrown.islandsurvivalcraft.world.generation.shaders.WorldLayerShader;
public class IslandWorldChunkGenerator extends ChunkGenerator implements Listener {
private final float TRANSITION_DEPTH_PORTION = 0.1f;
private final float DEEP_OCEAN_PORTION = 0.5f;
private final float HILL_BIOME_PORTION = 0.4f;
private final float SHORE_PORTION = 0.095f;
private final int BEDROCK_HEIGHT = 5;
private volatile GeneratorModes generatorType;
private volatile boolean initialized = false;
@ -106,29 +102,22 @@ public class IslandWorldChunkGenerator extends ChunkGenerator implements Listene
}
if (localBiomeInfos[localX][localZ] == null) throw new IllegalStateException("Biome column produced was null.");
Biome currentBiome = null;
if (islandMap.isIsland(worldX, worldZ)) {
LandBiomeInfo biomeInfo = (LandBiomeInfo) localBiomeInfos[localX][localZ];
double islandVal = islandMap.getWorldValue(worldX, worldZ);
if (islandVal >= HILL_BIOME_PORTION) {
currentBiome = biomeInfo.getHillBiome();
} else {
if (islandVal <= SHORE_PORTION) {
currentBiome = biomeInfo.getShoreBiome();
} else {
currentBiome = biomeInfo.getMainBiome();
}
}
BiomeInfo biomeInfo = localBiomeInfos[localX][localZ];
if (islandMap.isHill(worldX, worldZ)) {
currentBiome = ((LandBiomeInfo) biomeInfo).getHillBiome();
} else if (islandMap.isShore(worldX, worldZ)) {
currentBiome = ((LandBiomeInfo) biomeInfo).getShoreBiome();
} else if (islandMap.isLand(worldX, worldZ)) {
currentBiome = biomeInfo.getMainBiome();
} else if (islandMap.isTransitional(worldX, worldZ)) {
currentBiome = biomeInfo.getTransitionBiome();
} else if (islandMap.isDeep(worldX, worldZ)) {
currentBiome = ((OceanBiomeInfo) biomeInfo).getDeepAlternative();
} else {
OceanBiomeInfo biomeInfo = (OceanBiomeInfo) localBiomeInfos[localX][localZ];
double oceanVal = islandMap.getWorldValue(worldX, worldZ);
if (oceanVal >= -TRANSITION_DEPTH_PORTION) {
currentBiome = biomeInfo.getTransitionOcean();
} else if (oceanVal >= -DEEP_OCEAN_PORTION) {
currentBiome = biomeInfo.getOcean();
} else {
currentBiome = biomeInfo.getDeepAlternative();
}
currentBiome = biomeInfo.getMainBiome();
}
for (int y = 0; y < worldInfo.getWorldHeight(); y++) {
biomeGrid.setBiome(localX, y, localZ, currentBiome);
}

View File

@ -30,6 +30,7 @@ public class WorldHeightShader {
height = (int) Math.floor(calculateTerrainFactor(worldX, worldZ, seaLevel * 0.8d, 1.7d, 0.5d, 1d, 0.09d));
} else {
height = getIslandBiomeHeight(worldX, worldZ, biomeInfo.getMainBiome(), 0d);
if (!mapper.isShore(worldX, worldZ)) height ++;
}
height = Math.max(minimumHeight, height);

View File

@ -14,6 +14,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import ca.recrown.islandsurvivalcraft.utilities.datatypes.Point2;
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.CoordinateTargetValidatable;
import ca.recrown.islandsurvivalcraft.utilities.pathfinding.CoordinateValidatable;
@ -31,26 +32,24 @@ public class DepthFirstSearchTest {
}
@Override
public boolean validate(int x, int y) {
public boolean validate(Point2 point) {
try {
return map[y][x] >= 1;
return map[point.y][point.x] >= 1;
} catch (IndexOutOfBoundsException e) {
return false;
}
}
@Override
public boolean isCoordinateTarget(int x, int y) {
public boolean isCoordinateTarget(Point2 point) {
try {
return map[y][x] == 2;
return map[point.y][point.x] == 2;
} catch (IndexOutOfBoundsException e) {
return false;
}
}
}
private DepthFirstSearch dfs;
private byte[][] mapA;
private byte[][] mapB;
private byte[][] mapC;
@ -62,7 +61,6 @@ public class DepthFirstSearchTest {
@BeforeAll
public void setUp() throws IOException {
dfs = new DepthFirstSearch();
mapA = readMap("DFSTestMapSmallA.txt");
mapB = readMap("DFSTestMapSmallB.txt");
mapC = readMap("DFSTestMapSmallC.txt");
@ -100,130 +98,126 @@ public class DepthFirstSearchTest {
@Test
public void testDFSBuildPathToEndNodeMapAValid()
{
dfs.setValidatable(new Validator(mapA));
dfs.setStartPosition(1, 2);
dfs.setEndPosition(1, 0);
assertTrue(dfs.buildPathToEndNode());
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(1, 2), new Point2(1, 0));
dfs.buildToGoal(new Validator(mapA), null);
assertTrue(dfs.getResult() != null);
}
@Test
public void testDFSBuildPathToEndNodeMapBValid()
{
dfs.setValidatable(new Validator(mapB));
dfs.setStartPosition(3, 0);
dfs.setEndPosition(3, 2);
assertTrue(dfs.buildPathToEndNode());
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(3, 0), new Point2(3, 2));
dfs.buildToGoal(new Validator(mapB), null);
assertTrue(dfs.getResult() != null);
}
@Test
public void testDFSBuildPathToEndNodeMapDValid()
{
dfs.setValidatable(new Validator(mapD));
dfs.setStartPosition(0, 0);
dfs.setEndPosition(0, 3);
assertTrue(dfs.buildPathToEndNode());
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(0, 0), new Point2(0, 3));
dfs.buildToGoal(new Validator(mapD), null);
assertTrue(dfs.getResult() != null);
}
@Test
public void testDFSBuildPathToEndNodeMapCInvalid()
{
dfs.setValidatable(new Validator(mapC));
dfs.setStartPosition(3, 0);
dfs.setEndPosition(3, 2);
assertFalse(dfs.buildPathToEndNode());
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(3, 0), new Point2(3, 2));
dfs.buildToGoal(new Validator(mapC), null);
assertFalse(dfs.getResult() != null);
}
@Test
public void testDFSBuildPathToEndNodeMapEInvalid()
{
dfs.setValidatable(new Validator(mapE));
dfs.setStartPosition(3, 0);
dfs.setEndPosition(3, 2);
assertFalse(dfs.buildPathToEndNode());
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(3, 0), new Point2(3, 2));
dfs.buildToGoal(new Validator(mapE), null);
assertFalse(dfs.getResult() != null);
}
@Test
public void testDFSFindEndNodeMapBValid() {
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(3, 0), new Point2(0, 0));
Validator validator = new Validator(mapB);
dfs.setValidatable(validator);
dfs.setStartPosition(3, 0);
assertTrue(dfs.findTarget(validator));
assertEquals(1, dfs.getFoundNode().getX());
assertEquals(2, dfs.getFoundNode().getY());
dfs.buildToGoal(validator, validator);
assertTrue(dfs.getResult() != null);
assertEquals(1, dfs.getResult().x);
assertEquals(2, dfs.getResult().y);
}
@Test
public void testDFSFindEndNodeMapDValid() {
Validator validator = new Validator(mapD);
dfs = new DepthFirstSearch(validator);
dfs.setValidatable(validator);
dfs.setStartPosition(0, 0);
assertTrue(dfs.findTarget(validator));
assertEquals(0, dfs.getFoundNode().getX());
assertEquals(3, dfs.getFoundNode().getY());
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(0, 0), new Point2(0, 0));
dfs.buildToGoal(validator, validator);
assertTrue(dfs.getResult() != null);
assertEquals(0, dfs.getResult().x);
assertEquals(3, dfs.getResult().y);
}
@Test
public void testDFSFindEndNodeMapHValid() {
Validator validator = new Validator(mapH);
dfs.setValidatable(validator);
dfs.setStartPosition(3, 0);
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(3, 0), new Point2(0, 0));
dfs.buildToGoal(validator, validator);
assertTrue(dfs.findTarget(validator));
assertEquals(95, dfs.getFoundNode().getX());
assertEquals(49, dfs.getFoundNode().getY());
assertTrue(dfs.getResult() != null);
assertEquals(95, dfs.getResult().x);
assertEquals(49, dfs.getResult().y);
}
@Test
public void testDFSFindEndNodeMapHInvalidLimited() {
Validator validator = new Validator(mapH);
DepthFirstSearch temporary = new DepthFirstSearch(128, validator);
temporary.setStartPosition(3, 0);
assertFalse(temporary.findTarget(validator));
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(3, 0), new Point2(0, 0), null, 128);
dfs.buildToGoal(validator, validator);
assertFalse(dfs.getResult() != null);
}
@Test
public void testDFSFindEndNodeMapEInvalid() {
Validator validator = new Validator(mapE);
dfs = new DepthFirstSearch();
dfs.setValidatable(validator);
dfs.setStartPosition(0, 0);
assertFalse(dfs.findTarget(validator));
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(0, 0), new Point2(0, 0), null, 128);
dfs.buildToGoal(validator, validator);
assertFalse(dfs.getResult() != null);
}
@Test
public void testDFSFindEndNodeMapFValid() {
Validator validator = new Validator(mapF);
dfs.setValidatable(validator);
dfs.setStartPosition(0, 0);
assertTrue(dfs.findTarget(validator));
assertEquals(26, dfs.getFoundNode().getX());
assertEquals(32, dfs.getFoundNode().getY());
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(0, 0), new Point2(0, 0), null, 1024);
dfs.buildToGoal(validator, validator);
assertTrue(dfs.getResult() != null);
assertEquals(26, dfs.getResult().x);
assertEquals(32, dfs.getResult().y);
}
@Test
public void testDFSFindEndNodeMapGInvalid() {
Validator validator = new Validator(mapG);
dfs.setValidatable(validator);
dfs.setStartPosition(0, 0);
assertFalse(dfs.findTarget(validator));
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(0, 0), new Point2(0, 0), null, 128);
dfs.buildToGoal(validator, validator);
assertFalse(dfs.getResult() != null);
}
@Test
public void testDFSFindEndNodeMapHWithAssistValid() {
Validator validator = new Validator(mapH);
dfs.setValidatable(validator);
dfs.setStartPosition(3, 0);
dfs.setEndPosition(42, 84);
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(3, 0), new Point2(95, 49), null, 0);
dfs.buildToGoal(validator, validator);
assertTrue(dfs.getResult() != null);
assertEquals(95, dfs.getResult().x);
assertEquals(49, dfs.getResult().y);
}
assertTrue(dfs.findTarget(validator));
assertEquals(95, dfs.getFoundNode().getX());
assertEquals(49, dfs.getFoundNode().getY());
@Test
public void testDFSFindEndNodeMapHWithoutAssistValid() {
Validator validator = new Validator(mapH);
DepthFirstSearch dfs = new DepthFirstSearch(new Point2(3, 0), new Point2(0, 0), null, 0);
dfs.buildToGoal(validator, validator);
assertTrue(dfs.getResult() != null);
assertEquals(95, dfs.getResult().x);
assertEquals(49, dfs.getResult().y);
}
}

View File

@ -62,7 +62,7 @@ public class IslandWorldMapTest {
Point2 origin = null;
for (int i = 0; i < GRIDSIZE; i++) {
if (origin != null) {
if (mapper.isSameIsland(origin.x, origin.y, i, 0)) {
if (mapper.isSameIsland(origin, new Point2(i, 0))) {
assertEquals(origin, mapper.findIslandOrigin(i, 0), String.format("Looking at (%d, 0)", i));
} else {
origin = null;
@ -80,7 +80,7 @@ public class IslandWorldMapTest {
for (int y = 0; y < gridSize; y++) {
for (int x = 0; x < gridSize; x++) {
if (origin != null) {
if (mapper.isSameIsland(origin.x, origin.y, x, y)) {
if (mapper.isSameIsland(origin, new Point2(x, y))) {
assertEquals(origin, mapper.findIslandOrigin(x, y), String.format("Looking at (%d, %d)", x, y));
} else {
origin = null;