Behold, Nenya, Ring of Water and repository for our media and animation related
goodies, both Java 2D and LWJGL/JME 3D. git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@1 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// $Id: ActionCache.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
/**
|
||||
* A mechanism for caching composited character action animations on disk.
|
||||
*/
|
||||
public interface ActionCache
|
||||
{
|
||||
/**
|
||||
* Fetches from the cache a composited set of images for a particular
|
||||
* character for a particular action.
|
||||
*/
|
||||
public ActionFrames getActionFrames (
|
||||
CharacterDescriptor descrip, String action);
|
||||
|
||||
/**
|
||||
* Requests that the specified set of action frames for the specified
|
||||
* character be cached.
|
||||
*/
|
||||
public void cacheActionFrames (
|
||||
CharacterDescriptor descrip, String action, ActionFrames frames);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// $Id: ActionFrames.java 3310 2005-01-24 23:08:21Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import com.threerings.media.image.Colorization;
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
/**
|
||||
* Encapsulates a set of frames in each of {@link
|
||||
* DirectionCodes#DIRECTION_COUNT} orientations that are used to render a
|
||||
* character sprite.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public TrimmedMultiFrameImage getFrames (int orient);
|
||||
|
||||
/**
|
||||
* Returns the x offset from the upper left of the image to the
|
||||
* "origin" for this character frame. A sprite with location (x, y)
|
||||
* will be rendered such that its origin is coincident with that
|
||||
* location.
|
||||
*/
|
||||
public int getXOrigin (int orient, int frameIdx);
|
||||
|
||||
/**
|
||||
* Returns the y offset from the upper left of the image to the
|
||||
* "origin" for this character frame. A sprite with location (x, y)
|
||||
* will be rendered such that its origin is coincident with that
|
||||
* location.
|
||||
*/
|
||||
public int getYOrigin (int orient, int frameIdx);
|
||||
|
||||
/**
|
||||
* Creates a clone of these action frames which will have the supplied
|
||||
* colorizations applied to the frame images.
|
||||
*/
|
||||
public ActionFrames cloneColorized (Colorization[] zations);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// $Id: ActionSequence.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 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 implements Serializable
|
||||
{
|
||||
/**
|
||||
* Defines the name of the default action sequence. When component
|
||||
* tilesets are loaded to build a set of composited images for a
|
||||
* particular action sequence, a check is first made for a component
|
||||
* tileset specific to the action sequence and then for the
|
||||
* component's default tileset if the action specific tileset did not
|
||||
* exist.
|
||||
*/
|
||||
public static final String DEFAULT_SEQUENCE = "default";
|
||||
|
||||
/** The action sequence name. */
|
||||
public String name;
|
||||
|
||||
/** The number of frames per second to show when animating. */
|
||||
public float framesPerSecond;
|
||||
|
||||
/** 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 +
|
||||
", orients=" + (orients == null ? 0 : orients.length) + "]";
|
||||
}
|
||||
|
||||
/** Increase this value when object's serialized state is impacted by
|
||||
* a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// $Id: CastPrefs.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import com.samskivert.util.Config;
|
||||
|
||||
/**
|
||||
* Provides access to runtime configuration parameters for this package
|
||||
* and its subpackages.
|
||||
*/
|
||||
public class CastPrefs
|
||||
{
|
||||
/** Used to load our preferences from a properties file and map them
|
||||
* to the persistent Java preferences repository. */
|
||||
public static Config config = new Config("rsrc/config/cast");
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// $Id: CharacterComponent.java 3740 2005-10-20 22:12:45Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.media.sprite.Sprite;
|
||||
|
||||
/**
|
||||
* The character component represents a single component that can be
|
||||
* 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 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 with the specified id of the
|
||||
* specified class.
|
||||
*/
|
||||
public CharacterComponent (int componentId, String name,
|
||||
ComponentClass compClass, FrameProvider fprov)
|
||||
{
|
||||
this.componentId = componentId;
|
||||
this.name = name;
|
||||
this.componentClass = compClass;
|
||||
_frameProvider = fprov;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the image frames for the specified action animation or null if
|
||||
* no animation for the specified action is available for this component.
|
||||
*
|
||||
* @param type null for the normal action frames or one of the custom
|
||||
* action sub-types: {@link StandardActions#SHADOW_TYPE}, etc.
|
||||
*/
|
||||
public ActionFrames getFrames (String action, String type)
|
||||
{
|
||||
return _frameProvider.getFrames(this, action, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this component is equal to the other component. The
|
||||
* comparison is made on <code>componentId</code>.
|
||||
*/
|
||||
public boolean equals (Object other)
|
||||
{
|
||||
if (other instanceof CharacterComponent) {
|
||||
return componentId == ((CharacterComponent)other).componentId;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this character component.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
return StringUtil.fieldsToString(this);
|
||||
}
|
||||
|
||||
/** The entity from which we obtain our animation frames. */
|
||||
protected FrameProvider _frameProvider;
|
||||
|
||||
/** Increase this value when object's serialized state is impacted by
|
||||
* a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
//
|
||||
// $Id: CharacterDescriptor.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.util.Arrays;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.threerings.media.image.Colorization;
|
||||
|
||||
/**
|
||||
* The character descriptor object details the components that are
|
||||
* pieced together to create a single character image.
|
||||
*/
|
||||
public class CharacterDescriptor
|
||||
{
|
||||
/**
|
||||
* Constructs a character descriptor.
|
||||
*
|
||||
* @param components the component ids of the individual components
|
||||
* that make up this character.
|
||||
* @param zations the colorizations to apply to each of the character
|
||||
* component images when compositing actions (an array of
|
||||
* colorizations for each component, elements of which can be null to
|
||||
* make it easier to support per-component specialized colorizations).
|
||||
* This can be null if image recolorization is not desired.
|
||||
*/
|
||||
public CharacterDescriptor (int[] components, Colorization[][] zations)
|
||||
{
|
||||
_components = components;
|
||||
_zations = zations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the component identifiers comprising the
|
||||
* character described by this descriptor.
|
||||
*/
|
||||
public int[] getComponentIds ()
|
||||
{
|
||||
return _components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of colorization arrays to be applied to the
|
||||
* components when compositing action images (one array per
|
||||
* component).
|
||||
*/
|
||||
public Colorization[][] getColorizations ()
|
||||
{
|
||||
return _zations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the colorizations to be used by this character descriptor.
|
||||
*/
|
||||
public void setColorizations (Colorization[][] zations)
|
||||
{
|
||||
_zations = zations;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 false;
|
||||
}
|
||||
|
||||
// both the component ids and the colorizations must be equal
|
||||
CharacterDescriptor odesc = (CharacterDescriptor)other;
|
||||
if (!Arrays.equals(_components, odesc._components)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Colorization[][] zations = odesc._zations;
|
||||
if (zations == null && _zations == null) {
|
||||
// if neither has colorizations, we're clear
|
||||
return true;
|
||||
|
||||
} else if (zations == null || _zations == null) {
|
||||
// if one has colorizations whilst the other doesn't, they
|
||||
// can't be equal
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise, all of the colorizations must be equal as well
|
||||
int zlength = zations.length;
|
||||
if (zlength != _zations.length) {
|
||||
return false;
|
||||
}
|
||||
for (int ii = 0; ii < zlength; ii++) {
|
||||
if (!Arrays.equals(_zations[ii], zations[ii])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this character descriptor.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
return "[cids=" + StringUtil.toString(_components) +
|
||||
", colors=" + StringUtil.toString(_zations) + "]";
|
||||
}
|
||||
|
||||
/** The component identifiers comprising the character. */
|
||||
protected int[] _components;
|
||||
|
||||
/** The colorizations to apply when compositing this character. */
|
||||
protected Colorization[][] _zations;
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
//
|
||||
// $Id: CharacterManager.java 4022 2006-04-14 21:49:11Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.swing.RuntimeAdjust;
|
||||
import com.samskivert.util.LRUHashMap;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.samskivert.util.Throttle;
|
||||
import com.samskivert.util.Tuple;
|
||||
|
||||
import com.threerings.media.image.Colorization;
|
||||
import com.threerings.media.image.ImageManager;
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
import com.threerings.cast.CompositedActionFrames.ComponentFrames;
|
||||
import com.threerings.cast.Log;
|
||||
|
||||
/**
|
||||
* 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
|
||||
implements DirectionCodes
|
||||
{
|
||||
/**
|
||||
* Constructs the character manager.
|
||||
*/
|
||||
public CharacterManager (ImageManager imgr, ComponentRepository crepo)
|
||||
{
|
||||
// keep these around
|
||||
_imgr = imgr;
|
||||
_crepo = crepo;
|
||||
|
||||
// populate our actions table
|
||||
Iterator iter = crepo.enumerateActionSequences();
|
||||
while (iter.hasNext()) {
|
||||
ActionSequence action = (ActionSequence)iter.next();
|
||||
_actions.put(action.name, action);
|
||||
}
|
||||
|
||||
// create a cache for our composited action frames
|
||||
int acsize = _cacheSize.getValue();
|
||||
Log.debug("Creating action cache [size=" + acsize + "k].");
|
||||
_frameCache = new LRUHashMap(acsize*1024, new LRUHashMap.ItemSizer() {
|
||||
public int computeSize (Object value) {
|
||||
return (int)((CompositedMultiFrameImage)
|
||||
value).getEstimatedMemoryUsage();
|
||||
}
|
||||
});
|
||||
_frameCache.setTracking(true); // TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component repository being used by this manager.
|
||||
*/
|
||||
public ComponentRepository getComponentRepository ()
|
||||
{
|
||||
return _crepo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the character manager to use the provided cache for
|
||||
* composited action animations.
|
||||
*/
|
||||
public void setActionCache (ActionCache cache)
|
||||
{
|
||||
_acache = cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link CharacterSprite} representing the character
|
||||
* described by the given {@link CharacterDescriptor}, or
|
||||
* <code>null</code> if an error occurs.
|
||||
*
|
||||
* @param desc the character descriptor.
|
||||
*/
|
||||
public CharacterSprite getCharacter (CharacterDescriptor desc)
|
||||
{
|
||||
return getCharacter(desc, _charClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link CharacterSprite} representing the character
|
||||
* described by the given {@link CharacterDescriptor}, or
|
||||
* <code>null</code> if an error occurs.
|
||||
*
|
||||
* @param desc the character descriptor.
|
||||
* @param charClass the {@link CharacterSprite} derived class that
|
||||
* should be instantiated instead of the configured default (which is
|
||||
* set via {@link #setCharacterClass}).
|
||||
*/
|
||||
public CharacterSprite getCharacter (CharacterDescriptor desc,
|
||||
Class charClass)
|
||||
{
|
||||
try {
|
||||
CharacterSprite sprite = (CharacterSprite)
|
||||
charClass.newInstance();
|
||||
sprite.init(desc, this);
|
||||
return sprite;
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Failed to instantiate character sprite " +
|
||||
"[e=" + e + "].");
|
||||
Log.logStackTrace(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public ActionFrames getActionFrames (
|
||||
CharacterDescriptor descrip, String action)
|
||||
throws NoSuchComponentException
|
||||
{
|
||||
Tuple key = new Tuple(descrip, action);
|
||||
ActionFrames frames = (ActionFrames)_actionFrames.get(key);
|
||||
if (frames == null) {
|
||||
// this doesn't actually composite the images, but prepares an
|
||||
// object to be able to do so
|
||||
frames = createCompositeFrames(descrip, action);
|
||||
_actionFrames.put(key, frames);
|
||||
}
|
||||
|
||||
// periodically report our frame image cache performance
|
||||
if (!_cacheStatThrottle.throttleOp()) {
|
||||
long size = getEstimatedCacheMemoryUsage();
|
||||
int[] eff = _frameCache.getTrackedEffectiveness();
|
||||
Log.debug("CharacterManager LRU [mem=" + (size / 1024) + "k" +
|
||||
", size=" + _frameCache.size() + ", hits=" + eff[0] +
|
||||
", misses=" + eff[1] + "].");
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs the character manager that the action sequence for the
|
||||
* given character descriptor is likely to be needed in the near
|
||||
* future and so any efforts that can be made to load it into the
|
||||
* action sequence cache in advance should be undertaken.
|
||||
*
|
||||
* <p> This will eventually be revamped to spiffily load action
|
||||
* sequences in the background.
|
||||
*/
|
||||
public void resolveActionSequence (CharacterDescriptor desc, String action)
|
||||
{
|
||||
try {
|
||||
if (getActionFrames(desc, action) == null) {
|
||||
Log.warning("Failed to resolve action sequence " +
|
||||
"[desc=" + desc + ", action=" + action + "].");
|
||||
}
|
||||
|
||||
} catch (NoSuchComponentException nsce) {
|
||||
Log.warning("Failed to resolve action sequence " +
|
||||
"[nsce=" + nsce + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action sequence instance with the specified name or
|
||||
* null if no such sequence exists.
|
||||
*/
|
||||
public ActionSequence getActionSequence (String action)
|
||||
{
|
||||
return (ActionSequence)_actions.get(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the estimated memory usage in bytes for all images
|
||||
* currently cached by the cached action frames.
|
||||
*/
|
||||
protected long getEstimatedCacheMemoryUsage ()
|
||||
{
|
||||
long size = 0;
|
||||
Iterator iter = _frameCache.values().iterator();
|
||||
while (iter.hasNext()) {
|
||||
size += ((CompositedMultiFrameImage)
|
||||
iter.next()).getEstimatedMemoryUsage();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ActionFrames createCompositeFrames (
|
||||
CharacterDescriptor descrip, String action)
|
||||
throws NoSuchComponentException
|
||||
{
|
||||
int[] cids = descrip.getComponentIds();
|
||||
int ccount = cids.length;
|
||||
Colorization[][] zations = descrip.getColorizations();
|
||||
|
||||
Log.debug("Compositing action [action=" + action +
|
||||
", descrip=" + descrip + "].");
|
||||
|
||||
// this will be used to construct any shadow layers
|
||||
HashMap shadows = null;
|
||||
|
||||
// maps components by class name for masks
|
||||
HashMap ccomps = new HashMap();
|
||||
|
||||
// create colorized versions of all of the source action frames
|
||||
ArrayList sources = new ArrayList(ccount);
|
||||
for (int ii = 0; ii < ccount; ii++) {
|
||||
ComponentFrames cframes = new ComponentFrames();
|
||||
sources.add(cframes);
|
||||
CharacterComponent ccomp =
|
||||
(cframes.ccomp = _crepo.getComponent(cids[ii]));
|
||||
ccomps.put(ccomp.componentClass.name, ccomp);
|
||||
|
||||
// load up the main component images
|
||||
ActionFrames source = ccomp.getFrames(action, null);
|
||||
if (source == null) {
|
||||
String errmsg = "Cannot composite action frames; no such " +
|
||||
"action for component [action=" + action +
|
||||
", desc=" + descrip + ", comp=" + ccomp + "]";
|
||||
throw new RuntimeException(errmsg);
|
||||
}
|
||||
cframes.frames = (zations == null || zations[ii] == null) ?
|
||||
source : source.cloneColorized(zations[ii]);
|
||||
|
||||
// if this component has a shadow, make a note of it
|
||||
if (ccomp.componentClass.isShadowed()) {
|
||||
if (shadows == null) {
|
||||
shadows = new HashMap();
|
||||
}
|
||||
ArrayList shadlist = (ArrayList)
|
||||
shadows.get(ccomp.componentClass.shadow);
|
||||
if (shadlist == null) {
|
||||
shadows.put(ccomp.componentClass.shadow,
|
||||
shadlist = new ArrayList());
|
||||
}
|
||||
shadlist.add(ccomp);
|
||||
}
|
||||
}
|
||||
|
||||
// add any necessary masks
|
||||
for (int ii = 0; ii < ccount; ii++) {
|
||||
ComponentFrames cframes = (ComponentFrames)sources.get(ii);
|
||||
CharacterComponent mcomp = (CharacterComponent)ccomps.get(
|
||||
cframes.ccomp.componentClass.mask);
|
||||
if (mcomp != null) {
|
||||
cframes.frames = compositeMask(action, cframes.ccomp,
|
||||
cframes.frames, mcomp);
|
||||
}
|
||||
}
|
||||
|
||||
// now create any necessary shadow layers
|
||||
if (shadows != null) {
|
||||
Iterator iter = shadows.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry)iter.next();
|
||||
String sclass = (String)entry.getKey();
|
||||
ArrayList scomps = (ArrayList)entry.getValue();
|
||||
ComponentFrames scf = compositeShadow(action, sclass, scomps);
|
||||
if (scf != null) {
|
||||
sources.add(scf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use those to create an entity that will lazily composite things
|
||||
// together as they are needed
|
||||
ComponentFrames[] cfvec = (ComponentFrames[])sources.toArray(
|
||||
new ComponentFrames[sources.size()]);
|
||||
return new CompositedActionFrames(_imgr, _frameCache, action, cfvec);
|
||||
}
|
||||
|
||||
protected ActionFrames compositeMask (
|
||||
String action, CharacterComponent ccomp, ActionFrames cframes,
|
||||
CharacterComponent mcomp)
|
||||
{
|
||||
ActionFrames mframes = mcomp.getFrames(action,
|
||||
StandardActions.CROP_TYPE);
|
||||
if (mframes == null) {
|
||||
return cframes;
|
||||
}
|
||||
return new CompositedActionFrames(
|
||||
_imgr, _frameCache, action, new ComponentFrames[] {
|
||||
new ComponentFrames(ccomp, cframes),
|
||||
new ComponentFrames(mcomp, mframes) }) {
|
||||
protected CompositedMultiFrameImage createFrames (int orient) {
|
||||
return new CompositedMaskedImage(
|
||||
_imgr, _sources, _action, orient);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected ComponentFrames compositeShadow (
|
||||
String action, String sclass, ArrayList scomps)
|
||||
{
|
||||
final ComponentClass cclass = _crepo.getComponentClass(sclass);
|
||||
if (cclass == null) {
|
||||
Log.warning("Components reference non-existent shadow layer " +
|
||||
"class [sclass=" + sclass +
|
||||
", scomps=" + StringUtil.toString(scomps) + "].");
|
||||
return null;
|
||||
}
|
||||
|
||||
ComponentFrames cframes = new ComponentFrames();
|
||||
// create a fake component for the shadow layer
|
||||
cframes.ccomp = new CharacterComponent(-1, "shadow", cclass, null);
|
||||
|
||||
ArrayList sources = new ArrayList();
|
||||
for (int ii = 0, ll = scomps.size(); ii < ll; ii++) {
|
||||
ComponentFrames source = new ComponentFrames();
|
||||
source.ccomp = (CharacterComponent)scomps.get(ii);
|
||||
source.frames = source.ccomp.getFrames(
|
||||
action, StandardActions.SHADOW_TYPE);
|
||||
if (source.frames == null) {
|
||||
// skip this shadow component
|
||||
continue;
|
||||
}
|
||||
sources.add(source);
|
||||
}
|
||||
|
||||
// if we ended up with no shadow, no problem!
|
||||
if (sources.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// create custom action frames that use a special compositing
|
||||
// multi-frame image that does the necessary shadow magic
|
||||
ComponentFrames[] svec = (ComponentFrames[])
|
||||
sources.toArray(new ComponentFrames[sources.size()]);
|
||||
cframes.frames = new CompositedActionFrames(
|
||||
_imgr, _frameCache, action, svec) {
|
||||
protected CompositedMultiFrameImage createFrames (int orient) {
|
||||
return new CompositedShadowImage(
|
||||
_imgr, _sources, _action, orient, cclass.shadowAlpha);
|
||||
}
|
||||
};
|
||||
|
||||
return cframes;
|
||||
}
|
||||
|
||||
/** The image manager with whom we interact. */
|
||||
protected ImageManager _imgr;
|
||||
|
||||
/** The component repository. */
|
||||
protected ComponentRepository _crepo;
|
||||
|
||||
/** A table of our action sequences. */
|
||||
protected HashMap _actions = new HashMap();
|
||||
|
||||
/** A table of composited action sequences (these don't reference the
|
||||
* actual image data directly and thus take up little memory). */
|
||||
protected HashMap _actionFrames = new HashMap();
|
||||
|
||||
/** A cache of composited animation frames. */
|
||||
protected LRUHashMap _frameCache;
|
||||
|
||||
/** The character class to be created. */
|
||||
protected Class _charClass = CharacterSprite.class;
|
||||
|
||||
/** The action animation cache, if we have one. */
|
||||
protected ActionCache _acache;
|
||||
|
||||
/** Throttle our cache status logging to once every 30 seconds. */
|
||||
protected Throttle _cacheStatThrottle = new Throttle(1, 30000L);
|
||||
|
||||
/** Register our image cache size with the runtime adjustments
|
||||
* framework. */
|
||||
protected static RuntimeAdjust.IntAdjust _cacheSize =
|
||||
new RuntimeAdjust.IntAdjust(
|
||||
"Size (in kb of memory used) of the character manager LRU " +
|
||||
"action cache [requires restart]", "narya.cast.action_cache_size",
|
||||
CastPrefs.config, 1024);
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
//
|
||||
// $Id: CharacterSprite.java 3785 2005-12-15 22:14:37Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import com.threerings.media.sprite.ImageSprite;
|
||||
|
||||
/**
|
||||
* A character sprite is a sprite that animates itself while walking
|
||||
* about in a scene.
|
||||
*/
|
||||
public class CharacterSprite extends ImageSprite
|
||||
implements StandardActions
|
||||
{
|
||||
/**
|
||||
* Initializes this character sprite with the specified character
|
||||
* descriptor and character manager. It will obtain animation data
|
||||
* from the supplied character manager.
|
||||
*/
|
||||
public void init (CharacterDescriptor descrip, CharacterManager charmgr)
|
||||
{
|
||||
// keep track of this stuff
|
||||
_descrip = descrip;
|
||||
_charmgr = charmgr;
|
||||
|
||||
// sanity check our values
|
||||
sanityCheckDescrip();
|
||||
|
||||
// assign an arbitrary starting orientation
|
||||
_orient = SOUTHWEST;
|
||||
|
||||
// pass the buck to derived classes
|
||||
didInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after this sprite has been initialized with its character
|
||||
* descriptor and character manager. Derived classes can do post-init
|
||||
* business here.
|
||||
*/
|
||||
protected void didInit ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconfigures this sprite to use the specified character descriptor.
|
||||
*/
|
||||
public void setCharacterDescriptor (CharacterDescriptor descrip)
|
||||
{
|
||||
// keep the new descriptor
|
||||
_descrip = descrip;
|
||||
|
||||
// sanity check our values
|
||||
sanityCheckDescrip();
|
||||
|
||||
// update our action frames
|
||||
updateActionFrames();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the action to use when the sprite is at rest. The default
|
||||
* is <code>STANDING</code>.
|
||||
*/
|
||||
public void setRestingAction (String action)
|
||||
{
|
||||
_restingAction = action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action to be used when the sprite is at rest. Derived
|
||||
* classes may wish to override this method and vary the action based
|
||||
* on external parameters (or randomly).
|
||||
*/
|
||||
public String getRestingAction ()
|
||||
{
|
||||
return _restingAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the action to use when the sprite is following a path.
|
||||
* The default is <code>WALKING</code>.
|
||||
*/
|
||||
public void setFollowingPathAction (String action)
|
||||
{
|
||||
_followingPathAction = action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action to be used when the sprite is following a path.
|
||||
* Derived classes may wish to override this method and vary the
|
||||
* action based on external parameters (or randomly).
|
||||
*/
|
||||
public String getFollowingPathAction ()
|
||||
{
|
||||
return _followingPathAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action sequence used when rendering the character, from
|
||||
* the set of available sequences.
|
||||
*/
|
||||
public void setActionSequence (String action)
|
||||
{
|
||||
// sanity check
|
||||
if (action == null) {
|
||||
Log.warning("Refusing to set null action sequence " + this + ".");
|
||||
Thread.dumpStack();
|
||||
return;
|
||||
}
|
||||
|
||||
// no need to noop
|
||||
if (action.equals(_action)) {
|
||||
return;
|
||||
}
|
||||
_action = action;
|
||||
updateActionFrames();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void setOrientation (int orient)
|
||||
{
|
||||
if (orient < 0 || orient >= FINE_DIRECTION_COUNT) {
|
||||
Log.info("Refusing to set invalid orientation [sprite=" + this +
|
||||
", orient=" + orient + "].");
|
||||
Thread.dumpStack();
|
||||
return;
|
||||
}
|
||||
|
||||
int oorient = _orient;
|
||||
super.setOrientation(orient);
|
||||
if (_orient != oorient) {
|
||||
_frames = null;
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean hitTest (int x, int y)
|
||||
{
|
||||
// the irect adjustments are to account for our decorations
|
||||
return (_frames != null && _ibounds.contains(x, y) &&
|
||||
_frames.hitTest(_frameIdx, x - _ibounds.x, y - _ibounds.y));
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void tick (long tickStamp)
|
||||
{
|
||||
// composite our action frames if something since the last call to
|
||||
// tick caused them to become invalid
|
||||
compositeActionFrames();
|
||||
super.tick(tickStamp);
|
||||
// composite our action frames if something during tick() caused
|
||||
// them to become invalid
|
||||
compositeActionFrames();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void cancelMove ()
|
||||
{
|
||||
super.cancelMove();
|
||||
halt();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void pathBeginning ()
|
||||
{
|
||||
super.pathBeginning();
|
||||
|
||||
// enable walking animation
|
||||
setAnimationMode(TIME_BASED);
|
||||
setActionSequence(getFollowingPathAction());
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void pathCompleted (long timestamp)
|
||||
{
|
||||
super.pathCompleted(timestamp);
|
||||
halt();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void paint (Graphics2D gfx)
|
||||
{
|
||||
if (_frames != null) {
|
||||
decorateBehind(gfx);
|
||||
// paint the image using _ibounds rather than _bounds which
|
||||
// has been modified to include the bounds of our decorations
|
||||
_frames.paintFrame(gfx, _frameIdx, _ibounds.x, _ibounds.y);
|
||||
decorateInFront(gfx);
|
||||
|
||||
} else {
|
||||
super.paint(gfx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to paint any decorations that should appear behind the
|
||||
* character sprite image.
|
||||
*/
|
||||
protected void decorateBehind (Graphics2D gfx)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to paint any decorations that should appear in front of the
|
||||
* character sprite image.
|
||||
*/
|
||||
protected void decorateInFront (Graphics2D gfx)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds our action frames given our current character descriptor
|
||||
* and action sequence. This is called when either of those two things
|
||||
* changes.
|
||||
*/
|
||||
protected void updateActionFrames ()
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
try {
|
||||
// obtain our animation frames for this action sequence
|
||||
_aframes = _charmgr.getActionFrames(_descrip, _action);
|
||||
|
||||
// clear out our frames so that we recomposite on next tick
|
||||
_frames = null;
|
||||
|
||||
// update the sprite render attributes
|
||||
setFrameRate(actseq.framesPerSecond);
|
||||
|
||||
} catch (NoSuchComponentException nsce) {
|
||||
Log.warning("Character sprite references non-existent " +
|
||||
"component [sprite=" + this + ", err=" + nsce + "].");
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Failed to obtain action frames [sprite=" + this +
|
||||
", descrip=" + _descrip + ", action=" + _action + "].");
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Called to recomposite our action frames if needed. */
|
||||
protected final void compositeActionFrames ()
|
||||
{
|
||||
if (_frames == null && _aframes != null) {
|
||||
setFrames(_aframes.getFrames(_orient));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes it easier to track down problems with bogus character descriptors.
|
||||
*/
|
||||
protected void sanityCheckDescrip ()
|
||||
{
|
||||
if (_descrip.getComponentIds() == null ||
|
||||
_descrip.getComponentIds().length == 0) {
|
||||
Log.warning("Invalid character descriptor [sprite=" + this +
|
||||
", descrip=" + _descrip + "].");
|
||||
Thread.dumpStack();
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected boolean tickPath (long tickStamp)
|
||||
{
|
||||
boolean moved = super.tickPath(tickStamp);
|
||||
// composite our action frames if our path caused them to become
|
||||
// invalid
|
||||
compositeActionFrames();
|
||||
return moved;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void updateRenderOrigin ()
|
||||
{
|
||||
super.updateRenderOrigin();
|
||||
|
||||
// adjust our image bounds to reflect the new location
|
||||
_ibounds.x = _bounds.x + _ioff.x;
|
||||
_ibounds.y = _bounds.y + _ioff.y;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void accomodateFrame (int frameIdx, int width, int height)
|
||||
{
|
||||
// this will update our width and height
|
||||
super.accomodateFrame(frameIdx, width, height);
|
||||
|
||||
// we now need to update the render offset for this frame
|
||||
if (_aframes == null) {
|
||||
Log.warning("Have no action frames! " + _aframes + ".");
|
||||
} else {
|
||||
_oxoff = _aframes.getXOrigin(_orient, frameIdx);
|
||||
_oyoff = _aframes.getYOrigin(_orient, frameIdx);
|
||||
}
|
||||
|
||||
// and cause those changes to be reflected in our bounds
|
||||
updateRenderOrigin();
|
||||
|
||||
// start out with our bounds the same as our image bounds
|
||||
_ibounds.setBounds(_bounds);
|
||||
|
||||
// now we can call down and incorporate the dimensions of any
|
||||
// decorations that will be rendered along with our image
|
||||
unionDecorationBounds(_bounds);
|
||||
|
||||
// compute our new render origin
|
||||
_oxoff = _ox - _bounds.x;
|
||||
_oyoff = _oy - _bounds.y;
|
||||
|
||||
// track the offset from our expanded bounds to our image bounds
|
||||
_ioff.x = _ibounds.x - _bounds.x;
|
||||
_ioff.y = _ibounds.y - _bounds.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link #accomodateFrame} to give derived classes an
|
||||
* opportunity to incorporate the bounds of any decorations that will
|
||||
* be drawn along with this sprite. The {@link #_ibounds} rectangle
|
||||
* will contain the bounds of the image that comprises the undecorated
|
||||
* sprite. From that the position and size of decorations can be
|
||||
* computed and unioned with the supplied bounds rectangle (most
|
||||
* likely by using {@link SwingUtilities#computeUnion} which applies
|
||||
* the union in place rather than creating a new rectangle).
|
||||
*/
|
||||
protected void unionDecorationBounds (Rectangle bounds)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the sprite animation frame to reflect the cessation of
|
||||
* movement and disables any further animation.
|
||||
*/
|
||||
protected void halt ()
|
||||
{
|
||||
// only do something if we're actually animating
|
||||
if (_animMode != NO_ANIMATION) {
|
||||
// disable animation
|
||||
setAnimationMode(NO_ANIMATION);
|
||||
// come to a halt looking settled and at peace
|
||||
String rest = getRestingAction();
|
||||
if (rest != null) {
|
||||
setActionSequence(rest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The action to use when at rest. */
|
||||
protected String _restingAction = STANDING;
|
||||
|
||||
/** The action to use when following a path. */
|
||||
protected String _followingPathAction = WALKING;
|
||||
|
||||
/** A reference to the descriptor for the character that we're
|
||||
* visualizing. */
|
||||
protected CharacterDescriptor _descrip;
|
||||
|
||||
/** A reference to the character manager that created us. */
|
||||
protected CharacterManager _charmgr;
|
||||
|
||||
/** The action we are currently displaying. */
|
||||
protected String _action;
|
||||
|
||||
/** The animation frames for the active action sequence in each
|
||||
* orientation. */
|
||||
protected ActionFrames _aframes;
|
||||
|
||||
/** The offset from the upper-left of the total sprite bounds to the
|
||||
* upper-left of the image within those bounds. */
|
||||
protected Point _ioff = new Point();
|
||||
|
||||
/** The bounds of the current sprite image. */
|
||||
protected Rectangle _ibounds = new Rectangle();
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
//
|
||||
// $Id: ComponentClass.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.samskivert.util.ArrayIntSet;
|
||||
import com.samskivert.util.ComparableArrayList;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* <p> Components support render priority overrides for particular
|
||||
* actions, orientations or combinations of actions and orientations. The
|
||||
* system is currently structured with the expectation that the overrides
|
||||
* will be relatively few (less than fifteen, say) for any given component
|
||||
* class. A system that relied on many overrides for its components would
|
||||
* want to implement a more scalable algorithm for determining which, if
|
||||
* any, override matches a particular action and orientation combination.
|
||||
*/
|
||||
public class ComponentClass implements Serializable
|
||||
{
|
||||
/** Used to effect custom render orders for particular actions,
|
||||
* orientations, etc. */
|
||||
public static class PriorityOverride
|
||||
implements Comparable, Serializable
|
||||
{
|
||||
/** The overridden render priority value. */
|
||||
public int renderPriority;
|
||||
|
||||
/** The action, if any, for which this override is appropriate. */
|
||||
public String action;
|
||||
|
||||
/** The orientations, if any, for which this override is
|
||||
* appropriate. */
|
||||
public ArrayIntSet orients;
|
||||
|
||||
/**
|
||||
* Determines whether this priority override matches the specified
|
||||
* action and orientation combination.
|
||||
*/
|
||||
public boolean matches (String action, int orient)
|
||||
{
|
||||
return (((orients == null) || orients.contains(orient)) &&
|
||||
((this.action == null) || this.action.equals(action)));
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int compareTo (Object other)
|
||||
{
|
||||
// overrides with both an action and an orientation should
|
||||
// come first in the list
|
||||
PriorityOverride po = (PriorityOverride)other;
|
||||
int pri = priority(), opri = po.priority();
|
||||
if (pri == opri) {
|
||||
return hashCode() - po.hashCode();
|
||||
} else {
|
||||
return pri - opri;
|
||||
}
|
||||
}
|
||||
|
||||
protected int priority ()
|
||||
{
|
||||
int priority = 0;
|
||||
if (action != null) {
|
||||
priority++;
|
||||
}
|
||||
if (orients != null) {
|
||||
priority++;
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
|
||||
/** Generates a string representation of this instance. */
|
||||
public String toString ()
|
||||
{
|
||||
return "[pri=" + renderPriority + ", action=" + action +
|
||||
", orients=" + orients + "]";
|
||||
}
|
||||
|
||||
/** Increase this value when object's serialized state is impacted
|
||||
* by a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
|
||||
/** The component class name. */
|
||||
public String name;
|
||||
|
||||
/** The default render priority. */
|
||||
public int renderPriority;
|
||||
|
||||
/** The color classes to use when recoloring components of this class. May
|
||||
* be null if a system does not use recolorable components. */
|
||||
public String[] colors;
|
||||
|
||||
/** The class name of the layer from which this component class obtains a
|
||||
* mask to limit rendering to certain areas. */
|
||||
public String mask;
|
||||
|
||||
/** Indicates the class name of the shadow layer to which this component
|
||||
* class contributes a shadow. */
|
||||
public String shadow;
|
||||
|
||||
/** 1.0 for a normal component, the alpha value of the pre-composited
|
||||
* shadow for the special "shadow" component class. */
|
||||
public float shadowAlpha = 1.0f;
|
||||
|
||||
/**
|
||||
* Creates an uninitialized instance suitable for unserialization or
|
||||
* population during XML parsing.
|
||||
*/
|
||||
public ComponentClass ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the render priority appropriate for the specified action
|
||||
* and orientation.
|
||||
*/
|
||||
public int getRenderPriority (String action, int orientation)
|
||||
{
|
||||
// because we expect there to be relatively few priority
|
||||
// overrides, we simply search linearly through the list for the
|
||||
// closest match
|
||||
int ocount = (_overrides != null) ? _overrides.size() : 0;
|
||||
for (int ii = 0; ii < ocount; ii++) {
|
||||
PriorityOverride over = (PriorityOverride)_overrides.get(ii);
|
||||
// based on the way the overrides are sorted, the first match
|
||||
// is the most specific and the one we want
|
||||
if (over.matches(action, orientation)) {
|
||||
return over.renderPriority;
|
||||
}
|
||||
}
|
||||
|
||||
return renderPriority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the supplied render priority override record to this component
|
||||
* class.
|
||||
*/
|
||||
public void addPriorityOverride (PriorityOverride override)
|
||||
{
|
||||
if (_overrides == null) {
|
||||
_overrides = new ComparableArrayList();
|
||||
}
|
||||
_overrides.insertSorted(override);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this component class contributes a shadow to a
|
||||
* particular shadow layer. Note: this is different from <em>being</em> a
|
||||
* shadow layer which is determined by calling {@link #isShadow}.
|
||||
*/
|
||||
public boolean isShadowed ()
|
||||
{
|
||||
return (shadow != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this component class is a shadow layer rather than a
|
||||
* normal component class.
|
||||
*/
|
||||
public boolean isShadow ()
|
||||
{
|
||||
return (shadowAlpha != 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ()
|
||||
{
|
||||
StringBuilder buf = new StringBuilder("[");
|
||||
buf.append("name=").append(name);
|
||||
buf.append(", pri=").append(renderPriority);
|
||||
if (colors != null) {
|
||||
buf.append(", colors=").append(StringUtil.toString(colors));
|
||||
}
|
||||
if (mask != null) {
|
||||
buf.append(", mask=").append(mask);
|
||||
}
|
||||
if (shadowAlpha != 1.0f) {
|
||||
buf.append(", shadow=").append(shadowAlpha);
|
||||
} else if (shadow != null) {
|
||||
buf.append(", shadow=").append(shadow);
|
||||
}
|
||||
return buf.append("]").toString();
|
||||
}
|
||||
|
||||
/** A list of render priority overrides. */
|
||||
protected ComparableArrayList _overrides;
|
||||
|
||||
/** Increase this value when object's serialized state is impacted by
|
||||
* a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 3;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// $Id: ComponentIDBroker.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// $Id: ComponentRepository.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* component identifier.
|
||||
*/
|
||||
public CharacterComponent getComponent (int componentId)
|
||||
throws NoSuchComponentException;
|
||||
|
||||
/**
|
||||
* Returns the {@link CharacterComponent} object with the given
|
||||
* component class and name.
|
||||
*/
|
||||
public CharacterComponent getComponent (String className, String compName)
|
||||
throws NoSuchComponentException;
|
||||
|
||||
/**
|
||||
* Returns the {@link ComponentClass} with the specified name or null
|
||||
* if none exists with that name.
|
||||
*/
|
||||
public ComponentClass getComponentClass (String className);
|
||||
|
||||
/**
|
||||
* Iterates over the {@link ComponentClass} instances representing all
|
||||
* available character component classes.
|
||||
*/
|
||||
public Iterator enumerateComponentClasses ();
|
||||
|
||||
/**
|
||||
* Iterates over the {@link ActionSequence} instances representing
|
||||
* every available action sequence.
|
||||
*/
|
||||
public Iterator enumerateActionSequences ();
|
||||
|
||||
/**
|
||||
* Iterates over the component ids of all components in the specified
|
||||
* class.
|
||||
*/
|
||||
public Iterator enumerateComponentIds (ComponentClass compClass);
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
//
|
||||
// $Id: CompositedActionFrames.java 3967 2006-03-22 02:41:15Z andrzej $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import com.samskivert.util.LRUHashMap;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.media.image.Colorization;
|
||||
import com.threerings.media.image.ImageManager;
|
||||
import com.threerings.media.util.MultiFrameImage;
|
||||
|
||||
import com.threerings.cast.CharacterComponent;
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
/**
|
||||
* An implementation of the {@link MultiFrameImage} interface that is used
|
||||
* to lazily create composited character frames when they are requested.
|
||||
*/
|
||||
public class CompositedActionFrames
|
||||
implements ActionFrames, DirectionCodes
|
||||
{
|
||||
/** Used to associate a {@link CharacterComponent} with its {@link
|
||||
* ActionFrames} for a particular action. */
|
||||
public static class ComponentFrames
|
||||
{
|
||||
public CharacterComponent ccomp;
|
||||
|
||||
public ActionFrames frames;
|
||||
|
||||
public ComponentFrames () {
|
||||
}
|
||||
|
||||
public ComponentFrames (
|
||||
CharacterComponent ccomp, ActionFrames frames) {
|
||||
this.ccomp = ccomp;
|
||||
this.frames = frames;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return ccomp + ":" + frames;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a set of composited action frames with the supplied
|
||||
* source frames and colorization configuration. The actual component
|
||||
* frame images will not be composited until they are requested.
|
||||
*/
|
||||
public CompositedActionFrames (ImageManager imgr, LRUHashMap frameCache,
|
||||
String action, ComponentFrames[] sources)
|
||||
{
|
||||
// sanity check
|
||||
if (sources == null || sources.length == 0) {
|
||||
String errmsg = "Requested to composite invalid set of source " +
|
||||
"frames! [action=" + action +
|
||||
", sources=" + StringUtil.toString(sources) + "].";
|
||||
throw new RuntimeException(errmsg);
|
||||
}
|
||||
|
||||
_imgr = imgr;
|
||||
_frameCache = frameCache;
|
||||
_sources = sources;
|
||||
_action = action;
|
||||
|
||||
// the sources must all have the same orientation count, so we
|
||||
// just use the first
|
||||
_orientCount = _sources[0].frames.getOrientationCount();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getOrientationCount ()
|
||||
{
|
||||
return _orientCount;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public TrimmedMultiFrameImage getFrames (int orient)
|
||||
{
|
||||
_key.setOrient(orient);
|
||||
CompositedMultiFrameImage cmfi =
|
||||
(CompositedMultiFrameImage)_frameCache.get(_key);
|
||||
if (cmfi == null) {
|
||||
cmfi = createFrames(orient);
|
||||
_frameCache.put(new CompositedFramesKey(orient), cmfi);
|
||||
}
|
||||
return cmfi;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getXOrigin (int orient, int frameIdx)
|
||||
{
|
||||
CompositedMultiFrameImage cmfi = (CompositedMultiFrameImage)
|
||||
getFrames(orient);
|
||||
return cmfi.getXOrigin(frameIdx);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getYOrigin (int orient, int frameIdx)
|
||||
{
|
||||
CompositedMultiFrameImage cmfi = (CompositedMultiFrameImage)
|
||||
getFrames(orient);
|
||||
return cmfi.getYOrigin(frameIdx);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public ActionFrames cloneColorized (Colorization[] zations)
|
||||
{
|
||||
throw new RuntimeException("What you talkin' about Willis?");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates our underlying multi-frame image for a particular orientation.
|
||||
*/
|
||||
protected CompositedMultiFrameImage createFrames (int orient)
|
||||
{
|
||||
return new CompositedMultiFrameImage(_imgr, _sources, _action, orient);
|
||||
}
|
||||
|
||||
/** Used to cache composited frames for a particular action and
|
||||
* orientation. */
|
||||
protected class CompositedFramesKey
|
||||
{
|
||||
public CompositedFramesKey (int orient) {
|
||||
_orient = orient;
|
||||
}
|
||||
|
||||
public void setOrient (int orient) {
|
||||
_orient = orient;
|
||||
}
|
||||
|
||||
public CompositedActionFrames getOwner () {
|
||||
return CompositedActionFrames.this;
|
||||
}
|
||||
|
||||
public boolean equals (Object other) {
|
||||
CompositedFramesKey okey = (CompositedFramesKey)other;
|
||||
return ((getOwner() == okey.getOwner()) &&
|
||||
(_orient == okey._orient));
|
||||
}
|
||||
|
||||
public int hashCode () {
|
||||
return CompositedActionFrames.this.hashCode() ^ _orient;
|
||||
}
|
||||
|
||||
protected int _orient;
|
||||
}
|
||||
|
||||
/** The image manager from whom we can obtain prepared volatile images
|
||||
* onto which to render our composited actions. */
|
||||
protected ImageManager _imgr;
|
||||
|
||||
/** Used to cache our composited action frame images. */
|
||||
protected LRUHashMap _frameCache;
|
||||
|
||||
/** The action for which we're compositing frames. */
|
||||
protected String _action;
|
||||
|
||||
/** The number of orientations. */
|
||||
protected int _orientCount;
|
||||
|
||||
/** Our source components and action frames. */
|
||||
protected ComponentFrames[] _sources;
|
||||
|
||||
/** Used to avoid creating a new key object every time we do a cache
|
||||
* lookup. */
|
||||
protected CompositedFramesKey _key = new CompositedFramesKey(0);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// $Id: CompositedMultiFrameImage.java 3310 2005-01-24 23:08:21Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Transparency;
|
||||
|
||||
import com.threerings.media.image.ImageManager;
|
||||
import com.threerings.media.image.Mirage;
|
||||
import com.threerings.media.image.VolatileMirage;
|
||||
|
||||
import com.threerings.cast.CompositedActionFrames.ComponentFrames;
|
||||
|
||||
/**
|
||||
* Used to composite action frames with mask frames.
|
||||
*/
|
||||
public class CompositedMaskedImage extends CompositedMultiFrameImage
|
||||
{
|
||||
public CompositedMaskedImage (
|
||||
ImageManager imgr, ComponentFrames[] sources, String action,
|
||||
int orient)
|
||||
{
|
||||
super(imgr, sources, action, orient);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getWidth (int index) {
|
||||
return _sources[0].frames.getFrames(_orient).getWidth(index);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getHeight (int index) {
|
||||
return _sources[0].frames.getFrames(_orient).getHeight(index);
|
||||
}
|
||||
|
||||
public int getXOrigin (int index) {
|
||||
return _sources[0].frames.getXOrigin(_orient, index);
|
||||
}
|
||||
|
||||
public int getYOrigin (int index) {
|
||||
return _sources[0].frames.getYOrigin(_orient, index);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void paintFrame (Graphics2D g, int index, int x, int y) {
|
||||
_images[index].paint(g, x + _images[index].getX(),
|
||||
y + _images[index].getY());
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean hitTest (int index, int x, int y) {
|
||||
return _images[index].hitTest(x + _images[index].getX(),
|
||||
y + _images[index].getY());
|
||||
}
|
||||
|
||||
// documentation inherited from interface TrimmedMultiFrameImage
|
||||
public void getTrimmedBounds (int index, Rectangle bounds) {
|
||||
bounds.setBounds(_images[index].getX(), _images[index].getY(),
|
||||
_images[index].getWidth(), _images[index].getHeight());
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected CompositedMirage createCompositedMirage (int index)
|
||||
{
|
||||
return new MaskedMirage(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines the image in the first source with the mask in the second. */
|
||||
protected class MaskedMirage extends CompositedMirage
|
||||
{
|
||||
public MaskedMirage (int index)
|
||||
{
|
||||
super(index);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected Rectangle combineBounds (Rectangle bounds, Rectangle tbounds)
|
||||
{
|
||||
if (bounds.width == 0 && bounds.height == 0) {
|
||||
bounds.setBounds(tbounds);
|
||||
} else {
|
||||
bounds = bounds.intersection(tbounds);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void refreshVolatileImage ()
|
||||
{
|
||||
Graphics2D g = (Graphics2D)_image.getGraphics();
|
||||
try {
|
||||
TrimmedMultiFrameImage source =
|
||||
_sources[0].frames.getFrames(_orient),
|
||||
mask = _sources[1].frames.getFrames(_orient);
|
||||
source.paintFrame(g, _index, -_bounds.x, -_bounds.y);
|
||||
g.setComposite(AlphaComposite.DstIn);
|
||||
mask.paintFrame(g, _index, -_bounds.x, -_bounds.y);
|
||||
|
||||
} finally {
|
||||
// clean up after ourselves
|
||||
if (g != null) {
|
||||
g.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,254 @@
|
||||
//
|
||||
// $Id: CompositedMultiFrameImage.java 3967 2006-03-22 02:41:15Z andrzej $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Transparency;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import com.threerings.media.image.ImageManager;
|
||||
import com.threerings.media.image.Mirage;
|
||||
import com.threerings.media.image.VolatileMirage;
|
||||
|
||||
import com.threerings.cast.CompositedActionFrames.ComponentFrames;
|
||||
import com.threerings.cast.TrimmedMultiFrameImage;
|
||||
|
||||
/**
|
||||
* Used to composite the action frames for a particular orientation of a
|
||||
* {@link CompositedActionFrames}.
|
||||
*/
|
||||
public class CompositedMultiFrameImage
|
||||
implements TrimmedMultiFrameImage
|
||||
{
|
||||
public CompositedMultiFrameImage (
|
||||
ImageManager imgr, ComponentFrames[] sources,
|
||||
String action, int orient)
|
||||
{
|
||||
_imgr = imgr;
|
||||
_sources = sources;
|
||||
_action = action;
|
||||
_orient = orient;
|
||||
|
||||
// create our frame images (which will do the compositing)
|
||||
int fcount = sources[0].frames.getFrames(orient).getFrameCount();
|
||||
_images = new CompositedMirage[fcount];
|
||||
for (int ii = 0; ii < fcount; ii++) {
|
||||
_images[ii] = createCompositedMirage(ii);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public int getFrameCount () {
|
||||
return _images.length;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getWidth (int index) {
|
||||
return _images[index].getWidth();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getHeight (int index) {
|
||||
return _images[index].getHeight();
|
||||
}
|
||||
|
||||
public int getXOrigin (int index) {
|
||||
return _images[index].getXOrigin();
|
||||
}
|
||||
|
||||
public int getYOrigin (int index) {
|
||||
return _images[index].getYOrigin();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void paintFrame (Graphics2D g, int index, int x, int y) {
|
||||
_images[index].paint(g, x, y);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean hitTest (int index, int x, int y) {
|
||||
return _images[index].hitTest(x, y);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void getTrimmedBounds (int index, Rectangle bounds) {
|
||||
bounds.setBounds(0, 0, getWidth(index), getHeight(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the estimated memory usage of our composited frame images.
|
||||
*/
|
||||
public long getEstimatedMemoryUsage ()
|
||||
{
|
||||
long size = 0;
|
||||
for (int ii = 0; ii < _images.length; ii++) {
|
||||
size += _images[ii].getEstimatedMemoryUsage();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a composited image for the specified frame.
|
||||
*/
|
||||
protected CompositedMirage createCompositedMirage (int index)
|
||||
{
|
||||
return new CompositedMirage(index);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected Mirage getFrame (int orient, int index)
|
||||
{
|
||||
return _images[index];
|
||||
}
|
||||
|
||||
/** The image manager from whom we load our images. */
|
||||
protected ImageManager _imgr;
|
||||
|
||||
/** The action frames from which we obtain our source imagery. */
|
||||
protected ComponentFrames[] _sources;
|
||||
|
||||
/** The action we're compositing. */
|
||||
protected String _action;
|
||||
|
||||
/** The orientation we're compositing. */
|
||||
protected int _orient;
|
||||
|
||||
/** Our composited action frame images. */
|
||||
protected CompositedMirage[] _images;
|
||||
|
||||
/**
|
||||
* Used to create our mirage using the source action frame images.
|
||||
*/
|
||||
protected class CompositedMirage extends VolatileMirage
|
||||
implements Comparator
|
||||
{
|
||||
public CompositedMirage (int index)
|
||||
{
|
||||
super(CompositedMultiFrameImage.this._imgr,
|
||||
new Rectangle(0, 0, 0, 0));
|
||||
|
||||
// keep this for later
|
||||
_index = index;
|
||||
|
||||
// first we need to determine the bounds of the rectangle that
|
||||
// will enclose all of our various components
|
||||
Rectangle tbounds = new Rectangle();
|
||||
int scount = _sources.length;
|
||||
for (int ii = 0; ii < scount; ii++) {
|
||||
TrimmedMultiFrameImage source =
|
||||
_sources[ii].frames.getFrames(_orient);
|
||||
source.getTrimmedBounds(index, tbounds);
|
||||
_bounds = combineBounds(_bounds, tbounds);
|
||||
}
|
||||
|
||||
// compute our new origin
|
||||
_origin.x = (_sources[0].frames.getXOrigin(_orient, index) -
|
||||
_bounds.x);
|
||||
_origin.y = (_sources[0].frames.getYOrigin(_orient, index) -
|
||||
_bounds.y);
|
||||
// Log.info("New origin [x=" + _origin.x + ", y=" + _origin.y + "].");
|
||||
|
||||
// render our volatile image for the first time
|
||||
createVolatileImage();
|
||||
}
|
||||
|
||||
public int getXOrigin ()
|
||||
{
|
||||
return _origin.x;
|
||||
}
|
||||
|
||||
public int getYOrigin ()
|
||||
{
|
||||
return _origin.y;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int compare (Object o1, Object o2)
|
||||
{
|
||||
ComponentFrames cf1 = (ComponentFrames)o1,
|
||||
cf2 = (ComponentFrames)o2;
|
||||
return (cf1.ccomp.componentClass.getRenderPriority(
|
||||
_action, _orient) -
|
||||
cf2.ccomp.componentClass.getRenderPriority(
|
||||
_action, _orient));
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines the working bounds with a new set of bounds.
|
||||
*/
|
||||
protected Rectangle combineBounds (Rectangle bounds, Rectangle tbounds)
|
||||
{
|
||||
// the first one defines our initial bounds
|
||||
if (bounds.width == 0 && bounds.height == 0) {
|
||||
bounds.setBounds(tbounds);
|
||||
} else {
|
||||
bounds.add(tbounds);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected int getTransparency ()
|
||||
{
|
||||
return Transparency.BITMASK;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void refreshVolatileImage ()
|
||||
{
|
||||
// long start = System.currentTimeMillis();
|
||||
|
||||
// sort the sources appropriately for this orientation
|
||||
Arrays.sort(_sources, this);
|
||||
|
||||
// now render each of the components into a composited frame
|
||||
int scount = _sources.length;
|
||||
Graphics2D g = (Graphics2D)_image.getGraphics();
|
||||
try {
|
||||
for (int ii = 0; ii < scount; ii++) {
|
||||
TrimmedMultiFrameImage source =
|
||||
_sources[ii].frames.getFrames(_orient);
|
||||
source.paintFrame(g, _index, -_bounds.x, -_bounds.y);
|
||||
}
|
||||
} finally {
|
||||
// clean up after ourselves
|
||||
if (g != null) {
|
||||
g.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Log.info("Composited [orient=" + _orient + ", index=" + _index +
|
||||
// ", tbounds=" + StringUtil.toString(_bounds) + "].");
|
||||
|
||||
// long now = System.currentTimeMillis();
|
||||
// Log.info("Composited " + scount + " frames in " +
|
||||
// (now-start) + " millis.");
|
||||
}
|
||||
|
||||
protected int _index;
|
||||
protected Point _origin = new Point();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// $Id: CompositedMultiFrameImage.java 3310 2005-01-24 23:08:21Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
|
||||
import com.threerings.media.image.ImageManager;
|
||||
import com.threerings.media.image.Mirage;
|
||||
import com.threerings.media.image.VolatileMirage;
|
||||
|
||||
import com.threerings.cast.CompositedActionFrames.ComponentFrames;
|
||||
|
||||
/**
|
||||
* Used to composite the special shadow action frames for a particular
|
||||
* orientation of a {@link CompositedActionFrames}.
|
||||
*/
|
||||
public class CompositedShadowImage extends CompositedMultiFrameImage
|
||||
{
|
||||
public CompositedShadowImage (ImageManager imgr, ComponentFrames[] sources,
|
||||
String action, int orient, float shadowAlpha)
|
||||
{
|
||||
super(imgr, sources, action, orient);
|
||||
|
||||
// create the appropriate alpha composite for rendering the shadow
|
||||
_shadowAlpha = AlphaComposite.getInstance(
|
||||
AlphaComposite.SRC_OVER, shadowAlpha);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getWidth (int index) {
|
||||
return _sources[0].frames.getFrames(_orient).getWidth(index);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getHeight (int index) {
|
||||
return _sources[0].frames.getFrames(_orient).getHeight(index);
|
||||
}
|
||||
|
||||
public int getXOrigin (int index) {
|
||||
return _sources[0].frames.getXOrigin(_orient, index);
|
||||
}
|
||||
|
||||
public int getYOrigin (int index) {
|
||||
return _sources[0].frames.getYOrigin(_orient, index);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void paintFrame (Graphics2D g, int index, int x, int y) {
|
||||
Composite ocomp = g.getComposite();
|
||||
g.setComposite(_shadowAlpha);
|
||||
_images[index].paint(g, x + _images[index].getX(),
|
||||
y + _images[index].getY());
|
||||
g.setComposite(ocomp);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean hitTest (int index, int x, int y) {
|
||||
return _images[index].hitTest(x + _images[index].getX(),
|
||||
y + _images[index].getY());
|
||||
}
|
||||
|
||||
// documentation inherited from interface TrimmedMultiFrameImage
|
||||
public void getTrimmedBounds (int index, Rectangle bounds) {
|
||||
bounds.setBounds(_images[index].getX(), _images[index].getY(),
|
||||
_images[index].getWidth(), _images[index].getHeight());
|
||||
}
|
||||
|
||||
/** The alpha value at which we render our shadow. */
|
||||
protected AlphaComposite _shadowAlpha;
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// $Id: FrameProvider.java 3740 2005-10-20 22:12:45Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
/**
|
||||
* 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 ActionFrames getFrames (
|
||||
CharacterComponent component, String action, String type);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// $Id: Log.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
/**
|
||||
* A placeholder class that contains a reference to the log object used by
|
||||
* the cast package.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
public static com.samskivert.util.Log log =
|
||||
new com.samskivert.util.Log("cast");
|
||||
|
||||
/** Convenience function. */
|
||||
public static void debug (String message)
|
||||
{
|
||||
log.debug(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void info (String message)
|
||||
{
|
||||
log.info(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void warning (String message)
|
||||
{
|
||||
log.warning(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void logStackTrace (Throwable t)
|
||||
{
|
||||
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// $Id: NoSuchComponentException.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
/**
|
||||
* Thrown when an attempt is made to retrieve a non-existent character
|
||||
* component from the component repository.
|
||||
*/
|
||||
public class NoSuchComponentException extends Exception
|
||||
{
|
||||
public NoSuchComponentException (int componentId)
|
||||
{
|
||||
super("No such component [componentId=" + componentId + "]");
|
||||
_componentId = componentId;
|
||||
}
|
||||
|
||||
public NoSuchComponentException (
|
||||
String componentClass, String componentName)
|
||||
{
|
||||
super("No such component [class=" + componentClass +
|
||||
", name=" + componentName + "]");
|
||||
_componentId = -1;
|
||||
}
|
||||
|
||||
public int getComponentId ()
|
||||
{
|
||||
return _componentId;
|
||||
}
|
||||
|
||||
protected int _componentId;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// $Id: StandardActions.java 3740 2005-10-20 22:12:45Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
/**
|
||||
* Actions are referenced by name and this interface defines constants for
|
||||
* standard actions and action suffixes used by the shadow and cropping
|
||||
* support.
|
||||
*/
|
||||
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";
|
||||
|
||||
/** A special action sub-type for shadow imagery. */
|
||||
public static final String SHADOW_TYPE = "shadow";
|
||||
|
||||
/** A special action sub-type for crop imagery. */
|
||||
public static final String CROP_TYPE = "crop";
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// $Id: TrimmedMultiFrameImage.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
|
||||
import com.threerings.media.util.MultiFrameImage;
|
||||
|
||||
/**
|
||||
* Used to generate more memory efficient composited images in
|
||||
* circumstances where we have trimmed underlying component images.
|
||||
*/
|
||||
public interface TrimmedMultiFrameImage extends MultiFrameImage
|
||||
{
|
||||
/**
|
||||
* Fills in the minimum bounding rectangle for this image that
|
||||
* contains all non-transparent pixels. If this information is
|
||||
* unavailable, the bounds of the entire image may be returned in
|
||||
* exchange for improved performance.
|
||||
*/
|
||||
public void getTrimmedBounds (int index, Rectangle bounds);
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// $Id: BuilderModel.java 4145 2006-05-24 01:24:24Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.samskivert.util.CollectionUtil;
|
||||
|
||||
import com.threerings.cast.ComponentRepository;
|
||||
import com.threerings.cast.ComponentClass;
|
||||
|
||||
/**
|
||||
* The builder model represents the current state of the character the
|
||||
* user is building.
|
||||
*/
|
||||
public class BuilderModel
|
||||
{
|
||||
/**
|
||||
* Constructs a builder model.
|
||||
*/
|
||||
public BuilderModel (ComponentRepository crepo)
|
||||
{
|
||||
gatherComponentInfo(crepo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a builder model listener.
|
||||
*
|
||||
* @param l the listener.
|
||||
*/
|
||||
public void addListener (BuilderModelListener l)
|
||||
{
|
||||
if (!_listeners.contains(l)) {
|
||||
_listeners.add(l);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all model listeners that the builder model has changed.
|
||||
*/
|
||||
protected void notifyListeners (int event)
|
||||
{
|
||||
int size = _listeners.size();
|
||||
for (int ii = 0; ii < size; ii++) {
|
||||
((BuilderModelListener)_listeners.get(ii)).modelChanged(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of the available component classes.
|
||||
*/
|
||||
public List getComponentClasses ()
|
||||
{
|
||||
return Collections.unmodifiableList(_classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of components available in the specified class.
|
||||
*/
|
||||
public List getComponents (ComponentClass cclass)
|
||||
{
|
||||
List list = (List)_components.get(cclass);
|
||||
if (list == null) {
|
||||
list = new ArrayList();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected components in an array.
|
||||
*/
|
||||
public int[] getSelectedComponents ()
|
||||
{
|
||||
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.
|
||||
*/
|
||||
public void setSelectedComponent (ComponentClass cclass, int cid)
|
||||
{
|
||||
_selected.put(cclass, Integer.valueOf(cid));
|
||||
notifyListeners(BuilderModelListener.COMPONENT_CHANGED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers component class and component information from the
|
||||
* character manager for later reference by others.
|
||||
*/
|
||||
protected void gatherComponentInfo (ComponentRepository crepo)
|
||||
{
|
||||
// get the list of all component classes
|
||||
CollectionUtil.addAll(_classes, crepo.enumerateComponentClasses());
|
||||
|
||||
for (int ii = 0; ii < _classes.size(); ii++) {
|
||||
// get the list of components available for this class
|
||||
ComponentClass cclass = (ComponentClass)_classes.get(ii);
|
||||
Iterator iter = crepo.enumerateComponentIds(cclass);
|
||||
|
||||
while (iter.hasNext()) {
|
||||
Integer cid = (Integer)iter.next();
|
||||
ArrayList clist = (ArrayList)_components.get(cclass);
|
||||
if (clist == null) {
|
||||
_components.put(cclass, clist = new ArrayList());
|
||||
}
|
||||
|
||||
clist.add(cid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The currently selected character components. */
|
||||
protected HashMap _selected = new HashMap();
|
||||
|
||||
/** The hashtable of available component ids for each class. */
|
||||
protected HashMap _components = new HashMap();
|
||||
|
||||
/** The list of all available component classes. */
|
||||
protected ArrayList _classes = new ArrayList();
|
||||
|
||||
/** The model listeners. */
|
||||
protected ArrayList _listeners = new ArrayList();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// $Id: BuilderModelListener.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.builder;
|
||||
|
||||
/**
|
||||
* The builder model listener interface should be implemented by
|
||||
* classes that would like to be notified when the builder model is
|
||||
* changed.
|
||||
*
|
||||
* @see BuilderModel
|
||||
*/
|
||||
public interface BuilderModelListener
|
||||
{
|
||||
/**
|
||||
* Called by the {@link BuilderModel} when the model is changed.
|
||||
*/
|
||||
public void modelChanged (int event);
|
||||
|
||||
/** Notification event constants. */
|
||||
public static final int COMPONENT_CHANGED = 0;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// $Id: BuilderPanel.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.builder;
|
||||
|
||||
import javax.swing.*;
|
||||
import com.samskivert.swing.*;
|
||||
|
||||
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.
|
||||
*/
|
||||
public class BuilderPanel extends JPanel
|
||||
{
|
||||
/**
|
||||
* Constructs the builder panel.
|
||||
*/
|
||||
public BuilderPanel (CharacterManager charmgr,
|
||||
ComponentRepository crepo, String cprefix)
|
||||
{
|
||||
setLayout(new VGroupLayout());
|
||||
|
||||
// give ourselves a wee bit of a border
|
||||
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
|
||||
GroupLayout gl = new HGroupLayout(GroupLayout.STRETCH);
|
||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
||||
|
||||
// create the builder model
|
||||
BuilderModel model = new BuilderModel(crepo);
|
||||
|
||||
// create the component selection and sprite display panels
|
||||
JPanel sub = new JPanel(gl);
|
||||
sub.add(new ComponentPanel(model, cprefix));
|
||||
sub.add(new SpritePanel(charmgr, model));
|
||||
add(sub);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
//
|
||||
// $Id: ClassEditor.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.builder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import com.samskivert.swing.*;
|
||||
|
||||
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.
|
||||
*/
|
||||
public class ClassEditor extends JPanel implements ChangeListener
|
||||
{
|
||||
/**
|
||||
* Constructs a class editor.
|
||||
*/
|
||||
public ClassEditor (
|
||||
BuilderModel model, ComponentClass cclass, List components)
|
||||
{
|
||||
_model = model;
|
||||
_components = components;
|
||||
_cclass = cclass;
|
||||
|
||||
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
|
||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
||||
setLayout(gl);
|
||||
|
||||
gl = new HGroupLayout();
|
||||
gl.setJustification(GroupLayout.LEFT);
|
||||
JPanel sub = new JPanel(gl);
|
||||
|
||||
sub.add(new JLabel(cclass.name + ": "));
|
||||
sub.add(_clabel = new JLabel("0"));
|
||||
add(sub);
|
||||
|
||||
// create the slider allowing selection of available components
|
||||
int max = components.size() - 1;
|
||||
JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, max, 0);
|
||||
slider.setSnapToTicks(true);
|
||||
slider.addChangeListener(this);
|
||||
add(slider);
|
||||
|
||||
// set the starting component for this class
|
||||
setSelectedComponent(0);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void stateChanged (ChangeEvent e)
|
||||
{
|
||||
JSlider source = (JSlider)e.getSource();
|
||||
if (!source.getValueIsAdjusting()) {
|
||||
int val = source.getValue();
|
||||
// update the model with the newly selected component
|
||||
setSelectedComponent(val);
|
||||
// update the label with the new value
|
||||
_clabel.setText(Integer.toString(val));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selected component in the builder model for the
|
||||
* component class associated with this editor to the component at
|
||||
* the given index in this editor's list of available components.
|
||||
*/
|
||||
protected void setSelectedComponent (int idx)
|
||||
{
|
||||
int cid = ((Integer)_components.get(idx)).intValue();
|
||||
_model.setSelectedComponent(_cclass, cid);
|
||||
}
|
||||
|
||||
/** The component class associated with this editor. */
|
||||
protected ComponentClass _cclass;
|
||||
|
||||
/** The components selectable via this editor. */
|
||||
protected List _components;
|
||||
|
||||
/** The label denoting the currently selected component index. */
|
||||
protected JLabel _clabel;
|
||||
|
||||
/** The builder model. */
|
||||
protected BuilderModel _model;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// $Id: ComponentPanel.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.builder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import com.samskivert.swing.*;
|
||||
|
||||
import com.threerings.cast.Log;
|
||||
import com.threerings.cast.*;
|
||||
|
||||
/**
|
||||
* The component panel displays the available components for all
|
||||
* component classes and allows the user to choose a set of components
|
||||
* for compositing into a character image.
|
||||
*/
|
||||
public class ComponentPanel extends JPanel
|
||||
{
|
||||
/**
|
||||
* Constructs the component panel.
|
||||
*/
|
||||
public ComponentPanel (BuilderModel model, String cprefix)
|
||||
{
|
||||
setLayout(new VGroupLayout(GroupLayout.STRETCH));
|
||||
// set up a border
|
||||
setBorder(BorderFactory.createEtchedBorder());
|
||||
// add the component editors to the panel
|
||||
addClassEditors(model, cprefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds editor user interface elements for each component class to
|
||||
* allow the user to select the desired component.
|
||||
*/
|
||||
protected void addClassEditors (BuilderModel model, String cprefix)
|
||||
{
|
||||
List classes = model.getComponentClasses();
|
||||
int size = classes.size();
|
||||
for (int ii = 0; ii < size; ii++) {
|
||||
ComponentClass cclass = (ComponentClass)classes.get(ii);
|
||||
if (!cclass.name.startsWith(cprefix)) {
|
||||
continue;
|
||||
}
|
||||
List ccomps = model.getComponents(cclass);
|
||||
if (ccomps.size() > 0) {
|
||||
add(new ClassEditor(model, cclass, ccomps));
|
||||
} else {
|
||||
Log.info("Not creating editor for empty class " +
|
||||
"[class=" + cclass + "].");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// $Id: SpritePanel.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.builder;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
import com.threerings.cast.CharacterDescriptor;
|
||||
import com.threerings.cast.CharacterManager;
|
||||
import com.threerings.cast.CharacterSprite;
|
||||
import com.threerings.cast.StandardActions;
|
||||
|
||||
/**
|
||||
* The sprite panel displays a character sprite centered in the panel
|
||||
* suitable for user perusal.
|
||||
*/
|
||||
public class SpritePanel extends JPanel
|
||||
implements DirectionCodes, BuilderModelListener
|
||||
{
|
||||
/**
|
||||
* Constructs the sprite panel.
|
||||
*/
|
||||
public SpritePanel (CharacterManager charmgr, BuilderModel model)
|
||||
{
|
||||
// save off references
|
||||
_charmgr = charmgr;
|
||||
_model = model;
|
||||
|
||||
// listen to the builder model so that we can update the
|
||||
// sprite when a new component is selected
|
||||
_model.addListener(this);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void paintComponent (Graphics g)
|
||||
{
|
||||
super.paintComponent(g);
|
||||
Graphics2D gfx = (Graphics2D)g;
|
||||
|
||||
if (_sprite != null) {
|
||||
// render the sprite
|
||||
_sprite.paint(gfx);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void doLayout ()
|
||||
{
|
||||
super.doLayout();
|
||||
generateSprite();
|
||||
centerSprite();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void modelChanged (int event)
|
||||
{
|
||||
if (event == COMPONENT_CHANGED) {
|
||||
generateSprite();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new character sprite for display to reflect the
|
||||
* currently selected character components.
|
||||
*/
|
||||
protected void generateSprite ()
|
||||
{
|
||||
int components[] = _model.getSelectedComponents();
|
||||
CharacterDescriptor desc = new CharacterDescriptor(components, null);
|
||||
CharacterSprite sprite = _charmgr.getCharacter(desc);
|
||||
setSprite(sprite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sprite to be displayed.
|
||||
*/
|
||||
protected void setSprite (CharacterSprite sprite)
|
||||
{
|
||||
sprite.setActionSequence(StandardActions.STANDING);
|
||||
sprite.setOrientation(WEST);
|
||||
_sprite = sprite;
|
||||
centerSprite();
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sprite's location to render it centered within the panel.
|
||||
*/
|
||||
protected void centerSprite ()
|
||||
{
|
||||
if (_sprite != null) {
|
||||
Dimension d = getSize();
|
||||
int swid = _sprite.getWidth(), shei = _sprite.getHeight();
|
||||
int x = d.width / 2, y = (d.height + shei) / 2;
|
||||
_sprite.setLocation(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/** The sprite displayed by the panel. */
|
||||
protected CharacterSprite _sprite;
|
||||
|
||||
/** The character manager. */
|
||||
protected CharacterManager _charmgr;
|
||||
|
||||
/** The builder model. */
|
||||
protected BuilderModel _model;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// $Id: BundleUtil.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.bundle;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InvalidClassException;
|
||||
import java.io.ObjectInputStream;
|
||||
|
||||
import com.samskivert.io.StreamUtil;
|
||||
|
||||
import com.threerings.cast.Log;
|
||||
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";
|
||||
|
||||
/** The serialized tileset extension for our action tilesets. */
|
||||
public static final String TILESET_EXTENSION = ".dat";
|
||||
|
||||
/**
|
||||
* Attempts to load an object from the supplied resource bundle with
|
||||
* the specified path.
|
||||
*
|
||||
* @param wipeBundleOnFailure if there is an error reading the object
|
||||
* from the bundle and this parameter is true, we will instruct the
|
||||
* bundle to delete its underlying jar file before propagating the
|
||||
* exception with the expectation that it will be redownloaded and
|
||||
* repaired the next time the application is run.
|
||||
*
|
||||
* @return the unserialized object in question.
|
||||
*
|
||||
* @exception IOException thrown if an I/O error occurs while reading
|
||||
* the object from the bundle.
|
||||
*/
|
||||
public static Object loadObject (ResourceBundle bundle, String path,
|
||||
boolean wipeBundleOnFailure)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
InputStream bin = null;
|
||||
try {
|
||||
bin = bundle.getResource(path);
|
||||
if (bin == null) {
|
||||
return null;
|
||||
}
|
||||
return new ObjectInputStream(bin).readObject();
|
||||
|
||||
} catch (InvalidClassException ice) {
|
||||
Log.warning("Aiya! Serialized object is hosed " +
|
||||
"[bundle=" + bundle.getSource().getPath() +
|
||||
", element=" + path +
|
||||
", error=" + ice.getMessage() + "].");
|
||||
return null;
|
||||
|
||||
} catch (IOException ioe) {
|
||||
Log.warning("Error reading resource from bundle " +
|
||||
"[bundle=" + bundle + ", path=" + path +
|
||||
", wiping?=" + wipeBundleOnFailure + "].");
|
||||
if (wipeBundleOnFailure) {
|
||||
StreamUtil.close(bin);
|
||||
bin = null;
|
||||
bundle.wipeBundle(false);
|
||||
}
|
||||
throw ioe;
|
||||
|
||||
} finally {
|
||||
StreamUtil.close(bin);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,528 @@
|
||||
//
|
||||
// $Id: BundledComponentRepository.java 4023 2006-04-14 21:52:06Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.bundle;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.IntIntMap;
|
||||
import com.samskivert.util.Tuple;
|
||||
|
||||
import org.apache.commons.collections.iterators.FilterIterator;
|
||||
import org.apache.commons.collections.Predicate;
|
||||
|
||||
import com.threerings.resource.ResourceBundle;
|
||||
import com.threerings.resource.ResourceManager;
|
||||
|
||||
import com.threerings.media.image.Colorization;
|
||||
import com.threerings.media.image.FastImageIO;
|
||||
import com.threerings.media.image.ImageDataProvider;
|
||||
import com.threerings.media.image.ImageManager;
|
||||
|
||||
import com.threerings.media.tile.IMImageProvider;
|
||||
import com.threerings.media.tile.Tile;
|
||||
import com.threerings.media.tile.TileSet;
|
||||
import com.threerings.media.tile.TrimmedTile;
|
||||
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
import com.threerings.cast.ActionFrames;
|
||||
import com.threerings.cast.ActionSequence;
|
||||
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;
|
||||
import com.threerings.cast.StandardActions;
|
||||
import com.threerings.cast.TrimmedMultiFrameImage;
|
||||
|
||||
/**
|
||||
* A component repository implementation that obtains information from
|
||||
* resource bundles.
|
||||
*
|
||||
* @see ResourceManager
|
||||
*/
|
||||
public class BundledComponentRepository
|
||||
implements DirectionCodes, 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 imgr the image manager that we'll use to decode and cache
|
||||
* images.
|
||||
* @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, ImageManager imgr, String name)
|
||||
throws IOException
|
||||
{
|
||||
// keep this guy around
|
||||
_imgr = imgr;
|
||||
|
||||
// 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 {
|
||||
int rcount = (rbundles == null) ? 0 : rbundles.length;
|
||||
for (int i = 0; i < rcount; i++) {
|
||||
if (_actions == null) {
|
||||
_actions = (HashMap)BundleUtil.loadObject(
|
||||
rbundles[i], BundleUtil.ACTIONS_PATH, true);
|
||||
}
|
||||
if (_actionSets == null) {
|
||||
_actionSets = (HashMap)BundleUtil.loadObject(
|
||||
rbundles[i], BundleUtil.ACTION_SETS_PATH, true);
|
||||
}
|
||||
if (_classes == null) {
|
||||
_classes = (HashMap)BundleUtil.loadObject(
|
||||
rbundles[i], BundleUtil.CLASSES_PATH, true);
|
||||
}
|
||||
}
|
||||
|
||||
// now go back and load up all of the component information
|
||||
for (int i = 0; i < rcount; i++) {
|
||||
HashIntMap comps = null;
|
||||
comps = (HashIntMap)BundleUtil.loadObject(
|
||||
rbundles[i], BundleUtil.COMPONENTS_PATH, true);
|
||||
if (comps == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// create a frame provider for this bundle
|
||||
FrameProvider fprov =
|
||||
new ResourceBundleProvider(_imgr, 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 (IOException) new IOException(
|
||||
"Internal error unserializing metadata").initCause(cnfe);
|
||||
}
|
||||
|
||||
// if we failed to load our classes or actions, create empty
|
||||
// hashtables so that we can safely enumerate our emptiness
|
||||
if (_actions == null) {
|
||||
_actions = new HashMap();
|
||||
}
|
||||
if (_classes == null) {
|
||||
_classes = new HashMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bundled component repository to wipe any bundles
|
||||
* that report certain kinds of failure. In the event that an unpacked
|
||||
* bundle becomes corrupt, this is useful in that it will force the
|
||||
* bundle to be unpacked on the next application invocation,
|
||||
* potentially remedying the problem of a corrupt unpacking.
|
||||
*/
|
||||
public void setWipeOnFailure (boolean wipeOnFailure)
|
||||
{
|
||||
_wipeOnFailure = true;
|
||||
}
|
||||
|
||||
// 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 CharacterComponent getComponent (String className, String compName)
|
||||
throws NoSuchComponentException
|
||||
{
|
||||
// look up the list for that class
|
||||
ArrayList comps = (ArrayList)_classComps.get(className);
|
||||
if (comps != null) {
|
||||
// scan the list for the named component
|
||||
int ccount = comps.size();
|
||||
for (int i = 0; i < ccount; i++) {
|
||||
CharacterComponent comp = (CharacterComponent)comps.get(i);
|
||||
if (comp.name.equals(compName)) {
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new NoSuchComponentException(className, compName);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public ComponentClass getComponentClass (String className)
|
||||
{
|
||||
return (ComponentClass)_classes.get(className);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// stick it into the appropriate tables
|
||||
_components.put(componentId, component);
|
||||
|
||||
// we have a hash of lists for mapping components by class/name
|
||||
ArrayList comps = (ArrayList)_classComps.get(cclass);
|
||||
if (comps == null) {
|
||||
comps = new ArrayList();
|
||||
_classComps.put(cclass, comps);
|
||||
}
|
||||
if (!comps.contains(component)) {
|
||||
comps.add(component);
|
||||
} else {
|
||||
Log.info("Requested to register the same component twice? " +
|
||||
"[component=" + component + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instances of these provide images to our component action tilesets
|
||||
* and frames to our components.
|
||||
*/
|
||||
protected class ResourceBundleProvider extends IMImageProvider
|
||||
implements ImageDataProvider, FrameProvider
|
||||
{
|
||||
/**
|
||||
* Constructs an instance that will obtain image data from the
|
||||
* specified resource bundle.
|
||||
*/
|
||||
public ResourceBundleProvider (ImageManager imgr, ResourceBundle bundle)
|
||||
{
|
||||
super(imgr, (String)null);
|
||||
_dprov = this;
|
||||
_bundle = bundle;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public String getIdent ()
|
||||
{
|
||||
return "bcr:" + _bundle.getSource();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public BufferedImage loadImage (String path) throws IOException
|
||||
{
|
||||
return FastImageIO.read(_bundle.getResourceFile(path));
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public ActionFrames getFrames (
|
||||
CharacterComponent component, String action, String type)
|
||||
{
|
||||
// obtain the action sequence definition for this action
|
||||
ActionSequence actseq = (ActionSequence)_actions.get(action);
|
||||
if (actseq == null) {
|
||||
Log.warning("Missing action sequence definition " +
|
||||
"[action=" + action +
|
||||
", component=" + component + "].");
|
||||
return null;
|
||||
}
|
||||
|
||||
// determine our image path name
|
||||
String imgpath = action, dimgpath = ActionSequence.DEFAULT_SEQUENCE;
|
||||
if (type != null) {
|
||||
imgpath += "_" + type;
|
||||
dimgpath += "_" + type;
|
||||
}
|
||||
|
||||
String root = component.componentClass.name + "/" +
|
||||
component.name + "/";
|
||||
String cpath = root + imgpath + BundleUtil.TILESET_EXTENSION;
|
||||
String dpath = root + dimgpath + BundleUtil.TILESET_EXTENSION;
|
||||
|
||||
// look to see if this tileset is already cached (as the
|
||||
// custom action or the default action)
|
||||
TileSet aset = (TileSet)_setcache.get(cpath);
|
||||
if (aset == null) {
|
||||
aset = (TileSet)_setcache.get(dpath);
|
||||
if (aset != null) {
|
||||
// save ourselves a lookup next time
|
||||
_setcache.put(cpath, aset);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// then try loading up a tileset customized for this action
|
||||
if (aset == null) {
|
||||
aset = (TileSet)BundleUtil.loadObject(
|
||||
_bundle, cpath, false);
|
||||
}
|
||||
|
||||
// if that failed, try loading the default tileset
|
||||
if (aset == null) {
|
||||
aset = (TileSet)BundleUtil.loadObject(
|
||||
_bundle, dpath, false);
|
||||
_setcache.put(dpath, aset);
|
||||
}
|
||||
|
||||
// if that failed too, we're hosed
|
||||
if (aset == null) {
|
||||
// if this is a shadow image, no need to freak out as they
|
||||
// are optional
|
||||
if (!StandardActions.SHADOW_TYPE.equals(type)) {
|
||||
Log.warning("Unable to locate tileset for action '" +
|
||||
imgpath + "' " + component + ".");
|
||||
if (_wipeOnFailure) {
|
||||
_bundle.wipeBundle(false);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
aset.setImageProvider(this);
|
||||
_setcache.put(cpath, aset);
|
||||
return new TileSetFrameImage(aset, actseq);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Error loading tileset for action '" + imgpath +
|
||||
"' " + component + ".");
|
||||
Log.logStackTrace(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** The resource bundle from which we obtain image data. */
|
||||
protected ResourceBundle _bundle;
|
||||
|
||||
/** Cache of tilesets loaded from our bundle. */
|
||||
protected HashMap _setcache = new HashMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to provide multiframe images using data obtained from a
|
||||
* tileset.
|
||||
*/
|
||||
protected static class TileSetFrameImage implements ActionFrames
|
||||
{
|
||||
/**
|
||||
* Constructs a tileset frame image with the specified tileset and
|
||||
* for the specified orientation.
|
||||
*/
|
||||
public TileSetFrameImage (TileSet set, ActionSequence actseq)
|
||||
{
|
||||
_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
|
||||
public TrimmedMultiFrameImage getFrames (final int orient)
|
||||
{
|
||||
return new TrimmedMultiFrameImage() {
|
||||
// documentation inherited
|
||||
public int getFrameCount ()
|
||||
{
|
||||
return _fcount;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getWidth (int index)
|
||||
{
|
||||
Tile tile = getTile(orient, index);
|
||||
return (tile == null) ? 0 : tile.getWidth();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getHeight (int index)
|
||||
{
|
||||
Tile tile = getTile(orient, index);
|
||||
return (tile == null) ? 0 : tile.getHeight();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void paintFrame (Graphics2D g, int index, int x, int y)
|
||||
{
|
||||
Tile tile = getTile(orient, index);
|
||||
if (tile != null) {
|
||||
tile.paint(g, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean hitTest (int index, int x, int y)
|
||||
{
|
||||
Tile tile = getTile(orient, index);
|
||||
return (tile != null) ? tile.hitTest(x, y) : false;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void getTrimmedBounds (int index, Rectangle bounds)
|
||||
{
|
||||
Tile tile = getTile(orient, index);
|
||||
if (tile instanceof TrimmedTile) {
|
||||
((TrimmedTile)tile).getTrimmedBounds(bounds);
|
||||
} else {
|
||||
bounds.setBounds(
|
||||
0, 0, tile.getWidth(), tile.getHeight());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getXOrigin (int orient, int index)
|
||||
{
|
||||
return _actseq.origin.x;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getYOrigin (int orient, int index)
|
||||
{
|
||||
return _actseq.origin.y;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public ActionFrames cloneColorized (Colorization[] zations)
|
||||
{
|
||||
return new TileSetFrameImage(_set.clone(zations), _actseq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the requested tile.
|
||||
*/
|
||||
protected Tile getTile (int orient, int index)
|
||||
{
|
||||
int tileIndex = _orients.get(orient) * _fcount + index;
|
||||
return _set.getTile(tileIndex);
|
||||
}
|
||||
|
||||
/** The tileset from which we obtain our frame images. */
|
||||
protected TileSet _set;
|
||||
|
||||
/** 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. */
|
||||
protected ImageManager _imgr;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** A table of component lists indexed on classname. */
|
||||
protected HashMap _classComps = new HashMap();
|
||||
|
||||
/** The component table. */
|
||||
protected HashIntMap _components = new HashIntMap();
|
||||
|
||||
/** Whether or not we wipe our bundles on any failure. */
|
||||
protected boolean _wipeOnFailure;
|
||||
}
|
||||
@@ -0,0 +1,624 @@
|
||||
//
|
||||
// $Id: ComponentBundlerTask.java 4145 2006-05-24 01:24:24Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.bundle.tools;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import org.apache.commons.digester.Digester;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
import com.samskivert.util.ComparableArrayList;
|
||||
import com.samskivert.util.FileUtil;
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.Tuple;
|
||||
|
||||
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.media.tile.ImageProvider;
|
||||
import com.threerings.media.tile.SimpleCachingImageProvider;
|
||||
import com.threerings.media.tile.TileSet;
|
||||
import com.threerings.media.tile.TrimmedTileSet;
|
||||
import com.threerings.media.tile.tools.xml.SwissArmyTileSetRuleSet;
|
||||
|
||||
import com.threerings.cast.ComponentIDBroker;
|
||||
import com.threerings.cast.StandardActions;
|
||||
import com.threerings.cast.bundle.BundleUtil;
|
||||
import com.threerings.cast.tools.xml.ActionRuleSet;
|
||||
|
||||
/**
|
||||
* Ant task for creating component bundles. This task must be configured
|
||||
* with a number of parameters:
|
||||
*
|
||||
* <pre>
|
||||
* 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]
|
||||
* </pre>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to the action tilesets definition file.
|
||||
*/
|
||||
public void setActiondef (File actiondef)
|
||||
{
|
||||
_actionDef = actiondef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the root path which will be stripped from the image paths
|
||||
* prior to parsing them to obtain the component class, type and
|
||||
* action names.
|
||||
*/
|
||||
public void setRoot (File root)
|
||||
{
|
||||
_root = root.getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 everything was set up properly
|
||||
ensureSet(_target, "Must specify the path to the target bundle " +
|
||||
"file via the 'target' attribute.");
|
||||
ensureSet(_mapfile, "Must specify the path to the component map " +
|
||||
"file via the 'mapfile' attribute.");
|
||||
ensureSet(_actionDef, "Must specify the action sequence " +
|
||||
"definitions via the 'actiondef' attribute.");
|
||||
|
||||
// parse in the action tilesets
|
||||
HashMap actsets = parseActionTileSets();
|
||||
|
||||
// load up our component ID broker
|
||||
ComponentIDBroker broker = loadBroker(_mapfile);
|
||||
|
||||
// check to see if any of the source files are newer than the
|
||||
// target file
|
||||
ArrayList sources = (ArrayList)_filesets.clone();
|
||||
sources.add(_mapfile);
|
||||
sources.add(_actionDef);
|
||||
if (!outOfDate(sources, _target)) {
|
||||
System.out.println(_target.getPath() + " is up to date.");
|
||||
return;
|
||||
}
|
||||
|
||||
// create an image provider for loading our component images
|
||||
ImageProvider improv = new SimpleCachingImageProvider() {
|
||||
protected BufferedImage loadImage (String path)
|
||||
throws IOException {
|
||||
return ImageIO.read(new File(path));
|
||||
}
|
||||
};
|
||||
|
||||
System.out.println("Generating " + _target.getPath() + "...");
|
||||
|
||||
try {
|
||||
// make sure we can create our bundle file
|
||||
FileOutputStream fout = new FileOutputStream(_target);
|
||||
JarOutputStream jout = new JarOutputStream(fout);
|
||||
jout.setLevel(Deflater.BEST_COMPRESSION);
|
||||
|
||||
// we'll fill this with component id to tuple mappings
|
||||
HashIntMap mapping = new HashIntMap();
|
||||
|
||||
// herein we'll insert trimmed tileset objects that go along
|
||||
// with each of the trimmed action images
|
||||
HashIntMap actionSets = new HashIntMap();
|
||||
|
||||
// deal with the filesets
|
||||
for (int i = 0; i < _filesets.size(); i++) {
|
||||
FileSet fs = (FileSet)_filesets.get(i);
|
||||
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
||||
File fromDir = fs.getDir(getProject());
|
||||
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());
|
||||
|
||||
// make sure we have an action tileset definition
|
||||
TileSet aset = (TileSet)actsets.get(info[2]);
|
||||
if (aset == null) {
|
||||
System.err.println(
|
||||
"No tileset definition for component action " +
|
||||
"[class=" + info[0] + ", name=" + info[1] +
|
||||
", action=" + info[2] + "].");
|
||||
continue;
|
||||
}
|
||||
aset.setImageProvider(improv);
|
||||
|
||||
// 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]));
|
||||
|
||||
// process and store the main component image
|
||||
processComponent(info, aset, cfile, jout);
|
||||
|
||||
// pick up any auxiliary images as well like the shadow or
|
||||
// crop files
|
||||
String action = info[2];
|
||||
String ext = BundleUtil.IMAGE_EXTENSION;
|
||||
for (int aa = 0; aa < AUX_EXTS.length; aa++) {
|
||||
File afile = new File(
|
||||
FileUtil.resuffix(cfile, ext, AUX_EXTS[aa] + ext));
|
||||
if (afile.exists()) {
|
||||
info[2] = action + AUX_EXTS[aa];
|
||||
processComponent(info, aset, afile, 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);
|
||||
}
|
||||
|
||||
protected void processComponent (
|
||||
String[] info, TileSet aset, File cfile, JarOutputStream jout)
|
||||
throws IOException
|
||||
{
|
||||
// construct the path that'll go in the jar file
|
||||
String ipath = composePath(
|
||||
info, BundleUtil.IMAGE_EXTENSION);
|
||||
jout.putNextEntry(new JarEntry(ipath));
|
||||
|
||||
// create a trimmed tileset based on the source action tileset and
|
||||
// stuff the new trimmed image into the jar file at the same time
|
||||
aset.setImagePath(cfile.getPath());
|
||||
|
||||
TrimmedTileSet tset = null;
|
||||
try {
|
||||
tset = TrimmedTileSet.trimTileSet(aset, jout);
|
||||
tset.setImagePath(ipath);
|
||||
} catch (Throwable t) {
|
||||
System.err.println(
|
||||
"Failure trimming tileset " +
|
||||
"[class=" + info[0] + ", name=" + info[1] +
|
||||
", action=" + info[2] +
|
||||
", srcimg=" + aset.getImagePath() + "].");
|
||||
t.printStackTrace(System.err);
|
||||
}
|
||||
|
||||
// then write our trimmed tileset to the jar file
|
||||
if (tset != null) {
|
||||
String tpath = composePath(
|
||||
info, BundleUtil.TILESET_EXTENSION);
|
||||
jout.putNextEntry(new JarEntry(tpath));
|
||||
ObjectOutputStream oout = new ObjectOutputStream(jout);
|
||||
oout.writeObject(tset);
|
||||
oout.flush();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean outOfDate (ArrayList sources, File target)
|
||||
{
|
||||
for (int ii = 0; ii < sources.size(); ii++) {
|
||||
if (outOfDate(sources.get(ii), target)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean outOfDate (Object source, File target)
|
||||
{
|
||||
if (source instanceof FileSet) {
|
||||
FileSet fs = (FileSet)source;
|
||||
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
||||
File fromDir = fs.getDir(getProject());
|
||||
String[] srcFiles = ds.getIncludedFiles();
|
||||
for (int f = 0; f < srcFiles.length; f++) {
|
||||
File cfile = new File(fromDir, srcFiles[f]);
|
||||
if (newer(cfile, target)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
} else if (source instanceof File) {
|
||||
return newer((File)source, target);
|
||||
|
||||
} else {
|
||||
System.err.println("Can't compare " + source +
|
||||
" to " + target + ".");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if <code>source</code> is newer than
|
||||
* <code>target</code>.
|
||||
*/
|
||||
protected boolean newer (File source, File target)
|
||||
{
|
||||
return source.lastModified() > target.lastModified();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 root
|
||||
if (!path.startsWith(_root)) {
|
||||
throw new BuildException("Can't bundle images outside the " +
|
||||
"root directory [root=" + _root +
|
||||
", path=" + path + "].");
|
||||
}
|
||||
path = path.substring(_root.length());
|
||||
|
||||
// strip off any preceding slash
|
||||
if (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
|
||||
// now 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; the component type and action must
|
||||
// always be a single string but the class can span multiple
|
||||
// directories for easier component organization; thus
|
||||
// "male/head/goatee/standing" will be parsed as [class=male/head,
|
||||
// type=goatee, action=standing]
|
||||
String malmsg = "Can't decode malformed image path: '" + path + "'";
|
||||
String[] info = new String[3];
|
||||
int lsidx = path.lastIndexOf(File.separator);
|
||||
if (lsidx == -1) {
|
||||
throw new BuildException(malmsg);
|
||||
}
|
||||
info[2] = path.substring(lsidx+1);
|
||||
int slsidx = path.lastIndexOf(File.separator, lsidx-1);
|
||||
if (slsidx == -1) {
|
||||
throw new BuildException(malmsg);
|
||||
}
|
||||
info[1] = path.substring(slsidx+1, lsidx);
|
||||
info[0] = path.substring(0, slsidx);
|
||||
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, String extension)
|
||||
{
|
||||
return (info[0] + File.separator + info[1] +
|
||||
File.separator + info[2] + extension);
|
||||
}
|
||||
|
||||
protected void ensureSet (Object value, String errmsg)
|
||||
throws BuildException
|
||||
{
|
||||
if (value == null) {
|
||||
throw new BuildException(errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the action tileset definitions and puts them into a hash
|
||||
* map, keyed on action name.
|
||||
*/
|
||||
protected HashMap parseActionTileSets ()
|
||||
{
|
||||
Digester digester = new Digester();
|
||||
SwissArmyTileSetRuleSet srules = new SwissArmyTileSetRuleSet();
|
||||
String aprefix = "actions" + ActionRuleSet.ACTION_PATH;
|
||||
srules.setPrefix(aprefix);
|
||||
|
||||
digester.addRuleSet(srules);
|
||||
digester.addSetProperties(aprefix);
|
||||
digester.addSetNext(aprefix + SwissArmyTileSetRuleSet.TILESET_PATH,
|
||||
"addTileSet", TileSet.class.getName());
|
||||
|
||||
HashMap actsets = new ActionMap();
|
||||
digester.push(actsets);
|
||||
|
||||
try {
|
||||
FileInputStream fin = new FileInputStream(_actionDef);
|
||||
BufferedInputStream bin = new BufferedInputStream(fin);
|
||||
digester.parse(bin);
|
||||
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
String errmsg = "Unable to load action definition file " +
|
||||
"[path=" + _actionDef.getPath() + "].";
|
||||
throw new BuildException(errmsg, fnfe);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new BuildException("Parsing error.", e);
|
||||
}
|
||||
|
||||
return actsets;
|
||||
}
|
||||
|
||||
/** Used when parsing action tilesets. */
|
||||
public static class ActionMap extends HashMap
|
||||
{
|
||||
public void setName (String name) {
|
||||
_name = name;
|
||||
}
|
||||
public void addTileSet (TileSet set) {
|
||||
set.setName(_name);
|
||||
put(_name, set);
|
||||
}
|
||||
protected String _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the hashmap ID broker from its persistent representation in
|
||||
* the specified file.
|
||||
*/
|
||||
protected HashMapIDBroker loadBroker (File mapfile)
|
||||
throws BuildException
|
||||
{
|
||||
HashMapIDBroker broker = new HashMapIDBroker();
|
||||
|
||||
try {
|
||||
BufferedReader bin = new BufferedReader(new FileReader(mapfile));
|
||||
broker.readFrom(bin);
|
||||
bin.close();
|
||||
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
// if the file doesn't yet exist, start with a blank broker
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new BuildException("Error loading component ID map " +
|
||||
"[mapfile=" + mapfile + "]", 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
|
||||
{
|
||||
HashMapIDBroker hbroker = (HashMapIDBroker)broker;
|
||||
|
||||
// bail if the broker wasn't modified
|
||||
if (!hbroker.isModified()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
BufferedWriter bout = new BufferedWriter(new FileWriter(mapfile));
|
||||
hbroker.writeTo(bout);
|
||||
bout.close();
|
||||
} catch (IOException ioe) {
|
||||
throw new BuildException("Unable to store component ID map " +
|
||||
"[mapfile=" + mapfile + "]", 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 = Integer.valueOf(++_nextCID);
|
||||
put(key, cid);
|
||||
}
|
||||
return cid.intValue();
|
||||
}
|
||||
|
||||
public void commit ()
|
||||
throws PersistenceException
|
||||
{
|
||||
// nothing doing
|
||||
}
|
||||
|
||||
public boolean isModified ()
|
||||
{
|
||||
return _nextCID != _startCID;
|
||||
}
|
||||
|
||||
public void writeTo (BufferedWriter bout)
|
||||
throws IOException
|
||||
{
|
||||
// write out our most recently assigned component id
|
||||
String cidline = "" + _nextCID;
|
||||
bout.write(cidline, 0, cidline.length());
|
||||
bout.newLine();
|
||||
|
||||
// write out the keys and values
|
||||
ComparableArrayList lines = new ComparableArrayList();
|
||||
Iterator keys = keySet().iterator();
|
||||
while (keys.hasNext()) {
|
||||
Tuple key = (Tuple)keys.next();
|
||||
Integer value = (Integer)get(key);
|
||||
String line = key.left + SEP_STR + key.right + SEP_STR + value;
|
||||
lines.add(line);
|
||||
}
|
||||
|
||||
// sort the output
|
||||
lines.sort();
|
||||
|
||||
// now write it to the file
|
||||
int lcount = lines.size();
|
||||
for (int ii = 0; ii < lcount; ii++) {
|
||||
String line = (String)lines.get(ii);
|
||||
bout.write(line, 0, line.length());
|
||||
bout.newLine();
|
||||
}
|
||||
}
|
||||
|
||||
public void readFrom (BufferedReader bin)
|
||||
throws IOException
|
||||
{
|
||||
// read in our most recently assigned component id
|
||||
_nextCID = readInt(bin);
|
||||
// keep track of this so that we can tell if we were modified
|
||||
_startCID = _nextCID;
|
||||
|
||||
// now read in our keys and values
|
||||
String line;
|
||||
while ((line = bin.readLine()) != null) {
|
||||
String orig = line;
|
||||
int sidx = line.indexOf(SEP_STR);
|
||||
if (sidx == -1) {
|
||||
throw new IOException("Malformed line '" + orig + "'");
|
||||
}
|
||||
String cclass = line.substring(0, sidx);
|
||||
line = line.substring(sidx + SEP_STR.length());
|
||||
sidx = line.indexOf(SEP_STR);
|
||||
if (sidx == -1) {
|
||||
throw new IOException("Malformed line '" + orig + "'");
|
||||
}
|
||||
String cname = line.substring(0, sidx);
|
||||
line = line.substring(sidx + SEP_STR.length());
|
||||
try {
|
||||
put(new Tuple(cclass, cname), Integer.valueOf(line));
|
||||
} catch (NumberFormatException nfe) {
|
||||
String err = "Malformed line, invalid code '" + orig + "'";
|
||||
throw new IOException(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int readInt (BufferedReader bin)
|
||||
throws IOException
|
||||
{
|
||||
String line = bin.readLine();
|
||||
try {
|
||||
return Integer.parseInt(line);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new IOException("Expected number, got '" + line + "'");
|
||||
}
|
||||
}
|
||||
|
||||
protected int _nextCID = 0;
|
||||
protected int _startCID = 0;
|
||||
}
|
||||
|
||||
/** The path to our component bundle file. */
|
||||
protected File _target;
|
||||
|
||||
/** The path to our component map file. */
|
||||
protected File _mapfile;
|
||||
|
||||
/** The path to our action tilesets definition file. */
|
||||
protected File _actionDef;
|
||||
|
||||
/** The component directory root. */
|
||||
protected String _root;
|
||||
|
||||
/** A list of filesets that contain tile images. */
|
||||
protected ArrayList _filesets = new ArrayList();
|
||||
|
||||
/** Used to separate keys and values in the map file. */
|
||||
protected static final String SEP_STR = " := ";
|
||||
|
||||
/** Used to process auxilliary tilesets. */
|
||||
protected static final String[] AUX_EXTS = {
|
||||
"_" + StandardActions.SHADOW_TYPE,
|
||||
"_" + StandardActions.CROP_TYPE };
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// $Id: DumpBundle.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.bundle.tools;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.resource.ResourceBundle;
|
||||
import com.threerings.cast.bundle.BundleUtil;
|
||||
|
||||
/**
|
||||
* Dumps the contents of a component bundle to stdout.
|
||||
*/
|
||||
public class DumpBundle
|
||||
{
|
||||
public static void main (String[] args)
|
||||
{
|
||||
if (args.length < 1) {
|
||||
String usage = "Usage: DumpBundle bundle.jar [bundle.jar ...]";
|
||||
System.err.println(usage);
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
File file = new File(args[i]);
|
||||
try {
|
||||
ResourceBundle bundle = new ResourceBundle(file);
|
||||
|
||||
HashMap actions = (HashMap)BundleUtil.loadObject(
|
||||
bundle, BundleUtil.ACTIONS_PATH, false);
|
||||
dumpTable("actions: ", actions);
|
||||
|
||||
HashMap actionSets = (HashMap)BundleUtil.loadObject(
|
||||
bundle, BundleUtil.ACTION_SETS_PATH, false);
|
||||
dumpTable("actionSets: ", actionSets);
|
||||
|
||||
HashMap classes = (HashMap)BundleUtil.loadObject(
|
||||
bundle, BundleUtil.CLASSES_PATH, false);
|
||||
dumpTable("classes: ", classes);
|
||||
|
||||
HashIntMap comps = (HashIntMap)BundleUtil.loadObject(
|
||||
bundle, BundleUtil.COMPONENTS_PATH, false);
|
||||
dumpTable("components: ", comps);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error dumping bundle [path=" + args[i] +
|
||||
", error=" + e + "].");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void dumpTable (String prefix, Map table)
|
||||
{
|
||||
if (table != null) {
|
||||
Iterator iter = table.entrySet().iterator();
|
||||
System.out.println(prefix + StringUtil.toString(iter));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
//
|
||||
// $Id: MetadataBundlerTask.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.bundle.tools;
|
||||
|
||||
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.ObjectOutputStream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Task;
|
||||
|
||||
import org.apache.commons.digester.Digester;
|
||||
|
||||
import com.samskivert.util.Tuple;
|
||||
|
||||
import com.threerings.media.tile.TileSet;
|
||||
import com.threerings.media.tile.tools.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:
|
||||
*
|
||||
* <pre>
|
||||
* actiondef=[path to actions.xml]
|
||||
* classdef=[path to classes.xml]
|
||||
* file=[path to metadata bundle, which will be created]
|
||||
* </pre>
|
||||
*/
|
||||
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);
|
||||
jout.setLevel(Deflater.BEST_COMPRESSION);
|
||||
|
||||
// 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);
|
||||
// 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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
//
|
||||
// $Id: ActionRuleSet.java 3749 2005-11-09 04:00:16Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
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.util.DirectionCodes;
|
||||
import com.threerings.util.DirectionUtil;
|
||||
|
||||
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 <code>actions</code> will
|
||||
* match actions in the following XML file:
|
||||
*
|
||||
* <pre>
|
||||
* <actions>
|
||||
* <action>
|
||||
* // ...
|
||||
* </action>
|
||||
* </actions>
|
||||
* </pre>
|
||||
*
|
||||
* 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
|
||||
// <action> tag
|
||||
digester.addObjectCreate(_prefix + ACTION_PATH,
|
||||
ActionSequence.class.getName());
|
||||
|
||||
// grab the name attribute from the <action> tag
|
||||
digester.addRule(_prefix + ACTION_PATH, new SetPropertyFieldsRule());
|
||||
|
||||
// grab the other attributes from their respective tags
|
||||
digester.addRule(_prefix + ACTION_PATH + "/framesPerSecond",
|
||||
new SetFieldRule("framesPerSecond"));
|
||||
|
||||
CallMethodSpecialRule origin = new CallMethodSpecialRule() {
|
||||
public void parseAndSet (String bodyText, Object target)
|
||||
throws Exception {
|
||||
int[] coords = StringUtil.parseIntArray(bodyText);
|
||||
if (coords.length != 2) {
|
||||
String errmsg = "Invalid <origin> specification '" +
|
||||
bodyText + "'.";
|
||||
throw new Exception(errmsg);
|
||||
}
|
||||
((ActionSequence)target).origin.setLocation(
|
||||
coords[0], coords[1]);
|
||||
}
|
||||
};
|
||||
digester.addRule(_prefix + ACTION_PATH + "/origin", origin);
|
||||
|
||||
CallMethodSpecialRule orient = new CallMethodSpecialRule() {
|
||||
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.isBlank(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. */
|
||||
protected String _prefix;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// $Id: ClassRuleSet.java 3729 2005-10-13 00:00:33Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.tools.xml;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import org.apache.commons.digester.Digester;
|
||||
import org.apache.commons.digester.RuleSetBase;
|
||||
|
||||
import com.samskivert.util.ArrayIntSet;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.samskivert.xml.SetPropertyFieldsRule;
|
||||
import com.samskivert.xml.SetPropertyFieldsRule.FieldParser;
|
||||
|
||||
import com.threerings.cast.ComponentClass.PriorityOverride;
|
||||
import com.threerings.cast.ComponentClass;
|
||||
import com.threerings.util.DirectionUtil;
|
||||
|
||||
/**
|
||||
* The class rule set is used to parse the attributes of a component class
|
||||
* instance.
|
||||
*/
|
||||
public class ClassRuleSet extends RuleSetBase
|
||||
{
|
||||
/** The component of the digester path that is appended by the class
|
||||
* rule set to match a component class. This is appended to whatever
|
||||
* prefix is provided to the class rule set to obtain the complete XML
|
||||
* path to a matched class. */
|
||||
public static final String CLASS_PATH = "/class";
|
||||
|
||||
/**
|
||||
* Instructs the class rule set to match component classes with the
|
||||
* supplied prefix. For example, passing a prefix of
|
||||
* <code>classes</code> will match classes in the following XML file:
|
||||
*
|
||||
* <pre>
|
||||
* <classes>
|
||||
* <class .../>
|
||||
* </classes>
|
||||
* </pre>
|
||||
*
|
||||
* 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
|
||||
// <class> tag
|
||||
digester.addObjectCreate(_prefix + CLASS_PATH,
|
||||
ComponentClass.class.getName());
|
||||
|
||||
// grab the attributes from the <class> tag
|
||||
SetPropertyFieldsRule rule = new SetPropertyFieldsRule();
|
||||
rule.addFieldParser("shadowColor", new FieldParser() {
|
||||
public Object parse (String text) {
|
||||
int[] values = StringUtil.parseIntArray(text);
|
||||
return new Color(values[0], values[1], values[2], values[3]);
|
||||
}
|
||||
});
|
||||
digester.addRule(_prefix + CLASS_PATH, rule);
|
||||
|
||||
// parse render priority overrides
|
||||
String opath = _prefix + CLASS_PATH + "/override";
|
||||
digester.addObjectCreate(opath, PriorityOverride.class.getName());
|
||||
rule = new SetPropertyFieldsRule();
|
||||
rule.addFieldParser("orients", new FieldParser() {
|
||||
public Object parse (String text) {
|
||||
String[] orients = StringUtil.parseStringArray(text);
|
||||
ArrayIntSet oset = new ArrayIntSet();
|
||||
for (int ii = 0; ii < orients.length; ii++) {
|
||||
oset.add(DirectionUtil.fromShortString(orients[ii]));
|
||||
}
|
||||
return oset;
|
||||
}
|
||||
});
|
||||
digester.addRule(opath, rule);
|
||||
|
||||
digester.addSetNext(opath, "addPriorityOverride",
|
||||
PriorityOverride.class.getName());
|
||||
}
|
||||
|
||||
/** The prefix at which me match our component classes. */
|
||||
protected String _prefix;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// $Id: CastUtil.java 4188 2006-06-13 18:03:48Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.samskivert.util.CollectionUtil;
|
||||
import com.samskivert.util.RandomUtil;
|
||||
|
||||
import com.threerings.cast.CharacterDescriptor;
|
||||
import com.threerings.cast.ComponentClass;
|
||||
import com.threerings.cast.ComponentRepository;
|
||||
import com.threerings.cast.Log;
|
||||
|
||||
/**
|
||||
* Miscellaneous cast utility routines.
|
||||
*/
|
||||
public class CastUtil
|
||||
{
|
||||
/**
|
||||
* Returns a new character descriptor populated with a random set of
|
||||
* components.
|
||||
*/
|
||||
public static CharacterDescriptor getRandomDescriptor (
|
||||
String gender, ComponentRepository crepo)
|
||||
{
|
||||
// get all available classes
|
||||
ArrayList classes = new ArrayList();
|
||||
for (int i = 0; i < CLASSES.length; i++) {
|
||||
String cname = gender + "/" + CLASSES[i];
|
||||
ComponentClass cclass = crepo.getComponentClass(cname);
|
||||
|
||||
// make sure the component class exists
|
||||
if (cclass == null) {
|
||||
Log.warning("Missing definition for component class " +
|
||||
"[class=" + cname + "].");
|
||||
continue;
|
||||
}
|
||||
|
||||
// make sure there are some components in this class
|
||||
Iterator iter = crepo.enumerateComponentIds(cclass);
|
||||
if (!iter.hasNext()) {
|
||||
Log.info("Skipping class for which we have no components " +
|
||||
"[class=" + cclass + "].");
|
||||
continue;
|
||||
}
|
||||
|
||||
classes.add(cclass);
|
||||
}
|
||||
|
||||
// select the components
|
||||
int size = classes.size();
|
||||
int components[] = new int[size];
|
||||
for (int ii = 0; ii < size; ii++) {
|
||||
ComponentClass cclass = (ComponentClass)classes.get(ii);
|
||||
|
||||
// get the components available for this class
|
||||
ArrayList choices = new ArrayList();
|
||||
Iterator iter = crepo.enumerateComponentIds(cclass);
|
||||
CollectionUtil.addAll(choices, iter);
|
||||
|
||||
// choose a random component
|
||||
if (choices.size() > 0) {
|
||||
int idx = RandomUtil.getInt(choices.size());
|
||||
components[ii] = ((Integer)choices.get(idx)).intValue();
|
||||
} else {
|
||||
Log.info("Have no components in class [class=" + cclass + "].");
|
||||
}
|
||||
}
|
||||
|
||||
return new CharacterDescriptor(components, null);
|
||||
}
|
||||
|
||||
protected static final String[] CLASSES = {
|
||||
"legs", "feet", "hand_left", "hand_right", "torso",
|
||||
"head", "hair", "hat", "eyepatch" };
|
||||
}
|
||||
Reference in New Issue
Block a user