Enough AS cast implementation to render a basic character sprite from component bundles.

git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@970 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
Mike Thomas
2010-08-10 20:28:25 +00:00
parent 4b09709590
commit 57f020afb8
17 changed files with 1829 additions and 6 deletions
+1 -1
View File
@@ -38,7 +38,7 @@ public interface ActionFrames
* Returns the multi-frame image that comprises the frames for the
* specified orientation.
*/
function getFrames (orient :int) :DisplayObject;
function getFrames (orient :int, callback :Function) :void;
/**
* Returns the x offset from the upper left of the image to the
@@ -0,0 +1,106 @@
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// 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 flash.geom.Point;
import com.threerings.media.tile.SwissArmyTileSet;
import com.threerings.util.DirectionUtil;
import com.threerings.util.StringUtil;
/**
* 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
{
/**
* 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 const DEFAULT_SEQUENCE :String = "default";
/** The action sequence name. */
public var name :String;
/** The number of frames per second to show when animating. */
public var framesPerSecond :Number;
/** The position of the character's base for this sequence. */
public var origin :Point = new Point();
public var tileset :SwissArmyTileSet;
/** Orientation codes for the orientations available for this
* action. */
public var orients :Array;
public static function fromXml (xml :XML) :ActionSequence
{
var seq :ActionSequence = new ActionSequence();
seq.name = xml.@name;
seq.framesPerSecond = xml.framesPerSecond;
seq.orients = toOrientArray(xml.orients);
seq.origin = toPoint(xml.origin);
seq.tileset = SwissArmyTileSet.fromXml(xml.tileset[0]);
return seq;
}
protected static function toOrientArray (str :String) :Array
{
if (str == null || str.length == 0) {
return null;
}
return str.split(",").map(function(element :String, index :int, arr :Array) :int {
return DirectionUtil.fromShortString(StringUtil.trim(element));
});
}
protected static function toPoint (str :String) :Point
{
if (str == null || str.length == 0) {
return null;
}
var coords :Array =
str.split(",").map(function(element :String, index :int, arr :Array) :int {
return int(StringUtil.trim(element));
});
return new Point(coords[0], coords[1]);
}
public function toString () :String
{
return "[name=" + name + ", framesPerSecond=" + framesPerSecond +
", origin=" + origin +
", orients=" + (orients == null ? 0 : orients.length) + "]";
}
}
}
@@ -53,6 +53,17 @@ public class CharacterComponent
_frameProvider = fprov;
}
public function setFrameProvider (frameProvider :FrameProvider) :void
{
_frameProvider = frameProvider;
// Notify them all and clear our list.
for each (var func :Function in _notifyOnLoad) {
func(this);
}
_notifyOnLoad = [];
}
/**
* Returns the render priority appropriate for this component at the specified action and
* orientation.
@@ -71,7 +82,7 @@ public class CharacterComponent
*/
public function getFrames (action :String, type :String) :ActionFrames
{
return _frameProvider.getFrames(this, action, type);
return isLoaded() ? _frameProvider.getFrames(this, action, type) : null;
}
/**
@@ -104,10 +115,29 @@ public class CharacterComponent
public function toString () :String
{
return StringUtil.toString(this);
return StringUtil.simpleToString(this);
}
public function isLoaded () :Boolean
{
return _frameProvider != null;
}
public function notifyOnLoad (func :Function) :void
{
if (isLoaded()) {
func(this);
} else {
_notifyOnLoad.push(func);
}
}
/** The entity from which we obtain our animation frames. */
protected var _frameProvider :FrameProvider;
/** Everyone who cares when we're loaded. */
protected var _notifyOnLoad :Array = [];
}
}
@@ -0,0 +1,280 @@
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// 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 flash.geom.Point;
import com.threerings.util.Hashable;
import com.threerings.util.Log;
import com.threerings.util.Map;
import com.threerings.util.StringUtil;
import com.threerings.util.Maps;
import com.threerings.util.maps.LRMap;
/**
* 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
{
private static var log :Log = Log.getLog(CharacterManager);
/**
* Constructs the character manager.
*/
public function CharacterManager (crepo :ComponentRepository)
{
// keep this around
_crepo = crepo;
for each (var action :ActionSequence in crepo.getActionSequences()) {
_actions.put(action.name, action);
}
}
/**
* 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 function getCharacter (desc :CharacterDescriptor,
charClass :Class = null) :CharacterSprite
{
if (charClass == null) {
charClass = _charClass;
}
try {
var sprite :CharacterSprite = new charClass();
sprite.init(desc, this);
return sprite;
} catch (e :Error) {
log.warning("Failed to instantiate character sprite.", e);
return null;
}
return getCharacter(desc, charClass);
}
public function getActionSequence (action :String) :ActionSequence
{
return _actions.get(action);
}
public function getActionFrames (descrip :CharacterDescriptor, action :String) :ActionFrames
{
if (!isLoaded(descrip)) {
return null;
}
var key :FrameKey = new FrameKey(descrip, action);
var 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);
}
return frames;
}
/**
* Returns whether all the components are loaded and ready to go.
*/
protected function isLoaded (descrip :CharacterDescriptor) :Boolean
{
var cids :Array = descrip.getComponentIds();
var ccount :int = cids.length;
for (var ii :int = 0; ii < ccount; ii++) {
var ccomp :CharacterComponent = _crepo.getComponent(cids[ii]);
if (!ccomp.isLoaded()) {
return false;
}
}
return true;
}
public function load (descrip :CharacterDescriptor, notify :Function) :void
{
_crepo.load(descrip.getComponentIds(), notify);
}
protected function createCompositeFrames (descrip :CharacterDescriptor,
action :String) :ActionFrames
{
var cids :Array = descrip.getComponentIds();
var ccount :int = cids.length;
var zations :Array = descrip.getColorizations();
var xlations :Array = descrip.getTranslations();
log.debug("Compositing action [action=" + action +
", descrip=" + descrip + "].");
// this will be used to construct any shadow layers
var shadows :Map = null; /* of String, Array<TranslatedComponent> */
// maps components by class name for masks
var ccomps :Map = Maps.newMapOf(String); /* of String, ArrayList<TranslatedComponent> */
// create colorized versions of all of the source action frames
var sources :Array = [];
for (var ii :int = 0; ii < ccount; ii++) {
var cframes :ComponentFrames = new ComponentFrames();
sources.push(cframes);
var ccomp :CharacterComponent = cframes.ccomp = _crepo.getComponent(cids[ii]);
// load up the main component images
var source :ActionFrames = ccomp.getFrames(action, null);
if (source == null) {
var errmsg :String = "Cannot composite action frames; no such " +
"action for component [action=" + action +
", desc=" + descrip + ", comp=" + ccomp + "]";
throw new Error(errmsg);
}
source = (zations == null || zations[ii] == null) ?
source : source.cloneColorized(zations[ii]);
var xlation :Point = (xlations == null) ? null : xlations[ii];
cframes.frames = (xlation == null) ?
source : source.cloneTranslated(xlation.x, xlation.y);
// store the component with its translation under its class for masking
var tcomp :TranslatedComponent = new TranslatedComponent(ccomp, xlation);
var tcomps :Array = ccomps.get(ccomp.componentClass.name);
if (tcomps == null) {
ccomps.put(ccomp.componentClass.name, tcomps = []);
}
tcomps.push(tcomp);
// if this component has a shadow, make a note of it
if (ccomp.componentClass.isShadowed()) {
if (shadows == null) {
shadows = Maps.newMapOf(String);
}
var shadlist :Array = shadows.get(ccomp.componentClass.shadow);
if (shadlist == null) {
shadows.put(ccomp.componentClass.shadow, shadlist = []);
}
shadlist.push(tcomp);
}
}
/* TODO - Implement shadows & masks - I don't actually know if/where we use these, though.
// now create any necessary shadow layers
if (shadows != null) {
for (Map.Entry<String, ArrayList<TranslatedComponent>> entry : shadows.entrySet()) {
ComponentFrames scf = compositeShadow(action, entry.getKey(), entry.getValue());
if (scf != null) {
sources.add(scf);
}
}
}
// add any necessary masks
for (ComponentFrames cframes : sources) {
ArrayList<TranslatedComponent> mcomps = ccomps.get(cframes.ccomp.componentClass.mask);
if (mcomps != null) {
cframes.frames = compositeMask(action, cframes.ccomp, cframes.frames, mcomps);
}
}
*/
// use those to create an entity that will lazily composite things
// together as they are needed
return new CompositedActionFrames(_frameCache, _actions.get(action), sources);
}
protected var _crepo :ComponentRepository;
protected var _actions :Map = Maps.newMapOf(String);
protected var _actionFrames :Map = Maps.newMapOf(FrameKey);
protected var _frameCache :LRMap = new LRMap(Maps.newMapOf(CompositedFramesKey), MAX_FRAMES);
protected var _charClass :Class;
protected static const MAX_FRAMES :int = 1000;
}
}
import com.threerings.util.Hashable;
import com.threerings.util.StringUtil;
import com.threerings.cast.CharacterDescriptor;
class FrameKey
implements Hashable
{
public var desc :CharacterDescriptor;
public var action :String;
public function FrameKey (desc :CharacterDescriptor, action :String)
{
this.desc = desc;
this.action = action;
}
public function hashCode () :int
{
return desc.hashCode() ^ StringUtil.hashCode(action);
}
public function equals (other :Object) :Boolean
{
if (other is FrameKey) {
var okey :FrameKey = FrameKey(other);
return okey.desc.equals(desc) && okey.action == action;
} else {
return false;
}
}
}
import flash.geom.Point;
import com.threerings.cast.ActionFrames;
import com.threerings.cast.CharacterComponent;
class TranslatedComponent
{
public var ccomp :CharacterComponent;
public var xlation :Point;
public function TranslatedComponent (ccomp :CharacterComponent, xlation :Point)
{
this.ccomp = ccomp;
this.xlation = xlation;
}
public function getFrames (action :String, type :String) :ActionFrames
{
var frames :ActionFrames = ccomp.getFrames(action, type);
return (frames == null || xlation == null) ?
frames : frames.cloneTranslated(xlation.x, xlation.y);
}
}
@@ -0,0 +1,289 @@
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// 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 flash.display.DisplayObject;
import flash.display.Sprite;
import flash.geom.Point;
import flash.geom.Rectangle;
import com.threerings.display.DisplayUtil;
import com.threerings.util.DirectionCodes;
import com.threerings.util.Log;
/**
* A character sprite is a sprite that animates itself while walking
* about in a scene.
*/
public class CharacterSprite extends Sprite
{
private static var log :Log = Log.getLog(CharacterSprite);
/**
* Initializes this character sprite with the specified character
* descriptor and character manager. It will obtain animation data
* from the supplied character manager.
*/
public function init (descrip :CharacterDescriptor, charmgr :CharacterManager) :void
{
// keep track of this stuff
_descrip = descrip;
_charmgr = charmgr;
// sanity check our values
sanityCheckDescrip();
// assign an arbitrary starting orientation
_orient = DirectionCodes.SOUTHWEST;
// pass the buck to derived classes
didInit();
halt();
updateActionFrames();
}
/**
* Called after this sprite has been initialized with its character
* descriptor and character manager. Derived classes can do post-init
* business here.
*/
protected function didInit () :void
{
_mainSprite = new Sprite();
addChild(_mainSprite);
}
/**
* Reconfigures this sprite to use the specified character descriptor.
*/
public function setCharacterDescriptor (descrip :CharacterDescriptor) :void
{
// 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 function setRestingAction (action :String) :void
{
_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 function getRestingAction () :String
{
return _restingAction;
}
/**
* Specifies the action to use when the sprite is following a path.
* The default is <code>WALKING</code>.
*/
public function setFollowingPathAction (action :String) :void
{
_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 function getFollowingPathAction () :String
{
return _followingPathAction;
}
/**
* Sets the action sequence used when rendering the character, from
* the set of available sequences.
*/
public function setActionSequence (action :String) :void
{
// sanity check
if (action == null) {
log.warning("Refusing to set null action sequence " + this + ".", new Error());
return;
}
// no need to noop
if (action == _action) {
return;
}
_action = action;
updateActionFrames();
}
public function setOrientation (orient :int) :void
{
_orient = orient;
if (orient < 0 || orient >= DirectionCodes.FINE_DIRECTION_COUNT) {
log.info("Refusing to set invalid orientation [sprite=" + this +
", orient=" + orient + "].", new Error());
return;
}
var oorient :int = _orient;
if (_orient != oorient) {
updateMainSprite();
}
}
public function cancelMove () :void
{
halt();
}
public function pathBeginning () :void
{
// enable walking animation
setActionSequence(getFollowingPathAction());
}
public function pathCompleted () :void
{
halt();
}
/**
* Rebuilds our action frames given our current character descriptor
* and action sequence. This is called when either of those two things
* changes.
*/
protected function updateActionFrames () :void
{
// get a reference to the action sequence so that we can obtain
// our animation frames and configure our frames per second
var actseq :ActionSequence = _charmgr.getActionSequence(_action);
if (actseq == null) {
var errmsg :String = "No such action '" + _action + "'.";
throw new Error(errmsg);
}
try {
// obtain our animation frames for this action sequence
_aframes = _charmgr.getActionFrames(_descrip, _action);
if (_aframes == null) {
// Once the frames are really ready to go, we'll do this...
_charmgr.load(_descrip, updateActionFrames);
}
updateMainSprite();
} catch (nsce :NoSuchComponentError) {
log.warning("Character sprite references non-existent " +
"component [sprite=" + this + ", err=" + nsce + "].");
} catch (e :Error) {
log.warning("Failed to obtain action frames [sprite=" + this +
", descrip=" + _descrip + ", action=" + _action + "].", e);
}
}
protected function updateMainSprite () :void
{
DisplayUtil.removeAllChildren(_mainSprite);
if (_aframes != null) {
_aframes.getFrames(_orient, function(frames :MultiFrameBitmap) :void {
_mainSprite.addChild(frames);
});
} else {
updateWithUnloadedSprite();
}
}
protected function updateWithUnloadedSprite () :void
{
// Nothing by default.
}
/**
* Makes it easier to track down problems with bogus character descriptors.
*/
protected function sanityCheckDescrip () :void
{
if (_descrip.getComponentIds() == null ||
_descrip.getComponentIds().length == 0) {
log.warning("Invalid character descriptor [sprite=" + this +
", descrip=" + _descrip + "].", new Error());
}
}
/**
* Updates the sprite animation frame to reflect the cessation of
* movement and disables any further animation.
*/
protected function halt () :void
{
var rest :String = getRestingAction();
if (rest != null) {
setActionSequence(rest);
}
}
/** The action to use when at rest. */
protected var _restingAction :String = StandardActions.STANDING;
/** The action to use when following a path. */
protected var _followingPathAction :String = StandardActions.WALKING;
/** A reference to the descriptor for the character that we're
* visualizing. */
protected var _descrip :CharacterDescriptor;
/** A reference to the character manager that created us. */
protected var _charmgr :CharacterManager;
/** The action we are currently displaying. */
protected var _action :String;
/** The animation frames for the active action sequence in each
* orientation. */
protected var _aframes :ActionFrames;
/** The offset from the upper-left of the total sprite bounds to the
* upper-left of the image within those bounds. */
protected var _ioff :Point = new Point();
/** The bounds of the current sprite image. */
protected var _ibounds :Rectangle = new Rectangle();
/** The orientation of this sprite. */
protected var _orient :int = DirectionCodes.NONE;
protected var _mainSprite :Sprite;
}
}
+2 -2
View File
@@ -109,9 +109,9 @@ public class ComponentClass
{
// because we expect there to be relatively few priority overrides, we simply search
// linearly through the list for the closest match
var ocount :int = (_overrides != null) ? _overrides.size() : 0;
var ocount :int = (_overrides != null) ? _overrides.length : 0;
for (var ii :int = 0; ii < ocount; ii++) {
var over :PriorityOverride = _overrides.get(ii);
var over :PriorityOverride = _overrides[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, component, orientation)) {
@@ -0,0 +1,160 @@
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// 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 flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.events.Event;
import flash.utils.ByteArray;
import nochump.util.zip.ZipEntry;
import nochump.util.zip.ZipError;
import nochump.util.zip.ZipFile;
import com.threerings.media.image.PngRecolorUtil;
import com.threerings.media.tile.TileDataPack;
import com.threerings.media.tile.TileSet;
import com.threerings.util.DataPack;
import com.threerings.util.Log;
import com.threerings.util.Map;
import com.threerings.util.Maps;
import com.threerings.util.MultiLoader;
import com.threerings.util.Set;
import com.threerings.util.StringUtil;
/**
* Like a normal data pack, but we don't deal with any data, and all our filenames are a direct
* translation, so we need no metadata file. We also serve as a tile ImageProvider.
*/
public class ComponentDataPack extends TileDataPack
implements FrameProvider
{
private static var log :Log = Log.getLog(ComponentDataPack);
public function ComponentDataPack (source :Object, actions :Map,
completeListener :Function = null, errorListener :Function = null)
{
super(source, completeListener, errorListener);
_actions = actions;
}
// Documentation inherited from interface
public function getFrames (component :CharacterComponent, action :String,
type :String) :ActionFrames
{
// obtain the action sequence definition for this action
var 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
var imgpath :String = action;
var dimgpath :String = ActionSequence.DEFAULT_SEQUENCE;
if (type != null) {
imgpath += "_" + type;
dimgpath += "_" + type;
}
var root :String = component.componentClass.name + "/" + component.name + "/";
var cpath :String = root + imgpath + ".png";
var dpath :String = root + dimgpath + ".png";
// look to see if this tileset is already cached (as the custom action or the default
// action)
var aset :TileSet = _setcache.get(cpath);
if (aset == null) {
aset = _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) {
if (_zip.getEntry(cpath)) {
aset = TileSet(actseq.tileset.clone());
aset.setImagePath(cpath);
} else if (_zip.getEntry(dpath)) {
aset = TileSet(TileSet(_actions.get(ActionSequence.DEFAULT_SEQUENCE)).clone());
aset.setImagePath(dpath);
_setcache.put(dpath, aset);
}
}
// if that failed too, we're hosed
if (aset == null) {
// if this is a shadow or crop image, no need to freak out as they are optional
if (!StandardActions.CROP_TYPE == type &&
!StandardActions.SHADOW_TYPE == type) {
log.warning("Unable to locate tileset for action '" + imgpath + "' " +
component + ".");
}
return null;
}
aset.setImageProvider(this);
_setcache.put(cpath, aset);
return new TileSetFrameImage(aset, actseq);
} catch (e :Error) {
log.warning("Error loading tset for action '" + imgpath + "' " + component + ".", e);
}
return null;
}
// Documentation inherited from interface
public function getFramePath (
component :CharacterComponent, action :String, type :String, existentPaths :Set) :String
{
var actionPath :String = makePath(component, action, type);
if (!existentPaths.contains(actionPath)) {
return makePath(component, ActionSequence.DEFAULT_SEQUENCE, type);
}
return actionPath;
}
protected function makePath (component :CharacterComponent, action :String,
type :String) :String
{
var imgpath :String = action;
if (type != null) {
imgpath += "_" + type;
}
var root :String = component.componentClass.name + "/" + component.name + "/";
return root + imgpath + ".png";
}
protected var _actions :Map;
protected var _setcache :Map = Maps.newMapOf(String);
}
}
@@ -0,0 +1,40 @@
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// 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 {
/** Used to associate a {@link CharacterComponent} with its {@link
* ActionFrames} for a particular action. */
public class ComponentFrames
{
public var ccomp :CharacterComponent;
public var frames :ActionFrames;
public function ComponentFrames (ccomp :CharacterComponent = null,
frames :ActionFrames = null) {
this.ccomp = ccomp;
this.frames = frames;
}
public function toString () :String {
return ccomp + ":" + frames;
}
}
}
@@ -0,0 +1,67 @@
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// 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 {
/**
* 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.
*/
function getComponent (componentId :int) :CharacterComponent;
/**
* Returns the {@link CharacterComponent} object with the given component class and name.
*/
function getComponentByName (className :String, compName :String) :CharacterComponent;
/**
* Returns the {@link ComponentClass} with the specified name or null if none exists with that
* name.
*/
function getComponentClass (className :String) :ComponentClass;
/**
* Iterates over the {@link ComponentClass} instances representing all available character
* component classes.
*/
function getComponentClasses () :Array;
/**
* Iterates over the {@link ActionSequence} instances representing every available action
* sequence.
*/
function getActionSequences () :Array;
/**
* Iterates over the component ids of all components in the specified class.
*/
function getComponentIds (compClass :ComponentClass) :Array;
/**
* Loads up all the specified components and calls notify() when done.
*/
function load (compIds :Array, notify :Function) :void;
}
}
@@ -0,0 +1,197 @@
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// 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 flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.geom.Point;
import flash.geom.Rectangle;
import com.threerings.util.ArrayUtil;
import com.threerings.util.Hashable;
import com.threerings.util.Integer;
import com.threerings.util.Map;
import com.threerings.util.Maps;
import com.threerings.util.RandomUtil;
import com.threerings.util.StringUtil;
/**
* An implementation of the {@link ActionFrames} interface that is used
* to lazily create composited character frames when they are requested.
*/
public class CompositedActionFrames
implements ActionFrames, Hashable
{
/**
* 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 function CompositedActionFrames (frameCache :Map, action :ActionSequence, sources :Array)
{
// sanity check
if (sources == null || sources.length == 0) {
var errmsg :String = "Requested to composite invalid set of source " +
"frames! [action=" + action +
", sources=" + StringUtil.toString(sources) + "].";
throw new Error(errmsg);
}
_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 function getOrientationCount () :int
{
return _orientCount;
}
// documentation inherited from interface
public function getFrames (orient :int, callback :Function) :void
{
var key :CompositedFramesKey = new CompositedFramesKey(this, orient);
var disp :MultiFrameBitmap = _frameCache.get(key);
if (disp == null) {
var listeners :Array = _pending.get(key);
if (listeners != null) {
listeners.push(callback);
} else {
listeners = [callback];
_pending.put(key, listeners);
createFrames(orient, function(disp :MultiFrameBitmap) :void {
_frameCache.put(key, disp);
for each (var func :Function in listeners) {
func(disp);
}
});
}
} else {
callback(disp);
}
}
// documentation inherited from interface
public function getXOrigin (orient :int, frameIdx :int) :int
{
return 0;
}
// documentation inherited from interface
public function getYOrigin (orient :int, frameIdx :int) :int
{
return 0;
}
// documentation inherited from interface
public function cloneColorized (zations :Array) :ActionFrames
{
throw new Error("Unsupported: CompositedActionFrames.cloneColorized()");
}
// documentation inherited from interface
public function cloneTranslated (dx :int, dy :int) :ActionFrames
{
var tsources :Array = new Array(_sources.length);
for (var ii :int = 0; ii < _sources.length; ii++) {
tsources[ii] = new ComponentFrames(
_sources[ii].ccomp, _sources[ii].frames.cloneTranslated(dx, dy));
}
return new CompositedActionFrames(_frameCache, _action, tsources);
}
/**
* Creates our underlying multi-frame image for a particular orientation.
*/
protected function createFrames (orient :int, callback :Function) :void
{
ArrayUtil.stableSort(_sources, function(cf1 :ComponentFrames, cf2 :ComponentFrames) :int {
return (cf1.ccomp.getRenderPriority(_action.name, orient) -
cf2.ccomp.getRenderPriority(_action.name, orient));
});
var idx :int = ArrayUtil.indexOf(_action.orients, orient);
var frameCt :int = _action.tileset.getTileCounts()[idx];
var width :int = _action.tileset.getWidths()[idx];
var height :int = _action.tileset.getHeights()[idx];
var frameBitmaps :Array = new Array(frameCt);
for (var ff :int = 0; ff < frameCt; ff++) {
frameBitmaps[ff] = new Bitmap(new BitmapData(width, height, true, 0x00000000));
}
var region :Rectangle = new Rectangle(0, 0, width, height);
var pt :Point = new Point(0, 0);
compositeFrames(frameBitmaps, _sources, 0, orient, region, pt, frameCt, callback);
}
protected function compositeFrames (frameBitmaps :Array, sources :Array, idx :int, orient :int,
region :Rectangle, pt :Point, frameCt :int, callback :Function) :void
{
if (idx >= sources.length) {
callback(new MultiFrameBitmap(frameBitmaps, _action.framesPerSecond));
return;
}
_sources[idx].frames.getFrames(orient, function (compBitmaps :MultiFrameBitmap) :void {
for (var ii :int = 0; ii < frameCt; ii++) {
frameBitmaps[ii].bitmapData.draw(compBitmaps.getFrame(ii).bitmapData);
}
compositeFrames(frameBitmaps, sources, idx+1, orient, region, pt, frameCt, callback);
});
}
public function equals (other :Object) :Boolean
{
return this === other;
}
public function hashCode () :int
{
return _randHashCode;
}
/** Used to cache our composited action frame images. */
protected var _frameCache :Map;
protected static var _pending :Map = Maps.newMapOf(CompositedFramesKey);
/** The action for which we're compositing frames. */
protected var _action :ActionSequence;
/** The number of orientations. */
protected var _orientCount :int;
/** Our source components and action frames. */
protected var _sources :Array;
/** We keep a hash code to use to identify us nearly-uniquely from the CompositedFramesKey. */
protected var _randHashCode :int = int(Math.random() * Integer.MAX_VALUE);
}
}
@@ -0,0 +1,56 @@
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// 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.util.Hashable;
/** Used to cache composited frames for a particular action and
* orientation. */
public class CompositedFramesKey
implements Hashable
{
public function CompositedFramesKey (owner :CompositedActionFrames, orient :int) {
_orient = orient;
_owner = owner;
}
public function setOrient (orient :int) :void {
_orient = orient;
}
public function getOwner () :CompositedActionFrames {
return _owner;
}
public function equals (other :Object) :Boolean {
var okey :CompositedFramesKey = CompositedFramesKey(other);
return ((getOwner() == okey.getOwner()) &&
(_orient == okey._orient));
}
public function hashCode () :int {
return _owner.hashCode() ^ _orient;
}
protected var _orient :int;
protected var _owner :CompositedActionFrames;
}
}
@@ -0,0 +1,309 @@
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// 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 flash.events.Event;
import com.threerings.cast.ComponentDataPack;
import com.threerings.media.tile.TileDataPack;
import com.threerings.util.ArrayUtil;
import com.threerings.util.DataPack;
import com.threerings.util.Log;
import com.threerings.util.Map;
import com.threerings.util.Maps;
import com.threerings.util.StringUtil;
public class DataPackComponentRepository
implements ComponentRepository
{
private static var log :Log = Log.getLog(DataPackComponentRepository);
public function DataPackComponentRepository (compUrl :String)
{
_rootUrl = compUrl;
loadComponentMeta(compUrl + "metadata.jar");
}
protected function loadComponentMeta (compMetaUrl :String) :void
{
_metaPack = new TileDataPack(compMetaUrl, metaLoadingComplete);
}
/**
* Handle the successful completion of datapack loading.
*
* @private
*/
protected function metaLoadingComplete (event :Event) :void
{
var actionXml :XML = _metaPack.getFileAsXML("actions.xml");
var classesXml :XML = _metaPack.getFileAsXML("classes.xml");
for each (var aXml :XML in actionXml.action) {
var action :ActionSequence = ActionSequence.fromXml(aXml);
_actions.put(action.name, action);
}
for each (var cXml :XML in classesXml.elements("class")) {
var compClass :ComponentClass = ComponentClass.fromXml(cXml);
_classes.put(compClass.name, compClass);
}
parseComponentMap(_metaPack.getFileAsString("compmap.txt"));
// Notify them all and clear our list.
for each (var func :Function in _notifyOnLoad) {
func();
}
_notifyOnLoad = [];
}
protected function parseComponentMap (map :String) :void
{
var lines :Array = map.split("\n");
var tileCount :int = int(lines[0]);
for (var ii :int = 1; ii < lines.length; ii++) {
var line :String = lines[ii];
var toks :Array = line.split(" := ");
if (toks.length >= 3) {
var id :int = int(toks[2]);
var cclass :String = String(toks[0]);
var cname :String = String(toks[1]);
createComponent(id, cclass, cname);
}
}
}
/**
* Creates a component and inserts it into the component table.
*/
protected function createComponent (componentId :int, cclass :String, cname :String) :void
{
// look up the component class information
var clazz :ComponentClass = _classes.get(cclass);
if (clazz == null) {
log.warning("Non-existent component class",
"class", cclass, "name", cname, "id", componentId);
return;
}
// create the component - we'll set the frame provider as soon as we can.
var component :CharacterComponent = new CharacterComponent(componentId, cname, clazz, null);
// stick it into the appropriate tables
_components.put(componentId, component);
// we have a hash of lists for mapping components by class/name
var comps :Array = _classComps.get(cclass);
if (comps == null) {
comps = [];
_classComps.put(cclass, comps);
}
if (!ArrayUtil.contains(comps, component)) {
comps.push(component);
} else {
log.info("Requested to register the same component twice?", "comp", component);
}
}
public function notifyOnLoad (func :Function) :void
{
_notifyOnLoad.push(func);
}
/**
* Returns the {@link CharacterComponent} object for the given component identifier.
*/
public function getComponent (componentId :int) :CharacterComponent
{
var component :CharacterComponent = _components.get(componentId);
if (component == null) {
throw new NoSuchComponentError(componentId);
}
return component;
}
/**
* Returns the {@link CharacterComponent} object with the given component class and name.
*/
public function getComponentByName (className :String, compName :String) :CharacterComponent
{
// look up the list for that class
var comps :Array = _classComps.get(className);
if (comps != null) {
for each (var comp :CharacterComponent in comps) {
if (comp.name == compName) {
return comp;
}
}
}
throw new NoSuchComponentError(className + "/" + compName);
}
public function load (compIds :Array, notify :Function) :void
{
var complete :Boolean = true;
var toLoad :Array = [];
for each (var cid :int in compIds) {
var comp :CharacterComponent = getComponent(cid);
if (!comp.isLoaded()) {
toLoad.push(comp);
}
}
var waiting :int = toLoad.length;
if (waiting == 0) {
notify();
} else {
for each (var load :CharacterComponent in toLoad) {
load.notifyOnLoad(function() :void {
waiting--;
if (waiting == 0) {
notify();
}
});
loadComponent(load);
}
}
}
protected function loadComponent (comp :CharacterComponent) :void
{
var packName :String = comp.componentClass.name + "/" + comp.name;
var pack :ComponentDataPack = _packs.get(packName);
if (pack == null || !pack.isComplete()) {
loadPack(packName, comp);
} else {
comp.setFrameProvider(pack);
}
}
protected function loadPack (packName :String, comp :CharacterComponent) :void
{
var pack :ComponentDataPack = _packs.get(packName);
if (pack != null && pack.isComplete()) {
// Already loaded
return;
}
var completeListener :Function = function () :void {
comp.setFrameProvider(_packs.get(packName));
}
var listeners :Array = _packListeners.get(packName);
if (listeners == null) {
listeners = [];
_packListeners.put(packName, listeners);
var packCompleteListener :Function = function () :void {
for each (var listener :Function in listeners) {
listener();
}
_packListeners.remove(packName);
}
pack = new ComponentDataPack(getPackUrl(packName), _actions, packCompleteListener);
_packs.put(packName, pack);
}
listeners.push(completeListener);
}
protected function getPackUrl (packName :String) :String
{
return _rootUrl + packName + "/components.jar";
}
/**
* Returns the {@link ComponentClass} with the specified name or null if none exists with that
* name.
*/
public function getComponentClass (className :String) :ComponentClass
{
return _classes.get(className);
}
/**
* Iterates over the {@link ComponentClass} instances representing all available character
* component classes.
*/
public function getComponentClasses () :Array
{
return _classes.values();
}
/**
* Iterates over the {@link ActionSequence} instances representing every available action
* sequence.
*/
public function getActionSequences () :Array
{
return _actions.values();
}
/**
* Iterates over the component ids of all components in the specified class.
*/
public function getComponentIds (compClass :ComponentClass) :Array
{
var result :Array = [];
_classes.forEach(function (key :int, value :CharacterComponent) :Boolean {
if (value.componentClass.equals(compClass)) {
result.push(key);
}
return false;
});
return result;
}
/** Everyone who cares when we're loaded. */
protected var _notifyOnLoad :Array = [];
/** Contains the component meta-data for all components. */
protected var _metaPack : DataPack;
/** The URL for our components. */
protected var _rootUrl :String;
/** Map of pack name to listeners waiting for that pack to resolve. */
protected var _packListeners :Map = Maps.newMapOf(String);
/** The map of packs we've already loaded up and resolved. */
protected var _packs :Map = Maps.newMapOf(String);
/** All our component classes by name. */
protected var _classes :Map = Maps.newMapOf(String);
/** All our action sequences by name. */
protected var _actions :Map = Maps.newMapOf(String);
/** The map of componentId to CharacterComponent */
protected var _components :Map = Maps.newMapOf(int);
/** A table of component lists indexed on classname. */
protected var _classComps :Map = Maps.newMapOf(String);
}
}
@@ -0,0 +1,85 @@
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// 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 flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import com.threerings.util.StringUtil;
public class MultiFrameBitmap extends Sprite
{
public function MultiFrameBitmap (frames :Array, fps :Number)
{
_frames = frames;
_bitmap = new Bitmap();
_frameRate = fps / 1000.0;
addChild(_bitmap);
setFrame(0);
addEventListener(Event.ENTER_FRAME, enterFrame);
}
public function enterFrame (event :Event) :void
{
if (_start == 0) {
_start = (new Date().time);
}
var elapsedTime :int = (new Date().time - _start);
var frameIndex :int = Math.floor(elapsedTime * _frameRate);
var totalFrames :int = _frames.length;
if (frameIndex >= totalFrames) {
frameIndex = frameIndex % totalFrames;
}
setFrame(frameIndex);
}
protected function setFrame (index :int) :void
{
if (_curFrameIndex != index) {
_bitmap.bitmapData = Bitmap(_frames[index]).bitmapData;
_curFrameIndex = index;
}
}
public function getFrame (index :int) :Bitmap
{
return _frames[index];
}
public function getFrameCount () :int
{
return _frames.length;
}
protected var _frames :Array;
protected var _bitmap :Bitmap;
protected var _start :int;
protected var _frameRate :Number;
protected var _curFrameIndex :int = -1;
}
}
@@ -0,0 +1,34 @@
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// 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 NoSuchComponentError extends Error
{
public function NoSuchComponentError (comp :*)
{
super((comp is int) ? ("No component with id '" + int(comp) + "'") :
("No component named'" + String(comp) + "'"));
}
}
}
@@ -0,0 +1,41 @@
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// 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 class StandardActions
{
/** The name of the standard standing action. */
public static const STANDING :String = "standing";
/** The name of the standard walking action. */
public static const WALKING :String = "walking";
/** A special action sub-type for shadow imagery. */
public static const SHADOW_TYPE :String = "shadow";
/** A special action sub-type for crop imagery. */
public static const CROP_TYPE :String = "crop";
}
}
@@ -0,0 +1,129 @@
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// 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 flash.display.Bitmap;
import flash.geom.Rectangle;
import com.threerings.media.tile.Tile;
import com.threerings.media.tile.TileSet;
import com.threerings.util.Map;
import com.threerings.util.Maps;
public class TileSetFrameImage
implements ActionFrames
{
public function TileSetFrameImage (set :TileSet, actseq :ActionSequence,
dx :int = 0, dy :int = 0)
{
_set = set;
_actseq = actseq;
_dx = dx;
_dy = dy;
// 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 (var ii :int = 0; ii < _ocount; ii++) {
_orients.put(actseq.orients[ii], ii);
}
}
public function getFrames (orient :int, callback :Function) :void
{
var frames :Array = new Array(_fcount);
getFramesFromTiles(frames, 0, orient, callback);
}
protected function getFramesFromTiles (frames :Array, idx :int, orient :int,
callback :Function) :void
{
if (idx >= frames.length) {
callback(new MultiFrameBitmap(frames, _actseq.framesPerSecond));
} else {
var tile :Tile = getTile(orient, idx);
tile.notifyOnLoad(function() :void {
frames[idx] = Bitmap(tile.getImage());
getFramesFromTiles(frames, idx + 1, orient, callback);
});
}
}
// documentation inherited from interface
public function getOrientationCount () :int
{
return _ocount;
}
protected function getTileIndex (orient :int, index :int) :int
{
return _orients.get(orient) * _fcount + index;
}
protected function getTile (orient :int, index :int) :Tile
{
return _set.getTile(getTileIndex(orient, index));
}
// documentation inherited from interface
public function getXOrigin (orient :int, index :int) :int
{
return _actseq.origin.x;
}
// documentation inherited from interface
public function getYOrigin (orient :int, index :int) :int
{
return _actseq.origin.y;
}
// documentation inherited from interface
public function cloneColorized (zations :Array) :ActionFrames
{
return new TileSetFrameImage(_set.cloneWithZations(zations), _actseq);
}
// documentation inherited from interface
public function cloneTranslated (dx :int, dy :int) :ActionFrames
{
return new TileSetFrameImage(_set, _actseq, dx, dy);
}
/** The tileset from which we obtain our frame images. */
protected var _set :TileSet;
/** The action sequence for which we're providing frame images. */
protected var _actseq :ActionSequence;
/** A translation to apply to the images. */
protected var _dx :int;
protected var _dy :int;
/** Frame and orientation counts. */
protected var _fcount :int;
protected var _ocount :int;
/** A mapping from orientation code to animation sequence index. */
protected var _orients :Map = Maps.newMapOf(int);
}
}
+1 -1
View File
@@ -74,7 +74,7 @@ public class DirectionUtil extends DirectionCodes
public static function fromShortString (dirstr :String) :int
{
for (var ii :int = 0; ii < FINE_DIRECTION_COUNT; ii++) {
if (SHORT_DIR_STRINGS[ii].equals(dirstr)) {
if (SHORT_DIR_STRINGS[ii] == dirstr) {
return ii;
}
}