diff --git a/src/java/com/threerings/cast/CharacterManager.java b/src/java/com/threerings/cast/CharacterManager.java
index c45ed3741..42f7a6655 100644
--- a/src/java/com/threerings/cast/CharacterManager.java
+++ b/src/java/com/threerings/cast/CharacterManager.java
@@ -1,5 +1,5 @@
//
-// $Id: CharacterManager.java,v 1.2 2001/10/22 18:21:41 shaper Exp $
+// $Id: CharacterManager.java,v 1.3 2001/10/24 00:55:08 shaper Exp $
package com.threerings.miso.scene;
@@ -26,9 +26,11 @@ public class CharacterManager
/**
* Construct the character manager.
*/
- public CharacterManager (Config config, TileManager tilemgr)
+ public CharacterManager (
+ Config config, TileManager tilemgr, IsoSceneViewModel model)
{
_tilemgr = tilemgr;
+ _model = model;
// load character data descriptions
String file = config.getValue(CHARFILE_KEY, DEFAULT_CHARFILE);
@@ -58,8 +60,8 @@ public class CharacterManager
MultiFrameImage[] anims = TileUtil.getAmbulatorySpriteFrames(
_tilemgr, tsid, info.frameCount);
- AmbulatorySprite sprite = new AmbulatorySprite(0, 0, anims);
- sprite.setFrameRate(info.frameRate);
+ AmbulatorySprite sprite = new AmbulatorySprite(_model, 0, 0, anims);
+ sprite.setFrameRate(info.fps);
sprite.setOrigin(info.origin.x, info.origin.y);
return sprite;
@@ -79,6 +81,9 @@ public class CharacterManager
/** The tile manager. */
protected TileManager _tilemgr;
+ /** The iso scene view model. */
+ protected IsoSceneViewModel _model;
+
/**
* A class that contains character description information for a
* single character for use when constructing the character's
@@ -88,13 +93,13 @@ public class CharacterManager
{
public int tsid;
public int frameCount;
- public int frameRate;
+ public int fps;
public Point origin = new Point();
public String toString ()
{
return "[tsid=" + tsid + ", frameCount=" + frameCount +
- ", frameRate=" + frameRate + ", origin=" + origin + "]";
+ ", fps=" + fps + ", origin=" + origin + "]";
}
}
}
diff --git a/src/java/com/threerings/cast/CharacterSprite.java b/src/java/com/threerings/cast/CharacterSprite.java
index c21766967..bbda2b412 100644
--- a/src/java/com/threerings/cast/CharacterSprite.java
+++ b/src/java/com/threerings/cast/CharacterSprite.java
@@ -1,12 +1,15 @@
//
-// $Id: CharacterSprite.java,v 1.13 2001/10/22 18:21:41 shaper Exp $
+// $Id: CharacterSprite.java,v 1.14 2001/10/24 00:55:08 shaper Exp $
package com.threerings.miso.scene;
+import java.awt.Point;
+
import com.threerings.media.sprite.*;
import com.threerings.miso.Log;
import com.threerings.miso.tile.MisoTile;
+import com.threerings.miso.scene.util.IsoUtil;
/**
* An ambulatory sprite is a sprite that animates itself while walking
@@ -25,11 +28,13 @@ public class AmbulatorySprite extends Sprite implements Traverser
* @param anims the set of multi-frame images to use when animating
* the sprite in each of the compass directions.
*/
- public AmbulatorySprite (int x, int y, MultiFrameImage[] anims)
+ public AmbulatorySprite (
+ IsoSceneViewModel model, int x, int y, MultiFrameImage[] anims)
{
super(x, y);
// keep track of these
+ _model = model;
_anims = anims;
// give ourselves an initial orientation
@@ -115,9 +120,111 @@ public class AmbulatorySprite extends Sprite implements Traverser
return tile.passable;
}
+ /**
+ * Returns the sprite's location on the x-axis in tile coordinates.
+ */
+ public int getTileX ()
+ {
+ return _tilex;
+ }
+
+ /**
+ * Returns the sprite's location on the y-axis in tile coordinates.
+ */
+ public int getTileY ()
+ {
+ return _tiley;
+ }
+
+ /**
+ * Returns the sprite's location on the x-axis within its current
+ * tile in fine coordinates.
+ */
+ public int getFineX ()
+ {
+ return _finex;
+ }
+
+ /**
+ * Returns the sprite's location on the y-axis within its current
+ * tile in fine coordinates.
+ */
+ public int getFineY ()
+ {
+ return _finey;
+ }
+
+ // documentation inherited
+ public void setLocation (int x, int y)
+ {
+ super.setLocation(x, y);
+
+ if (_path == null) {
+ // we only calculate the sprite's tile and fine
+ // coordinates if we have no path, since paths that move
+ // us about are responsible for keeping our scene
+ // coordinates up to date since only they can know where
+ // we really are while in transition from one place to
+ // another.
+
+ // get the sprite's position in full coordinates
+ Point fpos = new Point();
+ IsoUtil.screenToFull(_model, _x, _y, fpos);
+
+ // save off the sprite's tile and fine coordinates
+ _tilex = IsoUtil.fullToTile(fpos.x);
+ _tiley = IsoUtil.fullToTile(fpos.y);
+ _finex = IsoUtil.fullToFine(fpos.x);
+ _finey = IsoUtil.fullToFine(fpos.y);
+ }
+ }
+
+ /**
+ * Sets the sprite's location in tile coordinates; the sprite is
+ * not actually moved in any way. This method is only intended
+ * for use in updating the sprite's stored position which is made
+ * accessible to others that may care to review it.
+ */
+ public void setTileLocation (int x, int y)
+ {
+ _tilex = x;
+ _tiley = y;
+ }
+
+ /**
+ * Sets the sprite's location in fine coordinates; the sprite is
+ * not actually moved in any way. This method is only intended
+ * for use in updating the sprite's stored position which is made
+ * accessible to others that may care to review it.
+ */
+ public void setFineLocation (int x, int y)
+ {
+ _finex = x;
+ _finey = y;
+ }
+
+ // documentation inherited
+ protected void toString (StringBuffer buf)
+ {
+ super.toString(buf);
+ buf.append(", tilex=").append(_tilex);
+ buf.append(", tiley=").append(_tiley);
+ buf.append(", finex=").append(_finex);
+ buf.append(", finey=").append(_finey);
+ }
+
/** The animation frames for the sprite facing each direction. */
protected MultiFrameImage[] _anims;
+ /** The iso scene view model. */
+ protected IsoSceneViewModel _model;
+
/** The origin of the sprite. */
protected int _xorigin, _yorigin;
+
+ /** The sprite location in tile coordinates. */
+ protected int _tilex, _tiley;
+
+ /** The sprite location in fine coordinates. */
+ protected int _finex, _finey;
}
diff --git a/src/java/com/threerings/media/sprite/DirtyItemList.java b/src/java/com/threerings/media/sprite/DirtyItemList.java
deleted file mode 100644
index 13ebe54ca..000000000
--- a/src/java/com/threerings/media/sprite/DirtyItemList.java
+++ /dev/null
@@ -1,93 +0,0 @@
-//
-// $Id: DirtyItemList.java,v 1.4 2001/10/22 18:14:57 shaper Exp $
-
-package com.threerings.media.sprite;
-
-import java.awt.*;
-import java.util.*;
-
-import com.threerings.media.sprite.Sprite;
-import com.threerings.media.tile.ObjectTile;
-
-import com.threerings.media.Log;
-
-/**
- * The dirty item list keeps track of dirty sprites and object tiles
- * in a scene.
- */
-public class DirtyItemList extends ArrayList
-{
- /**
- * Appends the dirty sprite at the given coordinates to the dirty
- * item list.
- */
- public void appendDirtySprite (
- Sprite sprite, int x, int y, Rectangle dirtyRect)
- {
- add(new DirtyItem(sprite, null, x, y, dirtyRect));
- }
-
- /**
- * Appends the dirty object tile at the given coordinates to the
- * dirty item list.
- */
- public void appendDirtyObject (
- ObjectTile tile, Shape bounds, int x, int y, Rectangle dirtyRect)
- {
- add(new DirtyItem(tile, bounds, x, y, dirtyRect));
- }
-
- /**
- * A class to hold the items inserted in the dirty list along with
- * all of the information necessary to render their dirty regions
- * to the target graphics context when the time comes to do so.
- */
- public class DirtyItem
- {
- public Object obj;
- public Shape bounds;
- public int x, y;
- public Rectangle dirtyRect;
-
- /**
- * Constructs a dirty item.
- */
- public DirtyItem (
- Object obj, Shape bounds, int x, int y, Rectangle dirtyRect)
- {
- this.obj = obj;
- this.bounds = bounds;
- this.x = x;
- this.y = y;
- this.dirtyRect = dirtyRect;
- }
-
- /**
- * Paints the dirty item to the given graphics context. Only
- * the portion of the item that falls within the given dirty
- * rectangle is actually drawn.
- */
- public void paint (Graphics2D gfx, Rectangle clip)
- {
- Shape oclip = gfx.getClip();
-
- // clip the draw region to the dirty portion of the item
- gfx.clip(clip);
-
- // paint the item
- if (obj instanceof Sprite) {
- ((Sprite)obj).paint(gfx);
- } else {
- ((ObjectTile)obj).paint(gfx, bounds);
- }
-
- // restore original clipping region
- gfx.setClip(oclip);
- }
-
- public String toString ()
- {
- return "[obj=" + obj + ", x=" + x + ", y=" + y + "]";
- }
- }
-}
diff --git a/src/java/com/threerings/media/util/LineSegmentPath.java b/src/java/com/threerings/media/util/LineSegmentPath.java
index d80e308d1..69b458bd2 100644
--- a/src/java/com/threerings/media/util/LineSegmentPath.java
+++ b/src/java/com/threerings/media/util/LineSegmentPath.java
@@ -1,5 +1,5 @@
//
-// $Id: LineSegmentPath.java,v 1.11 2001/10/12 00:36:03 shaper Exp $
+// $Id: LineSegmentPath.java,v 1.12 2001/10/24 00:55:08 shaper Exp $
package com.threerings.media.sprite;
@@ -9,21 +9,22 @@ import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Iterator;
+import java.util.List;
import com.samskivert.util.StringUtil;
import com.threerings.media.util.MathUtil;
/**
- * The LineSegmentPath class is used to cause a sprite to
- * follow a path that is made up of a sequence of line segments. There
- * must be at least two nodes in any worthwhile path. The direction of the
- * first node in the path is meaningless since the sprite begins at that
+ * The line segment path is used to cause a sprite to follow a path
+ * that is made up of a sequence of line segments. There must be at
+ * least two nodes in any worthwhile path. The direction of the first
+ * node in the path is meaningless since the sprite begins at that
* node and will therefore never be heading towards it.
*/
public class LineSegmentPath implements Path
{
/**
- * Construct a LineSegmentPath object.
+ * Constructs a line segment path.
*/
public LineSegmentPath ()
{
@@ -31,45 +32,17 @@ public class LineSegmentPath implements Path
}
/**
- * Construct a LineSegmentPath object with the specified
- * starting node coordinates. An arbitrary direction will be assigned
- * to the starting node.
+ * Constructs a line segment path with the specified list of
+ * points. An arbitrary direction will be assigned to the
+ * starting node.
*
* @param x the starting node x-position.
* @param y the starting node y-position.
*/
- public LineSegmentPath (int x, int y)
+ public LineSegmentPath (List points)
{
_nodes = new ArrayList();
-
- // add the starting node with an arbitrarily chosen direction
- // since direction is meaningless here
- addNode(x, y, Sprite.DIR_NORTH);
- }
-
- /**
- * Add a node to the path with the specified destination point and
- * facing direction.
- *
- * @param x the x-position.
- * @param y the y-position.
- * @param dir the facing direction.
- */
- public void addNode (int x, int y, int dir)
- {
- _nodes.add(new PathNode(x, y, dir));
- }
-
- /**
- * Add a node to the path with the specified destination point. An
- * arbitrary direction will be assigned to the node.
- *
- * @param x the x-position.
- * @param y the y-position.
- */
- public void addNode (int x, int y)
- {
- _nodes.add(new PathNode(x, y, Sprite.DIR_NORTH));
+ createPath(points);
}
/**
@@ -132,7 +105,7 @@ public class LineSegmentPath implements Path
_niter = _nodes.iterator();
// pretend like we were previously heading to our starting position
- _dest = (PathNode)_niter.next();
+ _dest = getNextNode();
// begin traversing the path
headToNextNode(sprite, timestamp, timestamp);
@@ -169,6 +142,12 @@ public class LineSegmentPath implements Path
return false;
}
+ /**
+ * Place the sprite moving along the path at the end of the
+ * previous path node, face it appropriately for the next node,
+ * and start it on its way. Returns whether the sprite position
+ * moved.
+ */
protected boolean headToNextNode (Sprite sprite, long startstamp, long now)
{
// check to see if we've completed our path
@@ -183,7 +162,7 @@ public class LineSegmentPath implements Path
_src = _dest;
// pop the next node off the path
- _dest = (PathNode)_niter.next();
+ _dest = getNextNode();
// adjust the sprite's orientation
sprite.setOrientation(_dest.dir);
@@ -205,6 +184,7 @@ public class LineSegmentPath implements Path
return updatePosition(sprite, now);
}
+ // documentation inherited
public void paint (Graphics2D gfx)
{
gfx.setColor(Color.red);
@@ -219,11 +199,79 @@ public class LineSegmentPath implements Path
}
}
+ // documentation inherited
public String toString ()
{
return StringUtil.toString(_nodes.iterator());
}
+ /**
+ * Populate the path with the path nodes that lead the sprite from
+ * its starting position to the given destination coordinates
+ * following the given list of screen coordinates.
+ */
+ protected void createPath (List points)
+ {
+ Point last = null;
+ int size = points.size();
+ for (int ii = 0; ii < size; ii++) {
+ Point p = (Point)points.get(ii);
+
+ int dir = (ii == 0) ? Sprite.DIR_NORTH : getDirection(last, p);
+ addNode(p.x, p.y, dir);
+ last = p;
+ }
+ }
+
+ /**
+ * Add a node to the path with the specified destination point and
+ * facing direction.
+ *
+ * @param x the x-position.
+ * @param y the y-position.
+ * @param dir the facing direction.
+ */
+ protected void addNode (int x, int y, int dir)
+ {
+ _nodes.add(new PathNode(x, y, dir));
+ }
+
+ /**
+ * Gets the next node in the path.
+ */
+ protected PathNode getNextNode ()
+ {
+ return (PathNode)_niter.next();
+ }
+
+ /**
+ * Returns the direction that point b lies in from
+ * point a as one of the Sprite
+ * direction constants.
+ */
+ protected int getDirection (Point a, Point b)
+ {
+ if (a.x == b.x && a.y > b.y) {
+ return Sprite.DIR_NORTH;
+ } else if (a.x < b.x && a.y > b.y) {
+ return Sprite.DIR_NORTHEAST;
+ } else if (a.x > b.x && a.y == b.y) {
+ return Sprite.DIR_EAST;
+ } else if (a.x > b.x && a.y < b.y) {
+ return Sprite.DIR_SOUTHEAST;
+ } else if (a.x == b.x && a.y < b.y) {
+ return Sprite.DIR_SOUTH;
+ } else if (a.x > b.x && a.y < b.y) {
+ return Sprite.DIR_SOUTHWEST;
+ } else if (a.x > b.x && a.y == b.y) {
+ return Sprite.DIR_WEST;
+ } else if (a.x > b.x && a.y > b.y) {
+ return Sprite.DIR_NORTHWEST;
+ }
+
+ return Sprite.DIR_NONE;
+ }
+
/** The nodes that make up the path. */
protected ArrayList _nodes;
diff --git a/src/java/com/threerings/media/util/PathNode.java b/src/java/com/threerings/media/util/PathNode.java
index 22800c4cd..e8607dc9b 100644
--- a/src/java/com/threerings/media/util/PathNode.java
+++ b/src/java/com/threerings/media/util/PathNode.java
@@ -1,5 +1,5 @@
//
-// $Id: PathNode.java,v 1.5 2001/09/05 00:40:33 shaper Exp $
+// $Id: PathNode.java,v 1.6 2001/10/24 00:55:08 shaper Exp $
package com.threerings.media.sprite;
@@ -35,9 +35,20 @@ public class PathNode
public String toString ()
{
StringBuffer buf = new StringBuffer();
- buf.append("[x=").append(loc.x);
- buf.append(", y=").append(loc.y);
- buf.append(", dir=").append(dir);
+ buf.append("[");
+ toString(buf);
return buf.append("]").toString();
}
+
+ /**
+ * This should be overridden by derived classes (which should be sure
+ * to call super.toString()) to append the derived class
+ * specific path node information to the string buffer.
+ */
+ public void toString (StringBuffer buf)
+ {
+ buf.append("x=").append(loc.x);
+ buf.append(", y=").append(loc.y);
+ buf.append(", dir=").append(dir);
+ }
}
diff --git a/src/java/com/threerings/miso/client/DirtyItemList.java b/src/java/com/threerings/miso/client/DirtyItemList.java
new file mode 100644
index 000000000..8f249ff6c
--- /dev/null
+++ b/src/java/com/threerings/miso/client/DirtyItemList.java
@@ -0,0 +1,234 @@
+//
+// $Id: DirtyItemList.java,v 1.1 2001/10/24 00:55:08 shaper Exp $
+
+package com.threerings.miso.scene;
+
+import java.awt.*;
+import java.util.*;
+
+import com.threerings.media.sprite.Sprite;
+import com.threerings.media.tile.ObjectTile;
+
+import com.threerings.media.Log;
+
+/**
+ * The dirty item list keeps track of dirty sprites and object tiles
+ * in a scene.
+ */
+public class DirtyItemList extends ArrayList
+{
+ /** The dirty item comparator used to sort dirty items back to front. */
+ public static final Comparator DIRTY_COMP = new DirtyItemComparator();
+
+ /**
+ * Appends the dirty sprite at the given coordinates to the dirty
+ * item list.
+ */
+ public void appendDirtySprite (
+ Sprite sprite, int x, int y, Rectangle dirtyRect)
+ {
+ add(new DirtyItem(sprite, null, x, y, dirtyRect));
+ }
+
+ /**
+ * Appends the dirty object tile at the given coordinates to the
+ * dirty item list.
+ */
+ public void appendDirtyObject (
+ ObjectTile tile, Shape bounds, int x, int y, Rectangle dirtyRect)
+ {
+ add(new DirtyItem(tile, bounds, x, y, dirtyRect));
+ }
+
+ /**
+ * Returns an array of the {@link DirtyItem} objects in the list
+ * sorted with the {@link DirtyItemComparator}.
+ */
+ public DirtyItem[] sort ()
+ {
+ DirtyItem items[] = new DirtyItem[size()];
+ toArray(items);
+ Arrays.sort(items, DirtyItemList.DIRTY_COMP);
+ return items;
+ }
+
+ /**
+ * A class to hold the items inserted in the dirty list along with
+ * all of the information necessary to render their dirty regions
+ * to the target graphics context when the time comes to do so.
+ */
+ public class DirtyItem
+ {
+ public Object obj;
+ public Shape bounds;
+ public int x, y;
+ public Rectangle dirtyRect;
+
+ /**
+ * Constructs a dirty item.
+ */
+ public DirtyItem (
+ Object obj, Shape bounds, int x, int y, Rectangle dirtyRect)
+ {
+ this.obj = obj;
+ this.bounds = bounds;
+ this.x = x;
+ this.y = y;
+ this.dirtyRect = dirtyRect;
+ }
+
+ /**
+ * Paints the dirty item to the given graphics context. Only
+ * the portion of the item that falls within the given dirty
+ * rectangle is actually drawn.
+ */
+ public void paint (Graphics2D gfx, Rectangle clip)
+ {
+ Shape oclip = gfx.getClip();
+
+ // clip the draw region to the dirty portion of the item
+ gfx.clip(clip);
+
+ // paint the item
+ if (obj instanceof Sprite) {
+ ((Sprite)obj).paint(gfx);
+ } else {
+ ((ObjectTile)obj).paint(gfx, bounds);
+ }
+
+ // restore original clipping region
+ gfx.setClip(oclip);
+ }
+
+ public boolean equals (Object other)
+ {
+ // we're never equal to something that's not our kind
+ if (!(other instanceof DirtyItem)) {
+ return false;
+ }
+
+ // sprites are equivalent if they're the same sprite
+ DirtyItem b = (DirtyItem)other;
+ if ((obj instanceof Sprite) && (b.obj instanceof Sprite)) {
+ return (obj == b.obj);
+ }
+
+ // object-to-object or object-to-sprite are distinguished
+ // simply by tile coordinate since they can never occupy
+ // the same tile
+ return (x == b.x && y == b.y);
+ }
+
+ public String toString ()
+ {
+ return "[obj=" + obj + ", x=" + x + ", y=" + y +
+ ", drect=" + dirtyRect + "]";
+ }
+ }
+
+ /**
+ * A comparator class for use in sorting the dirty sprites and
+ * objects in a scene in ascending x- and y-coordinate order
+ * suitable for rendering in the isometric view with proper visual
+ * results.
+ */
+ public static class DirtyItemComparator implements Comparator
+ {
+ public int compare (Object a, Object b)
+ {
+ DirtyItem da = (DirtyItem)a;
+ DirtyItem db = (DirtyItem)b;
+
+ if (da.x == db.x &&
+ da.y == db.y &&
+ da.obj != db.obj) {
+ // we're comparing two sprites co-existing on the same
+ // tile, so study their fine coordinates to determine
+ // rendering order
+ AmbulatorySprite as = (AmbulatorySprite)da.obj;
+ AmbulatorySprite bs = (AmbulatorySprite)db.obj;
+
+ int aloc = as.getFineX() + as.getFineY();
+ int bloc = bs.getFineX() + bs.getFineY();
+
+ if (aloc < bloc) {
+ return -1;
+ } else if (aloc > bloc) {
+ return 1;
+ } else {
+ // if they're at the same vertical row of
+ // intra-tile tiles, just use something consistent
+ return as.hashCode() - bs.hashCode();
+ }
+ }
+
+ // check whether right edge of a overlaps with left edge of b
+ int comp = getRightOverlap(da, db);
+ if (comp != 0) {
+ return comp;
+ }
+
+ // check whether right edge of b overlaps with left edge of a
+ comp = getRightOverlap(db, da);
+ if (comp != 0) {
+ // reverse ordering per reversed overlap check
+ return (comp == -1) ? 1 : -1;
+ }
+
+ // determine ordering based purely on coordinates
+ if (da.x <= db.x && da.y <= db.y) {
+ return -1;
+ }
+
+ return 1;
+ }
+
+ public boolean equals (Object obj)
+ {
+ return (obj == this);
+ }
+
+ /**
+ * Checks the right edge of da to see whether it
+ * overlaps with the left edge of db.
+ *
+ * @return -1 if da should be rendered behind
+ * db, 0 if the right edge of da
+ * does not overlap with db, and 1 if
+ * da should be rendered in front of
+ * db.
+ */
+ protected int getRightOverlap (DirtyItem da, DirtyItem db)
+ {
+ int ax = da.x, bx = db.x;
+ int ay = da.y, by = db.y;
+
+ // get da's rightmost corner coordinate
+ if (da.obj instanceof ObjectTile) {
+ ay -= (((ObjectTile)da.obj).baseHeight - 1);
+ }
+
+ // get db's leftmost corner coordinate
+ if (db.obj instanceof ObjectTile) {
+ bx -= (((ObjectTile)db.obj).baseWidth - 1);
+ }
+
+ if (ax < bx && ay > by) {
+ // we most certainly don't overlap
+ return 0;
+ }
+
+ // calculate inequality constant for db's leftmost corner
+ int k = (bx + by);
+
+ // we need to determine whether to render da in front of
+ // db, so we check whether da's rightmost corner is above
+ // or below db's leftmost corner.
+ if (ay <= k - ax) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ }
+}
diff --git a/src/java/com/threerings/miso/client/IsoSceneView.java b/src/java/com/threerings/miso/client/IsoSceneView.java
index 978f64109..88df9e02b 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.66 2001/10/23 02:04:07 shaper Exp $
+// $Id: IsoSceneView.java,v 1.67 2001/10/24 00:55:08 shaper Exp $
package com.threerings.miso.scene;
@@ -13,21 +13,21 @@ import java.util.*;
import com.samskivert.util.HashIntMap;
import com.threerings.media.sprite.*;
-import com.threerings.media.sprite.DirtyItemList.DirtyItem;
import com.threerings.media.tile.Tile;
import com.threerings.media.tile.ObjectTile;
import com.threerings.miso.Log;
+import com.threerings.miso.scene.DirtyItemList.DirtyItem;
import com.threerings.miso.scene.util.*;
/**
- * The IsoSceneView provides an isometric view of a
- * particular scene.
+ * The iso scene view provides an isometric view of a particular
+ * scene.
*/
public class IsoSceneView implements SceneView
{
/**
- * Construct an IsoSceneView object.
+ * Constructs an iso scene view.
*
* @param spritemgr the sprite manager.
* @param model the data model.
@@ -190,7 +190,7 @@ public class IsoSceneView implements SceneView
}
/**
- * Render the scene to the given graphics context.
+ * Renders the scene to the given graphics context.
*
* @param gfx the graphics context.
*/
@@ -236,21 +236,19 @@ public class IsoSceneView implements SceneView
*/
protected void renderDirtyItems (Graphics2D gfx)
{
+ // Log.info("renderDirtyItems");
+
// sort the dirty sprites and objects visually back-to-front
- int size = _dirtyItems.size();
- DirtyItem items[] = new DirtyItem[size];
- _dirtyItems.toArray(items);
- Arrays.sort(items, IsoUtil.DIRTY_COMP);
+ DirtyItem items[] = _dirtyItems.sort();
// merge all dirty rectangles for each item into a single
// rectangle before painting
Rectangle dirtyRect = new Rectangle();
DirtyItem cur = null, last = null;
- for (int ii = 0; ii < size; ii++) {
+ for (int ii = 0; ii < items.length; ii++) {
cur = items[ii];
- if (last == null ||
- (cur.x != last.x || cur.y != last.y)) {
+ if (last == null || !cur.equals(last)) {
if (last != null) {
// paint the item with its full dirty rectangle
@@ -590,16 +588,13 @@ public class IsoSceneView implements SceneView
int size = _dirtySprites.size();
for (int ii = 0; ii < size; ii++) {
- Sprite sprite = (Sprite)_dirtySprites.get(ii);
-
- // get the sprite's position in tile coordinates
- Point tpos = new Point();
- IsoUtil.screenToTile(_model, sprite.getX(), sprite.getY(), tpos);
+ AmbulatorySprite sprite = (AmbulatorySprite)_dirtySprites.get(ii);
// get the dirty portion of the sprite
Rectangle drect = sprite.getBounds().intersection(r);
- _dirtyItems.appendDirtySprite(sprite, tpos.x, tpos.y, drect);
+ _dirtyItems.appendDirtySprite(
+ sprite, sprite.getTileX(), sprite.getTileY(), drect);
// Log.info("Dirtied item: " + sprite);
}
@@ -633,78 +628,18 @@ public class IsoSceneView implements SceneView
return null;
}
- // constrain destination pixels to fine coordinates
- Point fpos = new Point();
- IsoUtil.screenToFull(_model, x, y, fpos);
+ // get the destination tile coordinates
+ Point dest = new Point();
+ IsoUtil.screenToTile(_model, x, y, dest);
- // calculate tile coordinates for start and end position
- Point stpos = new Point();
- IsoUtil.screenToTile(_model, sprite.getX(), sprite.getY(), stpos);
- int tbx = IsoUtil.fullToTile(fpos.x);
- int tby = IsoUtil.fullToTile(fpos.y);
-
- // Log.info("Seeking path [sx=" + stpos.x + ", sy=" + stpos.y +
- // ", dx=" + tbx + ", dy=" + tby + " ,fdx=" + fpos.x +
- // ", fdy=" + fpos.y + "].");
-
- // get a reasonable path from start to end
- List tilepath = AStarPathUtil.getPath(
+ // get a reasonable tile path through the scene
+ List points = AStarPathUtil.getPath(
_scene.getBaseLayer(), _model.scenewid, _model.scenehei,
- sprite, stpos.x, stpos.y, tbx, tby);
- if (tilepath == null) {
- return null;
- }
+ sprite, sprite.getTileX(), sprite.getTileY(), dest.x, dest.y);
- // TODO: make more visually appealing path segments from start
- // to second tile, and penultimate to ultimate tile.
-
- // construct path with starting screen position
- LineSegmentPath path =
- new LineSegmentPath(sprite.getX(), sprite.getY());
-
- // add all nodes on the calculated path
- Point nspos = new Point();
- Point prev = stpos;
- int size = tilepath.size();
- for (int ii = 1; ii < size - 1; 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, wandering through the middle
- // of each tile in the path for now
- path.addNode(nspos.x + _model.tilehwid,
- nspos.y + _model.tilehhei, dir);
-
- prev = n;
- }
-
- // get the final destination point's screen coordinates
- // constrained to the closest full coordinate
- Point spos = new Point();
- IsoUtil.fullToScreen(_model, fpos.x, fpos.y, spos);
-
- // get the direction we're to face while heading toward the end
- int dir;
- if (prev == stpos) {
- // if our destination is within our origination tile,
- // direction is based on fine coordinates
- dir = IsoUtil.getDirection(
- _model, sprite.getX(), sprite.getY(), spos.x, spos.y);
- } else {
- // else it's based on the last tile we traversed
- dir = IsoUtil.getIsoDirection(prev.x, prev.y, tbx, tby);
- }
-
- // add the final destination path node
- path.addNode(spos.x, spos.y, dir);
-
- return path;
+ // construct a path object to guide the sprite on its merry way
+ return (points == null) ? null :
+ new TilePath(_model, sprite, points, x, y);
}
/** The stroke used to draw dirty rectangles. */
diff --git a/src/java/com/threerings/miso/client/TilePath.java b/src/java/com/threerings/miso/client/TilePath.java
new file mode 100644
index 000000000..640ded525
--- /dev/null
+++ b/src/java/com/threerings/miso/client/TilePath.java
@@ -0,0 +1,185 @@
+//
+// $Id: TilePath.java,v 1.1 2001/10/24 00:55:08 shaper Exp $
+
+package com.threerings.miso.scene;
+
+import java.awt.*;
+import java.util.List;
+
+import com.threerings.media.sprite.*;
+
+import com.threerings.miso.Log;
+import com.threerings.miso.scene.util.IsoUtil;
+
+/**
+ * The tile path represents a path of tiles through a scene. The path
+ * is traversed by treating each pair of connected tiles as a line
+ * segment. Only ambulatory sprites can follow a tile path, and their
+ * tile coordinates are updated as the path is traversed.
+ */
+public class TilePath extends LineSegmentPath
+{
+ /**
+ * Constructs a tile path.
+ *
+ * @param model the iso scene view model the path is associated with.
+ * @param sprite the sprite to follow the path.
+ * @param tiles the tiles to be traversed during the path.
+ * @param destx the destination x-position in screen pixel
+ * coordinates.
+ * @param desty the destination y-position in screen pixel
+ * coordinates.
+ */
+ public TilePath (
+ IsoSceneViewModel model, AmbulatorySprite sprite, List tiles,
+ int destx, int desty)
+ {
+ _model = model;
+
+ // set up the path nodes
+ createPath(sprite, tiles, destx, desty);
+ }
+
+ // documentation inherited
+ public boolean updatePosition (Sprite sprite, long timestamp)
+ {
+ boolean moved = super.updatePosition(sprite, timestamp);
+
+ if (moved) {
+ AmbulatorySprite as = (AmbulatorySprite)sprite;
+ int sx = sprite.getX(), sy = sprite.getY();
+ Point pos = new Point();
+
+ // check whether we've arrived at the destination tile
+ if (!_arrived) {
+ // get the sprite's latest tile coordinates
+ IsoUtil.screenToTile(_model, sx, sy, pos);
+
+ // if the sprite has reached the destination tile,
+ // update the sprite's tile location and remember
+ // we've arrived
+ int dtx = _dest.getTileX(), dty = _dest.getTileY();
+ if (pos.x == dtx && pos.y == dty) {
+ as.setTileLocation(dtx, dty);
+ // Log.info("Sprite arrived [dtx=" + dtx +
+ // ", dty=" + dty + "].");
+ _arrived = true;
+ }
+ }
+
+ // get the sprite's latest fine coordinates
+ IsoUtil.tileToScreen(_model, as.getTileX(), as.getTileY(), pos);
+ Point fpos = new Point();
+ IsoUtil.pixelToFine(_model, sx - pos.x, sy - pos.y, fpos);
+
+ // inform the sprite
+ as.setFineLocation(fpos.x, fpos.y);
+
+ // Log.info("Sprite moved [s=" + as + "].");
+ }
+
+ return moved;
+ }
+
+ // documentation inherited
+ protected PathNode getNextNode ()
+ {
+ // upgrade the path node to a tile path node
+ _dest = (TilePathNode)super.getNextNode();
+
+ // note that we've not yet arrived at the destination tile
+ _arrived = false;
+
+ return _dest;
+ }
+
+ /**
+ * Populate the path with the tile path nodes that lead the sprite
+ * from its starting position to the given destination coordinates
+ * following the given list of tile coordinates.
+ */
+ protected void createPath (
+ AmbulatorySprite sprite, List tiles, int destx, int desty)
+ {
+ // constrain destination pixels to fine coordinates
+ Point fpos = new Point();
+ IsoUtil.screenToFull(_model, destx, desty, fpos);
+
+ // add the starting path node
+ int stx = sprite.getTileX(), sty = sprite.getTileY();
+ int sx = sprite.getX(), sy = sprite.getY();
+ addNode(stx, sty, sx, sy, Sprite.DIR_NORTH);
+
+ // TODO: make more visually appealing path segments from start
+ // to second tile, and penultimate to ultimate tile.
+
+ // add all remaining path nodes excepting the last one
+ Point prev = new Point(stx, sty);
+ Point spos = new Point();
+ int size = tiles.size();
+ for (int ii = 1; ii < size - 1; ii++) {
+ Point next = (Point)tiles.get(ii);
+
+ // determine the direction from previous to next node
+ int dir = IsoUtil.getIsoDirection(prev.x, prev.y, next.x, next.y);
+
+ // determine the node's position in screen pixel coordinates
+ IsoUtil.tileToScreen(_model, next.x, next.y, spos);
+
+ // add the node to the path, wandering through the middle
+ // of each tile in the path for now
+ int dsx = spos.x + _model.tilehwid;
+ int dsy = spos.y + _model.tilehhei;
+ addNode(next.x, next.y, dsx, dsy, dir);
+
+ prev = next;
+ }
+
+ // get the final destination point's screen coordinates
+ // constrained to the closest full coordinate
+ IsoUtil.fullToScreen(_model, fpos.x, fpos.y, spos);
+
+ // get the tile coordinates for the final destination tile
+ int tdestx = IsoUtil.fullToTile(fpos.x);
+ int tdesty = IsoUtil.fullToTile(fpos.y);
+
+ // get the facing direction for the final node
+ int dir;
+ if (prev.x == stx && prev.y == sty) {
+ // if destination is within starting tile, direction is
+ // determined by studying the fine coordinates
+ dir = IsoUtil.getDirection(_model, sx, sy, spos.x, spos.y);
+
+ } else {
+ // else it's based on the last tile we traversed
+ dir = IsoUtil.getIsoDirection(prev.x, prev.y, tdestx, tdesty);
+ }
+
+ // add the final destination path node
+ addNode(tdestx, tdesty, spos.x, spos.y, dir);
+ }
+
+ /**
+ * Add a node to the path with the specified destination
+ * coordinates and facing direction.
+ *
+ * @param tx the tile x-position.
+ * @param ty the tile y-position.
+ * @param x the x-position.
+ * @param y the y-position.
+ * @param dir the facing direction.
+ */
+ protected void addNode (int tx, int ty, int x, int y, int dir)
+ {
+ _nodes.add(new TilePathNode(tx, ty, x, y, dir));
+ }
+
+ /** Whether the sprite has arrived at the current destination tile. */
+ protected boolean _arrived;
+
+ /** The destination tile path node. */
+ protected TilePathNode _dest;
+
+ /** The iso scene view model. */
+ protected IsoSceneViewModel _model;
+}
diff --git a/src/java/com/threerings/miso/client/TilePathNode.java b/src/java/com/threerings/miso/client/TilePathNode.java
new file mode 100644
index 000000000..55427bada
--- /dev/null
+++ b/src/java/com/threerings/miso/client/TilePathNode.java
@@ -0,0 +1,51 @@
+//
+// $Id: TilePathNode.java,v 1.1 2001/10/24 00:55:08 shaper Exp $
+
+package com.threerings.miso.scene;
+
+import com.threerings.media.sprite.PathNode;
+
+/**
+ * The tile path nodes extends the path node class to allow
+ * associating tile coordinates with a node in a path.
+ */
+public class TilePathNode extends PathNode
+{
+ /**
+ * Constructs a tile path node.
+ */
+ public TilePathNode (int tilex, int tiley, int x, int y, int dir)
+ {
+ super(x, y, dir);
+
+ _tilex = tilex;
+ _tiley = tiley;
+ }
+
+ /**
+ * Returns the node's x-axis tile coordinates.
+ */
+ public int getTileX ()
+ {
+ return _tilex;
+ }
+
+ /**
+ * Returns the node's y-axis tile coordinates.
+ */
+ public int getTileY ()
+ {
+ return _tiley;
+ }
+
+ // documentation inherited
+ public void toString (StringBuffer buf)
+ {
+ super.toString(buf);
+ buf.append(", tilex=").append(_tilex);
+ buf.append(", tiley=").append(_tiley);
+ }
+
+ /** The path node tile coordinates. */
+ protected int _tilex, _tiley;
+}
diff --git a/src/java/com/threerings/miso/client/util/IsoUtil.java b/src/java/com/threerings/miso/client/util/IsoUtil.java
index cb5ff64b7..9573e1224 100644
--- a/src/java/com/threerings/miso/client/util/IsoUtil.java
+++ b/src/java/com/threerings/miso/client/util/IsoUtil.java
@@ -1,13 +1,11 @@
//
-// $Id: IsoUtil.java,v 1.12 2001/10/22 18:21:41 shaper Exp $
+// $Id: IsoUtil.java,v 1.13 2001/10/24 00:55:08 shaper Exp $
package com.threerings.miso.scene.util;
import java.awt.*;
-import java.util.Comparator;
import com.threerings.media.sprite.Sprite;
-import com.threerings.media.sprite.DirtyItemList.DirtyItem;
import com.threerings.media.tile.ObjectTile;
import com.threerings.media.util.MathUtil;
@@ -20,9 +18,6 @@ import com.threerings.miso.scene.*;
*/
public class IsoUtil
{
- /** The dirty item comparator used to sort dirty items back to front. */
- public static final Comparator DIRTY_COMP = new DirtyItemComparator();
-
/**
* Returns a polygon bounding all footprint tiles of the given
* object tile.
@@ -399,87 +394,4 @@ public class IsoUtil
/** Multiplication factor to embed tile coords in full coords. */
protected static final int FULL_TILE_FACTOR = 100;
-
- /**
- * A comparator class for use in sorting the dirty sprites and
- * objects in a scene in ascending x- and y-coordinate order
- * suitable for rendering in the isometric view with proper visual
- * results.
- */
- public static class DirtyItemComparator implements Comparator
- {
- public int compare (Object a, Object b)
- {
- DirtyItem da = (DirtyItem)a;
- DirtyItem db = (DirtyItem)b;
-
- // check whether right edge of a overlaps with left edge of b
- int comp = getRightOverlap(da, db);
- if (comp != 0) {
- return comp;
- }
-
- // check whether right edge of b overlaps with left edge of a
- comp = getRightOverlap(db, da);
- if (comp != 0) {
- // reverse ordering per reversed overlap check
- return (comp == -1) ? 1 : -1;
- }
-
- // determine ordering based purely on coordinates
- if (da.x <= db.x && da.y <= db.y) {
- return -1;
- }
-
- return 1;
- }
-
- public boolean equals (Object obj)
- {
- return (obj == this);
- }
-
- /**
- * Checks the right edge of da to see whether it
- * overlaps with the left edge of db.
- *
- * @return -1 if da should be rendered behind
- * db, 0 if the right edge of da
- * does not overlap with db, and 1 if
- * da should be rendered in front of
- * db.
- */
- protected int getRightOverlap (DirtyItem da, DirtyItem db)
- {
- int ax = da.x, bx = db.x;
- int ay = da.y, by = db.y;
-
- // get da's rightmost corner coordinate
- if (da.obj instanceof ObjectTile) {
- ay -= (((ObjectTile)da.obj).baseHeight - 1);
- }
-
- // get db's leftmost corner coordinate
- if (db.obj instanceof ObjectTile) {
- bx -= (((ObjectTile)db.obj).baseWidth - 1);
- }
-
- if (ax < bx && ay > by) {
- // we most certainly don't overlap
- return 0;
- }
-
- // calculate inequality constant for db's leftmost corner
- int k = (bx + by);
-
- // we need to determine whether to render da in front of
- // db, so we check whether da's rightmost corner is above
- // or below db's leftmost corner.
- if (ay <= k - ax) {
- return -1;
- } else {
- return 1;
- }
- }
- }
}
diff --git a/tests/src/java/com/threerings/miso/viewer/ViewerFrame.java b/tests/src/java/com/threerings/miso/viewer/ViewerFrame.java
index d3c20f04e..e1affd587 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.23 2001/10/23 02:03:49 shaper Exp $
+// $Id: ViewerFrame.java,v 1.24 2001/10/24 00:55:08 shaper Exp $
package com.threerings.miso.viewer;
@@ -9,7 +9,6 @@ import com.threerings.media.sprite.SpriteManager;
import com.threerings.media.tile.TileManager;
import com.threerings.miso.Log;
-import com.threerings.miso.scene.CharacterManager;
import com.threerings.miso.viewer.util.ViewerContext;
/**
@@ -35,13 +34,9 @@ public class ViewerFrame extends JFrame
SpriteManager spritemgr = new SpriteManager();
TileManager tilemgr = ctx.getTileManager();
- // construct the character manager from which we obtain our sprite
- CharacterManager charmgr = new CharacterManager(
- ctx.getConfig(), tilemgr);
-
// set up the scene view panel with a default scene
ViewerSceneViewPanel svpanel =
- new ViewerSceneViewPanel(ctx, spritemgr, charmgr);
+ new ViewerSceneViewPanel(ctx, spritemgr);
// add the main panel to the frame
getContentPane().add(svpanel);
diff --git a/tests/src/java/com/threerings/miso/viewer/ViewerSceneViewPanel.java b/tests/src/java/com/threerings/miso/viewer/ViewerSceneViewPanel.java
index 21f7d2ef6..b9dc9aa84 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.20 2001/10/23 02:03:49 shaper Exp $
+// $Id: ViewerSceneViewPanel.java,v 1.21 2001/10/24 00:55:08 shaper Exp $
package com.threerings.miso.viewer;
@@ -25,8 +25,7 @@ public class ViewerSceneViewPanel extends SceneViewPanel
/**
* Construct the panel and initialize it with a context.
*/
- public ViewerSceneViewPanel (
- ViewerContext ctx, SpriteManager spritemgr, CharacterManager charmgr)
+ public ViewerSceneViewPanel (ViewerContext ctx, SpriteManager spritemgr)
{
super(ctx.getConfig(), spritemgr);
@@ -35,6 +34,19 @@ public class ViewerSceneViewPanel extends SceneViewPanel
// create an animation manager for this panel
_animmgr = new AnimationManager(spritemgr, this);
+ // load up the initial scene
+ prepareStartingScene();
+
+ // construct the character manager from which we obtain sprites
+ CharacterManager charmgr = new CharacterManager(
+ ctx.getConfig(), ctx.getTileManager(), _scenemodel);
+
+ // create the manipulable sprite
+ _sprite = createSprite(spritemgr, charmgr, TSID_CHAR_USER);
+
+ // create the decoy sprites
+ createDecoys(spritemgr, charmgr);
+
// listen to the desired events
addMouseListener(new MouseAdapter() {
public void mousePressed (MouseEvent e) {
@@ -42,15 +54,6 @@ public class ViewerSceneViewPanel extends SceneViewPanel
}
});
- // load up the initial scene
- prepareStartingScene();
-
- // create the manipulable sprite
- _sprite = createSprite(spritemgr, charmgr);
-
- // create the decoy sprites
- createDecoys(spritemgr, charmgr);
-
PerformanceMonitor.register(this, "paint", 1000);
}
@@ -58,9 +61,9 @@ public class ViewerSceneViewPanel extends SceneViewPanel
* Creates a new sprite.
*/
protected AmbulatorySprite createSprite (
- SpriteManager spritemgr, CharacterManager charmgr)
+ SpriteManager spritemgr, CharacterManager charmgr, int tsid)
{
- AmbulatorySprite s = charmgr.getCharacter(TSID_CHAR);
+ AmbulatorySprite s = charmgr.getCharacter(tsid);
if (s != null) {
s.setLocation(300, 300);
s.addSpriteObserver(this);
@@ -78,7 +81,8 @@ public class ViewerSceneViewPanel extends SceneViewPanel
{
_decoys = new AmbulatorySprite[NUM_DECOYS];
for (int ii = 0; ii < NUM_DECOYS; ii++) {
- if ((_decoys[ii] = createSprite(spritemgr, charmgr)) != null) {
+ _decoys[ii] = createSprite(spritemgr, charmgr, TSID_CHAR);
+ if (_decoys[ii] != null) {
createRandomPath(_decoys[ii]);
}
}
@@ -153,8 +157,6 @@ public class ViewerSceneViewPanel extends SceneViewPanel
do {
x = RandomUtil.getInt(d.width);
y = RandomUtil.getInt(d.height);
- // Log.info("Moving sprite [s=" + s + ", x=" + x +
- // ", y=" + y + "].");
} while (!createPath(s, x, y));
}
@@ -163,7 +165,6 @@ public class ViewerSceneViewPanel extends SceneViewPanel
{
if (event instanceof PathCompletedEvent) {
AmbulatorySprite s = (AmbulatorySprite)event.getSprite();
- // Log.info("Path completed [sprite=" + s + "].");
if (s != _sprite) {
// move the sprite to a new random location
@@ -173,13 +174,16 @@ public class ViewerSceneViewPanel extends SceneViewPanel
}
/** The number of decoy characters milling about. */
- protected static final int NUM_DECOYS = 10;
+ protected static final int NUM_DECOYS = 2;
- /** The tileset id for the character tiles. */
+ /** The tileset id for the decoy character tiles. */
protected static final int TSID_CHAR = 1011;
+ /** The tileset id for the user character tiles. */
+ protected static final int TSID_CHAR_USER = 1012;
+
/** The animation manager. */
- AnimationManager _animmgr;
+ protected AnimationManager _animmgr;
/** The sprite we're manipulating within the view. */
protected AmbulatorySprite _sprite;