diff --git a/src/java/com/threerings/cast/ActionFrames.java b/src/java/com/threerings/cast/ActionFrames.java
index a03736e20..168e4ddee 100644
--- a/src/java/com/threerings/cast/ActionFrames.java
+++ b/src/java/com/threerings/cast/ActionFrames.java
@@ -1,5 +1,5 @@
//
-// $Id: ActionFrames.java,v 1.4 2002/06/19 23:31:57 mdb Exp $
+// $Id: ActionFrames.java,v 1.5 2002/06/26 23:53:06 mdb Exp $
package com.threerings.cast;
@@ -17,6 +17,12 @@ import com.threerings.util.DirectionCodes;
*/
public interface ActionFrames
{
+ /**
+ * Returns the number of orientations available in this set of action
+ * frames.
+ */
+ public int getOrientationCount ();
+
/**
* Returns the multi-frame image that comprises the frames for the
* specified orientation.
diff --git a/src/java/com/threerings/cast/ActionSequence.java b/src/java/com/threerings/cast/ActionSequence.java
index 82ee59d2d..fa8b0874b 100644
--- a/src/java/com/threerings/cast/ActionSequence.java
+++ b/src/java/com/threerings/cast/ActionSequence.java
@@ -1,5 +1,5 @@
//
-// $Id: ActionSequence.java,v 1.3 2002/03/27 21:51:33 mdb Exp $
+// $Id: ActionSequence.java,v 1.4 2002/06/26 23:53:06 mdb Exp $
package com.threerings.cast;
@@ -24,12 +24,17 @@ public class ActionSequence implements Serializable
/** The position of the character's base for this sequence. */
public Point origin = new Point();
+ /** Orientation codes for the orientations available for this
+ * action. */
+ public int[] orients;
+
/**
* Returns a string representation of this action sequence.
*/
public String toString ()
{
return "[name=" + name + ", framesPerSecond=" + framesPerSecond +
- ", origin=" + origin + "]";
+ ", origin=" + origin +
+ ", orients=" + (orients == null ? 0 : orients.length) + "]";
}
}
diff --git a/src/java/com/threerings/cast/CompositedActionFrames.java b/src/java/com/threerings/cast/CompositedActionFrames.java
index 01c7ea3de..1a63a2eaa 100644
--- a/src/java/com/threerings/cast/CompositedActionFrames.java
+++ b/src/java/com/threerings/cast/CompositedActionFrames.java
@@ -1,5 +1,5 @@
//
-// $Id: CompositedActionFrames.java,v 1.6 2002/06/20 18:31:03 mdb Exp $
+// $Id: CompositedActionFrames.java,v 1.7 2002/06/26 23:53:06 mdb Exp $
package com.threerings.cast;
@@ -37,12 +37,19 @@ public class CompositedActionFrames
}
_sources = sources;
- // the sources must all have the same frame count, and each
+ // the sources must all have the same orientation count, and each
// orientation must also have the same frame count, so we just use
- // the count from the first source and first orientation
+ // the counts from the first source and orientation
+ _orientCount = _sources[0].getOrientationCount();
_frameCount = _sources[0].getFrames(NORTH).getFrameCount();
- _images = new Image[DIRECTION_COUNT][_frameCount];
- _bounds = new Rectangle[DIRECTION_COUNT][_frameCount];
+ _images = new Image[_orientCount][_frameCount];
+ _bounds = new Rectangle[_orientCount][_frameCount];
+ }
+
+ // documentation inherited from interface
+ public int getOrientationCount ()
+ {
+ return _orientCount;
}
// documentation inherited from interface
@@ -198,6 +205,9 @@ public class CompositedActionFrames
return dest;
}
+ /** The number of orientations. */
+ protected int _orientCount;
+
/** The number of frames in each orientation. */
protected int _frameCount;
diff --git a/src/java/com/threerings/cast/bundle/BundledComponentRepository.java b/src/java/com/threerings/cast/bundle/BundledComponentRepository.java
index 4d6ba2d6b..3cda544b2 100644
--- a/src/java/com/threerings/cast/bundle/BundledComponentRepository.java
+++ b/src/java/com/threerings/cast/bundle/BundledComponentRepository.java
@@ -1,5 +1,5 @@
//
-// $Id: BundledComponentRepository.java,v 1.14 2002/06/19 23:31:57 mdb Exp $
+// $Id: BundledComponentRepository.java,v 1.15 2002/06/26 23:53:06 mdb Exp $
package com.threerings.cast.bundle;
@@ -19,6 +19,7 @@ import java.util.Iterator;
import com.samskivert.io.NestableIOException;
import com.samskivert.util.HashIntMap;
+import com.samskivert.util.IntIntMap;
import com.samskivert.util.Tuple;
import org.apache.commons.collections.FilterIterator;
@@ -307,6 +308,22 @@ public class BundledComponentRepository
{
_set = set;
_actseq = actseq;
+
+ // compute these now to avoid pointless recomputation later
+ _ocount = actseq.orients.length;
+ _fcount = set.getTileCount() / _ocount;
+
+ // create our mapping from orientation to animation sequence
+ // index
+ for (int ii = 0; ii < _ocount; ii++) {
+ _orients.put(actseq.orients[ii], ii);
+ }
+ }
+
+ // documentation inherited from interface
+ public int getOrientationCount ()
+ {
+ return _ocount;
}
// documentation inherited from interface
@@ -316,7 +333,7 @@ public class BundledComponentRepository
// documentation inherited
public int getFrameCount ()
{
- return _set.getTileCount() / DIRECTION_COUNT;
+ return _fcount;
}
// documentation inherited from interface
@@ -388,8 +405,7 @@ public class BundledComponentRepository
*/
protected Tile getTile (int orient, int index)
{
- int fcount = _set.getTileCount() / DIRECTION_COUNT;
- int tileIndex = orient * fcount + index;
+ int tileIndex = _orients.get(orient) * _fcount + index;
try {
return _set.getTile(tileIndex);
} catch (NoSuchTileException nste) {
@@ -404,6 +420,13 @@ public class BundledComponentRepository
/** The action sequence for which we're providing frame images. */
protected ActionSequence _actseq;
+
+ /** Frame and orientation counts. */
+ protected int _fcount, _ocount;
+
+ /** A mapping from orientation code to animation sequence
+ * index. */
+ protected IntIntMap _orients = new IntIntMap();
}
/** We use the image manager to decode and cache images. */
diff --git a/src/java/com/threerings/cast/bundle/tools/MetadataBundlerTask.java b/src/java/com/threerings/cast/bundle/tools/MetadataBundlerTask.java
index be7d83426..56046e070 100644
--- a/src/java/com/threerings/cast/bundle/tools/MetadataBundlerTask.java
+++ b/src/java/com/threerings/cast/bundle/tools/MetadataBundlerTask.java
@@ -1,5 +1,5 @@
//
-// $Id: MetadataBundlerTask.java,v 1.2 2002/02/05 20:29:09 mdb Exp $
+// $Id: MetadataBundlerTask.java,v 1.3 2002/06/26 23:53:07 mdb Exp $
package com.threerings.cast.bundle.tools;
@@ -169,6 +169,14 @@ public class MetadataBundlerTask extends Task
for (int i = 0; i < setlist.size(); i++) {
TileSet set = (TileSet)setlist.get(i);
ActionSequence act = (ActionSequence)actlist.get(i);
+ // make sure nothing was missing in the action sequence
+ // definition parsed from XML
+ String errmsg = ActionRuleSet.validate(act);
+ if (errmsg != null) {
+ errmsg = "Action sequence invalid [seq=" + act +
+ ", error=" + errmsg + "].";
+ throw new BuildException(errmsg);
+ }
actmap.put(act.name, act);
setmap.put(act.name, set);
}
diff --git a/src/java/com/threerings/cast/tools/xml/ActionRuleSet.java b/src/java/com/threerings/cast/tools/xml/ActionRuleSet.java
index 3513a3baa..d30bc3197 100644
--- a/src/java/com/threerings/cast/tools/xml/ActionRuleSet.java
+++ b/src/java/com/threerings/cast/tools/xml/ActionRuleSet.java
@@ -1,5 +1,5 @@
//
-// $Id: ActionRuleSet.java,v 1.1 2001/11/27 08:09:35 mdb Exp $
+// $Id: ActionRuleSet.java,v 1.2 2002/06/26 23:53:07 mdb Exp $
package com.threerings.cast.tools.xml;
@@ -12,6 +12,9 @@ import com.samskivert.xml.CallMethodSpecialRule;
import com.samskivert.xml.SetFieldRule;
import com.samskivert.xml.SetPropertyFieldsRule;
+import com.threerings.util.DirectionCodes;
+import com.threerings.util.DirectionUtil;
+
import com.threerings.cast.ActionSequence;
/**
@@ -78,6 +81,47 @@ public class ActionRuleSet extends RuleSetBase
}
};
digester.addRule(_prefix + ACTION_PATH + "/origin", origin);
+
+ CallMethodSpecialRule orient = new CallMethodSpecialRule(digester) {
+ public void parseAndSet (String bodyText, Object target)
+ throws Exception {
+ ActionSequence seq = ((ActionSequence)target);
+ String[] ostrs = StringUtil.parseStringArray(bodyText);
+ seq.orients = new int[ostrs.length];
+ for (int ii = 0; ii < ostrs.length; ii++) {
+ int orient = DirectionUtil.fromShortString(ostrs[ii]);
+ if (orient != DirectionCodes.NONE) {
+ seq.orients[ii] = orient;
+ } else {
+ String errmsg = "Invalid orientation specification " +
+ "[index=" + ii + ", orient=" + ostrs[ii] + "].";
+ throw new Exception(errmsg);
+ }
+ }
+ }
+ };
+ digester.addRule(_prefix + ACTION_PATH + "/orients", orient);
+ }
+
+ /**
+ * Validates that all necessary fields have been parsed and set in
+ * this action sequence object and are valid.
+ *
+ * @return null if the sequence is valid, a string explaining the
+ * invalidity if it is not.
+ */
+ public static String validate (ActionSequence seq)
+ {
+ if (StringUtil.blank(seq.name)) {
+ return "Missing 'name' definition.";
+ }
+ if (seq.framesPerSecond == 0) {
+ return "Missing 'framesPerSecond' definition.";
+ }
+ if (seq.orients == null) {
+ return "Missing 'orients' definition.";
+ }
+ return null;
}
/** The prefix at which me match our actions. */
diff --git a/src/java/com/threerings/miso/client/util/IsoUtil.java b/src/java/com/threerings/miso/client/util/IsoUtil.java
index 6f147dcce..fd4fa31be 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.34 2002/06/21 18:52:36 mdb Exp $
+// $Id: IsoUtil.java,v 1.35 2002/06/26 23:53:07 mdb Exp $
package com.threerings.miso.scene.util;
@@ -14,6 +14,7 @@ import com.threerings.media.tile.ObjectTile;
import com.threerings.media.util.MathUtil;
import com.threerings.util.DirectionCodes;
+import com.threerings.util.DirectionUtil;
import com.threerings.miso.Log;
import com.threerings.miso.scene.IsoSceneViewModel;
@@ -228,7 +229,9 @@ public class IsoUtil
// compare tile coordinates to determine direction
int dir = getIsoDirection(tax, tay, tbx, tby);
- if (dir != DirectionCodes.NONE) return dir;
+ if (dir != DirectionCodes.NONE) {
+ return dir;
+ }
// destination point is in the same tile as the
// origination point, so consider fine coordinates
@@ -248,9 +251,11 @@ public class IsoUtil
}
/**
- * Given two points in an isometric coordinate system, return the
- * compass direction that point B lies in from point A. This method
- * is used to determine direction for both tile coordinates and fine
+ * Given two points in an isometric coordinate system (in which {@link
+ * NORTH} is in the direction of the negative x-axis and {@link WEST}
+ * in the direction of the negative y-axis), return the compass
+ * direction that point B lies in from point A. This method is used
+ * to determine direction for both tile coordinates and fine
* coordinates within a tile, since the coordinate systems are the
* same.
*
@@ -291,9 +296,8 @@ public class IsoUtil
}
/**
- * Given two points in screen (cartesian) coordinates, return the
- * isometrically projected compass direction that point B lies in from
- * point A.
+ * Given two points in screen coordinates, return the isometrically
+ * projected compass direction that point B lies in from point A.
*
* @param ax the x-position of point A.
* @param ay the y-position of point A.
@@ -306,10 +310,20 @@ public class IsoUtil
*/
public static int getProjectedIsoDirection (int ax, int ay, int bx, int by)
{
- int dir = getIsoDirection(ax, ay, bx, by);
+ return toIsoDirection(DirectionUtil.getDirection(ax, ay, bx, by));
+ }
+
+ /**
+ * Converts a non-isometric orientation (where north points toward the
+ * top of the screen) to an isometric orientation where north points
+ * toward the upper-left corner of the screen.
+ */
+ public static int toIsoDirection (int dir)
+ {
if (dir != DirectionCodes.NONE) {
- // offset the direction by -1, ie change SOUTHWEST to SOUTH
- dir = (dir + 7) % 8;
+ // rotate the direction clockwise (ie. change SOUTHEAST to
+ // SOUTH)
+ dir = DirectionUtil.rotateCW(dir, 2);
}
return dir;
}
diff --git a/src/java/com/threerings/util/DirectionUtil.java b/src/java/com/threerings/util/DirectionUtil.java
index a52029b54..faf888646 100644
--- a/src/java/com/threerings/util/DirectionUtil.java
+++ b/src/java/com/threerings/util/DirectionUtil.java
@@ -1,5 +1,5 @@
//
-// $Id: DirectionUtil.java,v 1.5 2002/06/26 02:54:56 mdb Exp $
+// $Id: DirectionUtil.java,v 1.6 2002/06/26 23:53:07 mdb Exp $
package com.threerings.util;
@@ -37,6 +37,36 @@ public class DirectionUtil implements DirectionCodes
SHORT_DIR_STRINGS[direction] : "?";
}
+ /**
+ * Returns the direction code that corresponds to the supplied string
+ * or {@link NONE} if the string does not correspond to a known
+ * direction code.
+ */
+ public static int fromString (String dirstr)
+ {
+ for (int ii = 0; ii < FINE_DIRECTION_COUNT; ii++) {
+ if (DIR_STRINGS[ii].equals(dirstr)) {
+ return ii;
+ }
+ }
+ return NONE;
+ }
+
+ /**
+ * Returns the direction code that corresponds to the supplied short
+ * string or {@link NONE} if the string does not correspond to a known
+ * direction code.
+ */
+ public static int fromShortString (String dirstr)
+ {
+ for (int ii = 0; ii < FINE_DIRECTION_COUNT; ii++) {
+ if (SHORT_DIR_STRINGS[ii].equals(dirstr)) {
+ return ii;
+ }
+ }
+ return NONE;
+ }
+
/**
* Returns a string representation of an array of direction codes. The
* directions are represented by the abbreviated names.
@@ -100,7 +130,19 @@ public class DirectionUtil implements DirectionCodes
*/
public static int getDirection (int ax, int ay, int bx, int by)
{
- double theta = Math.atan2(by-ay, bx-ax);
+ return getDirection(Math.atan2(by-ay, bx-ax));
+ }
+
+ /**
+ * Returns which of the eight compass directions is associated with
+ * the specified angle theta. Note: that the angle supplied
+ * is assumed to increase clockwise around the origin (which screen
+ * angles do) rather than counter-clockwise around the origin (which
+ * cartesian angles do) and NORTH is considered to point
+ * toward the top of the screen.
+ */
+ public static int getDirection (double theta)
+ {
theta = ((theta + Math.PI) * 4) / Math.PI;
return (int)(Math.round(theta) + WEST) % 8;
}
@@ -128,7 +170,19 @@ public class DirectionUtil implements DirectionCodes
*/
public static int getFineDirection (int ax, int ay, int bx, int by)
{
- double theta = Math.atan2(by-ay, bx-ax);
+ return getFineDirection(Math.atan2(by-ay, bx-ax));
+ }
+
+ /**
+ * Returns which of the sixteen compass directions is associated with
+ * the specified angle theta. Note: that the angle supplied
+ * is assumed to increase clockwise around the origin (which screen
+ * angles do) rather than counter-clockwise around the origin (which
+ * cartesian angles do) and NORTH is considered to point
+ * toward the top of the screen.
+ */
+ public static int getFineDirection (double theta)
+ {
theta = ((theta + Math.PI) * 8) / Math.PI;
return ANGLE_MAP[(int)Math.round(theta) % FINE_DIRECTION_COUNT];
}
diff --git a/tests/rsrc/bundles/components/actions.xml b/tests/rsrc/bundles/components/actions.xml
index 5c3af32cb..4c8513f3a 100644
--- a/tests/rsrc/bundles/components/actions.xml
+++ b/tests/rsrc/bundles/components/actions.xml
@@ -1,8 +1,9 @@
-
+
+
5
50,112
@@ -42,11 +43,12 @@
+
8
87,80
- SW, W, NW, N, NE, E, SE, S, WSW, WNW, NNW, NNE, ENE, ESE, SSE, SSW
+ S, SSW, SW, WSW, W, WNW, NW, NNW, N, NNE, NE, ENE, E, ESE, SE, SSE
168, 168, 168, 168, 168, 168, 168, 168
@@ -61,7 +63,7 @@
8
42,34
- SW, W, NW, N, NE, E, SE, S, WSW, WNW, NNW, NNE, ENE, ESE, SSE, SSW
+ S, SSW, SW, WSW, W, WNW, NW, NNW, N, NNE, NE, ENE, E, ESE, SE, SSE
84, 84, 84, 84, 84, 84, 84, 84
diff --git a/tests/src/java/com/threerings/cast/CharSpriteViz.java b/tests/src/java/com/threerings/cast/CharSpriteViz.java
new file mode 100644
index 000000000..32f740309
--- /dev/null
+++ b/tests/src/java/com/threerings/cast/CharSpriteViz.java
@@ -0,0 +1,134 @@
+//
+// $Id: CharSpriteViz.java,v 1.1 2002/06/26 23:53:07 mdb Exp $
+
+package com.threerings.cast;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Graphics;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+import com.samskivert.swing.util.SwingUtil;
+
+import com.threerings.media.ImageManager;
+import com.threerings.resource.ResourceManager;
+import com.threerings.util.DirectionCodes;
+import com.threerings.util.DirectionUtil;
+
+import com.threerings.cast.bundle.BundledComponentRepository;
+
+/**
+ * Displays a character sprite in all of the various orientations.
+ */
+public class CharSpriteViz extends JPanel
+ implements DirectionCodes, MouseMotionListener
+{
+ public CharSpriteViz (
+ CharacterManager charmgr, CharacterComponent ccomp, String action)
+ {
+ // get a handle on our sprite
+ _sprite = charmgr.getCharacter(
+ new CharacterDescriptor(
+ new int[] { ccomp.componentId }, null));
+
+ // put the sprite in the appropriate action mode
+ _sprite.setRestingAction(action);
+ _sprite.setFollowingPathAction(action);
+ _sprite.setActionSequence(action);
+
+ addMouseMotionListener(this);
+ }
+
+ public void mouseDragged (MouseEvent event)
+ {
+ }
+
+ public void mouseMoved (MouseEvent event)
+ {
+ int orient = DirectionUtil.getFineDirection(
+ getWidth()/2, getHeight()/2, event.getX(), event.getY());
+ if (_orient != orient) {
+ System.out.println("Switching to " +
+ DirectionUtil.toShortString(orient) + ".");
+ _orient = orient;
+ repaint();
+ }
+ }
+
+ public void paintComponent (Graphics g)
+ {
+ super.paintComponent(g);
+
+ int width = getWidth(), height = getHeight();
+ int cx = width/2, cy = height/2;
+ g.setColor(Color.darkGray);
+ g.drawLine(cx, cy, 0, cy);
+ g.drawLine(cx, cy, 0, cy/2);
+ g.drawLine(cx, cy, 0, 0);
+ g.drawLine(cx, cy, cx/2, 0);
+ g.drawLine(cx, cy, cx, 0);
+ g.drawLine(cx, cy, 3*width/4, 0);
+ g.drawLine(cx, cy, width, 0);
+ g.drawLine(cx, cy, width, cy/2);
+ g.drawLine(cx, cy, width, cy);
+ g.drawLine(cx, cy, width, 3*height/4);
+ g.drawLine(cx, cy, width, height);
+ g.drawLine(cx, cy, 3*width/4, height);
+ g.drawLine(cx, cy, cx, height);
+ g.drawLine(cx, cy, cx/2, height);
+ g.drawLine(cx, cy, 0, height);
+ g.drawLine(cx, cy, 0, 3*height/4);
+
+ _sprite.setLocation(cx, cy);
+ _sprite.setOrientation(_orient);
+ _sprite.paint((Graphics2D)g);
+ }
+
+ public static void main (String[] args)
+ {
+ if (args.length < 3) {
+ System.err.println("Usage: CharSpriteViz cclass cname action");
+ System.err.println(" (eg. CharSpriteViz navsail smsloop sailing");
+ System.exit(-1);
+ }
+
+ JFrame frame = new JFrame("CharSpriteViz");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ try {
+ ResourceManager rmgr = new ResourceManager(
+ "rsrc", null, "config/resource/manager.properties");
+ ImageManager imgr = new ImageManager(rmgr, frame);
+ ComponentRepository crepo =
+ new BundledComponentRepository(rmgr, imgr, "components");
+ CharacterManager charmgr = new CharacterManager(crepo);
+ CharacterComponent ccomp = crepo.getComponent(args[0], args[1]);
+
+ frame.getContentPane().add(
+ new CharSpriteViz(charmgr, ccomp, args[2]),
+ BorderLayout.CENTER);
+ frame.setSize(200, 200);
+ SwingUtil.centerWindow(frame);
+ frame.show();
+
+ } catch (NoSuchComponentException nsce) {
+ System.err.println("No component with specified class " +
+ "and name [cclass=" + args[0] +
+ ", cname=" + args[1] + "].");
+ System.exit(-1);
+
+ } catch (Exception e) {
+ e.printStackTrace(System.err);
+ System.exit(-1);
+ }
+ }
+
+ protected CharacterSprite _sprite;
+ protected int _orient = NORTH;
+}
diff --git a/tests/src/java/com/threerings/util/DirectionTest.java b/tests/src/java/com/threerings/util/DirectionTest.java
index ebac4571f..edc038d63 100644
--- a/tests/src/java/com/threerings/util/DirectionTest.java
+++ b/tests/src/java/com/threerings/util/DirectionTest.java
@@ -1,5 +1,5 @@
//
-// $Id: DirectionTest.java,v 1.1 2002/06/26 02:54:56 mdb Exp $
+// $Id: DirectionTest.java,v 1.2 2002/06/26 23:53:07 mdb Exp $
package com.threerings.util;
@@ -34,6 +34,12 @@ public class DirectionTest extends TestCase
}
// System.out.println(DirectionUtil.toShortString(orient));
assertTrue("CCW rotate", orient == NORTH);
+
+// for (double theta = -Math.PI; theta <= Math.PI; theta += Math.PI/1000) {
+// orient = DirectionUtil.getFineDirection(theta);
+// System.out.println(Math.toDegrees(theta) + " => " +
+// DirectionUtil.toShortString(orient));
+// }
}
public static Test suite ()