diff --git a/forge-gui-mobile/src/forge/screens/planarconquest/ConquestMapScreen.java b/forge-gui-mobile/src/forge/screens/planarconquest/ConquestMapScreen.java index 018903ee464..015ea81665a 100644 --- a/forge-gui-mobile/src/forge/screens/planarconquest/ConquestMapScreen.java +++ b/forge-gui-mobile/src/forge/screens/planarconquest/ConquestMapScreen.java @@ -151,7 +151,7 @@ public class ConquestMapScreen extends FScreen { //if any bordering grid square has been conquered, instead show unconquered color for (ConquestLocation loc : ConquestLocation.getNeighbors(plane, i, r, c)) { - if (planeData.getEventResult(loc.getRegionIndex(), loc.getRow(), loc.getCol()) > 0) { + if (planeData.getEventResult(loc) > 0) { color = UNCONQUERED_COLOR; break; } @@ -264,22 +264,39 @@ public class ConquestMapScreen extends FScreen { } private class MoveAnimation extends ForgeAnimation { + private static final float DURATION_PER_SEGMENT = 0.5f; + private final List path; - private final Vector2 pos; - private int pathIndex; + private final float duration; + private Vector2 pos; + private float progress; private MoveAnimation(List path0) { path = path0; pos = getPosition(path.get(0)); + duration = (path.size() - 1) * DURATION_PER_SEGMENT; } @Override protected boolean advance(float dt) { - return false; + progress += dt; + if (progress >= duration) { + //we've reached our destination, so stop animation + pos = getPosition(path.get(path.size() - 1)); + return false; + } + + int currentSegment = (int)(progress / DURATION_PER_SEGMENT); + float r = (progress - currentSegment * DURATION_PER_SEGMENT) / DURATION_PER_SEGMENT; + Vector2 p1 = getPosition(path.get(currentSegment)); + Vector2 p2 = getPosition(path.get(currentSegment + 1)); + pos = new Vector2((1.0f - r) * p1.x + r * p2.x, (1.0f - r) * p1.y + r * p2.y); + return true; } @Override protected void onEnd(boolean endingAll) { + model.setCurrentLocation(path.get(path.size() - 1)); activeMoveAnimation = null; } } diff --git a/forge-gui/src/main/java/forge/planarconquest/ConquestData.java b/forge-gui/src/main/java/forge/planarconquest/ConquestData.java index 663aeb7eb00..e7ad4be4c38 100644 --- a/forge-gui/src/main/java/forge/planarconquest/ConquestData.java +++ b/forge-gui/src/main/java/forge/planarconquest/ConquestData.java @@ -29,6 +29,7 @@ import forge.util.ItemPool; import java.io.File; import java.util.ArrayList; +import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; @@ -226,26 +227,16 @@ public final class ConquestData { }; public List getPath(ConquestLocation destLoc) { - //if destination isn't traversable, there's no path to reach it - if (!destLoc.isTraversable()) { return null; } - - List path = new ArrayList(); - path.add(currentLocation); //start path with current location - - - - return path; + PathFinder pathFinder = new PathFinder(); + return pathFinder.findPath(destLoc); } private class PathFinder { - private final HashSet closed = new HashSet(); - private final HashSet open = new HashSet(); + private final HashSet closedSet = new HashSet(); + private final HashSet openSet = new HashSet(); private final Node[][] map; - private final ConquestLocation destLoc; - - private PathFinder(ConquestLocation destLoc0) { - destLoc = destLoc0; + private PathFinder() { ConquestPlane plane = getCurrentPlane(); int xMax = Region.COLS_PER_REGION; int yMax = plane.getRegions().size() * Region.ROWS_PER_REGION + 2; @@ -257,11 +248,104 @@ public final class ConquestData { } } - private class Node { - private final ConquestLocation loc; - private boolean blocked; + public List findPath(ConquestLocation destLoc) { + Node goal = getNode(destLoc); + if (goal.isBlocked()) { return null; } //if goal is blocked, there's no path to reach it + + Node start = getNode(getCurrentLocation()); + openSet.add(start); + start.g_score = 0; + start.f_score = start.g_score + distance(start, goal); + + Node current; + while (!openSet.isEmpty()) { + //find node in open set with lowest f_score + current = null; + for (Node node : openSet) { + if (current == null || node.f_score < current.f_score) { + current = node; + } + } + + //if we've reach goal, reconstruct path and return it + if (current == goal) { + List path = new ArrayList(); + while (current != null) { + path.add(current.loc); + current = current.came_from; + } + Collections.reverse(path); //reverse path so it begins with start location + return path; + } + + //move that node from open set to closed set + openSet.remove(current); + closedSet.add(current); + + //check neighbors for path + checkNeighbor(current, goal, current.x - 1, current.y); + checkNeighbor(current, goal, current.x + 1, current.y); + checkNeighbor(current, goal, current.x, current.y - 1); + checkNeighbor(current, goal, current.x, current.y + 1); + } + return null; + } + + private void checkNeighbor(Node current, Node goal, int x, int y) { + if (x < 0 || x >= map.length) { return; } + Node[] column = map[x]; + if (y < 0 || y >= column.length) { return; } + Node neighbor = column[y]; + if (neighbor.isBlocked()) { return; } + if (closedSet.contains(neighbor)) { return; } + + int g_score = current.g_score + 1; + if (!openSet.contains(neighbor)) { + openSet.add(neighbor); + } + else if (g_score >= neighbor.g_score) { + return; //not a better path + } + + //this path is the best, so record it + neighbor.came_from = current; + neighbor.g_score = g_score; + neighbor.f_score = neighbor.g_score + distance(neighbor, goal); + } + + private int distance(Node from, Node to) { + return Math.abs(from.x - to.x) + Math.abs(from.y - to.y); + } + + private Node getNode(ConquestLocation loc) { + int x = loc.getCol(); + int y; + int regionCount = loc.getPlane().getRegions().size(); + int regionIndex = loc.getRegionIndex(); + if (regionIndex == -1) { + y = 0; + } + else if (regionIndex == regionCount) { + y = map[x].length - 1; + } + else { + y = regionIndex * Region.ROWS_PER_REGION + loc.getRow() + 1; + } + return map[x][y]; + } + + private class Node { + private final int x, y; + private final ConquestLocation loc; + private int g_score = Integer.MAX_VALUE; + private int f_score = Integer.MAX_VALUE; + private Node came_from; + private Boolean blocked = null; + + public Node(ConquestPlane plane, int x0, int y0) { + x = x0; + y = y0; - public Node(ConquestPlane plane, int x, int y) { int row; int col = x; int rowIndex = y - 1; @@ -280,6 +364,29 @@ public final class ConquestData { } loc = new ConquestLocation(plane, regionIndex, row, col); } + + public boolean isBlocked() { + if (blocked == null) { //determine if node is blocked one time + blocked = false; //assume not blocked by default + if (!loc.isTraversable()) { + blocked = true; + } + else { + //if location isn't conquered or bordering a conquered location, there's no path to reach it + ConquestPlaneData planeData = getCurrentPlaneData(); + if (planeData.getEventResult(loc) == 0) { + blocked = true; + for (ConquestLocation neighbor : loc.getNeighbors()) { + if (planeData.getEventResult(neighbor) > 0) { + blocked = false; + break; + } + } + } + } + } + return blocked; + } } } } diff --git a/forge-gui/src/main/java/forge/planarconquest/ConquestLocation.java b/forge-gui/src/main/java/forge/planarconquest/ConquestLocation.java index d87f00ea131..2164a81f4f9 100644 --- a/forge-gui/src/main/java/forge/planarconquest/ConquestLocation.java +++ b/forge-gui/src/main/java/forge/planarconquest/ConquestLocation.java @@ -10,6 +10,7 @@ public class ConquestLocation { private int regionIndex; private int row; private int col; + private List neighbors; public ConquestLocation() { } @@ -52,7 +53,10 @@ public class ConquestLocation { } public List getNeighbors() { - return getNeighbors(plane, regionIndex, row, col); + if (neighbors == null) { //cache neighbors for performance + neighbors = getNeighbors(plane, regionIndex, row, col); + } + return neighbors; } public static List getNeighbors(ConquestPlane plane0, int regionIndex0, int row0, int col0) { diff --git a/forge-gui/src/main/java/forge/planarconquest/ConquestPlaneData.java b/forge-gui/src/main/java/forge/planarconquest/ConquestPlaneData.java index 632c6059082..a159dda7ed8 100644 --- a/forge-gui/src/main/java/forge/planarconquest/ConquestPlaneData.java +++ b/forge-gui/src/main/java/forge/planarconquest/ConquestPlaneData.java @@ -19,6 +19,9 @@ public class ConquestPlaneData { eventResults = new int[plane.getRegions().size()][Region.ROWS_PER_REGION][Region.COLS_PER_REGION]; } + public int getEventResult(ConquestLocation loc) { + return getEventResult(loc.getRegionIndex(), loc.getRow(), loc.getCol()); + } public int getEventResult(int regionIndex, int row, int col) { if (regionIndex == -1) { return 1; //bottom portal is always conquered