diff --git a/src/java/com/threerings/cast/ActionSequence.java b/src/java/com/threerings/cast/ActionSequence.java
index 95cf208a2..9faee72f8 100644
--- a/src/java/com/threerings/cast/ActionSequence.java
+++ b/src/java/com/threerings/cast/ActionSequence.java
@@ -1,37 +1,25 @@
//
-// $Id: ActionSequence.java,v 1.1 2001/11/01 01:40:42 shaper Exp $
+// $Id: ActionSequence.java,v 1.2 2001/11/27 08:09:34 mdb Exp $
package com.threerings.cast;
import java.awt.Point;
-
-import com.threerings.media.tile.TileSet;
+import java.io.Serializable;
/**
- * The action sequence class describes a single character animation
- * sequence. An animation sequence may consist of multiple frames of
- * animation, renders at a particular frame rate, and has an origin
- * point that specifies where the base of the character in the
- * animation sequence is to be placed.
+ * The action sequence class describes a particular character animation
+ * sequence. An animation sequence consists of one or more frames of
+ * animation, renders at a particular frame rate, and has an origin point
+ * that specifies the location of the base of the character in relation to
+ * the bounds of the animation images.
*/
-public class ActionSequence
+public class ActionSequence implements Serializable
{
- /** The unique action sequence identifier. */
- public int asid;
-
/** The action sequence name. */
public String name;
- /** The file id specifier for the tile set image file name. */
- public String fileid;
-
- /** The tile set description for this sequence. Intended for
- * cloning with an image path to reference an actual set of tile
- * images suiting the action sequence. */
- public TileSet tileset;
-
/** The number of frames per second to show when animating. */
- public int fps;
+ public int framesPerSecond;
/** The position of the character's base for this sequence. */
public Point origin = new Point();
@@ -41,7 +29,7 @@ public class ActionSequence
*/
public String toString ()
{
- return "[asid=" + asid + ", name=" + name +
- ", fps=" + fps + ", origin=" + origin + "]";
+ return "[name=" + name + ", framesPerSecond=" + framesPerSecond +
+ ", origin=" + origin + "]";
}
}
diff --git a/src/java/com/threerings/cast/CharacterComponent.java b/src/java/com/threerings/cast/CharacterComponent.java
index 1a2142fe3..c2b02dad0 100644
--- a/src/java/com/threerings/cast/CharacterComponent.java
+++ b/src/java/com/threerings/cast/CharacterComponent.java
@@ -1,8 +1,10 @@
//
-// $Id: CharacterComponent.java,v 1.3 2001/11/01 01:40:42 shaper Exp $
+// $Id: CharacterComponent.java,v 1.4 2001/11/27 08:09:34 mdb Exp $
package com.threerings.cast;
+import java.io.Serializable;
+
import com.samskivert.util.StringUtil;
import com.threerings.media.sprite.MultiFrameImage;
@@ -10,71 +12,43 @@ import com.threerings.media.sprite.Sprite;
/**
* The character component represents a single component that can be
- * composited with other character components to comprise an image
- * representing a single monolithic character displayable in any of
- * the eight cardinal compass directions as detailed in the {@link
- * com.threerings.media.sprite.Sprite} class's direction constants.
+ * composited with other character components to generate an image
+ * representing a complete character displayable in any of the eight
+ * compass directions as detailed in the {@link Sprite} class direction
+ * constants.
*/
-public class CharacterComponent
+public class CharacterComponent implements Serializable
{
+ /** The unique component identifier. */
+ public int componentId;
+
+ /** The component's name. */
+ public String name;
+
+ /** The class of components to which this one belongs. */
+ public ComponentClass componentClass;
+
/**
- * Constructs a character component.
+ * Constructs a character component with the specified id of the
+ * specified class.
*/
- public CharacterComponent (
- int cid, String fileid, ActionSequence seqs[], ComponentClass cclass)
+ public CharacterComponent (int componentId, String name,
+ ComponentClass compClass, FrameProvider fprov)
{
- _cid = cid;
- _fileid = fileid;
- _seqs = seqs;
- _cclass = cclass;
+ this.componentId = componentId;
+ this.name = name;
+ this.componentClass = compClass;
+ _frameProvider = fprov;
}
/**
- * Returns the unique component identifier.
+ * Returns the image frames for the specified action animation or null
+ * if no animation for the specified action is available for this
+ * component.
*/
- public int getId ()
+ public MultiFrameImage[] getFrames (String action)
{
- return _cid;
- }
-
- /**
- * Returns the action sequences for this component.
- */
- public ActionSequence[] getActionSequences ()
- {
- return _seqs;
- }
-
- /**
- * Returns the display frames used to display this component.
- */
- public MultiFrameImage[][] getFrames ()
- {
- return _frames;
- }
-
- /**
- * Returns the file id.
- */
- public String getFileId ()
- {
- return _fileid;
- }
-
- /**
- * Returns the component class associated with this component.
- */
- public ComponentClass getComponentClass ()
- {
- return _cclass;
- }
-
- /**
- * Sets the frames used to render this component.
- */
- public void setFrames (MultiFrameImage frames[][])
- {
- _frames = frames;
+ return _frameProvider.getFrames(this, action);
}
/**
@@ -82,22 +56,10 @@ public class CharacterComponent
*/
public String toString ()
{
- return "[cid=" + _cid + ", clid=" + _cclass.clid +
- ", seqs=" + StringUtil.toString(_seqs) + "]";
+ return "[componentId=" + componentId + ", name=" + name +
+ ", class=" + componentClass + "]";
}
- /** The unique character component identifier. */
- protected int _cid;
-
- /** The file id specifier for the tile set image file name. */
- protected String _fileid;
-
- /** The animation frames for each action sequence and orientation. */
- protected MultiFrameImage _frames[][];
-
- /** The component class. */
- protected ComponentClass _cclass;
-
- /** The character action sequences. */
- protected ActionSequence _seqs[];
+ /** The entity from which we obtain our animation frames. */
+ protected FrameProvider _frameProvider;
}
diff --git a/src/java/com/threerings/cast/CharacterDescriptor.java b/src/java/com/threerings/cast/CharacterDescriptor.java
index 2d18a20d2..de3ab0d1f 100644
--- a/src/java/com/threerings/cast/CharacterDescriptor.java
+++ b/src/java/com/threerings/cast/CharacterDescriptor.java
@@ -1,8 +1,9 @@
//
-// $Id: CharacterDescriptor.java,v 1.3 2001/11/01 01:40:42 shaper Exp $
+// $Id: CharacterDescriptor.java,v 1.4 2001/11/27 08:09:34 mdb Exp $
package com.threerings.cast;
+import java.util.Arrays;
import com.samskivert.util.StringUtil;
/**
@@ -14,7 +15,7 @@ public class CharacterDescriptor
/**
* Constructs the character descriptor.
*/
- public CharacterDescriptor (int components[])
+ public CharacterDescriptor (int[] components)
{
_components = components;
}
@@ -23,19 +24,42 @@ public class CharacterDescriptor
* Returns an array of the component identifiers comprising the
* character described by this descriptor.
*/
- public int[] getComponents ()
+ public int[] getComponentIds ()
{
return _components;
}
+ /**
+ * Compute a sensible hashcode for this object.
+ */
+ public int hashCode ()
+ {
+ int code = 0, clength = _components.length;
+ for (int i = 0; i < clength; i++) {
+ code ^= _components[i];
+ }
+ return code;
+ }
+
+ /**
+ * Compares this character descriptor to another.
+ */
+ public boolean equals (Object other)
+ {
+ if (other instanceof CharacterDescriptor) {
+ return Arrays.equals(_components,
+ ((CharacterDescriptor)other)._components);
+ } else {
+ return false;
+ }
+ }
+
/**
* Returns a string representation of this character descriptor.
*/
public String toString ()
{
- StringBuffer buf = new StringBuffer();
- buf.append("[").append(StringUtil.toString(_components));
- return buf.append("]").toString();
+ return "[cids=" + StringUtil.toString(_components) + "]";
}
/** The component identifiers comprising the character. */
diff --git a/src/java/com/threerings/cast/CharacterManager.java b/src/java/com/threerings/cast/CharacterManager.java
index 59fba17df..c0cfdce26 100644
--- a/src/java/com/threerings/cast/CharacterManager.java
+++ b/src/java/com/threerings/cast/CharacterManager.java
@@ -1,11 +1,13 @@
//
-// $Id: CharacterManager.java,v 1.9 2001/11/02 15:28:20 shaper Exp $
+// $Id: CharacterManager.java,v 1.10 2001/11/27 08:09:34 mdb Exp $
package com.threerings.cast;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.HashMap;
-import com.samskivert.util.CollectionUtil;
+import com.samskivert.util.Tuple;
import com.threerings.media.sprite.MultiFrameImage;
import com.threerings.media.sprite.Sprite;
@@ -14,21 +16,47 @@ import com.threerings.cast.Log;
import com.threerings.cast.util.TileUtil;
/**
- * The character manager provides facilities for constructing sprites
- * that are used to represent characters in a scene.
+ * The character manager provides facilities for constructing sprites that
+ * are used to represent characters in a scene. It also handles the
+ * compositing and caching of composited character animations.
*/
public class CharacterManager
{
/**
* Constructs the character manager.
*/
- public CharacterManager (ComponentRepository repo)
+ public CharacterManager (ComponentRepository crepo)
{
// keep this around
- _repo = repo;
+ _crepo = crepo;
- // determine component class render order
- _renderRank = getRenderRank();
+ // populate our actions table
+ Iterator iter = crepo.enumerateActionSequences();
+ while (iter.hasNext()) {
+ ActionSequence action = (ActionSequence)iter.next();
+ _actions.put(action.name, action);
+ }
+ }
+
+ /**
+ * Instructs the character manager to construct instances of this
+ * derived class of {@link CharacterSprite} when creating new sprites.
+ *
+ * @exception IllegalArgumentException thrown if the supplied class
+ * does not derive from {@link CharacterSprite}.
+ */
+ public void setCharacterClass (Class charClass)
+ {
+ // sanity check
+ if (!CharacterSprite.class.isAssignableFrom(charClass)) {
+ String errmsg = "Requested to use character sprite class that " +
+ "does not derive from CharacterSprite " +
+ "[class=" + charClass.getName() + "].";
+ throw new IllegalArgumentException(errmsg);
+ }
+
+ // make a note of it
+ _charClass = charClass;
}
/**
@@ -40,127 +68,12 @@ public class CharacterManager
*/
public CharacterSprite getCharacter (CharacterDescriptor desc)
{
- long start = System.currentTimeMillis();
-
- // get the array of component ids of each class
- CharacterComponent components[] = getComponents(desc.getComponents());
- if (components == null || components.length == 0) {
- Log.warning("No character components in descriptor.");
- return null;
- }
- // assume all components support the same set of action sequences
- ActionSequence seqs[] = components[0].getActionSequences();
-
- // create the composite character image
- MultiFrameImage frames[][] =
- createCompositeFrames(seqs.length, components);
- if (frames == null) {
- return null;
- }
-
- // instantiate the character sprite
- CharacterSprite sprite = createSprite();
- if (sprite == null) {
- return null;
- }
-
- // populate the character sprite with its attributes
- sprite.setAnimations(seqs, frames);
-
- long end = System.currentTimeMillis();
- Log.info("Generated character sprite [ms=" + (end - start) + "].");
-
- return sprite;
- }
-
- /**
- * Returns an iterator over the {@link ComponentClass} objects
- * representing all available character component classes.
- */
- public Iterator enumerateComponentClasses ()
- {
- return _repo.enumerateComponentClasses();
- }
-
- /**
- * Returns an iterator over the Integer objects
- * representing all available character component identifiers for
- * the given character component class identifier.
- */
- public Iterator enumerateComponentsByClass (int clid)
- {
- return _repo.enumerateComponentsByClass(clid);
- }
-
- /**
- * Instructs the character manager to construct instances of this
- * derived class of CharacterSprite.
- */
- public void setCharacterClass (Class charClass)
- {
- // sanity check
- if (!CharacterSprite.class.isAssignableFrom(charClass)) {
- Log.warning("Requested to use character class that does not " +
- "derive from CharacterSprite " +
- "[class=" + charClass.getName() + "].");
- return;
- }
-
- // make a note of it
- _charClass = charClass;
- }
-
- /**
- * Returns an array of the character component objects specified
- * in the given array of component ids.
- */
- protected CharacterComponent[] getComponents (int cids[])
- {
- int size = cids.length;
- CharacterComponent components[] = new CharacterComponent[size];
-
try {
- for (int ii = 0; ii < size; ii++) {
- components[ii] = _repo.getComponent(cids[ii]);
- }
+ CharacterSprite sprite = (CharacterSprite)
+ _charClass.newInstance();
+ sprite.init(desc, this);
+ return sprite;
- } catch (NoSuchComponentException nsce) {
- Log.warning("Exception retrieving character component " +
- "[nsce=" + nsce + "].");
- return null;
- }
-
- return components;
- }
-
- /**
- * Returns an array of multi frame images containing the fully
- * composited images for the given action sequences and
- * components.
- */
- protected MultiFrameImage[][] createCompositeFrames (
- int seqCount, CharacterComponent components[])
- {
- MultiFrameImage frames[][] =
- new MultiFrameImage[seqCount][Sprite.NUM_DIRECTIONS];
-
- // render all component frames one atop another
- for (int ii = 0; ii < _renderRank.length; ii++) {
- int clidx = _renderRank[ii].clid;
- TileUtil.compositeFrames(frames, components[clidx].getFrames());
- }
-
- return frames;
- }
-
- /**
- * Returns a new instance of the {@link CharacterSprite}-derived
- * class specified for use by this character manager.
- */
- protected CharacterSprite createSprite ()
- {
- try {
- return (CharacterSprite)_charClass.newInstance();
} catch (Exception e) {
Log.warning("Failed to instantiate character sprite " +
"[e=" + e + "].");
@@ -170,27 +83,82 @@ public class CharacterManager
}
/**
- * Returns an array of {@link ComponentClass} objects sorted into
- * the appropriate rendering order as specified by each component
- * class object.
+ * Returns the action sequence instance with the specified name or
+ * null if no such sequence exists.
*/
- protected ComponentClass[] getRenderRank ()
+ protected ActionSequence getActionSequence (String action)
{
- ArrayList classes = new ArrayList();
- CollectionUtil.addAll(classes, _repo.enumerateComponentClasses());
-
- ComponentClass rank[] = new ComponentClass[classes.size()];
- classes.toArray(rank);
- Arrays.sort(rank, ComponentClass.RENDER_COMP);
-
- return rank;
+ return (ActionSequence)_actions.get(action);
}
- /** The order in which to render component classes. */
- protected ComponentClass _renderRank[];
+ /**
+ * Obtains the composited animation frames for the specified action
+ * for a character with the specified descriptor. The resulting
+ * composited animation will be cached.
+ *
+ * @exception NoSuchComponentException thrown if any of the components
+ * in the supplied descriptor do not exist.
+ * @exception IllegalArgumentException thrown if any of the components
+ * referenced in the descriptor do not support the specified action.
+ */
+ protected MultiFrameImage[] getActionFrames (
+ CharacterDescriptor descrip, String action)
+ throws NoSuchComponentException
+ {
+ // the cache is keyed on both values
+ Tuple key = new Tuple(descrip, action);
+ MultiFrameImage[] frames = (MultiFrameImage[])_frames.get(key);
+ if (frames == null) {
+ // do the compositing
+ frames = createCompositeFrames(descrip, action);
+ // cache the result
+ _frames.put(key, frames);
+ }
+ return frames;
+ }
+
+ /**
+ * Generates the composited animation frames for the specified action
+ * for a character with the specified descriptor.
+ *
+ * @exception NoSuchComponentException thrown if any of the components
+ * in the supplied descriptor do not exist.
+ * @exception IllegalArgumentException thrown if any of the components
+ * referenced in the descriptor do not support the specified action.
+ */
+ protected MultiFrameImage[] createCompositeFrames (
+ CharacterDescriptor descrip, String action)
+ throws NoSuchComponentException
+ {
+ MultiFrameImage[] frames = new MultiFrameImage[Sprite.NUM_DIRECTIONS];
+
+ // obtain the necessary components
+ int[] cids = descrip.getComponentIds();
+ int ccount = cids.length;
+ CharacterComponent[] components = new CharacterComponent[ccount];
+ for (int i = 0; i < ccount; i++) {
+ components[i] = _crepo.getComponent(cids[i]);
+ }
+
+ // sort them into the proper rendering order
+ Arrays.sort(components, ComponentClass.RENDER_COMP);
+
+ // now composite the component frames, one atop the next
+ for (int i = 0; i < ccount; i++) {
+ TileUtil.compositeFrames(frames, components[i].getFrames(action));
+ }
+
+ return frames;
+ }
/** The component repository. */
- protected ComponentRepository _repo;
+ protected ComponentRepository _crepo;
+
+ /** A table of our action sequences. */
+ protected HashMap _actions = new HashMap();
+
+ /** A cache of composited animation frames. */
+ protected HashMap _frames = new HashMap();
/** The character class to be created. */
protected Class _charClass = CharacterSprite.class;
diff --git a/src/java/com/threerings/cast/CharacterSprite.java b/src/java/com/threerings/cast/CharacterSprite.java
index 2be6087a5..d68eab56c 100644
--- a/src/java/com/threerings/cast/CharacterSprite.java
+++ b/src/java/com/threerings/cast/CharacterSprite.java
@@ -1,65 +1,70 @@
//
-// $Id: CharacterSprite.java,v 1.17 2001/11/01 01:40:42 shaper Exp $
+// $Id: CharacterSprite.java,v 1.18 2001/11/27 08:09:35 mdb Exp $
package com.threerings.cast;
-import java.awt.Point;
-
-import com.threerings.media.sprite.*;
-
-import com.threerings.cast.Log;
+import com.threerings.media.sprite.MultiFrameImage;
+import com.threerings.media.sprite.Path;
+import com.threerings.media.sprite.Sprite;
/**
* A character sprite is a sprite that animates itself while walking
* about in a scene.
*/
-public class CharacterSprite extends Sprite
+public class CharacterSprite
+ extends Sprite implements StandardActions
{
/**
- * Constructs a character sprite.
+ * Initializes this character sprite with the specified character
+ * descriptor and character manager. It will obtain animation data
+ * from the supplied character manager.
*/
- public CharacterSprite ()
+ public void init (CharacterDescriptor descrip, CharacterManager charmgr)
{
+ // keep track of this stuff
+ _descrip = descrip;
+ _charmgr = charmgr;
+
// assign an arbitrary starting orientation
_orient = DIR_NORTH;
}
/**
- * Sets the action sequences available for this character sprite
- * and the animation frames that go along with each action.
- * Resets the character's currently selected action sequence to
- * the standing sequence.
+ * Sets the action sequence used when rendering the character, from
+ * the set of available sequences.
*/
- public void setAnimations (ActionSequence[] seqs,
- MultiFrameImage anims[][])
+ public void setActionSequence (String action)
{
- _seqs = seqs;
- _anims = anims;
- setActionSequence(WALKING);
- }
+ // get a reference to the action sequence so that we can obtain
+ // our animation frames and configure our frames per second
+ ActionSequence actseq = _charmgr.getActionSequence(action);
+ if (actseq == null) {
+ String errmsg = "No such action '" + action + "'.";
+ throw new IllegalArgumentException(errmsg);
+ }
- /**
- * Sets the action sequence used when rendering the character,
- * from the set of available sequences.
- */
- public void setActionSequence (int seqidx)
- {
- // save off the action sequence index
- _seqidx = seqidx;
+ try {
+ // obtain our animation frames for this action sequence
+ _frames = _charmgr.getActionFrames(_descrip, action);
- // update the sprite render attributes
- ActionSequence seq = _seqs[_seqidx];
- setFrames(_anims[_seqidx][_orient]);
- setFrameRate(seq.fps);
- setOrigin(seq.origin.x, seq.origin.y);
+ // update the sprite render attributes
+ setOrigin(actseq.origin.x, actseq.origin.y);
+ setFrameRate(actseq.framesPerSecond);
+ setFrames(_frames[_orient]);
+
+ } catch (NoSuchComponentException nsce) {
+ Log.warning("Character sprite referneces non-existent " +
+ "component [sprite=" + this + ", err=" + nsce + "].");
+ }
}
// documentation inherited
public void setOrientation (int orient)
{
super.setOrientation(orient);
+
// update the sprite frames to reflect the direction
- setActionSequence((_path == null) ? STANDING : WALKING);
+ setFrames(_frames[orient]);
}
/**
@@ -117,26 +122,22 @@ public class CharacterSprite extends Sprite
*/
protected void halt ()
{
+ // disable animation
+ setAnimationMode(NO_ANIMATION);
// come to a halt looking settled and at peace
setActionSequence(STANDING);
- // disable walking animation
- setAnimationMode(NO_ANIMATION);
}
- /** The action sequence constant for standing. */
- protected static final int STANDING = 0;
+ /** A reference to the descriptor for the character that we're
+ * visualizing. */
+ protected CharacterDescriptor _descrip;
- /** The action sequence constant for walking. */
- protected static final int WALKING = 1;
+ /** A reference to the character manager that created us. */
+ protected CharacterManager _charmgr;
- /** The currently selected action sequence. */
- protected int _seqidx;
-
- /** The available action sequences. */
- protected ActionSequence _seqs[];
-
- /** The animation frames for each action sequence and orientation. */
- protected MultiFrameImage _anims[][];
+ /** The animation frames for the active action sequence in each
+ * orientation. */
+ protected MultiFrameImage[] _frames;
/** The origin of the sprite. */
protected int _xorigin, _yorigin;
diff --git a/src/java/com/threerings/cast/ComponentClass.java b/src/java/com/threerings/cast/ComponentClass.java
index 72117b56a..89c2c1b9a 100644
--- a/src/java/com/threerings/cast/ComponentClass.java
+++ b/src/java/com/threerings/cast/ComponentClass.java
@@ -1,42 +1,60 @@
//
-// $Id: ComponentClass.java,v 1.1 2001/10/30 16:16:01 shaper Exp $
+// $Id: ComponentClass.java,v 1.2 2001/11/27 08:09:35 mdb Exp $
package com.threerings.cast;
+import java.io.Serializable;
import java.util.Comparator;
/**
- * The component class object denotes the particular class of
- * components that a {@link CharacterComponent} object belongs to.
- * Examples of feasible component classes might include "Hat", "Head",
- * or "Feet".
+ * Denotes a class of components to which {@link CharacterComponent}
+ * objects belong. Examples include "Hat", "Head", and "Feet". A component
+ * class dictates a component's rendering priority so that components can
+ * be rendered in an order that causes them to overlap properly.
*/
-public class ComponentClass
+public class ComponentClass implements Serializable
{
/** The comparator used to sort component class objects in render
* priority order. */
public static final Comparator RENDER_COMP = new RenderComparator();
- /** The unique component class identifier. */
- public int clid;
-
/** The component class name. */
public String name;
/** The render priority. */
- public int render;
+ public int renderPriority;
+
+ /**
+ * Classes with the same name are the same.
+ */
+ public boolean equals (Object other)
+ {
+ if (other instanceof ComponentClass) {
+ return name.equals(((ComponentClass)other).name);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Hashcode is based on component class name.
+ */
+ public int hashCode ()
+ {
+ return name.hashCode();
+ }
/**
* Returns a string representation of this component class.
*/
public String toString ()
{
- return "[clid=" + clid + ", name=" + name + "]";
+ return "[name=" + name + ", renderPriority=" + renderPriority + "]";
}
/**
- * The comparator used to sort component class objects in render
- * priority order so that compositing components into a single
+ * The comparator used to sort {@link CharacterComponent} instances in
+ * render priority order so that compositing components into a single
* character image can be done in the proper order.
*/
protected static class RenderComparator implements Comparator
@@ -44,21 +62,15 @@ public class ComponentClass
// documentation inherited
public int compare (Object a, Object b)
{
- if (!(a instanceof ComponentClass) ||
- !(b instanceof ComponentClass)) {
+ if (!(a instanceof CharacterComponent) ||
+ !(b instanceof CharacterComponent)) {
return -1;
}
- ComponentClass ca = (ComponentClass)a;
- ComponentClass cb = (ComponentClass)b;
-
- if (ca.render < cb.render) {
- return -1;
- } else if (ca.render == cb.render) {
- return 0;
- } else {
- return 1;
- }
+ CharacterComponent ca = (CharacterComponent)a;
+ CharacterComponent cb = (CharacterComponent)b;
+ return (ca.componentClass.renderPriority -
+ cb.componentClass.renderPriority);
}
// documentation inherited
diff --git a/src/java/com/threerings/cast/ComponentIDBroker.java b/src/java/com/threerings/cast/ComponentIDBroker.java
new file mode 100644
index 000000000..f575986fb
--- /dev/null
+++ b/src/java/com/threerings/cast/ComponentIDBroker.java
@@ -0,0 +1,45 @@
+//
+// $Id: ComponentIDBroker.java,v 1.1 2001/11/27 08:09:35 mdb Exp $
+
+package com.threerings.cast;
+
+import com.samskivert.io.PersistenceException;
+
+/**
+ * Brokers component ids. The component repository interface makes
+ * available a collection of components based on a unique identifier. The
+ * expectation is that a collection of components will be used to populate
+ * a repository and in that population process, component ids will be
+ * assigned to the components. The component id broker system provides a
+ * means by which named components can be mapped consistently to a set of
+ * component ids. Humans can then be responsible for assigning unique
+ * names to the components and the broker will ensure that those names map
+ * to unique ids that won't change if the repository is rebuilt from the
+ * source components.
+ */
+public interface ComponentIDBroker
+{
+ /**
+ * Returns the unique identifier for the named component. If no
+ * identifier has yet been assigned to the specified named component,
+ * one should be assigned and returned.
+ *
+ * @param cclass the name of the class to which the component belongs.
+ * @param cname the name of the component.
+ *
+ * @exception PersistenceException thrown if an error occurs
+ * communicating with the underlying persistence mechanism used to
+ * store the name to id mappings.
+ */
+ public int getComponentID (String cclass, String cname)
+ throws PersistenceException;
+
+ /**
+ * When the user of a component id broker is done obtaining component
+ * ids, it must call this method to give the component id broker an
+ * opportunity to flush any newly created component ids back to its
+ * persistent store.
+ */
+ public void commit ()
+ throws PersistenceException;
+}
diff --git a/src/java/com/threerings/cast/ComponentRepository.java b/src/java/com/threerings/cast/ComponentRepository.java
index ca575a285..0c90a19aa 100644
--- a/src/java/com/threerings/cast/ComponentRepository.java
+++ b/src/java/com/threerings/cast/ComponentRepository.java
@@ -1,34 +1,40 @@
//
-// $Id: ComponentRepository.java,v 1.3 2001/11/01 01:40:42 shaper Exp $
+// $Id: ComponentRepository.java,v 1.4 2001/11/27 08:09:35 mdb Exp $
package com.threerings.cast;
import java.util.Iterator;
/**
- * The component repository interface is intended to be implemented by
- * classes that provide access to {@link CharacterComponent} objects
- * keyed on their unique component identifier.
+ * Makes available a collection of character components and associated
+ * metadata. Character components are animated sequences that can be
+ * composited together to create a complete character visualization
+ * (imagine interchanging pairs of boots, torsos, hats, etc.).
*/
public interface ComponentRepository
{
/**
* Returns the {@link CharacterComponent} object for the given
- * unique component identifier.
+ * component identifier.
*/
- public CharacterComponent getComponent (int cid)
+ public CharacterComponent getComponent (int componentId)
throws NoSuchComponentException;
/**
- * Returns an iterator over the {@link ComponentClass} objects
- * representing all available character component classes.
+ * Iterates over the {@link ComponentClass} instances representing all
+ * available character component classes.
*/
public Iterator enumerateComponentClasses ();
/**
- * Returns an iterator over the Integer objects
- * representing all available character component identifiers for
- * the given character component class identifier.
+ * Iterates over the {@link ActionSequence} instances representing
+ * every available action sequence.
*/
- public Iterator enumerateComponentsByClass (int clid);
+ public Iterator enumerateActionSequences ();
+
+ /**
+ * Iterates over the component ids of all components in the specified
+ * class.
+ */
+ public Iterator enumerateComponentIds (ComponentClass compClass);
}
diff --git a/src/java/com/threerings/cast/FrameProvider.java b/src/java/com/threerings/cast/FrameProvider.java
new file mode 100644
index 000000000..9ee56c895
--- /dev/null
+++ b/src/java/com/threerings/cast/FrameProvider.java
@@ -0,0 +1,21 @@
+//
+// $Id: FrameProvider.java,v 1.1 2001/11/27 08:09:35 mdb Exp $
+
+package com.threerings.cast;
+
+import com.threerings.media.sprite.MultiFrameImage;
+
+/**
+ * Provides a mechanism where by a character component can obtain access
+ * to its image frames for a particular action in an on demand manner.
+ */
+public interface FrameProvider
+{
+ /**
+ * Returns the animation frames (in the eight sprite directions) for
+ * the specified action of the specified component. May return null if
+ * the specified action does not exist for the specified component.
+ */
+ public MultiFrameImage[] getFrames (
+ CharacterComponent component, String action);
+}
diff --git a/src/java/com/threerings/cast/NoSuchComponentException.java b/src/java/com/threerings/cast/NoSuchComponentException.java
index cfc5b16d4..60c40b405 100644
--- a/src/java/com/threerings/cast/NoSuchComponentException.java
+++ b/src/java/com/threerings/cast/NoSuchComponentException.java
@@ -1,5 +1,5 @@
//
-// $Id: NoSuchComponentException.java,v 1.1 2001/10/26 01:17:21 shaper Exp $
+// $Id: NoSuchComponentException.java,v 1.2 2001/11/27 08:09:35 mdb Exp $
package com.threerings.cast;
@@ -9,16 +9,16 @@ package com.threerings.cast;
*/
public class NoSuchComponentException extends Exception
{
- public NoSuchComponentException (int cid)
+ public NoSuchComponentException (int componentId)
{
- super("No such component [cid=" + cid + "]");
- _cid = cid;
+ super("No such component [componentId=" + componentId + "]");
+ _componentId = componentId;
}
- public int getId ()
+ public int getComponentId ()
{
- return _cid;
+ return _componentId;
}
- protected int _cid;
+ protected int _componentId;
}
diff --git a/src/java/com/threerings/cast/StandardActions.java b/src/java/com/threerings/cast/StandardActions.java
new file mode 100644
index 000000000..0495ad6cd
--- /dev/null
+++ b/src/java/com/threerings/cast/StandardActions.java
@@ -0,0 +1,19 @@
+//
+// $Id: StandardActions.java,v 1.1 2001/11/27 08:09:35 mdb Exp $
+
+package com.threerings.cast;
+
+/**
+ * Actions are referenced by name and this interface defines constants for
+ * two standard actions: standing and walking. Because character sprites
+ * follow paths, it is helpful for them to take care of switching between
+ * the standing and walking actions automatically.
+ */
+public interface StandardActions
+{
+ /** The name of the standard standing action. */
+ public static final String STANDING = "standing";
+
+ /** The name of the standard walking action. */
+ public static final String WALKING = "walking";
+}
diff --git a/src/java/com/threerings/cast/builder/BuilderModel.java b/src/java/com/threerings/cast/builder/BuilderModel.java
index 8d49be6d3..baa9ea835 100644
--- a/src/java/com/threerings/cast/builder/BuilderModel.java
+++ b/src/java/com/threerings/cast/builder/BuilderModel.java
@@ -1,29 +1,32 @@
//
-// $Id: BuilderModel.java,v 1.3 2001/11/18 04:09:21 mdb Exp $
+// $Id: BuilderModel.java,v 1.4 2001/11/27 08:09:35 mdb Exp $
-package com.threerings.cast.tools.builder;
+package com.threerings.cast.builder;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import com.samskivert.util.CollectionUtil;
-import com.samskivert.util.HashIntMap;
-import com.threerings.cast.CharacterManager;
+import com.threerings.cast.ComponentRepository;
import com.threerings.cast.ComponentClass;
/**
- * The builder model represents the current state of the character
- * the user is building.
+ * The builder model represents the current state of the character the
+ * user is building.
*/
public class BuilderModel
{
/**
* Constructs a builder model.
*/
- public BuilderModel (CharacterManager charmgr)
+ public BuilderModel (ComponentRepository crepo)
{
- gatherComponentInfo(charmgr);
- _selected = new int[_classes.size()];
+ gatherComponentInfo(crepo);
}
/**
@@ -58,28 +61,36 @@ public class BuilderModel
}
/**
- * Returns a map of the components available for each component
- * class, keyed on component class id.
+ * Returns the list of components available in the specified class.
*/
- public Map getComponents ()
+ public List getComponents (ComponentClass cclass)
{
- return Collections.unmodifiableMap(_components);
+ List list = (List)_components.get(cclass);
+ if (list == null) {
+ list = new ArrayList();
+ }
+ return list;
}
/**
- * Returns an array of the currently selected component ids.
+ * Returns the selected components in an array.
*/
public int[] getSelectedComponents ()
{
- return _selected;
+ int[] values = new int[_selected.size()];
+ Iterator iter = _selected.values().iterator();
+ for (int i = 0; iter.hasNext(); i++) {
+ values[i] = ((Integer)iter.next()).intValue();
+ }
+ return values;
}
/**
- * Sets the selected component for the given component class id.
+ * Sets the selected component for the given component class.
*/
- public void setSelectedComponent (int clid, int cid)
+ public void setSelectedComponent (ComponentClass cclass, int cid)
{
- _selected[clid] = cid;
+ _selected.put(cclass, new Integer(cid));
notifyListeners(BuilderModelListener.COMPONENT_CHANGED);
}
@@ -87,22 +98,21 @@ public class BuilderModel
* Gathers component class and component information from the
* character manager for later reference by others.
*/
- protected void gatherComponentInfo (CharacterManager charmgr)
+ protected void gatherComponentInfo (ComponentRepository crepo)
{
// get the list of all component classes
- CollectionUtil.addAll(_classes, charmgr.enumerateComponentClasses());
+ CollectionUtil.addAll(_classes, crepo.enumerateComponentClasses());
for (int ii = 0; ii < _classes.size(); ii++) {
// get the list of components available for this class
- int clid = ((ComponentClass)_classes.get(ii)).clid;
- Iterator iter = charmgr.enumerateComponentsByClass(clid);
+ ComponentClass cclass = (ComponentClass)_classes.get(ii);
+ Iterator iter = crepo.enumerateComponentIds(cclass);
while (iter.hasNext()) {
Integer cid = (Integer)iter.next();
-
- ArrayList clist = (ArrayList)_components.get(clid);
+ ArrayList clist = (ArrayList)_components.get(cclass);
if (clist == null) {
- _components.put(clid, clist = new ArrayList());
+ _components.put(cclass, clist = new ArrayList());
}
clist.add(cid);
@@ -111,10 +121,10 @@ public class BuilderModel
}
/** The currently selected character components. */
- protected int _selected[];
+ protected HashMap _selected = new HashMap();
/** The hashtable of available component ids for each class. */
- protected HashIntMap _components = new HashIntMap();
+ protected HashMap _components = new HashMap();
/** The list of all available component classes. */
protected ArrayList _classes = new ArrayList();
diff --git a/src/java/com/threerings/cast/builder/BuilderModelListener.java b/src/java/com/threerings/cast/builder/BuilderModelListener.java
index 56b4bce50..3b223cd6e 100644
--- a/src/java/com/threerings/cast/builder/BuilderModelListener.java
+++ b/src/java/com/threerings/cast/builder/BuilderModelListener.java
@@ -1,7 +1,7 @@
//
-// $Id: BuilderModelListener.java,v 1.2 2001/11/18 04:09:21 mdb Exp $
+// $Id: BuilderModelListener.java,v 1.3 2001/11/27 08:09:35 mdb Exp $
-package com.threerings.cast.tools.builder;
+package com.threerings.cast.builder;
/**
* The builder model listener interface should be implemented by
diff --git a/src/java/com/threerings/cast/builder/BuilderPanel.java b/src/java/com/threerings/cast/builder/BuilderPanel.java
index 572e10936..1d82daecf 100644
--- a/src/java/com/threerings/cast/builder/BuilderPanel.java
+++ b/src/java/com/threerings/cast/builder/BuilderPanel.java
@@ -1,30 +1,25 @@
//
-// $Id: BuilderPanel.java,v 1.4 2001/11/18 04:09:21 mdb Exp $
+// $Id: BuilderPanel.java,v 1.5 2001/11/27 08:09:35 mdb Exp $
-package com.threerings.cast.tools.builder;
-
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.Iterator;
+package com.threerings.cast.builder;
import javax.swing.*;
-
import com.samskivert.swing.*;
-import com.samskivert.util.StringUtil;
-import com.threerings.cast.*;
+import com.threerings.cast.CharacterManager;
+import com.threerings.cast.ComponentRepository;
/**
- * The builder panel presents the user with an overview of a
- * composited character and facilities for altering the individual
- * components that comprise the character's display image.
+ * The builder panel presents the user with an overview of a composited
+ * character and facilities for altering the individual components that
+ * comprise the character's display image.
*/
public class BuilderPanel extends JPanel
{
/**
* Constructs the builder panel.
*/
- public BuilderPanel (CharacterManager charmgr)
+ public BuilderPanel (CharacterManager charmgr, ComponentRepository crepo)
{
setLayout(new VGroupLayout());
@@ -35,7 +30,7 @@ public class BuilderPanel extends JPanel
gl.setOffAxisPolicy(GroupLayout.STRETCH);
// create the builder model
- BuilderModel model = new BuilderModel(charmgr);
+ BuilderModel model = new BuilderModel(crepo);
// create the component selection and sprite display panels
JPanel sub = new JPanel(gl);
diff --git a/src/java/com/threerings/cast/builder/ClassEditor.java b/src/java/com/threerings/cast/builder/ClassEditor.java
index d9ade9560..40f27f2d2 100644
--- a/src/java/com/threerings/cast/builder/ClassEditor.java
+++ b/src/java/com/threerings/cast/builder/ClassEditor.java
@@ -1,7 +1,7 @@
//
-// $Id: ClassEditor.java,v 1.2 2001/11/18 04:09:21 mdb Exp $
+// $Id: ClassEditor.java,v 1.3 2001/11/27 08:09:35 mdb Exp $
-package com.threerings.cast.tools.builder;
+package com.threerings.cast.builder;
import java.util.List;
@@ -17,20 +17,20 @@ import com.threerings.cast.Log;
import com.threerings.cast.ComponentClass;
/**
- * The class editor displays a label and a slider that allow the user
- * to select the desired component for a given component class.
+ * The class editor displays a label and a slider that allow the user to
+ * select the desired component for a given component class.
*/
public class ClassEditor extends JPanel implements ChangeListener
{
/**
* Constructs a class editor.
*/
- public ClassEditor (BuilderModel model, ComponentClass cclass,
- List components)
+ public ClassEditor (
+ BuilderModel model, ComponentClass cclass, List components)
{
_model = model;
_components = components;
- _clid = cclass.clid;
+ _cclass = cclass;
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
gl.setOffAxisPolicy(GroupLayout.STRETCH);
@@ -76,11 +76,11 @@ public class ClassEditor extends JPanel implements ChangeListener
protected void setSelectedComponent (int idx)
{
int cid = ((Integer)_components.get(idx)).intValue();
- _model.setSelectedComponent(_clid, cid);
+ _model.setSelectedComponent(_cclass, cid);
}
- /** The component class id associated with this editor. */
- protected int _clid;
+ /** The component class associated with this editor. */
+ protected ComponentClass _cclass;
/** The components selectable via this editor. */
protected List _components;
diff --git a/src/java/com/threerings/cast/builder/ComponentPanel.java b/src/java/com/threerings/cast/builder/ComponentPanel.java
index fe1d552e5..d3fd261f9 100644
--- a/src/java/com/threerings/cast/builder/ComponentPanel.java
+++ b/src/java/com/threerings/cast/builder/ComponentPanel.java
@@ -1,7 +1,7 @@
//
-// $Id: ComponentPanel.java,v 1.5 2001/11/18 04:09:21 mdb Exp $
+// $Id: ComponentPanel.java,v 1.6 2001/11/27 08:09:35 mdb Exp $
-package com.threerings.cast.tools.builder;
+package com.threerings.cast.builder;
import java.util.List;
import java.util.Map;
@@ -40,12 +40,10 @@ public class ComponentPanel extends JPanel
protected void addClassEditors (BuilderModel model)
{
List classes = model.getComponentClasses();
- Map components = model.getComponents();
-
int size = classes.size();
for (int ii = 0; ii < size; ii++) {
ComponentClass cclass = (ComponentClass)classes.get(ii);
- List ccomps = (List)components.get(new Integer(cclass.clid));
+ List ccomps = model.getComponents(cclass);
add(new ClassEditor(model, cclass, ccomps));
}
}
diff --git a/src/java/com/threerings/cast/builder/SpritePanel.java b/src/java/com/threerings/cast/builder/SpritePanel.java
index e8d4394b8..b83c8e2db 100644
--- a/src/java/com/threerings/cast/builder/SpritePanel.java
+++ b/src/java/com/threerings/cast/builder/SpritePanel.java
@@ -1,7 +1,7 @@
//
-// $Id: SpritePanel.java,v 1.3 2001/11/18 04:09:21 mdb Exp $
+// $Id: SpritePanel.java,v 1.4 2001/11/27 08:09:35 mdb Exp $
-package com.threerings.cast.tools.builder;
+package com.threerings.cast.builder;
import java.awt.*;
import javax.swing.BorderFactory;
diff --git a/src/java/com/threerings/cast/bundle/BundleUtil.java b/src/java/com/threerings/cast/bundle/BundleUtil.java
new file mode 100644
index 000000000..af4e09e88
--- /dev/null
+++ b/src/java/com/threerings/cast/bundle/BundleUtil.java
@@ -0,0 +1,55 @@
+//
+// $Id: BundleUtil.java,v 1.1 2001/11/27 08:09:35 mdb Exp $
+
+package com.threerings.cast.bundle;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+
+import com.threerings.resource.ResourceBundle;
+
+/**
+ * Utility functions related to creating and manipulating component
+ * bundles.
+ */
+public class BundleUtil
+{
+ /** The path in the metadata bundle to the serialized action table. */
+ public static final String ACTIONS_PATH = "actions.dat";
+
+ /** The path in the metadata bundle to the serialized action tile sets
+ * table. */
+ public static final String ACTION_SETS_PATH = "action_sets.dat";
+
+ /** The path in the metadata bundle to the serialized component class
+ * table. */
+ public static final String CLASSES_PATH = "classes.dat";
+
+ /** The path in the component bundle to the serialized component id to
+ * class/type mapping. */
+ public static final String COMPONENTS_PATH = "components.dat";
+
+ /** The file extension of our action tile images. */
+ public static final String IMAGE_EXTENSION = ".png";
+
+ /**
+ * Attempts to load an object from the supplied resource bundle with
+ * the specified path.
+ *
+ * @return the unserialized object in question or null if no
+ * serialized object data was available at the specified path.
+ *
+ * @exception IOException thrown if an I/O error occurs while reading
+ * the object from the bundle.
+ */
+ public static Object loadObject (ResourceBundle bundle, String path)
+ throws IOException, ClassNotFoundException
+ {
+ InputStream bin = bundle.getResource(path);
+ if (bin == null) {
+ return null;
+ }
+ return new ObjectInputStream(bin).readObject();
+ }
+}
diff --git a/src/java/com/threerings/cast/bundle/BundledComponentRepository.java b/src/java/com/threerings/cast/bundle/BundledComponentRepository.java
new file mode 100644
index 000000000..998b38218
--- /dev/null
+++ b/src/java/com/threerings/cast/bundle/BundledComponentRepository.java
@@ -0,0 +1,303 @@
+//
+// $Id: BundledComponentRepository.java,v 1.1 2001/11/27 08:09:35 mdb Exp $
+
+package com.threerings.cast.bundle;
+
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import javax.imageio.ImageIO;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+import com.samskivert.io.NestableIOException;
+import com.samskivert.util.HashIntMap;
+import com.samskivert.util.Tuple;
+
+import org.apache.commons.collections.FilterIterator;
+import org.apache.commons.collections.Predicate;
+
+import com.threerings.resource.ResourceBundle;
+import com.threerings.resource.ResourceManager;
+
+import com.threerings.media.sprite.MultiFrameImage;
+import com.threerings.media.sprite.Sprite;
+
+import com.threerings.media.tile.ImageProvider;
+import com.threerings.media.tile.TileSet;
+import com.threerings.media.tile.NoSuchTileException;
+
+import com.threerings.cast.CharacterComponent;
+import com.threerings.cast.ComponentClass;
+import com.threerings.cast.ComponentRepository;
+import com.threerings.cast.FrameProvider;
+import com.threerings.cast.Log;
+import com.threerings.cast.NoSuchComponentException;
+
+/**
+ * A component repository implementation that obtains information from
+ * resource bundles.
+ *
+ * @see ResourceManager
+ */
+public class BundledComponentRepository
+ implements ComponentRepository
+{
+ /**
+ * Constructs a repository which will obtain its resource set from the
+ * supplied resource manager.
+ *
+ * @param rmgr the resource manager from which to obtain our resource
+ * set.
+ * @param name the name of the resource set from which we will be
+ * loading our component data.
+ *
+ * @exception IOException thrown if an I/O error occurs while reading
+ * our metadata from the resource bundles.
+ */
+ public BundledComponentRepository (ResourceManager rmgr, String name)
+ throws IOException
+ {
+ // first we obtain the resource set from whence will come our
+ // bundles
+ ResourceBundle[] rbundles = rmgr.getResourceSet(name);
+
+ // look for our metadata info in each of the bundles
+ try {
+ for (int i = 0; i < rbundles.length; i++) {
+ if (_actions == null) {
+ _actions = (HashMap)BundleUtil.loadObject(
+ rbundles[i], BundleUtil.ACTIONS_PATH);
+ }
+ if (_actionSets == null) {
+ _actionSets = (HashMap)BundleUtil.loadObject(
+ rbundles[i], BundleUtil.ACTION_SETS_PATH);
+ }
+ if (_classes == null) {
+ _classes = (HashMap)BundleUtil.loadObject(
+ rbundles[i], BundleUtil.CLASSES_PATH);
+ }
+ }
+
+ // now go back and load up all of the component information
+ for (int i = 0; i < rbundles.length; i++) {
+ HashIntMap comps = (HashIntMap)BundleUtil.loadObject(
+ rbundles[i], BundleUtil.COMPONENTS_PATH);
+ if (comps == null) {
+ continue;
+ }
+
+ // create a frame provider for this bundle
+ FrameProvider fprov = new ResourceBundleProvider(rbundles[i]);
+
+ // now create character component instances for each component
+ // in the serialized table
+ Iterator iter = comps.keySet().iterator();
+ while (iter.hasNext()) {
+ int componentId = ((Integer)iter.next()).intValue();
+ Tuple info = (Tuple)comps.get(componentId);
+ createComponent(componentId, (String)info.left,
+ (String)info.right, fprov);
+ }
+ }
+
+ } catch (ClassNotFoundException cnfe) {
+ throw new NestableIOException(
+ "Internal error unserializing metadata", cnfe);
+ }
+ }
+
+ // documentation inherited
+ public CharacterComponent getComponent (int componentId)
+ throws NoSuchComponentException
+ {
+ CharacterComponent component = (CharacterComponent)
+ _components.get(componentId);
+ if (component == null) {
+ throw new NoSuchComponentException(componentId);
+ }
+ return component;
+ }
+
+ // documentation inherited
+ public Iterator enumerateComponentClasses ()
+ {
+ return _classes.values().iterator();
+ }
+
+ // documentation inherited
+ public Iterator enumerateActionSequences ()
+ {
+ return _actions.values().iterator();
+ }
+
+ // documentation inherited
+ public Iterator enumerateComponentIds (final ComponentClass compClass)
+ {
+ Predicate classP = new Predicate() {
+ public boolean evaluate (Object input) {
+ CharacterComponent comp = (CharacterComponent)
+ _components.get(input);
+ return comp.componentClass.equals(compClass);
+ }
+ };
+ return new FilterIterator(_components.keySet().iterator(), classP);
+ }
+
+ /**
+ * Creates a component and inserts it into the component table.
+ */
+ protected void createComponent (
+ int componentId, String cclass, String cname, FrameProvider fprov)
+ {
+ // look up the component class information
+ ComponentClass clazz = (ComponentClass)_classes.get(cclass);
+ if (clazz == null) {
+ Log.warning("Non-existent component class " +
+ "[class=" + cclass + ", name=" + cname +
+ ", id=" + componentId + "].");
+ return;
+ }
+
+ // create the component
+ CharacterComponent component = new CharacterComponent(
+ componentId, cname, clazz, fprov);
+
+ // cache it
+ _components.put(componentId, component);
+ }
+
+ /**
+ * Instances of these provide images to our component action tilesets
+ * and frames to our components.
+ */
+ protected class ResourceBundleProvider
+ implements ImageProvider, FrameProvider
+ {
+ /**
+ * Constructs an instance that will obtain image data from the
+ * specified resource bundle.
+ */
+ public ResourceBundleProvider (ResourceBundle bundle)
+ {
+ _bundle = bundle;
+ }
+
+ // documentation inherited
+ public BufferedImage loadImage (String path)
+ throws IOException
+ {
+ // obtain the image data from our resource bundle
+ InputStream imgin = _bundle.getResource(path);
+ if (imgin == null) {
+ String errmsg = "No such image in resource bundle " +
+ "[bundle=" + _bundle + ", path=" + path + "].";
+ throw new FileNotFoundException(errmsg);
+ }
+ return ImageIO.read(imgin);
+ }
+
+ // documentation inherited
+ public MultiFrameImage[] getFrames (
+ CharacterComponent component, String action)
+ {
+ // get the tileset for this action
+ TileSet aset = (TileSet)_actionSets.get(action);
+ if (aset == null) {
+ Log.warning("Can't provide animation frames for undefined " +
+ "action [action=" + action +
+ ", component=" + component + "].");
+ return null;
+ }
+
+ // construct the appropriate image path for the component
+ String path = component.componentClass.name + File.separator +
+ component.name + File.separator + action +
+ BundleUtil.IMAGE_EXTENSION;
+
+ // clone the tileset with this new image path
+ try {
+ TileSet fset = aset.clone(path);
+ fset.setImageProvider(this);
+
+ // and create the necessary multiframe image instances
+ MultiFrameImage[] frames =
+ new MultiFrameImage[Sprite.NUM_DIRECTIONS];
+ for (int i = 0; i < frames.length; i++) {
+ frames[i] = new TileSetFrameImage(fset, i);
+ }
+
+ return frames;
+
+ } catch (CloneNotSupportedException cnse) {
+ Log.warning("Unable to clone tileset [action=" + action +
+ ", component=" + component +
+ ", set=" + aset + "].");
+ return null;
+ }
+ }
+
+ /** The resource bundle from which we obtain image data. */
+ protected ResourceBundle _bundle;
+ }
+
+ /**
+ * Used to provide multiframe images using data obtained from a
+ * tileset.
+ */
+ protected static class TileSetFrameImage implements MultiFrameImage
+ {
+ /**
+ * Constructs a tileset frame image with the specified tileset and
+ * for the specified orientation.
+ */
+ public TileSetFrameImage (TileSet set, int orientation)
+ {
+ _set = set;
+ _orient = orientation;
+ }
+
+ // documentation inherited
+ public int getFrameCount ()
+ {
+ return _set.getTileCount() / Sprite.NUM_DIRECTIONS;
+ }
+
+ // documentation inherited
+ public Image getFrame (int index)
+ {
+ int tileIndex = _orient * Sprite.NUM_DIRECTIONS + index;
+ try {
+ return _set.getTile(tileIndex).getImage();
+ } catch (NoSuchTileException nste) {
+ Log.warning("Can't extract action frame [set=" + _set +
+ ", orient=" + _orient + ", index=" + index + "].");
+ return null;
+ }
+ }
+
+ /** The tileset from which we obtain our frame images. */
+ protected TileSet _set;
+
+ /** The particular orientation for which we are providing
+ * frames. */
+ protected int _orient;
+ }
+
+ /** A table of action sequences. */
+ protected HashMap _actions;
+
+ /** A table of action sequence tilesets. */
+ protected HashMap _actionSets;
+
+ /** A table of component classes. */
+ protected HashMap _classes;
+
+ /** The component table. */
+ protected HashIntMap _components = new HashIntMap();
+}
diff --git a/src/java/com/threerings/cast/bundle/tools/ComponentBundlerTask.java b/src/java/com/threerings/cast/bundle/tools/ComponentBundlerTask.java
new file mode 100644
index 000000000..59c5e2fed
--- /dev/null
+++ b/src/java/com/threerings/cast/bundle/tools/ComponentBundlerTask.java
@@ -0,0 +1,263 @@
+//
+// $Id: ComponentBundlerTask.java,v 1.1 2001/11/27 08:09:35 mdb Exp $
+
+package com.threerings.cast.tools.bundle;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import java.util.jar.JarOutputStream;
+import java.util.jar.JarEntry;
+
+import com.samskivert.io.PersistenceException;
+import com.samskivert.util.HashIntMap;
+import com.samskivert.util.StringUtil;
+import com.samskivert.util.Tuple;
+
+import org.apache.commons.util.StreamUtils;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+
+import com.threerings.cast.ComponentIDBroker;
+import com.threerings.cast.bundle.BundleUtil;
+
+/**
+ * Ant task for creating component bundles. This task must be configured
+ * with a number of parameters:
+ *
+ *
+ * target=[path to bundle file, which will be created] + * mapfile=[path to the component map file which maintains a mapping from + * component id to component class/name, it will be created the + * first time and updated as new components are mapped] + *+ * + * It should also contain one or more nested <fileset> elements that + * enumerate the action tileset images that should be included in the + * component bundle. + */ +public class ComponentBundlerTask extends Task +{ + /** + * Sets the path to the bundle file that we'll be creating. + */ + public void setTarget (File target) + { + _target = target; + } + + /** + * Sets the path to the component map file that we'll use to obtain + * component ids for the bundled components. + */ + public void setMapfile (File mapfile) + { + _mapfile = mapfile; + } + + /** + * Adds a nested <fileset> element. + */ + public void addFileset (FileSet set) + { + _filesets.add(set); + } + + /** + * Performs the actual work of the task. + */ + public void execute () throws BuildException + { + // make sure everythign was set up properly + ensureSet(_target, "Must specify the path to the target bundle " + + "file via the 'file' attribute."); + ensureSet(_mapfile, "Must specify the path to the component map " + + "file via the 'mapfile' attribute."); + + // load up our component ID broker + ComponentIDBroker broker = loadBroker(_mapfile); + + try { + // make sure we can create our bundle file + FileOutputStream fout = new FileOutputStream(_target); + JarOutputStream jout = new JarOutputStream(fout); + + // we'll fill this with component id to tuple mappings as we go + HashIntMap mapping = new HashIntMap(); + + // deal with the filesets + for (int i = 0; i < _filesets.size(); i++) { + FileSet fs = (FileSet)_filesets.get(i); + DirectoryScanner ds = fs.getDirectoryScanner(project); + File fromDir = fs.getDir(project); + String[] srcFiles = ds.getIncludedFiles(); + + for (int f = 0; f < srcFiles.length; f++) { + File cfile = new File(fromDir, srcFiles[f]); + // determine the [class, name, action] triplet + String[] info = decomposePath(cfile.getPath()); + // obtain the component id from our id broker + int cid = broker.getComponentID(info[0], info[1]); + // add a mapping for this component + mapping.put(cid, new Tuple(info[0], info[1])); + // construct the path that'll go in the jar file and + // stuff the component into the jarfile + jout.putNextEntry(new JarEntry(composePath(info))); + StreamUtils.pipe(new FileInputStream(cfile), jout); + } + } + + // write our mapping table to the jar file as well + jout.putNextEntry(new JarEntry(BundleUtil.COMPONENTS_PATH)); + ObjectOutputStream oout = new ObjectOutputStream(jout); + oout.writeObject(mapping); + oout.flush(); + + // seal up our jar file + jout.close(); + + } catch (IOException ioe) { + String errmsg = "Unable to create component bundle."; + throw new BuildException(errmsg, ioe); + + } catch (PersistenceException pe) { + String errmsg = "Unable to obtain component ID mapping."; + throw new BuildException(errmsg, pe); + } + + // save our updated component ID broker + saveBroker(_mapfile, broker); + } + + /** + * Decomposes the full path to a component image into a [class, name, + * action] triplet. + */ + protected String[] decomposePath (String path) + throws BuildException + { + // first strip off the file extension + if (!path.endsWith(BundleUtil.IMAGE_EXTENSION)) { + throw new BuildException("Can't bundle malformed image file " + + "[path=" + path + "]."); + } + path = path.substring(0, path.length() - + BundleUtil.IMAGE_EXTENSION.length()); + + // now decompose the path + String[] components = StringUtil.split(path, File.separator); + int clen = components.length; + if (clen < 3) { + throw new BuildException("Can't bundle malformed image file " + + "[path=" + path + "]."); + } + String[] info = new String[3]; + System.arraycopy(components, clen-3, info, 0, 3); + return info; + } + + /** + * Composes a triplet of [class, name, action] into the path that + * should be supplied to the JarEntry that contains the associated + * image data. + */ + protected String composePath (String[] info) + { + return (info[0] + File.separator + info[1] + File.separator + + info[2] + BundleUtil.IMAGE_EXTENSION); + } + + protected void ensureSet (Object value, String errmsg) + throws BuildException + { + if (value == null) { + throw new BuildException(errmsg); + } + } + + /** + * Loads the hashmap ID broker from its persistent representation in + * the specified file. + */ + protected HashMapIDBroker loadBroker (File mapfile) + throws BuildException + { + HashMapIDBroker broker; + + try { + FileInputStream fin = new FileInputStream(mapfile); + ObjectInputStream oin = new ObjectInputStream(fin); + broker = (HashMapIDBroker)oin.readObject(); + + } catch (FileNotFoundException fnfe) { + // if the file doesn't yet exist, start with a blank broker + broker = new HashMapIDBroker(); + + } catch (Exception e) { + throw new BuildException("Error loading component ID map.", e); + } + + return broker; + } + + /** + * Stores a persistent representation of the supplied hashmap ID + * broker in the specified file. + */ + protected void saveBroker (File mapfile, ComponentIDBroker broker) + throws BuildException + { + try { + FileOutputStream fout = new FileOutputStream(mapfile); + ObjectOutputStream oout = new ObjectOutputStream(fout); + oout.writeObject(broker); + oout.close(); + } catch (IOException ioe) { + throw new BuildException("Unable to store component ID map.", ioe); + } + } + + protected static class HashMapIDBroker + extends HashMap implements ComponentIDBroker + { + public int getComponentID (String cclass, String cname) + throws PersistenceException + { + Tuple key = new Tuple(cclass, cname); + Integer cid = (Integer)get(key); + if (cid == null) { + cid = new Integer(++_nextCID); + put(key, cid); + } + return cid.intValue(); + } + + public void commit () + throws PersistenceException + { + // nothing doing + } + + protected int _nextCID = 0; + } + + /** The path to our component bundle file. */ + protected File _target; + + /** The path to our component map file. */ + protected File _mapfile; + + /** A list of filesets that contain tile images. */ + protected ArrayList _filesets = new ArrayList(); +} diff --git a/src/java/com/threerings/cast/bundle/tools/MetadataBundlerTask.java b/src/java/com/threerings/cast/bundle/tools/MetadataBundlerTask.java new file mode 100644 index 000000000..100508c53 --- /dev/null +++ b/src/java/com/threerings/cast/bundle/tools/MetadataBundlerTask.java @@ -0,0 +1,241 @@ +// +// $Id: MetadataBundlerTask.java,v 1.1 2001/11/27 08:09:35 mdb Exp $ + +package com.threerings.cast.tools.bundle; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectOutputStream; + +import java.util.ArrayList; +import java.util.HashMap; + +import java.util.jar.JarOutputStream; +import java.util.jar.JarEntry; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Task; + +import org.xml.sax.SAXException; +import org.apache.commons.digester.Digester; + +import com.samskivert.util.Tuple; + +import com.threerings.media.tile.TileSet; +import com.threerings.media.tools.tile.xml.SwissArmyTileSetRuleSet; + +import com.threerings.cast.ActionSequence; +import com.threerings.cast.ComponentClass; +import com.threerings.cast.bundle.BundleUtil; + +import com.threerings.cast.tools.xml.ActionRuleSet; +import com.threerings.cast.tools.xml.ClassRuleSet; + +/** + * Ant task for creating metadata bundles, which contain action sequence + * and component class definition information. This task must be + * configured with a number of parameters: + * + *
+ * actiondef=[path to actions.xml] + * classdef=[path to classes.xml] + * file=[path to metadata bundle, which will be created] + *+ */ +public class MetadataBundlerTask extends Task +{ + public void setActiondef (String actiondef) + { + _actionDef = actiondef; + } + + public void setClassdef (String classdef) + { + _classDef = classdef; + } + + public void setTarget (File target) + { + _target = target; + } + + /** + * Performs the actual work of the task. + */ + public void execute () + throws BuildException + { + // make sure everythign was set up properly + ensureSet(_actionDef, "Must specify the action sequence " + + "definitions via the 'actiondef' attribute."); + ensureSet(_classDef, "Must specify the component class definitions " + + "via the 'classdef' attribute."); + ensureSet(_target, "Must specify the path to the target bundle " + + "file via the 'target' attribute."); + + // make sure we can write to the target bundle file + FileOutputStream fout = null; + try { + fout = new FileOutputStream(_target); + + // parse our metadata + Tuple tuple = parseActions(); + HashMap actions = (HashMap)tuple.left; + HashMap actionSets = (HashMap)tuple.right; + HashMap classes = parseClasses(); + + // and create the bundle file + JarOutputStream jout = new JarOutputStream(fout); + + // throw the serialized actions table in there + JarEntry aentry = new JarEntry(BundleUtil.ACTIONS_PATH); + jout.putNextEntry(aentry); + ObjectOutputStream oout = new ObjectOutputStream(jout); + oout.writeObject(actions); + oout.flush(); + + // throw the serialized action tilesets table in there + JarEntry sentry = new JarEntry(BundleUtil.ACTION_SETS_PATH); + jout.putNextEntry(sentry); + oout = new ObjectOutputStream(jout); + oout.writeObject(actionSets); + oout.flush(); + + // throw the serialized classes table in there + JarEntry centry = new JarEntry(BundleUtil.CLASSES_PATH); + jout.putNextEntry(centry); + oout = new ObjectOutputStream(jout); + oout.writeObject(classes); + oout.flush(); + + // close it up and we're done + jout.close(); + + } catch (IOException ioe) { + String errmsg = "Unable to output to target bundle " + + "[path=" + _target.getPath() + "]."; + throw new BuildException(errmsg, ioe); + + } finally { + if (fout != null) { + try { + fout.close(); + } catch (IOException ioe) { + // nothing to complain about here + } + } + } + } + + protected Tuple parseActions () + throws BuildException + { + // scan through the XML once to read the actions + Digester digester = new Digester(); + ActionRuleSet arules = new ActionRuleSet(); + arules.setPrefix("actions"); + digester.addRuleSet(arules); + digester.addSetNext("actions" + ActionRuleSet.ACTION_PATH, + "add", Object.class.getName()); + ArrayList actlist = parseList(digester, _actionDef); + + // now go through a second time reading the tileset info + digester = new Digester(); + SwissArmyTileSetRuleSet srules = new SwissArmyTileSetRuleSet(); + srules.setPrefix("actions" + ActionRuleSet.ACTION_PATH); + digester.addRuleSet(srules); + digester.addSetNext("actions" + ActionRuleSet.ACTION_PATH + + SwissArmyTileSetRuleSet.TILESET_PATH, "add", + Object.class.getName()); + ArrayList setlist = parseList(digester, _actionDef); + + // sanity check + if (actlist.size() != setlist.size()) { + String errmsg = "An action is missing its tileset " + + "definition, or something even wackier is going on."; + throw new BuildException(errmsg); + } + + // now create our mappings + HashMap actmap = new HashMap(); + HashMap setmap = new HashMap(); + + // create the action map + for (int i = 0; i < setlist.size(); i++) { + TileSet set = (TileSet)setlist.get(i); + ActionSequence act = (ActionSequence)actlist.get(i); + actmap.put(act.name, act); + setmap.put(act.name, set); + } + + return new Tuple(actmap, setmap); + } + + protected HashMap parseClasses () + throws BuildException + { + // load up our action and class info + Digester digester = new Digester(); + + // add our action rule set and a a rule to grab parsed actions + ClassRuleSet crules = new ClassRuleSet(); + crules.setPrefix("classes"); + digester.addRuleSet(crules); + digester.addSetNext("classes" + ClassRuleSet.CLASS_PATH, + "add", Object.class.getName()); + + ArrayList setlist = parseList(digester, _classDef); + HashMap clmap = new HashMap(); + + // create the action map + for (int i = 0; i < setlist.size(); i++) { + ComponentClass cl = (ComponentClass)setlist.get(i); + clmap.put(cl.name, cl); + } + + return clmap; + } + + protected ArrayList parseList (Digester digester, String path) + throws BuildException + { + try { + FileInputStream fin = new FileInputStream(path); + BufferedInputStream bin = new BufferedInputStream(fin); + + ArrayList setlist = new ArrayList(); + digester.push(setlist); + + // now fire up the digester to parse the stream + try { + digester.parse(bin); + } catch (Exception e) { + throw new BuildException("Parsing error.", e); + } + + return setlist; + + } catch (FileNotFoundException fnfe) { + String errmsg = "Unable to load metadata definition file " + + "[path=" + path + "]."; + throw new BuildException(errmsg, fnfe); + } + } + + protected void ensureSet (Object value, String errmsg) + throws BuildException + { + if (value == null) { + throw new BuildException(errmsg); + } + } + + protected String _actionDef; + protected String _classDef; + protected File _target; +} diff --git a/src/java/com/threerings/cast/tools/xml/ActionRuleSet.java b/src/java/com/threerings/cast/tools/xml/ActionRuleSet.java new file mode 100644 index 000000000..3513a3baa --- /dev/null +++ b/src/java/com/threerings/cast/tools/xml/ActionRuleSet.java @@ -0,0 +1,85 @@ +// +// $Id: ActionRuleSet.java,v 1.1 2001/11/27 08:09:35 mdb Exp $ + +package com.threerings.cast.tools.xml; + +import org.apache.commons.digester.Digester; +import org.apache.commons.digester.RuleSetBase; + +import com.samskivert.util.StringUtil; + +import com.samskivert.xml.CallMethodSpecialRule; +import com.samskivert.xml.SetFieldRule; +import com.samskivert.xml.SetPropertyFieldsRule; + +import com.threerings.cast.ActionSequence; + +/** + * The action rule set is used to parse the attributes of an action + * sequence instance. + */ +public class ActionRuleSet extends RuleSetBase +{ + /** The component of the digester path that is appended by the action + * rule set to match a action. This is appended to whatever prefix is + * provided to the action rule set to obtain the complete XML path to + * a matched action. */ + public static final String ACTION_PATH = "/action"; + + /** + * Instructs the action rule set to match actions with the supplied + * prefix. For example, passing a prefix of
actions will
+ * match actions in the following XML file:
+ *
+ * + * <actions> + * <action> + * // ... + * </action> + * </actions> + *+ * + * This must be called before adding the ruleset to a digester. + */ + public void setPrefix (String prefix) + { + _prefix = prefix; + } + + /** + * Adds the necessary rules to the digester to parse our actions. + */ + public void addRuleInstances (Digester digester) + { + // this creates the appropriate instance when we encounter a + //
classes will match classes in the following XML file:
+ *
+ * + * <classes> + * <class .../> + * </classes> + *+ * + * This must be called before adding the ruleset to a digester. + */ + public void setPrefix (String prefix) + { + _prefix = prefix; + } + + /** + * Adds the necessary rules to the digester to parse our classes. + */ + public void addRuleInstances (Digester digester) + { + // this creates the appropriate instance when we encounter a + //
Does not currently perform validation on the input XML stream,
- * though the parsing code assumes the XML document is well-formed.
- */
-public class XMLComponentParser extends SimpleParser
-{
- // documentation inherited
- public void startElement (
- String uri, String localName, String qName, Attributes attributes)
- {
- if (qName.equals("charactercomponents")) {
- // save off the image directory
- _imagedir = attributes.getValue("imagedir");
- // load the tile sets
- loadTileSets(attributes.getValue("tilesets"));
-
- } else if (qName.equals("action")) {
- // construct and save off the action sequence object
- ActionSequence as = getActionSequence(attributes);
- _actions.put(as.asid, as);
-
- } else if (qName.equals("class")) {
- // construct and save off the component class object
- ComponentClass cclass = getComponentClass(attributes);
- _classes.put(cclass.clid, cclass);
-
- } else if (qName.equals("component")) {
- // construct and save off the character component
- CharacterComponent component = getComponent(attributes);
- _components.put(component.getId(), component);
- }
- }
-
- /**
- * Loads the component descriptions in the given file into the
- * given component data hashtables.
- */
- public void loadComponents (String file, HashIntMap actions,
- HashIntMap classes, HashIntMap components)
- throws IOException
- {
- // save off hashtables for reference while parsing
- _actions = actions;
- _classes = classes;
- _components = components;
-
- // clear out remnants of any previous parsing antics
- _imagedir = null;
- _tilesets.clear();
-
- // parse the file
- parseFile(file);
- }
-
- /**
- * Returns the image directory the component tile images reside
- * within.
- */
- public String getImageDir ()
- {
- return _imagedir;
- }
-
- /**
- * Loads the tile sets in the XML file at the given path into the
- * tile sets available for reference by the action sequences.
- */
- protected void loadTileSets (String path)
- {
- // make sure we have a reasonable path
- if (path == null) {
- Log.warning("Null component tile set description file path.");
- return;
- }
-
- // parse the tile set description file
- XMLTileSetParser p = new XMLTileSetParser();
- try {
- p.loadTileSets(path, _tilesets);
- } catch (IOException e) {
- Log.warning("Exception loading component tile set descriptions " +
- "[path=" + path + "].");
- }
- }
-
- /**
- * Returns a new action sequence object as described by the given
- * attributes.
- */
- protected ActionSequence getActionSequence (Attributes attrs)
- {
- ActionSequence as = new ActionSequence();
-
- as.asid = parseInt(attrs.getValue("asid"));
- as.name = attrs.getValue("name");
- as.fileid = attrs.getValue("fileid");
-
- String tileset = attrs.getValue("tileset");
- as.tileset = (TileSet)_tilesets.get(tileset);
- if (as.tileset == null) {
- Log.warning("Action sequence references non-existent " +
- "tile set [asid=" + as.asid +
- ", tileset=" + tileset + "].");
- }
-
- as.fps = parseInt(attrs.getValue("fps"));
- parsePoint(attrs.getValue("origin"), as.origin);
-
- return as;
- }
-
- /**
- * Returns a new component class object as described by the given
- * attributes.
- */
- protected ComponentClass getComponentClass (Attributes attrs)
- {
- ComponentClass cclass = new ComponentClass();
- cclass.clid = parseInt(attrs.getValue("clid"));
- cclass.name = attrs.getValue("name");
- cclass.render = parseInt(attrs.getValue("render"));
- return cclass;
- }
-
- /**
- * Returns a new character component object as described by the
- * given attributes.
- */
- protected CharacterComponent getComponent (Attributes attrs)
- {
- // retrieve the component attributes
- int cid = parseInt(attrs.getValue("cid"));
- String fileid = attrs.getValue("fileid");
- String val = attrs.getValue("asids");
- int asids[] = StringUtil.parseIntArray(val);
- int clid = parseInt(attrs.getValue("clid"));
- ComponentClass cclass = (ComponentClass)_classes.get(clid);
-
- // gather the array of relevant action sequences
- ActionSequence seqs[] = getActionSequences(asids);
-
- // construct the component sans frames for now
- CharacterComponent component =
- new CharacterComponent(cid, fileid, seqs, cclass);
-
- // warn if the component has notably invalid attributes
- validateComponent(cid, asids, clid);
-
- return component;
- }
-
- /**
- * Outputs a warning regarding the given component id if the given
- * action sequence ids and component class id for that component
- * don't exist.
- */
- protected void validateComponent (int cid, int asids[], int clid)
- {
- // check the action sequence ids
- for (int ii = 0; ii < asids.length; ii++) {
- int asid = asids[ii];
- if (_actions.get(asid) == null) {
- Log.warning(
- "Component references non-existent action sequence " +
- "[cid=" + cid + ", asid=" + asid + "].");
- }
- }
-
- // check the class id
- if (_classes.get(clid) == null) {
- Log.warning("Component references non-existent class " +
- "[cid=" + cid + ", clid=" + clid + "].");
- }
- }
-
- /**
- * Returns an array of action sequence objects corresponding to
- * the action sequence ids in the given array.
- */
- protected ActionSequence[] getActionSequences (int asids[])
- {
- // sort the action sequence ids for better regularity
- Arrays.sort(asids);
-
- // create and populate the array
- ActionSequence[] seqs = new ActionSequence[asids.length];
- for (int ii = 0; ii < asids.length; ii++) {
- seqs[ii] = (ActionSequence)_actions.get(asids[ii]);
- }
-
- return seqs;
- }
-
- /**
- * Converts a string containing values as (x, y) into the
- * corresponding integer values and populates the given point
- * object.
- *
- * @param str the point values in string format.
- * @param point the point object to populate.
- */
- protected void parsePoint (String str, Point point)
- {
- int vals[] = StringUtil.parseIntArray(str);
- point.setLocation(vals[0], vals[1]);
- }
-
- /** The image directory containing the component image files. */
- protected String _imagedir;
-
- /** The hashtable of component tile sets. */
- protected HashMap _tilesets = new HashMap();
-
- /** The hashtable of action sequences gathered while parsing. */
- protected HashIntMap _actions;
-
- /** The hashtable of component classes gathered while parsing. */
- protected HashIntMap _classes;
-
- /** The hashtable of character components gathered while parsing. */
- protected HashIntMap _components;
-}
diff --git a/src/java/com/threerings/cast/tools/xml/XMLComponentRepository.java b/src/java/com/threerings/cast/tools/xml/XMLComponentRepository.java
deleted file mode 100644
index 350dc0a57..000000000
--- a/src/java/com/threerings/cast/tools/xml/XMLComponentRepository.java
+++ /dev/null
@@ -1,155 +0,0 @@
-//
-// $Id: XMLComponentRepository.java,v 1.7 2001/11/18 04:09:21 mdb Exp $
-
-package com.threerings.cast.tools.xml;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.Iterator;
-
-import com.samskivert.util.Config;
-import com.samskivert.util.HashIntMap;
-
-import com.threerings.cast.*;
-import com.threerings.cast.util.TileUtil;
-
-import com.threerings.media.ImageManager;
-import com.threerings.media.tile.TileManager;
-
-import com.threerings.miso.Log;
-import com.threerings.miso.util.MisoUtil;
-
-/**
- * The xml component repository provides access to character
- * components obtained from component descriptions stored in an XML
- * file.
- */
-public class XMLComponentRepository implements ComponentRepository
-{
- /**
- * Constructs an xml component repository.
- */
- public XMLComponentRepository (Config config)
- {
- // load component types and components
- String file = config.getValue(COMPONENTS_KEY, DEFAULT_COMPONENTS);
- try {
- XMLComponentParser p = new XMLComponentParser();
- p.loadComponents(file, _actions, _classes, _components);
- _imagedir = p.getImageDir();
-
- } catch (IOException ioe) {
- Log.warning("Exception loading component descriptions " +
- "[ioe=" + ioe + "].");
- }
- }
-
- // documentation inherited
- public CharacterComponent getComponent (int cid)
- throws NoSuchComponentException
- {
- // get the component information
- CharacterComponent c = (CharacterComponent)_components.get(cid);
- if (c == null) {
- throw new NoSuchComponentException(cid);
- }
-
- // get the character animation frames
- c.setFrames(TileUtil.getComponentFrames(_imagedir, c));
-
- return c;
- }
-
- // documentation inherited
- public Iterator enumerateComponentClasses ()
- {
- return Collections.unmodifiableMap(_classes).values().iterator();
- }
-
- // documentation inherited
- public Iterator enumerateComponentsByClass (int clid)
- {
- return new ComponentIterator(clid);
- }
-
- /**
- * Iterates over all components of a specified component type, and
- * optionally a specified component class, in the component
- * hashtable.
- */
- protected class ComponentIterator implements Iterator
- {
- /**
- * Constructs an iterator that iterates over all components of
- * the specified component class.
- */
- public ComponentIterator (int clid)
- {
- _clid = clid;
- _iter = _components.keys();
- advance();
- }
-
- public boolean hasNext ()
- {
- return (_next != null);
- }
-
- public Object next ()
- {
- Object next = _next;
- advance();
- return next;
- }
-
- public void remove ()
- {
- throw new UnsupportedOperationException();
- }
-
- protected void advance ()
- {
- while (_iter.hasNext()) {
- Integer cid = (Integer)_iter.next();
-
- CharacterComponent c =
- (CharacterComponent)_components.get(cid);
- if (c.getComponentClass().clid == _clid) {
- _next = cid;
- return;
- }
- }
- _next = null;
- }
-
- /** The component class id for inclusion in the iterator. */
- protected int _clid;
-
- /** The next character component associated with this
- * iterator, or null if no more exist. */
- protected Object _next;
-
- /** The iterator over all components in the repository. */
- protected Iterator _iter;
- }
-
- /** The config key for the character description file. */
- protected static final String COMPONENTS_KEY =
- MisoUtil.CONFIG_KEY + ".components";
-
- /** The default character description file. */
- protected static final String DEFAULT_COMPONENTS =
- "rsrc/config/miso/components.xml";
-
- /** The image directory containing the component image files. */
- protected String _imagedir;
-
- /** The hashtable of component types. */
- protected HashIntMap _actions = new HashIntMap();
-
- /** The hashtable of component classes. */
- protected HashIntMap _classes = new HashIntMap();
-
- /** The hashtable of character components. */
- protected HashIntMap _components = new HashIntMap();
-}
diff --git a/src/java/com/threerings/cast/util/TileUtil.java b/src/java/com/threerings/cast/util/TileUtil.java
index 5cf846b00..76754c311 100644
--- a/src/java/com/threerings/cast/util/TileUtil.java
+++ b/src/java/com/threerings/cast/util/TileUtil.java
@@ -1,16 +1,17 @@
//
-// $Id: TileUtil.java,v 1.3 2001/11/18 04:09:21 mdb Exp $
+// $Id: TileUtil.java,v 1.4 2001/11/27 08:09:35 mdb Exp $
package com.threerings.cast.util;
import java.awt.Image;
import java.awt.image.BufferedImage;
-import com.threerings.media.sprite.*;
-import com.threerings.media.tile.*;
+import com.threerings.media.sprite.MultiFrameImage;
+import com.threerings.media.sprite.Sprite;
+// import com.threerings.media.tile.*;
import com.threerings.cast.Log;
-import com.threerings.cast.*;
+// import com.threerings.cast.*;
/**
* Miscellaneous tile-related utility functions.
@@ -18,126 +19,105 @@ import com.threerings.cast.*;
public class TileUtil
{
/**
- * Renders each of the given src component frames
- * into the corresponding frames of dest, allocating
- * blank image frames for dest if none yet exist.
+ * Renders each of the given src component frames into
+ * the corresponding frames of dest, allocating blank
+ * image frames for dest if none yet exist.
+ *
+ * @throws IllegalArgumentException if the frame count of the source
+ * and destination images don't match.
*/
public static void compositeFrames (
- MultiFrameImage[][] dest, MultiFrameImage[][] src)
+ MultiFrameImage[] dest, MultiFrameImage[] src)
{
- for (int ii = 0; ii < dest.length; ii++) {
- for (int orient = 0; orient < Sprite.NUM_DIRECTIONS; orient++) {
- // create blank destination frames if needed
- if (dest[ii][orient] == null) {
- dest[ii][orient] = createBlankFrames(src[ii][orient]);
- }
+ for (int orient = 0; orient < Sprite.NUM_DIRECTIONS; orient++) {
+ MultiFrameImage sframes = src[orient];
+ MultiFrameImage dframes = dest[orient];
- // slap the images together
- compositeFrames(dest[ii][orient], src[ii][orient]);
+ // create blank destination frames if needed
+ if (dframes == null) {
+ dest[orient] = new BlankFrameImage(sframes);
+ }
+
+ // sanity check
+ int dsize = dframes.getFrameCount();
+ int ssize = sframes.getFrameCount();
+ if (dsize != ssize) {
+ String errmsg = "Can't composite images with inequal " +
+ "frame counts [dest=" + dsize + ", src=" + ssize + "].";
+ throw new IllegalArgumentException(errmsg);
+ }
+
+ // slap the images together
+ for (int ii = 0; ii < dsize; ii++) {
+ Image dimg = dframes.getFrame(ii);
+ Image simg = sframes.getFrame(ii);
+ dimg.getGraphics().drawImage(simg, 0, 0, null);
}
}
}
- /**
- * Returns a new {@link MultiFrameImage} that has empty images in
- * all frames. The number of frames in the resulting multi frame
- * image is the same as the frame count for src.
- */
- public static MultiFrameImage createBlankFrames (MultiFrameImage src)
- {
- // TODO: for now, just use the first frame from the source to
- // get the width and height for all of the blank frames. fix
- // this hack soon.
- Image img = src.getFrame(0);
- int wid = img.getWidth(null), hei = img.getHeight(null);
- return new BlankFrameImage(wid, hei, src.getFrameCount());
- }
+// /**
+// * Returns a two-dimensional array of multi frame images containing
+// * the frames of animation used to render the sprite while standing or
+// * walking in each of the directions it may face.
+// */
+// public static MultiFrameImage[][] getComponentFrames (
+// String imagedir, CharacterComponent c)
+// {
+// ActionSequence seqs[] = c.getActionSequences();
+// MultiFrameImage frames[][] =
+// new MultiFrameImage[seqs.length][Sprite.NUM_DIRECTIONS];
- /**
- * Returns a two-dimensional array of multi frame images
- * containing the frames of animation used to render the sprite
- * while standing or walking in each of the directions it may
- * face.
- */
- public static MultiFrameImage[][] getComponentFrames (
- String imagedir, CharacterComponent c)
- {
- ActionSequence seqs[] = c.getActionSequences();
- MultiFrameImage frames[][] =
- new MultiFrameImage[seqs.length][Sprite.NUM_DIRECTIONS];
+// try {
+// for (int ii = 0; ii < seqs.length; ii++) {
+// ActionSequence as = seqs[ii];
- try {
- for (int ii = 0; ii < seqs.length; ii++) {
- ActionSequence as = seqs[ii];
+// // get the tile set containing the component tiles for
+// // this action sequence
+// String file = getImageFile(imagedir, as, c);
+// TileSet tset = null;
- // get the tile set containing the component tiles for
- // this action sequence
- String file = getImageFile(imagedir, as, c);
- TileSet tset = null;
+// try {
+// tset = as.tileset.clone(file);
+// } catch (CloneNotSupportedException e) {
+// Log.warning("Failed to clone tile set " +
+// "[tset=" + as.tileset + "].");
+// return null;
+// }
- try {
- tset = as.tileset.clone(file);
- } catch (CloneNotSupportedException e) {
- Log.warning("Failed to clone tile set " +
- "[tset=" + as.tileset + "].");
- return null;
- }
+// // get the number of frames of animation
+// int frameCount = tset.getTileCount() / Sprite.NUM_DIRECTIONS;
- // get the number of frames of animation
- int frameCount = tset.getTileCount() / Sprite.NUM_DIRECTIONS;
+// for (int dir = 0; dir < Sprite.NUM_DIRECTIONS; dir++) {
+// // retrieve all images for the sequence and direction
+// Image imgs[] = new Image[frameCount];
+// for (int jj = 0; jj < frameCount; jj++) {
+// int idx = (dir * frameCount) + jj;
+// imgs[jj] = tset.getTileImage(idx);
+// }
- for (int dir = 0; dir < Sprite.NUM_DIRECTIONS; dir++) {
- // retrieve all images for the sequence and direction
- Image imgs[] = new Image[frameCount];
- for (int jj = 0; jj < frameCount; jj++) {
- int idx = (dir * frameCount) + jj;
- imgs[jj] = tset.getTileImage(idx);
- }
+// // create the multi frame image
+// frames[ii][dir] = new MultiFrameImageImpl(imgs);
+// }
+// }
- // create the multi frame image
- frames[ii][dir] = new MultiFrameImageImpl(imgs);
- }
- }
+// } catch (TileException te) {
+// Log.warning("Exception retrieving character images " +
+// "[te=" + te + "].");
+// return null;
+// }
- } catch (TileException te) {
- Log.warning("Exception retrieving character images " +
- "[te=" + te + "].");
- return null;
- }
+// return frames;
+// }
- return frames;
- }
-
- /**
- * Returns the file path for the given action sequence and component.
- */
- protected static String getImageFile (
- String imagedir, ActionSequence as, CharacterComponent c)
- {
- return imagedir + as.fileid + "_" + c.getFileId() + IMAGE_SUFFIX;
- }
-
- /**
- * Renders each of the given src frames into the
- * corresponding frames of dest.
- */
- protected static void compositeFrames (
- MultiFrameImage dest, MultiFrameImage src)
- {
- int dsize = dest.getFrameCount(), ssize = src.getFrameCount();
- if (dsize != ssize) {
- Log.warning(
- "Can't composite images with differing frame counts " +
- "[dest=" + dsize + ", src=" + ssize + "].");
- return;
- }
-
- for (int ii = 0; ii < dsize; ii++) {
- Image dimg = dest.getFrame(ii);
- Image simg = src.getFrame(ii);
- dimg.getGraphics().drawImage(simg, 0, 0, null);
- }
- }
+// /**
+// * Returns the file path for the given action sequence and component.
+// */
+// protected static String getImageFile (
+// String imagedir, ActionSequence as, CharacterComponent c)
+// {
+// return imagedir + as.fileid + "_" + c.getFileId() + IMAGE_SUFFIX;
+// }
/**
* An implementation of the {@link MultiFrameImage} interface that
@@ -147,31 +127,35 @@ public class TileUtil
protected static class BlankFrameImage implements MultiFrameImage
{
/**
- * Constructs a blank frame image.
+ * Constructs a blank frame image based on the dimensions of
+ * template multi-frame image.
*/
- public BlankFrameImage (int width, int height, int frameCount)
+ public BlankFrameImage (MultiFrameImage template)
{
- _imgs = new Image[frameCount];
- for (int ii = 0; ii < frameCount; ii++) {
- _imgs[ii] = new BufferedImage(
- width, height, BufferedImage.TYPE_INT_ARGB);
+ int frameCount = template.getFrameCount();
+ _images = new Image[frameCount];
+ for (int i = 0; i < frameCount; i++) {
+ Image img = template.getFrame(i);
+ _images[i] = new BufferedImage(img.getWidth(null),
+ img.getHeight(null),
+ BufferedImage.TYPE_INT_ARGB);
}
}
// documentation inherited
public int getFrameCount ()
{
- return _imgs.length;
+ return _images.length;
}
// documentation inherited
public Image getFrame (int index)
{
- return _imgs[index];
+ return _images[index];
}
/** The frame images. */
- protected Image _imgs[];
+ protected Image[] _images;
}
/** The image file name suffix appended to component image file names. */
diff --git a/src/java/com/threerings/resource/ResourceManager.java b/src/java/com/threerings/resource/ResourceManager.java
index c0adf9f84..0619feeb4 100644
--- a/src/java/com/threerings/resource/ResourceManager.java
+++ b/src/java/com/threerings/resource/ResourceManager.java
@@ -1,5 +1,5 @@
//
-// $Id: ResourceManager.java,v 1.5 2001/11/26 23:48:04 mdb Exp $
+// $Id: ResourceManager.java,v 1.6 2001/11/27 08:09:35 mdb Exp $
package com.threerings.resource;
@@ -186,7 +186,7 @@ public class ResourceManager
while (tok.hasMoreTokens()) {
// obtain the path and fully qualify it
- String path = tok.nextToken();
+ String path = tok.nextToken().trim();
if (!path.startsWith(File.separator)) {
path = appRoot + path;
}