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 ()