diff --git a/src/java/com/threerings/cast/CharacterSprite.java b/src/java/com/threerings/cast/CharacterSprite.java
index a35b1bd9b..6d5884e22 100644
--- a/src/java/com/threerings/cast/CharacterSprite.java
+++ b/src/java/com/threerings/cast/CharacterSprite.java
@@ -1,16 +1,19 @@
//
-// $Id: CharacterSprite.java,v 1.4 2001/08/14 23:35:22 mdb Exp $
+// $Id: CharacterSprite.java,v 1.5 2001/08/15 02:30:27 shaper Exp $
-package com.threerings.media.sprite;
+package com.threerings.miso.scene;
-import com.threerings.media.Log;
+import com.threerings.media.sprite.*;
+import com.threerings.miso.Log;
+import com.threerings.miso.tile.Tile;
+import com.threerings.miso.tile.Traverser;
/**
* An AmbulatorySprite is a sprite that can face in one of
* the various compass directions and that can animate itself walking
* along some chosen path.
*/
-public class AmbulatorySprite extends Sprite
+public class AmbulatorySprite extends Sprite implements Traverser
{
/**
* Construct an AmbulatorySprite, with a multi-frame
@@ -61,6 +64,12 @@ public class AmbulatorySprite extends Sprite
setAnimationDelay(0);
}
+ public boolean canTraverse (Tile tile)
+ {
+ // by default, passability is solely the province of the tile
+ return tile.passable;
+ }
+
/** The animation frames for the sprite facing each direction. */
protected MultiFrameImage[] _anims;
diff --git a/src/java/com/threerings/media/tile/Tile.java b/src/java/com/threerings/media/tile/Tile.java
index 80f281771..78b507e48 100644
--- a/src/java/com/threerings/media/tile/Tile.java
+++ b/src/java/com/threerings/media/tile/Tile.java
@@ -1,5 +1,5 @@
//
-// $Id: Tile.java,v 1.9 2001/08/13 19:54:39 shaper Exp $
+// $Id: Tile.java,v 1.10 2001/08/15 02:30:27 shaper Exp $
package com.threerings.miso.tile;
@@ -30,10 +30,10 @@ public class Tile
/**
* Construct a new tile with the specified identifiers. Intended
- * only for use by the TileManager. Do not call this
+ * only for use by the TileSet. Do not call this
* method.
*
- * @see TileManager#getTile
+ * @see TileSet#getTile
*/
public Tile (int tsid, int tid)
{
diff --git a/src/java/com/threerings/miso/client/IsoSceneView.java b/src/java/com/threerings/miso/client/IsoSceneView.java
index d485d5250..390c0523d 100644
--- a/src/java/com/threerings/miso/client/IsoSceneView.java
+++ b/src/java/com/threerings/miso/client/IsoSceneView.java
@@ -1,5 +1,5 @@
//
-// $Id: IsoSceneView.java,v 1.44 2001/08/15 01:08:49 mdb Exp $
+// $Id: IsoSceneView.java,v 1.45 2001/08/15 02:30:27 shaper Exp $
package com.threerings.miso.scene;
@@ -15,6 +15,7 @@ import com.threerings.media.sprite.*;
import com.threerings.miso.Log;
import com.threerings.miso.tile.Tile;
import com.threerings.miso.tile.TileManager;
+import com.threerings.miso.scene.util.AStarPathUtil;
import com.threerings.miso.scene.util.IsoUtil;
/**
@@ -539,7 +540,7 @@ public class IsoSceneView implements EditableSceneView
_model.precalculate();
}
- public Path getPath (Sprite sprite, int x, int y)
+ public Path getPath (AmbulatorySprite sprite, int x, int y)
{
// make sure the destination point is within our bounds
if (x < 0 || x >= _model.bounds.width ||
@@ -550,13 +551,43 @@ public class IsoSceneView implements EditableSceneView
// constrain destination pixels to fine coordinates
Point fpos = new Point();
IsoUtil.screenToFull(_model, x, y, fpos);
- Point spos = new Point();
- IsoUtil.fullToScreen(_model, fpos.x, fpos.y, spos);
+// Point spos = new Point();
+// IsoUtil.fullToScreen(_model, fpos.x, fpos.y, spos);
- // create path from current loc to destination
+ // calculate tile coordinates for start and end position
+ Point stpos = new Point();
+ IsoUtil.screenToTile(_model, sprite.x, sprite.y, stpos);
+ int tbx = IsoUtil.fullToTile(fpos.x);
+ int tby = IsoUtil.fullToTile(fpos.y);
+
+ // get a reasonable path from start to end
+ List tilepath =
+ AStarPathUtil.getPath(
+ _scene.tiles, MisoScene.TILE_WIDTH, MisoScene.TILE_HEIGHT,
+ sprite, stpos.x, stpos.y, tbx, tby);
+
+ // construct the path object and starting node
Path path = new Path(sprite.x, sprite.y);
- int dir = IsoUtil.getDirection(_model, sprite.x, sprite.y, x, y);
- path.addNode(spos.x, spos.y, dir);
+
+ // add all nodes on the calculated path
+ Point nspos = new Point();
+ int size = tilepath.size();
+ Point prev = stpos;
+ for (int ii = 0; ii < size; ii++) {
+ Point n = (Point)tilepath.get(ii);
+
+ // determine the direction this node lies in from the
+ // previous node
+ int dir = IsoUtil.getIsoDirection(prev.x, prev.y, n.x, n.y);
+
+ // determine the node's position in screen pixel coordinates
+ IsoUtil.tileToScreen(_model, n.x, n.y, nspos);
+
+ // add the node to the path
+ path.addNode(nspos.x, nspos.y, dir);
+
+ prev = n;
+ }
return path;
}
diff --git a/src/java/com/threerings/miso/client/SceneView.java b/src/java/com/threerings/miso/client/SceneView.java
index 99aed6eed..e00d8846e 100644
--- a/src/java/com/threerings/miso/client/SceneView.java
+++ b/src/java/com/threerings/miso/client/SceneView.java
@@ -1,5 +1,5 @@
//
-// $Id: SceneView.java,v 1.11 2001/08/15 01:08:49 mdb Exp $
+// $Id: SceneView.java,v 1.12 2001/08/15 02:30:27 shaper Exp $
package com.threerings.miso.scene;
@@ -41,5 +41,5 @@ public interface SceneView extends AnimatedView
*
* @return the sprite's path or null if no valid path exists.
*/
- public Path getPath (Sprite sprite, int x, int y);
+ public Path getPath (AmbulatorySprite sprite, int x, int y);
}
diff --git a/src/java/com/threerings/miso/client/Traverser.java b/src/java/com/threerings/miso/client/Traverser.java
new file mode 100644
index 000000000..80f772b94
--- /dev/null
+++ b/src/java/com/threerings/miso/client/Traverser.java
@@ -0,0 +1,23 @@
+//
+// $Id: Traverser.java,v 1.1 2001/08/15 02:30:27 shaper Exp $
+
+package com.threerings.miso.tile;
+
+/**
+ * The Traverser interface should be implemented by
+ * sprites that are going to traverse some path in a scene. This
+ * allows path determination to take into account any special
+ * abilities the traverser may have that alter the traversability of
+ * tiles.
+ */
+public interface Traverser
+{
+ /**
+ * Return whether the traverser can traverse the specified tile.
+ *
+ * @param tile the tile to traverse.
+ *
+ * @return whether the tile is traversable.
+ */
+ public boolean canTraverse (Tile tile);
+}
diff --git a/src/java/com/threerings/miso/client/util/AStarPathUtil.java b/src/java/com/threerings/miso/client/util/AStarPathUtil.java
new file mode 100644
index 000000000..102e7a0b2
--- /dev/null
+++ b/src/java/com/threerings/miso/client/util/AStarPathUtil.java
@@ -0,0 +1,277 @@
+//
+// $Id: AStarPathUtil.java,v 1.1 2001/08/15 02:30:27 shaper Exp $
+
+package com.threerings.miso.scene.util;
+
+import java.awt.Point;
+import java.util.*;
+
+import com.threerings.miso.Log;
+import com.threerings.miso.tile.Tile;
+import com.threerings.miso.tile.Traverser;
+import com.threerings.media.util.MathUtil;
+
+/**
+ * The AStarPathUtil class provides a facility for
+ * finding a reasonable path between two points in a scene using the
+ * A* search algorithm.
+ *
+ *
See the path-finding article on
+ *
+ * Gamasutra for more detailed information.
+ */
+public class AStarPathUtil
+{
+ /**
+ * Return a list of Point objects representing a path
+ * from coordinates (ax, by) to
+ * (bx, by), inclusive, determined by performing an
+ * A* search in the given array of tiles. Assumes the starting
+ * and destination nodes are traversable by the specified
+ * traverser.
+ *
+ * @param tiles the tile array.
+ * @param tilewid the tile array width.
+ * @param tilehei the tile array height.
+ * @param trav the traverser to follow the path.
+ * @param ax the starting x-position in tile coordinates.
+ * @param ay the starting y-position in tile coordinates.
+ * @param bx the ending x-position in tile coordinates.
+ * @param by the ending y-position in tile coordinates.
+ *
+ * @return the list of points in the path.
+ */
+ public static List getPath (
+ Tile tiles[][][], int tilewid, int tilehei, Traverser trav,
+ int ax, int ay, int bx, int by)
+ {
+ AStarInfo info = new AStarInfo(tiles, tilewid, tilehei, trav, bx, by);
+
+ // set up the starting node
+ AStarNode s = getNode(info, ax, ay);
+ s.g = 0;
+ s.h = getDistanceEstimate(ax, ay, bx, by);
+ s.f = s.g + s.h;
+
+ // push starting node on the open list
+ info.open.add(s);
+
+ // while there are more nodes on the open list
+ while (info.open.size() > 0) {
+
+ // pop the best node so far from open
+ AStarNode n = (AStarNode)info.open.first();
+ info.open.remove(n);
+
+ // if node is a goal node
+ if (n.x == bx && n.y == by) {
+ // construct and return the acceptable path
+ return getNodePath(n);
+ }
+
+ // TODO: don't allow diagonal traversal if horiz and vert
+ // are impassable.
+
+ // consider each successor of the node
+ considerStep(info, n, n.x - 1, n.y - 1);
+ considerStep(info, n, n.x, n.y - 1);
+ considerStep(info, n, n.x + 1, n.y - 1);
+ considerStep(info, n, n.x - 1, n.y);
+ considerStep(info, n, n.x + 1, n.y);
+ considerStep(info, n, n.x - 1, n.y + 1);
+ considerStep(info, n, n.x, n.y + 1);
+ considerStep(info, n, n.x + 1, n.y + 1);
+
+ // push the node on the closed list
+ info.closed.add(n);
+ }
+
+ // no path found
+ return null;
+ }
+
+ /**
+ * Consider the step (n.x, n.y) to (x, y)
+ * for possible inclusion in the path.
+ *
+ * @param info the info object.
+ * @param n the originating node for the step.
+ * @param x the x-coordinate for the destination step.
+ * @param y the y-coordinate for the destination step.
+ */
+ protected static void considerStep (
+ AStarInfo info, AStarNode n, int x, int y)
+ {
+ // skip node if it's outside the map bounds
+ if (x < 0 || y < 0 || x >= info.tilewid || y >= info.tilehei) {
+ return;
+ }
+
+ // skip node if it's impassable
+ // TODO: fix hard-coded consideration of only the base layer
+ if (!info.trav.canTraverse(info.tiles[x][y][0])) return;
+
+ // calculate the new cost for this node
+ int newg = n.g + 1; // getCost() is always 1?
+
+ // retrieve the node corresponding to this location
+ AStarNode np = getNode(info, 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
+ // TODO: shouldn't <= below be >=?
+ if ((info.open.contains(np) || info.closed.contains(np)) &&
+ np.g <= newg) {
+ return;
+ }
+
+ // update the node's information
+ np.parent = n;
+ np.g = newg;
+ np.h = getDistanceEstimate(np.x, np.y, info.destx, info.desty);
+ np.f = np.g + np.h;
+
+ // remove it from the closed list if it's present
+ info.closed.remove(np);
+
+ // add it to the open list in case it's not already there
+ info.open.add(np);
+ }
+
+ /**
+ * Return a list of Point objects detailing the path
+ * from the first node (the given node's ultimate parent) to the
+ * ending node (the given node itself.)
+ *
+ * @param n the ending node in the path.
+ *
+ * @return the list detailing the path.
+ */
+ protected static List getNodePath (AStarNode n)
+ {
+ AStarNode cur = n;
+ ArrayList path = new ArrayList();
+
+ while (cur != null) {
+ // add to the head of the list since we're traversing from
+ // the end to the beginning
+ path.add(0, new Point(cur.x, cur.y));
+
+ // advance to the next node in the path
+ cur = cur.parent;
+ }
+
+ return path;
+ }
+
+ /**
+ * 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).
+ */
+ protected static int getDistanceEstimate (int ax, int ay, int bx, int by)
+ {
+ return (int)MathUtil.distance(ax, ay, bx, by);
+ }
+}
+
+/**
+ * A holding class to contain the wealth of information referenced
+ * while performing an A* search for a path through a tile array.
+ */
+class AStarInfo
+{
+ /** The array of tiles being traversed. */
+ public Tile tiles[][][];
+
+ /** The tile array dimensions. */
+ public int tilewid, tilehei;
+
+ /** 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;
+
+ /** The set of closed nodes being searched. */
+ public ArrayList closed;
+
+ /** The destination coordinates in the tile array. */
+ public int destx, desty;
+
+ public AStarInfo (
+ Tile tiles[][][], int tilewid, int tilehei, Traverser trav,
+ int destx, int desty)
+ {
+ // save off references
+ this.tiles = tiles;
+ this.tilewid = tilewid;
+ this.tilehei = tilehei;
+ this.trav = trav;
+ this.destx = destx;
+ this.desty = desty;
+
+ // construct the node array
+ nodes = new AStarNode[tilewid][tilehei];
+
+ // construct the open and closed lists
+ open = new TreeSet();
+ closed = new ArrayList();
+ }
+}
+
+/**
+ * A class that represents a single traversable node in the tile array
+ * along with its current A*-specific search information.
+ */
+class AStarNode implements Comparable
+{
+ /** The node coordinates. */
+ public int x, y;
+
+ /** The actual cheapest cost of arriving here from the start. */
+ public int g;
+
+ /** The heuristic estimate of the cost to the goal from here. */
+ public int h;
+
+ /** The score assigned to this node. */
+ public int f;
+
+ /** The node from which we reached this node. */
+ public AStarNode parent;
+
+ public AStarNode (int x, int y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+
+ public int compareTo (Object o)
+ {
+ int bf = ((AStarNode)o).f;
+
+ if (f == bf){
+ return 0;
+ }
+
+ if (f < bf) {
+ return -1;
+ }
+
+ return 1;
+ }
+}
diff --git a/src/java/com/threerings/miso/client/util/IsoUtil.java b/src/java/com/threerings/miso/client/util/IsoUtil.java
index 4deea2a46..c2de4038e 100644
--- a/src/java/com/threerings/miso/client/util/IsoUtil.java
+++ b/src/java/com/threerings/miso/client/util/IsoUtil.java
@@ -1,5 +1,5 @@
//
-// $Id: IsoUtil.java,v 1.2 2001/08/14 23:35:22 mdb Exp $
+// $Id: IsoUtil.java,v 1.3 2001/08/15 02:30:27 shaper Exp $
package com.threerings.miso.scene.util;
@@ -48,7 +48,7 @@ public class IsoUtil
int tby = bfpos.y / FULL_TILE_FACTOR;
// compare tile coordinates to determine direction
- int dir = getIsoDirection(model, tax, tay, tbx, tby);
+ int dir = getIsoDirection(tax, tay, tbx, tby);
if (dir != Path.DIR_NONE) return dir;
// destination point is in the same tile as the
@@ -62,7 +62,7 @@ public class IsoUtil
int fby = bfpos.y - (tby * FULL_TILE_FACTOR);
// compare fine coordinates to determine direction
- dir = getIsoDirection(model, fax, fay, fbx, fby);
+ dir = getIsoDirection(fax, fay, fbx, fby);
// arbitrarily return southwest if fine coords were also equivalent
return (dir == -1) ? Path.DIR_SOUTHWEST : dir;
@@ -84,8 +84,7 @@ public class IsoUtil
* class's direction constants, or Path.DIR_NONE
* if point B is equivalent to point A.
*/
- public static int getIsoDirection (
- IsoSceneModel model, int ax, int ay, int bx, int by)
+ public static int getIsoDirection (int ax, int ay, int bx, int by)
{
if (bx > ax) {
if (by == ay) return Path.DIR_SOUTH;
@@ -101,6 +100,16 @@ public class IsoUtil
}
}
+ public static int fullToTile (int val)
+ {
+ return (val / FULL_TILE_FACTOR);
+ }
+
+ public static int fullToFine (int val)
+ {
+ return (val - ((val / FULL_TILE_FACTOR) * FULL_TILE_FACTOR));
+ }
+
/**
* Convert the given screen-based pixel coordinates to their
* corresponding tile-based coordinates. Converted coordinates
diff --git a/src/java/com/threerings/miso/client/util/PathUtil.java b/src/java/com/threerings/miso/client/util/PathUtil.java
deleted file mode 100644
index 3f5745a4d..000000000
--- a/src/java/com/threerings/miso/client/util/PathUtil.java
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-// $Id: PathUtil.java,v 1.1 2001/08/14 21:29:40 shaper Exp $
-
-package com.threerings.miso.scene.util;
-
-import java.util.ArrayList;
-
-import com.threerings.miso.Log;
-import com.threerings.miso.tile.Tile;
-
-/**
- * The PathUtil class provides utility routines for
- * finding a reasonable path between two points in a scene.
- */
-public class PathUtil
-{
- /**
- * Return a list of Point objects representing a path
- * from coordinates (ax, by) to (bx, by), inclusive, determined by
- * performing an A* search in the given array of tiles.
- *
- * @param tiles the tile array.
- * @param ax the starting x-position in tile coordinates.
- * @param ay the starting y-position in tile coordinates.
- * @param bx the ending x-position in tile coordinates.
- * @param by the ending y-position in tile coordinates.
- *
- * @return the list of points in the path.
- */
- public static ArrayList getAStarPath (
- Tile tiles[][], int ax, int ay, int bx, int by)
- {
- return null;
- }
-}
diff --git a/src/java/com/threerings/miso/tile/TileUtil.java b/src/java/com/threerings/miso/tile/TileUtil.java
index 89cfc0dbc..3a53ef55c 100644
--- a/src/java/com/threerings/miso/tile/TileUtil.java
+++ b/src/java/com/threerings/miso/tile/TileUtil.java
@@ -1,13 +1,13 @@
//
-// $Id: TileUtil.java,v 1.1 2001/08/14 23:35:22 mdb Exp $
+// $Id: TileUtil.java,v 1.2 2001/08/15 02:30:27 shaper Exp $
package com.threerings.miso.tile;
import java.awt.Image;
-import com.threerings.media.sprite.AmbulatorySprite;
import com.threerings.media.sprite.MultiFrameImage;
import com.threerings.media.sprite.Path;
+import com.threerings.miso.scene.AmbulatorySprite;
/**
* Tile-related utility functions.
diff --git a/tests/src/java/com/threerings/miso/viewer/ViewerFrame.java b/tests/src/java/com/threerings/miso/viewer/ViewerFrame.java
index 3a54aee2f..e7d51f405 100644
--- a/tests/src/java/com/threerings/miso/viewer/ViewerFrame.java
+++ b/tests/src/java/com/threerings/miso/viewer/ViewerFrame.java
@@ -1,5 +1,5 @@
//
-// $Id: ViewerFrame.java,v 1.11 2001/08/15 01:08:49 mdb Exp $
+// $Id: ViewerFrame.java,v 1.12 2001/08/15 02:30:28 shaper Exp $
package com.threerings.miso.viewer;
@@ -13,6 +13,7 @@ import com.samskivert.swing.*;
import com.threerings.media.sprite.*;
import com.threerings.miso.Log;
+import com.threerings.miso.scene.AmbulatorySprite;
import com.threerings.miso.tile.TileManager;
import com.threerings.miso.tile.TileUtil;
import com.threerings.miso.viewer.util.ViewerContext;
diff --git a/tests/src/java/com/threerings/miso/viewer/ViewerSceneViewPanel.java b/tests/src/java/com/threerings/miso/viewer/ViewerSceneViewPanel.java
index 4b447eac0..ee7af4999 100644
--- a/tests/src/java/com/threerings/miso/viewer/ViewerSceneViewPanel.java
+++ b/tests/src/java/com/threerings/miso/viewer/ViewerSceneViewPanel.java
@@ -1,5 +1,5 @@
//
-// $Id: ViewerSceneViewPanel.java,v 1.9 2001/08/15 00:10:58 mdb Exp $
+// $Id: ViewerSceneViewPanel.java,v 1.10 2001/08/15 02:30:28 shaper Exp $
package com.threerings.miso.viewer;
@@ -24,7 +24,7 @@ public class ViewerSceneViewPanel extends SceneViewPanel
* Construct the panel and initialize it with a context.
*/
public ViewerSceneViewPanel (
- ViewerContext ctx, SpriteManager spritemgr, Sprite sprite)
+ ViewerContext ctx, SpriteManager spritemgr, AmbulatorySprite sprite)
{
super(ctx.getTileManager(), spritemgr);
@@ -120,7 +120,7 @@ public class ViewerSceneViewPanel extends SceneViewPanel
AnimationManager _animmgr;
/** The sprite we're manipulating within the view. */
- protected Sprite _sprite;
+ protected AmbulatorySprite _sprite;
/** The context object. */
protected ViewerContext _ctx;