Split Nenya into proper Maven submodules (same treatment Narya got).
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
function getOrientationCount () :int;
|
||||
|
||||
/**
|
||||
* Returns the multi-frame image that comprises the frames for the
|
||||
* specified orientation.
|
||||
*/
|
||||
function getFrames (orient :int, callback :Function) :void;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
function getXOrigin (orient :int, frameIdx :int) :int;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
function getYOrigin (orient :int, frameIdx :int) :int;
|
||||
|
||||
/**
|
||||
* Creates a clone of these action frames which will have the supplied
|
||||
* colorizations applied to the frame images.
|
||||
*/
|
||||
function cloneColorized (zations :Array) :ActionFrames;
|
||||
|
||||
/**
|
||||
* Creates a clone of these action frames which will have the supplied
|
||||
* translation applied to the frame images.
|
||||
*/
|
||||
function cloneTranslated (dx :int, dy :int) :ActionFrames;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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) + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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;
|
||||
import com.threerings.util.Equalable;
|
||||
import com.threerings.util.Set;
|
||||
import com.threerings.util.StringUtil;
|
||||
|
||||
/**
|
||||
* 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 Equalable, Hashable
|
||||
{
|
||||
/** The unique component identifier. */
|
||||
public var componentId :int;
|
||||
|
||||
/** The component's name. */
|
||||
public var name :String;
|
||||
|
||||
/** The class of components to which this one belongs. */
|
||||
public var componentClass :ComponentClass;
|
||||
|
||||
/**
|
||||
* Constructs a character component with the specified id of the specified class.
|
||||
*/
|
||||
public function CharacterComponent (
|
||||
componentId :int, name :String, compClass :ComponentClass, fprov :FrameProvider)
|
||||
{
|
||||
this.componentId = componentId;
|
||||
this.name = name;
|
||||
this.componentClass = compClass;
|
||||
_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.
|
||||
*/
|
||||
public function getRenderPriority (action :String, orientation :int) :int
|
||||
{
|
||||
return componentClass.getRenderPriority(action, name, orientation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 function getFrames (action :String, type :String) :ActionFrames
|
||||
{
|
||||
return isLoaded() ? _frameProvider.getFrames(this, action, type) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to 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.
|
||||
*
|
||||
* @param existentPaths the set of all paths for which there are valid frames.
|
||||
*/
|
||||
public function getFramePath (action :String, type :String, existentPaths :Set) :String
|
||||
{
|
||||
return _frameProvider.getFramePath(this, action, type, existentPaths);
|
||||
}
|
||||
|
||||
public function equals (other :Object) :Boolean
|
||||
{
|
||||
if (other is CharacterComponent) {
|
||||
return componentId == (CharacterComponent(other)).componentId;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function hashCode () :int
|
||||
{
|
||||
return componentId;
|
||||
}
|
||||
|
||||
public function toString () :String
|
||||
{
|
||||
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,160 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.media.image.Colorization;
|
||||
import com.threerings.util.Arrays;
|
||||
import com.threerings.util.Equalable;
|
||||
import com.threerings.util.Hashable;
|
||||
import com.threerings.util.StringUtil;
|
||||
|
||||
/**
|
||||
* The character descriptor object details the components that are
|
||||
* pieced together to create a single character image.
|
||||
*/
|
||||
public class CharacterDescriptor
|
||||
implements Equalable, Hashable
|
||||
{
|
||||
/**
|
||||
* 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 function CharacterDescriptor (components :Array, zations :Array)
|
||||
{
|
||||
_components = components;
|
||||
_zations = zations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the component identifiers comprising the
|
||||
* character described by this descriptor.
|
||||
*/
|
||||
public function getComponentIds () :Array
|
||||
{
|
||||
return _components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of colorization arrays to be applied to the
|
||||
* components when compositing action images (one array per
|
||||
* component).
|
||||
*/
|
||||
public function getColorizations () :Array
|
||||
{
|
||||
return _zations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the colorizations to be used by this character descriptor.
|
||||
*/
|
||||
public function setColorizations (zations :Array) :void
|
||||
{
|
||||
_zations = zations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of translations to be applied to the components
|
||||
* when compositing action images.
|
||||
*/
|
||||
public function getTranslations () :Array
|
||||
{
|
||||
return _xlations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the translations to be used by this character descriptor.
|
||||
*/
|
||||
public function setTranslations (xlations :Array) :void
|
||||
{
|
||||
_xlations = xlations;
|
||||
}
|
||||
|
||||
public function hashCode () :int
|
||||
{
|
||||
var code :int = 0;
|
||||
var clength :int = _components.length;
|
||||
for (var ii :int = 0; ii < clength; ii++) {
|
||||
code ^= _components[ii];
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
public function equals (other :Object) :Boolean
|
||||
{
|
||||
if (!(other is CharacterDescriptor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// both the component ids and the colorizations must be equal
|
||||
var odesc :CharacterDescriptor = CharacterDescriptor(other);
|
||||
if (!Arrays.equals(_components, odesc._components)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var zations :Array = 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
|
||||
var zlength :int= zations.length;
|
||||
if (zlength != _zations.length) {
|
||||
return false;
|
||||
}
|
||||
for (var ii :int= 0; ii < zlength; ii++) {
|
||||
if (!Arrays.equals(_zations[ii], zations[ii])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return Arrays.equals(_xlations, odesc._xlations);
|
||||
}
|
||||
|
||||
public function toString () :String
|
||||
{
|
||||
return "[cids=" + StringUtil.toString(_components) +
|
||||
", colors=" + StringUtil.toString(_zations) + "]";
|
||||
}
|
||||
|
||||
/** The component identifiers comprising the character. */
|
||||
protected var _components :Array;
|
||||
|
||||
/** The colorizations to apply when compositing this character. */
|
||||
protected var _zations :Array;
|
||||
|
||||
/** The translations to apply when compositing this character. */
|
||||
protected var _xlations :Array;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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 getComponent (compId :int) :CharacterComponent
|
||||
{
|
||||
return _crepo.getComponent(compId);
|
||||
}
|
||||
|
||||
public function getComponentByName (cclass :String, cname :String) :CharacterComponent
|
||||
{
|
||||
return _crepo.getComponentByName(cclass, cname);
|
||||
}
|
||||
|
||||
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,317 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.media.Tickable;
|
||||
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
|
||||
implements Tickable
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
public function tick (tickStamp :int) :void
|
||||
{
|
||||
if (_framesBitmap != null) {
|
||||
_framesBitmap.tick(tickStamp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
var oorient :int = _orient;
|
||||
_orient = orient;
|
||||
if (orient < 0 || orient >= DirectionCodes.FINE_DIRECTION_COUNT) {
|
||||
log.info("Refusing to set invalid orientation [sprite=" + this +
|
||||
", orient=" + orient + "].", new Error());
|
||||
return;
|
||||
}
|
||||
|
||||
if (_orient != oorient) {
|
||||
updateMainSprite();
|
||||
}
|
||||
}
|
||||
|
||||
public function getOrientation () :int
|
||||
{
|
||||
return _orient;
|
||||
}
|
||||
|
||||
public function cancelMove () :void
|
||||
{
|
||||
halt();
|
||||
}
|
||||
|
||||
public function pathBeginning () :void
|
||||
{
|
||||
// enable walking animation
|
||||
setActionSequence(getFollowingPathAction());
|
||||
}
|
||||
|
||||
public function pathCompleted (timestamp :int) :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);
|
||||
}
|
||||
|
||||
x = -actseq.origin.x;
|
||||
y = -actseq.origin.y;
|
||||
}
|
||||
|
||||
protected function updateMainSprite () :void
|
||||
{
|
||||
DisplayUtil.removeAllChildren(_mainSprite);
|
||||
if (_aframes != null) {
|
||||
updateWithUnloadedSprite();
|
||||
var curFrames :ActionFrames = _aframes;
|
||||
_aframes.getFrames(_orient, function(frames :MultiFrameBitmap) :void {
|
||||
// Ensure our action hasn't been swapped out on us since we started loading.
|
||||
if (_aframes == curFrames) {
|
||||
_framesBitmap = frames;
|
||||
DisplayUtil.removeAllChildren(_mainSprite);
|
||||
_mainSprite.addChild(frames);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
_framesBitmap = null;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public function hitTest (stageX :int, stageY :int) :Boolean
|
||||
{
|
||||
return _framesBitmap == null ? false : _framesBitmap.hitTest(stageX, stageY);
|
||||
}
|
||||
|
||||
/** 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 currently active set of bitmaps for this character. */
|
||||
protected var _framesBitmap :MultiFrameBitmap;
|
||||
|
||||
/** The orientation of this sprite. */
|
||||
protected var _orient :int = DirectionCodes.NONE;
|
||||
|
||||
protected var _mainSprite :Sprite;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.Arrays;
|
||||
import com.threerings.util.Hashable;
|
||||
import com.threerings.util.Equalable;
|
||||
import com.threerings.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 Equalable, Hashable
|
||||
{
|
||||
/** The component class name. */
|
||||
public var name :String;
|
||||
|
||||
/** The default render priority. */
|
||||
public var renderPriority :int;
|
||||
|
||||
/** The color classes to use when recoloring components of this class. May
|
||||
* be null if a system does not use recolorable components. */
|
||||
public var colors :Array;
|
||||
|
||||
/** The class name of the layer from which this component class obtains a
|
||||
* mask to limit rendering to certain areas. */
|
||||
public var mask :String;
|
||||
|
||||
/** Indicates the class name of the shadow layer to which this component
|
||||
* class contributes a shadow. */
|
||||
public var shadow :String;
|
||||
|
||||
/** 1.0 for a normal component, the alpha value of the pre-composited
|
||||
* shadow for the special "shadow" component class. */
|
||||
public var shadowAlpha :Number = 1.0;
|
||||
|
||||
/** Whether or not components of this class will have translations applied. */
|
||||
public var translate :Boolean;
|
||||
|
||||
/**
|
||||
* Creates an uninitialized instance suitable for unserialization or
|
||||
* population during XML parsing.
|
||||
*/
|
||||
public function ComponentClass ()
|
||||
{
|
||||
}
|
||||
|
||||
public static function fromXml (xml :XML) :ComponentClass
|
||||
{
|
||||
var compClass :ComponentClass = new ComponentClass();
|
||||
compClass.name = xml.@name;
|
||||
compClass.renderPriority = xml.@renderPriority;
|
||||
compClass.colors = toStrArray(xml.@colors);
|
||||
compClass.mask = xml.@mask;
|
||||
compClass.shadow = xml.@shadow;
|
||||
compClass.shadowAlpha = xml.@shadowAlpha;
|
||||
compClass.translate = xml.@translate;
|
||||
for each (var overrideXml :XML in xml.override) {
|
||||
compClass.addPriorityOverride(PriorityOverride.fromXml(overrideXml));
|
||||
}
|
||||
return compClass;
|
||||
}
|
||||
|
||||
protected static function toStrArray (str :String) :Array
|
||||
{
|
||||
if (str == null || str.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return str.split(",").map(function(element :String, index :int, arr :Array) :String {
|
||||
return StringUtil.trim(element);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the render priority appropriate for the specified action, orientation and
|
||||
* component.
|
||||
*/
|
||||
public function getRenderPriority (action :String, component :String, orientation :int) :int
|
||||
{
|
||||
// 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.length : 0;
|
||||
for (var ii :int = 0; ii < ocount; 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)) {
|
||||
return over.renderPriority;
|
||||
}
|
||||
}
|
||||
|
||||
return renderPriority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the supplied render priority override record to this component class.
|
||||
*/
|
||||
public function addPriorityOverride (override :PriorityOverride) :void
|
||||
{
|
||||
if (_overrides == null) {
|
||||
_overrides = [];
|
||||
}
|
||||
Arrays.sortedInsert(_overrides, 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 function isShadowed () :Boolean
|
||||
{
|
||||
return (shadow != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this component class is a shadow layer rather than a normal component class.
|
||||
*/
|
||||
public function isShadow () :Boolean
|
||||
{
|
||||
return (shadowAlpha != 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes with the same name are the same.
|
||||
*/
|
||||
public function equals (other :Object) :Boolean
|
||||
{
|
||||
if (other is ComponentClass) {
|
||||
return name == (ComponentClass(other)).name;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashcode is based on component class name.
|
||||
*/
|
||||
public function hashCode () :int
|
||||
{
|
||||
return StringUtil.hashCode(name);
|
||||
}
|
||||
|
||||
public function toString () :String
|
||||
{
|
||||
var buf :String = "[";
|
||||
buf = buf.concat("name=", name);
|
||||
buf = buf.concat(", pri=", renderPriority);
|
||||
if (colors != null) {
|
||||
buf = buf.concat(", colors=", StringUtil.toString(colors));
|
||||
}
|
||||
if (mask != null) {
|
||||
buf = buf.concat(", mask=", mask);
|
||||
}
|
||||
if (shadowAlpha != 1.0) {
|
||||
buf = buf.concat(", shadow=", shadowAlpha);
|
||||
} else if (shadow != null) {
|
||||
buf = buf.concat(", shadow=", shadow);
|
||||
}
|
||||
return buf.concat("]");
|
||||
}
|
||||
|
||||
/** A list of render priority overrides. */
|
||||
protected var _overrides :Array = [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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)) {
|
||||
actseq = _actions.get(ActionSequence.DEFAULT_SEQUENCE);
|
||||
aset = TileSet(actseq.tileset.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,42 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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,69 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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,199 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.Arrays;
|
||||
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.clone());
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
callback(disp.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
Arrays.stableSort(_sources, function(cf1 :ComponentFrames, cf2 :ComponentFrames) :int {
|
||||
return (cf1.ccomp.getRenderPriority(_action.name, orient) -
|
||||
cf2.ccomp.getRenderPriority(_action.name, orient));
|
||||
});
|
||||
|
||||
var idx :int = Arrays.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,58 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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,311 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.Arrays;
|
||||
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 (!Arrays.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,50 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.Set;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
function getFrames (
|
||||
component :CharacterComponent, action :String, type :String) :ActionFrames;
|
||||
|
||||
/**
|
||||
* Returns the file path of the animation frames (in the eight sprite directions) for the
|
||||
* specified action of the specified component. May return a path to the default action or
|
||||
* null if the specified action does not exist for the specified component.
|
||||
*
|
||||
* @param existentPaths the set of all paths for which there are valid frames.
|
||||
*/
|
||||
function getFramePath (
|
||||
component :CharacterComponent, action :String, type :String, existentPaths :Set):String;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.geom.Point;
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
import com.threerings.media.Tickable;
|
||||
import com.threerings.util.StringUtil;
|
||||
|
||||
public class MultiFrameBitmap extends Sprite
|
||||
implements Tickable
|
||||
{
|
||||
public function MultiFrameBitmap (frames :Array, fps :Number)
|
||||
{
|
||||
_frames = frames;
|
||||
_bitmap = new Bitmap();
|
||||
_fps = fps;
|
||||
_frameRate = fps / 1000.0;
|
||||
addChild(_bitmap);
|
||||
setFrame(0);
|
||||
}
|
||||
|
||||
public function tick (tickStamp :int) :void
|
||||
{
|
||||
if (_start == 0) {
|
||||
_start = tickStamp;
|
||||
}
|
||||
|
||||
var elapsedTime :int = (tickStamp - _start);
|
||||
var frameIndex :int = Math.floor(elapsedTime * _frameRate) % _frames.length;
|
||||
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;
|
||||
}
|
||||
|
||||
public function hitTest (stageX :int, stageY :int) :Boolean
|
||||
{
|
||||
if (_curFrameIndex == -1) {
|
||||
return false;
|
||||
} else {
|
||||
if (!_bitmap.hitTestPoint(stageX, stageY, true)) {
|
||||
// Doesn't even hit the bounds...
|
||||
return false;
|
||||
}
|
||||
// Check the actual pixels...
|
||||
var pt :Point = _bitmap.globalToLocal(new Point(stageX, stageY));
|
||||
return _bitmap.bitmapData.hitTest(new Point(0, 0), 0, pt);
|
||||
}
|
||||
}
|
||||
|
||||
public function clone () :MultiFrameBitmap
|
||||
{
|
||||
return new MultiFrameBitmap(_frames, _fps);
|
||||
}
|
||||
|
||||
protected var _frames :Array;
|
||||
|
||||
protected var _bitmap :Bitmap;
|
||||
|
||||
protected var _start :int;
|
||||
|
||||
protected var _frameRate :Number;
|
||||
|
||||
protected var _fps :Number;
|
||||
|
||||
protected var _curFrameIndex :int = -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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,138 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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.ClassUtil;
|
||||
import com.threerings.util.Comparable;
|
||||
import com.threerings.util.DirectionUtil;
|
||||
import com.threerings.util.Hashable;
|
||||
import com.threerings.util.Equalable;
|
||||
import com.threerings.util.Set;
|
||||
import com.threerings.util.Sets;
|
||||
import com.threerings.util.StringUtil;
|
||||
import com.threerings.util.XmlUtil;
|
||||
|
||||
/** Used to effect custom render orders for particular actions, orientations, etc. */
|
||||
public class PriorityOverride
|
||||
implements Comparable, Hashable, Equalable
|
||||
{
|
||||
/** The overridden render priority value. */
|
||||
public var renderPriority :int;
|
||||
|
||||
/** The action, if any, for which this override is appropriate. */
|
||||
public var action :String = null;
|
||||
|
||||
/** The component, if any, for which this override is appropriate. */
|
||||
public var component :String = null;
|
||||
|
||||
/** The orientations, if any, for which this override is appropriate. */
|
||||
public var orients :Set;
|
||||
|
||||
public static function fromXml (xml :XML) :PriorityOverride
|
||||
{
|
||||
var override :PriorityOverride = new PriorityOverride;
|
||||
override.renderPriority = XmlUtil.getIntAttr(xml, "renderPriority");
|
||||
override.action = XmlUtil.getStringAttr(xml, "action", null);
|
||||
override.component = XmlUtil.getStringAttr(xml, "component", null);
|
||||
|
||||
override.orients = Sets.newSetOf(int);
|
||||
for each (var orient :int in toOrientArray(
|
||||
XmlUtil.getStringAttr(xml, "orients", null))) {
|
||||
override.orients.add(orient);
|
||||
}
|
||||
return override;
|
||||
}
|
||||
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this priority override matches the specified
|
||||
* action, orientation ant component combination.
|
||||
*/
|
||||
public function matches (action :String, component :String, orient :int) :Boolean
|
||||
{
|
||||
return (((this.orients == null) || orients.contains(orient)) &&
|
||||
((this.component == null) || this.component == component) &&
|
||||
((this.action == null) || this.action == action));
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public function compareTo (po :Object) :int
|
||||
{
|
||||
// overrides with both an action and an orientation should come first in the list
|
||||
var pri :int = priority();
|
||||
var opri :int = PriorityOverride(po).priority();
|
||||
if (pri == opri) {
|
||||
return hashCode() - PriorityOverride(po).hashCode();
|
||||
} else {
|
||||
return pri - opri;
|
||||
}
|
||||
}
|
||||
|
||||
public function hashCode () :int
|
||||
{
|
||||
return StringUtil.hashCode(action) ^ StringUtil.hashCode(component) ^ renderPriority;
|
||||
}
|
||||
|
||||
public function equals (other :Object) :Boolean
|
||||
{
|
||||
if (!(other is PriorityOverride)) {
|
||||
return false;
|
||||
}
|
||||
var otherOverride :PriorityOverride = PriorityOverride(other);
|
||||
|
||||
return otherOverride.renderPriority == renderPriority && otherOverride.action == action &&
|
||||
otherOverride.component == component;
|
||||
}
|
||||
|
||||
protected function priority () :int
|
||||
{
|
||||
var priority :int = 0;
|
||||
if (component != null) {
|
||||
priority += 3; // Extra priority to things that are for specific components
|
||||
}
|
||||
if (action != null) {
|
||||
priority++;
|
||||
}
|
||||
if (orients != null) {
|
||||
priority++;
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
|
||||
public function toString () :String
|
||||
{
|
||||
return "[pri=" + renderPriority + ", action=" + action + ", component=" + component +
|
||||
", orients=" + orients + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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,131 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user