diff --git a/src/java/com/threerings/miso/client/util/AStarPathUtil.java b/src/java/com/threerings/miso/client/util/AStarPathUtil.java
index a9df6df6a..c308b75fe 100644
--- a/src/java/com/threerings/miso/client/util/AStarPathUtil.java
+++ b/src/java/com/threerings/miso/client/util/AStarPathUtil.java
@@ -1,11 +1,13 @@
//
-// $Id: AStarPathUtil.java,v 1.18 2002/06/12 23:30:40 ray Exp $
+// $Id: AStarPathUtil.java,v 1.19 2002/06/22 00:49:15 ray Exp $
package com.threerings.miso.scene.util;
import java.awt.Point;
import java.util.*;
+import com.samskivert.util.HashIntMap;
+
import com.threerings.media.util.MathUtil;
import com.threerings.miso.Log;
@@ -49,7 +51,7 @@ public class AStarPathUtil
AStarInfo info = new AStarInfo(scene, tilewid, tilehei, trav, bx, by);
// set up the starting node
- AStarNode s = getNode(info, ax, ay);
+ AStarNode s = info.getNode(ax, ay);
s.g = 0;
s.h = getDistanceEstimate(ax, ay, bx, by);
s.f = s.g + s.h;
@@ -71,14 +73,14 @@ public class AStarPathUtil
}
// consider each successor of the node
- considerStep(info, n, n.x - 1, n.y - 1, 14);
- considerStep(info, n, n.x, n.y - 1, 10);
- considerStep(info, n, n.x + 1, n.y - 1, 14);
- considerStep(info, n, n.x - 1, n.y, 10);
- considerStep(info, n, n.x + 1, n.y, 10);
- considerStep(info, n, n.x - 1, n.y + 1, 14);
- considerStep(info, n, n.x, n.y + 1, 10);
- considerStep(info, n, n.x + 1, n.y + 1, 14);
+ considerStep(info, n, n.x - 1, n.y - 1, DIAGONAL_COST);
+ considerStep(info, n, n.x, n.y - 1, ADJACENT_COST);
+ considerStep(info, n, n.x + 1, n.y - 1, DIAGONAL_COST);
+ considerStep(info, n, n.x - 1, n.y, ADJACENT_COST);
+ considerStep(info, n, n.x + 1, n.y, ADJACENT_COST);
+ considerStep(info, n, n.x - 1, n.y + 1, DIAGONAL_COST);
+ considerStep(info, n, n.x, n.y + 1, ADJACENT_COST);
+ considerStep(info, n, n.x + 1, n.y + 1, DIAGONAL_COST);
// push the node on the closed list
info.closed.add(n);
@@ -101,15 +103,27 @@ public class AStarPathUtil
AStarInfo info, AStarNode n, int x, int y, int cost)
{
// skip node if it's outside the map bounds or otherwise impassable
- if (!isStepValid(info, n.x, n.y, x, y)) {
+ if (!info.isStepValid(n.x, n.y, x, y)) {
return;
}
+ // if it's offscreen, bang up the cost considerably
+ if (!info.isCoordinateValid(x, y)) {
+ cost += OFFSCREEN_COST;
+ }
+
// calculate the new cost for this node
int newg = n.g + cost;
+ // make sure the cost is reasonable (so we don't go crazy computing
+ // offscreen costs)
+ if (newg > info.maxcost) {
+// Log.info("Rejected costly step.");
+ return;
+ }
+
// retrieve the node corresponding to this location
- AStarNode np = getNode(info, x, y);
+ AStarNode np = info.getNode(x, y);
// skip if it's already in the open or closed list or if its
// actual cost is less than the just-calculated cost
@@ -161,71 +175,6 @@ public class AStarPathUtil
return path;
}
- /**
- * Returns whether moving from the given source to destination
- * coordinates is a valid move.
- */
- protected static boolean isStepValid (
- AStarInfo info, int sx, int sy, int dx, int dy)
- {
- // not traversable if the destination itself fails test
- if (!isTraversable(info, dx, dy)) {
- return false;
- }
-
- // if the step is diagonal, make sure the corners don't impede
- // our progress
- if ((Math.abs(dx - sx) == 1) && (Math.abs(dy - sy) == 1)) {
- return isTraversable(info, dx, sy, sx, dy);
- }
-
- // non-diagonals are always traversable
- return true;
- }
-
- /**
- * Returns whether the given coordinate is valid and traversable.
- */
- protected static boolean isTraversable (AStarInfo info, int x, int y)
- {
- if (isCoordinateValid(info, x, y)) {
- BaseTile tile = info.scene.getBaseTile(x, y);
- return (tile != null) && info.trav.canTraverse(tile);
- }
- return false;
- }
-
- /**
- * Returns whether both of the given coordinates are valid and
- * traversable.
- */
- protected static boolean isTraversable (
- AStarInfo info, int x1, int y1, int x2, int y2)
- {
- return (isTraversable(info, x1, y1) &&
- isTraversable(info, x2, y2));
- }
-
- /**
- * Returns whether the given coordinate is valid based on the
- * dimensions of the map being traversed.
- */
- protected static boolean isCoordinateValid (AStarInfo info, int x, int y)
- {
- return (x >= 0 && y >= 0 && x < info.tilewid && y < info.tilehei);
- }
-
- /**
- * Return the AStarNode object corresponding to the
- * specified tile coordinate. Creates the node and saves it in
- * the node array if this is its first reference.
- */
- protected static AStarNode getNode (AStarInfo info, int x, int y)
- {
- AStarNode n = info.nodes[x][y];
- return (n == null) ? (info.nodes[x][y] = new AStarNode(x, y)) : n;
- }
-
/**
* Return a heuristic estimate of the cost to get from (ax,
* ay) to (bx, by).
@@ -234,10 +183,20 @@ public class AStarPathUtil
{
// we're doing all of our cost calculations based on geometric
// distance times ten
- int xsq = 10 * (bx - ax); xsq = xsq * xsq;
- int ysq = 10 * (by - ay); ysq = ysq * ysq;
- return (int)Math.sqrt(xsq + ysq);
+ int xsq = bx - ax;
+ int ysq = by - ay;
+ return (int) (ADJACENT_COST * Math.sqrt(xsq * xsq + ysq * ysq));
}
+
+ /** The standard cost to move between nodes. */
+ public static final int ADJACENT_COST = 10;
+
+ /** The cost to move diagonally. */
+ public static final int DIAGONAL_COST = (int) Math.sqrt(
+ (ADJACENT_COST * ADJACENT_COST) * 2);
+
+ /** A big old additional cost incurred for offscreen movement. */
+ public static final int OFFSCREEN_COST = 1000;
}
/**
@@ -255,9 +214,6 @@ class AStarInfo
/** The traverser moving along the path. */
public Traverser trav;
- /** The array of A*-specific node info to match the tile array. */
- public AStarNode nodes[][];
-
/** The set of open nodes being searched. */
public SortedSet open;
@@ -267,6 +223,9 @@ class AStarInfo
/** The destination coordinates in the tile array. */
public int destx, desty;
+ /** The maximum cost of any path that we'll consider. */
+ public int maxcost;
+
public AStarInfo (
DisplayMisoScene scene, int tilewid, int tilehei, Traverser trav,
int destx, int desty)
@@ -279,13 +238,79 @@ class AStarInfo
this.destx = destx;
this.desty = desty;
- // construct the node array
- nodes = new AStarNode[tilewid][tilehei];
+ // compute the maximum cost as the maximum onscreen path plus
+ // the maximum offscreen cost
+ this.maxcost = ((tilewid + tilehei) * AStarPathUtil.ADJACENT_COST) +
+ MAX_OFFSCREEN * AStarPathUtil.OFFSCREEN_COST;
// construct the open and closed lists
open = new TreeSet();
closed = new ArrayList();
}
+
+ /**
+ * Returns whether the given coordinate is valid based on the
+ * dimensions of the map being traversed.
+ */
+ protected boolean isCoordinateValid (int x, int y)
+ {
+ return (x >= 0 && y >= 0 && x < tilewid && y < tilehei);
+ }
+
+ /**
+ * Returns whether moving from the given source to destination
+ * coordinates is a valid move.
+ */
+ protected boolean isStepValid (int sx, int sy, int dx, int dy)
+ {
+ // not traversable if the destination itself fails test
+ if (!isTraversable(dx, dy)) {
+ return false;
+ }
+
+ // if the step is diagonal, make sure the corners don't impede
+ // our progress
+ if ((Math.abs(dx - sx) == 1) && (Math.abs(dy - sy) == 1)) {
+ return isTraversable(dx, sy) && isTraversable(sx, dy);
+ }
+
+ // non-diagonals are always traversable
+ return true;
+ }
+
+ /**
+ * Returns whether the given coordinate is valid and traversable.
+ */
+ protected boolean isTraversable (int x, int y)
+ {
+ if (isCoordinateValid(x, y)) {
+ BaseTile tile = scene.getBaseTile(x, y);
+ return (tile == null) || trav.canTraverse(tile);
+ }
+ return true;
+ }
+
+ /**
+ * Get or create the node for the specified point.
+ */
+ public AStarNode getNode (int x, int y)
+ {
+ // note: this _could_ break for unusual values of x and y.
+ // perhaps use a IntTuple as a key? Bleah.
+ int key = (x << 16) | (y & 0xffff);
+ AStarNode node = (AStarNode) _nodes.get(key);
+ if (node == null) {
+ node = new AStarNode(x, y);
+ _nodes.put(key, node);
+ }
+ return node;
+ }
+
+ /** The nodes being considered in the path. */
+ protected HashIntMap _nodes = new HashIntMap();
+
+ /** The maximum number of offscreen points we'll consider. */
+ protected static final int MAX_OFFSCREEN = 6;
}
/**