diff --git a/src/as/com/threerings/media/image/Colorization.as b/src/as/com/threerings/media/image/Colorization.as
new file mode 100644
index 00000000..e604bc42
--- /dev/null
+++ b/src/as/com/threerings/media/image/Colorization.as
@@ -0,0 +1,185 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.image {
+
+import com.threerings.display.ColorUtil;
+import com.threerings.util.Hashable;
+import com.threerings.util.Short;
+import com.threerings.util.StringUtil;
+
+public class Colorization
+ implements Hashable
+{
+ /** Every colorization must have a unique id that can be used to
+ * compare a particular colorization record with another. */
+ public var colorizationId :int;
+
+ /** The root color for the colorization. */
+ public var rootColor :uint;
+
+ /** The range around the root color that will be colorized (in
+ * delta-hue, delta-saturation, delta-value. */
+ public var range :Array;
+
+ /** The adjustments to make to hue, saturation and value. */
+ public var offsets :Array;
+
+ /**
+ * Constructs a colorization record with the specified identifier.
+ */
+ public function Colorization (colorizationId :int, rootColor :uint,
+ range :Array, offsets :Array)
+ {
+ this.colorizationId = colorizationId;
+ this.rootColor = rootColor;
+ this.range = range;
+ this.offsets = offsets;
+
+ // compute our HSV and fixed HSV
+ _hsv = ColorUtil.RGBtoHSB(ColorUtil.getRed(rootColor), ColorUtil.getGreen(rootColor),
+ ColorUtil.getBlue(rootColor));
+ _fhsv = toFixedHSV(_hsv, null);
+ }
+
+ /**
+ * Returns the root color adjusted by the colorization.
+ */
+ public function getColorizedRoot () :uint
+ {
+ return recolorColor(_hsv);
+ }
+
+ /**
+ * Adjusts the supplied color by the offests in this colorization,
+ * taking the appropriate measures for hue (wrapping it around) and
+ * saturation and value (clipping).
+ *
+ * @return the RGB value of the recolored color.
+ */
+ public function recolorColor (hsv :Array) :uint
+ {
+ // for hue, we wrap around
+ var hue :Number = hsv[0] + offsets[0];
+ if (hue > 1.0) {
+ hue -= 1.0;
+ }
+
+ // otherwise we clip
+ var sat :Number = Math.min(Math.max(hsv[1] + offsets[1], 0), 1);
+ var val :Number = Math.min(Math.max(hsv[2] + offsets[2], 0), 1);
+
+ // convert back to RGB space
+ return ColorUtil.HSBtoRGB(hue, sat, val);
+ }
+
+ /**
+ * Returns true if this colorization matches the supplied color, false
+ * otherwise.
+ *
+ * @param hsv the HSV values for the color in question.
+ * @param fhsv the HSV values converted to fixed point via {@link
+ * #toFixedHSV} for the color in question.
+ */
+ public function matches (hsv :Array, fhsv :Array) :Boolean
+ {
+ // check to see that this color is sufficiently "close" to the
+ // root color based on the supplied distance parameters
+ if (distance(fhsv[0], _fhsv[0], Short.MAX_VALUE) >=
+ range[0] * Short.MAX_VALUE) {
+ return false;
+ }
+
+ // saturation and value don't wrap around like hue
+ if (Math.abs(_hsv[1] - hsv[1]) >= range[1] ||
+ Math.abs(_hsv[2] - hsv[2]) >= range[2]) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public function hashCode () :int
+ {
+ return colorizationId ^ rootColor;
+ }
+
+ public function equals (other :Object) :Boolean
+ {
+ if (other is Colorization) {
+ return (Colorization(other)).colorizationId == colorizationId;
+ } else {
+ return false;
+ }
+ }
+
+ public function toString () :String
+ {
+ return String(colorizationId);
+ }
+
+ /**
+ * Returns a long string representation of this colorization.
+ */
+ public function toVerboseString () :String
+ {
+ return StringUtil.toString(this);
+ }
+
+ /**
+ * Converts floating point HSV values to a fixed point integer
+ * representation.
+ *
+ * @param hsv the HSV values to be converted.
+ * @param fhsv the destination array into which the fixed values will
+ * be stored. If this is null, a new array will be created of the
+ * appropriate length.
+ *
+ * @return the fhsv parameter if it was non-null or the
+ * newly created target array.
+ */
+ public static function toFixedHSV (hsv :Array, fhsv :Array) :Array
+ {
+ if (fhsv == null) {
+ fhsv = new Array(hsv.length);
+ }
+ for (var ii :int = 0; ii < hsv.length; ii++) {
+ // fhsv[i] = (int)(hsv[i]*Integer.MAX_VALUE);
+ fhsv[ii] = (int)(hsv[ii]*Short.MAX_VALUE);
+ }
+ return fhsv;
+ }
+
+ /**
+ * Returns the distance between the supplied to numbers modulo N.
+ */
+ public static function distance (a :int, b :int, N :int) :int
+ {
+ return (a > b) ? Math.min(a - b, b + N - a) : Math.min(b - a, a + N - b);
+ }
+
+ /** Fixed HSV values for our root color; used when calculating
+ * recolorizations using this colorization. */
+ protected var _fhsv :Array;
+
+ /** HSV values for our root color; used when calculating
+ * recolorizations using this colorization. */
+ protected var _hsv :Array;
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/BaseTile.as b/src/as/com/threerings/media/tile/BaseTile.as
new file mode 100644
index 00000000..7fe5a4c4
--- /dev/null
+++ b/src/as/com/threerings/media/tile/BaseTile.as
@@ -0,0 +1,81 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+public class BaseTile extends Tile
+{
+ /**
+ * Returns whether or not this tile can be walked upon by character
+ * sprites.
+ */
+ public function isPassable () :Boolean
+ {
+ return _passable;
+ }
+
+ /**
+ * Configures this base tile as passable or impassable.
+ */
+ public function setPassable (passable :Boolean) :void
+ {
+ _passable = passable;
+ }
+
+ /**
+ * Returns the x offset into the tile image of the origin (which will be aligned with the
+ * bottom center of the origin tile) or Integer.MIN_VALUE if the origin is not
+ * explicitly specified and should be computed from the image size and tile footprint.
+ */
+ public override function getOriginX () :int
+ {
+ return getWidth()/2;
+ }
+
+ /**
+ * Returns the y offset into the tile image of the origin (which will be aligned with the
+ * bottom center of the origin tile) or Integer.MIN_VALUE if the origin is not
+ * explicitly specified and should be computed from the image size and tile footprint.
+ */
+ public override function getOriginY () :int
+ {
+ return getHeight();
+ }
+
+
+ /**
+ * Returns the object footprint width in tile units.
+ */
+ public override function getBaseWidth () :int
+ {
+ return 1;
+ }
+
+ /**
+ * Returns the object footprint height in tile units.
+ */
+ public override function getBaseHeight () :int
+ {
+ return 1;
+ }
+
+ /** Whether the tile is passable. */
+ protected var _passable :Boolean = true;
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/BaseTileSet.as b/src/as/com/threerings/media/tile/BaseTileSet.as
new file mode 100644
index 00000000..6906437c
--- /dev/null
+++ b/src/as/com/threerings/media/tile/BaseTileSet.as
@@ -0,0 +1,77 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+import com.threerings.util.StringUtil;
+
+public class BaseTileSet extends SwissArmyTileSet
+{
+ /**
+ * Sets the passability information for the tiles in this tileset.
+ * Each entry in the array corresponds to the tile at that tile index.
+ */
+ public function setPassability (passable :Array) :void
+ {
+ _passable = passable;
+ }
+
+ /**
+ * Returns the passability information for the tiles in this tileset.
+ */
+ public function getPassability () :Array
+ {
+ return _passable;
+ }
+
+ protected override function createTile () :Tile
+ {
+ return new BaseTile();
+ }
+
+ protected override function initTile (tile :Tile, tileIndex :int, zations :Array) :void
+ {
+ super.initTile(tile, tileIndex, zations);
+ (BaseTile(tile)).setPassable(_passable[tileIndex]);
+ }
+
+ protected override function toStringBuf (buf :String) :String
+ {
+ buf = super.toStringBuf(buf);
+ buf = buf.concat(", passable=", StringUtil.toString(_passable));
+ return buf;
+ }
+
+ public static function fromXml (xml :XML) :SwissArmyTileSet
+ {
+ var set :BaseTileSet = new BaseTileSet();
+ set.populateFromXml(xml);
+ return set;
+ }
+
+ protected override function populateFromXml (xml :XML) :void
+ {
+ super.populateFromXml(xml);
+ _passable = toBoolArray(xml.passable);
+ }
+
+ /** Whether each tile is passable. */
+ protected var _passable :Array;
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/Colorizer.as b/src/as/com/threerings/media/tile/Colorizer.as
new file mode 100644
index 00000000..d2a376f0
--- /dev/null
+++ b/src/as/com/threerings/media/tile/Colorizer.as
@@ -0,0 +1,34 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+import com.threerings.media.image.Colorization;
+
+public interface Colorizer
+{
+ /**
+ * Returns the colorization to be used for the specified named colorization class.
+ *
+ * @param index the index of the colorization being requested in the tileset's colorization
+ * list.
+ */
+ function getColorization (index :int, zation :String) :Colorization;
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/DataPackTileSetRepository.as b/src/as/com/threerings/media/tile/DataPackTileSetRepository.as
new file mode 100644
index 00000000..2da41070
--- /dev/null
+++ b/src/as/com/threerings/media/tile/DataPackTileSetRepository.as
@@ -0,0 +1,215 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+import nochump.util.zip.ZipEntry;
+
+import flash.net.URLLoader;
+import flash.net.URLLoaderDataFormat;
+import flash.net.URLRequest;
+import flash.events.Event;
+import flash.events.ErrorEvent;
+import flash.events.SecurityErrorEvent;
+import flash.events.IOErrorEvent;
+import flash.utils.ByteArray;
+
+import com.threerings.io.ObjectInputStream;
+import com.threerings.media.tile.TileDataPack;
+import com.threerings.media.tile.bundle.TileSetBundle;
+import com.threerings.util.DataPack;
+import com.threerings.util.Integer;
+import com.threerings.util.Log;
+import com.threerings.util.Maps;
+import com.threerings.util.Map;
+import com.threerings.util.StringUtil;
+
+public class DataPackTileSetRepository
+ implements TileSetRepository, TileSetIdMap
+{
+ private static var log :Log = Log.getLog(DataPackTileSetRepository);
+
+ public function DataPackTileSetRepository (tileSetUrl :String)
+ {
+ _rootUrl = tileSetUrl;
+ loadTileSetMeta(tileSetUrl + "metadata.jar");
+ }
+
+ protected function loadTileSetMeta (tileSetMetaUrl :String) :void
+ {
+ _metaPack = new TileDataPack(tileSetMetaUrl, metaLoadingComplete);
+ }
+
+ protected function parseTileSetMap (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 >= 2) {
+ var id :int = int(toks[1]);
+ var name :String = String(toks[0]);
+ _nameMap.put(name, id);
+ }
+ }
+ }
+
+ /**
+ * Handle the successful completion of datapack loading.
+ *
+ * @private
+ */
+ protected function metaLoadingComplete (event :Event) :void
+ {
+ parseTileSetMap(_metaPack.getFileAsString("tilesetmap.txt"));
+
+ for each (var entry :ZipEntry in _metaPack.getFiles()) {
+ var bundleName :String = entry.name;
+ if (StringUtil.endsWith(bundleName, "/bundle.xml")) {
+ loadTileSetsFromPack(bundleName.substr(0, bundleName.length - 11));
+ }
+ }
+ }
+
+ public function enumerateTileSetIds () :Array
+ {
+ return _idMap.keys();
+ }
+
+ public function enumerateTileSets () :Array
+ {
+ return _idMap.values();
+ }
+
+ public function getTileSet (tileSetId :int) :TileSet
+ {
+ var tileSet :TileSet = _idMap.get(tileSetId);
+
+ // We don't have this tileset loaded just yet.... start grabbing the data pack.
+ var packName :String = _packMap.get(tileSetId);
+ if (packName == null) {
+ log.warning("Attempted to load unknown tile set", "tileSetId", tileSetId);
+ throw new NoSuchTileSetError(tileSetId);
+ }
+
+ var pack :TileDataPack = _packs.get(packName);
+
+ if (pack == null || !pack.isComplete()) {
+ loadPack(packName, tileSetId, tileSet);
+
+ // Loading the pack should've put us in the map, so let's go retrieve it...
+ tileSet = _idMap.get(tileSetId);
+ } else {
+ tileSet.setImageProvider(_packs.get(packName));
+ }
+
+ return tileSet;
+ }
+
+ protected function loadPack (packName :String, tileSetId :int, tileSet :TileSet) :void
+ {
+ var pack :TileDataPack = _packs.get(packName);
+
+ if (pack != null && pack.isComplete()) {
+ // Already loaded
+ return;
+ }
+
+ var completeListener :Function = function () :void {
+ tileSet.setImageProvider(_packs.get(packName));
+ tileSet.loaded();
+ };
+
+ 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 TileDataPack(getPackUrl(packName), packCompleteListener);
+ _packs.put(packName, pack);
+ }
+
+ listeners.push(completeListener);
+ }
+
+ protected function loadTileSetsFromPack (packName :String) :void
+ {
+ var xml :XML = _metaPack.getFileAsXML(packName + "/bundle.xml");
+ var bundle :TileSetBundle = TileSetBundle.fromXml(xml, this);
+ bundle.forEach(function(key :*, val :*) :void {
+ _idMap.put(key, val);
+ _packMap.put(key, packName);
+ });
+ }
+
+ protected function getPackUrl (packName :String) :String
+ {
+ return _rootUrl + packName + "/bundle.jar";
+ }
+
+ public function getTileSetId (setName :String) :int
+ {
+ return _nameMap.get(setName);
+ }
+
+ public function getTileSetByName (setName :String) :TileSet
+ {
+ return getTileSet(getTileSetId(setName));
+ }
+
+ protected function getPackName (name :String) :String
+ {
+ var lastSlashIdx :int = name.lastIndexOf("/");
+ if (lastSlashIdx == -1) {
+ return name;
+ } else {
+ return name.substring(0, lastSlashIdx).toLowerCase();
+ }
+ }
+
+ /** Contains the tileset meta-data for all tilesets. */
+ protected var _metaPack : DataPack;
+
+ /** Maps name to tileSetId. */
+ protected var _nameMap :Map = Maps.newMapOf(String);
+
+ /** Maps tileSetId to actual TileSet. */
+ protected var _idMap : Map = Maps.newMapOf(int);
+
+ /** Maps tileSetId to DataPack name. */
+ protected var _packMap :Map = Maps.newMapOf(int);
+
+ /** The URL for our tilesets. */
+ 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);
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/ImageProvider.as b/src/as/com/threerings/media/tile/ImageProvider.as
new file mode 100644
index 00000000..61658af2
--- /dev/null
+++ b/src/as/com/threerings/media/tile/ImageProvider.as
@@ -0,0 +1,50 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+import flash.display.DisplayObject;
+import flash.geom.Rectangle;
+
+public interface ImageProvider
+{
+ /**
+ * Returns the raw tileset image with the specified path.
+ *
+ * @param path the path that identifies the desired image (corresponds to the image path from
+ * the tileset).
+ * @param zations if non-null, colorizations to apply to the source image before returning it.
+ */
+ function getTileSetImage (path :String, zations :Array, callback :Function) :void;
+
+ /**
+ * Obtains the tile image with the specified path in the form of a {@link Mirage}. It should be
+ * cropped from the tileset image identified by the supplied path.
+ *
+ * @param path the path that identifies the desired image (corresponds to the image path from
+ * the tileset).
+ * @param bounds if non-null, the region of the image to be returned as a mirage. If null, the
+ * entire image should be returned.
+ * @param zations if non-null, colorizations to apply to the image before converting it into a
+ * mirage.
+ */
+ function getTileImage (path :String, bounds :Rectangle, zations :Array, callback :Function)
+ :void;
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/NoSuchTileSetError.as b/src/as/com/threerings/media/tile/NoSuchTileSetError.as
new file mode 100644
index 00000000..f811df6e
--- /dev/null
+++ b/src/as/com/threerings/media/tile/NoSuchTileSetError.as
@@ -0,0 +1,31 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+public class NoSuchTileSetError extends Error
+{
+ public function NoSuchTileSetError (tileSet :*)
+ {
+ (tileSet is int) ?
+ super("No tile set with id '" + int(tileSet) + "'") :
+ super("No tile set named'" + String(tileSet) + "'");
+ }
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/ObjectTile.as b/src/as/com/threerings/media/tile/ObjectTile.as
new file mode 100644
index 00000000..067221a5
--- /dev/null
+++ b/src/as/com/threerings/media/tile/ObjectTile.as
@@ -0,0 +1,210 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+import flash.geom.Point;
+
+import com.threerings.media.tile.Tile;
+import com.threerings.util.ArrayUtil;
+import com.threerings.util.DirectionUtil;
+import com.threerings.util.Integer;
+import com.threerings.util.StringUtil;
+
+public class ObjectTile extends Tile
+{
+ /**
+ * Returns the object footprint width in tile units.
+ */
+ public override function getBaseWidth () :int
+ {
+ return _base.x;
+ }
+
+ /**
+ * Returns the object footprint height in tile units.
+ */
+ public override function getBaseHeight () :int
+ {
+ return _base.y;
+ }
+
+ /**
+ * Sets the object footprint in tile units.
+ */
+ public function setBase (width :int, height :int) :void
+ {
+ _base.x = width;
+ _base.y = height;
+ }
+
+ /**
+ * Returns the x offset into the tile image of the origin (which will be aligned with the
+ * bottom center of the origin tile) or Integer.MIN_VALUE if the origin is not
+ * explicitly specified and should be computed from the image size and tile footprint.
+ */
+ public override function getOriginX () :int
+ {
+ return _origin.x;
+ }
+
+ /**
+ * Returns the y offset into the tile image of the origin (which will be aligned with the
+ * bottom center of the origin tile) or Integer.MIN_VALUE if the origin is not
+ * explicitly specified and should be computed from the image size and tile footprint.
+ */
+ public override function getOriginY () :int
+ {
+ return _origin.y;
+ }
+
+ /**
+ * Sets the offset in pixels from the origin of the tile image to the origin of the object.
+ * The object will be rendered such that its origin is at the bottom center of its origin
+ * tile. If no origin is specified, the bottom of the image is aligned with the bottom of the
+ * origin tile and the left side of the image is aligned with the left edge of the left-most
+ * base tile.
+ */
+ public function setOrigin (x :int, y :int) :void
+ {
+ _origin.x = x;
+ _origin.y = y;
+ }
+
+ /**
+ * Returns this object tile's default render priority.
+ */
+ public function getPriority () :int
+ {
+ return _priority;
+ }
+
+ /**
+ * Sets this object tile's default render priority.
+ */
+ public function setPriority (priority :int) :void
+ {
+ _priority = priority;
+ }
+
+ /**
+ * Configures the "spot" associated with this object.
+ */
+ public function setSpot (x :int, y :int, orient :int) :void
+ {
+ _spot = new Point(x, y);
+ _sorient = orient;
+ }
+
+ /**
+ * Returns true if this object has a spot.
+ */
+ public function hasSpot () :Boolean
+ {
+ return (_spot != null);
+ }
+
+ /**
+ * Returns the x-coordinate of the "spot" associated with this object.
+ */
+ public function getSpotX () :int
+ {
+ return (_spot == null) ? 0 : _spot.x;
+ }
+
+ /**
+ * Returns the x-coordinate of the "spot" associated with this object.
+ */
+ public function getSpotY () :int
+ {
+ return (_spot == null) ? 0 : _spot.y;
+ }
+
+ /**
+ * Returns the orientation of the "spot" associated with this object.
+ */
+ public function getSpotOrient () :int
+ {
+ return _sorient;
+ }
+
+ /**
+ * Returns the list of constraints associated with this object, or null if the
+ * object has no constraints.
+ */
+ public function getConstraints () :Array
+ {
+ return _constraints;
+ }
+
+ /**
+ * Checks whether this object has the given constraint.
+ */
+ public function hasConstraint (constraint :String) :Boolean
+ {
+ return (_constraints == null) ? false :
+ ArrayUtil.contains(_constraints, constraint);
+ }
+
+ /**
+ * Configures this object's constraints.
+ */
+ public function setConstraints (constraints :Array) :void
+ {
+ _constraints = constraints;
+ }
+
+ protected override function toStringBuf (buf :String) :String
+ {
+ buf = super.toStringBuf(buf);
+ buf.concat(", base=", StringUtil.toString(_base));
+ buf.concat(", origin=", StringUtil.toString(_origin));
+ buf.concat(", priority=", _priority);
+ if (_spot != null) {
+ buf.concat(", spot=", StringUtil.toString(_spot));
+ buf.concat(", sorient=");
+ buf.concat(DirectionUtil.toShortString(_sorient));
+ }
+ if (_constraints != null) {
+ buf.concat(", constraints=", StringUtil.toString(_constraints));
+ }
+
+ return buf;
+ }
+
+ /** The object footprint width in unit tile units. */
+ protected var _base :Point = new Point(1, 1);
+
+ /** The offset from the origin of the tile image to the object's origin or MIN_VALUE if the
+ * origin should be calculated based on the footprint. */
+ protected var _origin :Point = new Point(Integer.MIN_VALUE, Integer.MIN_VALUE);
+
+ /** This object tile's default render priority. */
+ protected var _priority :int;
+
+ /** The coordinates of the "spot" associated with this object. */
+ protected var _spot :Point;
+
+ /** The orientation of the "spot" associated with this object. */
+ protected var _sorient :int;
+
+ /** The list of constraints associated with this object. */
+ protected var _constraints :Array;
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/ObjectTileSet.as b/src/as/com/threerings/media/tile/ObjectTileSet.as
new file mode 100644
index 00000000..ea421e51
--- /dev/null
+++ b/src/as/com/threerings/media/tile/ObjectTileSet.as
@@ -0,0 +1,302 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+import com.threerings.util.ArrayUtil;
+import com.threerings.util.StringUtil;
+
+/**
+ * The object tileset supports the specification of object information for object tiles in
+ * addition to all of the features of the swiss army tileset.
+ *
+ * @see ObjectTile
+ */
+public class ObjectTileSet extends SwissArmyTileSet
+ implements RecolorableTileSet
+{
+ /** A constraint prefix indicating that the object must have empty space in
+ * the suffixed direction (N, E, S, or W). */
+ public static const SPACE :String = "SPACE_";
+
+ /** A constraint indicating that the object is a surface (e.g., table). */
+ public static const SURFACE :String = "SURFACE";
+
+ /** A constraint indicating that the object must be placed on a surface. */
+ public static const ON_SURFACE :String = "ON_SURFACE";
+
+ /** A constraint prefix indicating that the object is a wall facing the
+ * suffixed direction (N, E, S, or W). */
+ public static const WALL :String = "WALL_";
+
+ /** A constraint prefix indicating that the object must be placed on a
+ * wall facing the suffixed direction (N, E, S, or W). */
+ public static const ON_WALL :String = "ON_WALL_";
+
+ /** A constraint prefix indicating that the object must be attached to a
+ * wall facing the suffixed direction (N, E, S, or W). */
+ public static const ATTACH :String = "ATTACH_";
+
+ /** The low suffix for walls and attachments. Low attachments can be placed
+ * on low or normal walls; normal attachments can only be placed on normal
+ * walls. */
+ public static const LOW :String = "_LOW";
+
+ /**
+ * Sets the widths (in unit tile count) of the objects in this tileset. This must be
+ * accompanied by a call to {@link #setObjectHeights}.
+ */
+ public function setObjectWidths (objectWidths :Array) :void
+ {
+ _owidths = objectWidths;
+ }
+
+ /**
+ * Sets the heights (in unit tile count) of the objects in this tileset. This must be
+ * accompanied by a call to {@link #setObjectWidths}.
+ */
+ public function setObjectHeights (objectHeights :Array) :void
+ {
+ _oheights = objectHeights;
+ }
+
+ /**
+ * Sets the x offset in pixels to the image origin.
+ */
+ public function setXOrigins (xorigins :Array) :void
+ {
+ _xorigins = xorigins;
+ }
+
+ /**
+ * Sets the y offset in pixels to the image origin.
+ */
+ public function setYOrigins (yorigins :Array) :void
+ {
+ _yorigins = yorigins;
+ }
+
+ /**
+ * Sets the default render priorities for our object tiles.
+ */
+ public function setPriorities (priorities :Array) :void
+ {
+ _priorities = priorities;
+ }
+
+ /**
+ * Provides a set of colorization classes that apply to objects in this tileset.
+ */
+ public function setColorizations (zations :Array) :void
+ {
+ _ozations = zations;
+ }
+
+ /**
+ * Sets the x offset to the "spots" associated with our object tiles.
+ */
+ public function setXSpots (xspots :Array) :void
+ {
+ _xspots = xspots;
+ }
+
+ /**
+ * Sets the y offset to the "spots" associated with our object tiles.
+ */
+ public function setYSpots (yspots :Array) :void
+ {
+ _yspots = yspots;
+ }
+
+ /**
+ * Sets the orientation of the "spots" associated with our object
+ * tiles.
+ */
+ public function setSpotOrients (sorients :Array) :void
+ {
+ _sorients = sorients;
+ }
+
+ /**
+ * Sets the lists of constraints associated with our object tiles.
+ */
+ public function setConstraints (constraints :Array) :void
+ {
+ _constraints = constraints;
+ }
+
+ /**
+ * Returns the x coordinate of the spot associated with the specified tile index.
+ */
+ public function getXSpot (tileIdx :int) :int
+ {
+ return (_xspots == null) ? 0 : _xspots[tileIdx];
+ }
+
+ /**
+ * Returns the y coordinate of the spot associated with the specified tile index.
+ */
+ public function getYSpot (tileIdx :int) :int
+ {
+ return (_yspots == null) ? 0 : _yspots[tileIdx];
+ }
+
+ /**
+ * Returns the orientation of the spot associated with the specified tile index.
+ */
+ public function getSpotOrient (tileIdx :int) :int
+ {
+ return (_sorients == null) ? 0 : _sorients[tileIdx];
+ }
+
+ /**
+ * Returns the list of constraints associated with the specified tile index, or
+ * null if the index has no constraints.
+ */
+ public function getConstraints (tileIdx :int) :Array
+ {
+ return (_constraints == null) ? null : _constraints[tileIdx];
+ }
+
+ /**
+ * Checks whether the tile at the specified index has the given constraint.
+ */
+ public function hasConstraint (tileIdx :int, constraint :String) :Boolean
+ {
+ return (_constraints == null) ? false :
+ ArrayUtil.contains(_constraints[tileIdx], constraint);
+ }
+
+ // documentation inherited from interface RecolorableTileSet
+ public function getColorizations () :Array
+ {
+ return _ozations;
+ }
+
+ protected override function toStringBuf (buf :String) :String
+ {
+ buf = super.toStringBuf(buf);
+ buf = buf.concat(", owidths=", StringUtil.toString(_owidths));
+ buf = buf.concat(", oheights=", StringUtil.toString(_oheights));
+ buf = buf.concat(", xorigins=", StringUtil.toString(_xorigins));
+ buf = buf.concat(", yorigins=", StringUtil.toString(_yorigins));
+ buf = buf.concat(", prios=", StringUtil.toString(_priorities));
+ buf = buf.concat(", zations=", StringUtil.toString(_ozations));
+ buf = buf.concat(", xspots=", StringUtil.toString(_xspots));
+ buf = buf.concat(", yspots=", StringUtil.toString(_yspots));
+ buf = buf.concat(", sorients=", StringUtil.toString(_sorients));
+ buf = buf.concat(", constraints=", StringUtil.toString(_constraints));
+ return buf;
+ }
+
+ protected override function getTileColorizations (tileIndex :int, rizer :Colorizer) :Array
+ {
+ var zations :Array = null;
+ if (rizer != null && _ozations != null) {
+ zations = new Array(_ozations.length);
+ for (var ii :int = 0; ii < _ozations.length; ii++) {
+ zations[ii] = rizer.getColorization(ii, _ozations[ii]);
+ }
+ }
+ return zations;
+ }
+
+ protected override function createTile () :Tile
+ {
+ return new ObjectTile();
+ }
+
+ protected override function initTile (tile :Tile, tileIndex :int, zations :Array) :void
+ {
+ super.initTile(tile, tileIndex, zations);
+
+ var otile :ObjectTile = ObjectTile(tile);
+ if (_owidths != null) {
+ otile.setBase(_owidths[tileIndex], _oheights[tileIndex]);
+ }
+ if (_xorigins != null) {
+ otile.setOrigin(_xorigins[tileIndex], _yorigins[tileIndex]);
+ }
+ if (_priorities != null) {
+ otile.setPriority(_priorities[tileIndex]);
+ }
+ if (_xspots != null) {
+ otile.setSpot(_xspots[tileIndex], _yspots[tileIndex], _sorients[tileIndex]);
+ }
+ if (_constraints != null) {
+ otile.setConstraints(_constraints[tileIndex]);
+ }
+ }
+
+ public static function fromXml (xml :XML) :SwissArmyTileSet
+ {
+ var set :ObjectTileSet = new ObjectTileSet();
+ set.populateFromXml(xml);
+ return set;
+ }
+
+ protected override function populateFromXml (xml :XML) :void
+ {
+ super.populateFromXml(xml);
+ _owidths = toIntArray(xml.objectWidths);
+ _oheights = toIntArray(xml.objectHeights);
+ _xorigins = toIntArray(xml.xOrigins);
+ _yorigins = toIntArray(xml.yOrigins);
+ _priorities = toIntArray(xml.priorities);
+ _ozations = toStrArray(xml.zations);
+ _xspots = toIntArray(xml.xSpots);
+ _yspots = toIntArray(xml.ySpots);
+ _sorients = toIntArray(xml.sorients);
+ var constraintStrArr :Array = toStrArray(xml.constraints);
+ _constraints = constraintStrArr.map(function(element :*, index :int, arr :Array) :Array {
+ return element.split("|");
+ });
+ }
+
+ /** The width (in tile units) of our object tiles. */
+ protected var _owidths :Array;
+
+ /** The height (in tile units) of our object tiles. */
+ protected var _oheights :Array;
+
+ /** The x offset in pixels to the origin of the tile images. */
+ protected var _xorigins :Array;
+
+ /** The y offset in pixels to the origin of the tile images. */
+ protected var _yorigins :Array;
+
+ /** The default render priorities of our objects. */
+ protected var _priorities :Array;
+
+ /** Colorization classes that apply to our objects. */
+ protected var _ozations :Array;
+
+ /** The x offset to the "spots" associated with our tiles. */
+ protected var _xspots :Array;
+
+ /** The y offset to the "spots" associated with our tiles. */
+ protected var _yspots :Array;
+
+ /** The orientation of the "spots" associated with our tiles. */
+ protected var _sorients :Array;
+
+ /** Lists of constraints associated with our tiles. */
+ protected var _constraints :Array;
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/RecolorableTileSet.as b/src/as/com/threerings/media/tile/RecolorableTileSet.as
new file mode 100644
index 00000000..c838be9e
--- /dev/null
+++ b/src/as/com/threerings/media/tile/RecolorableTileSet.as
@@ -0,0 +1,30 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+public interface RecolorableTileSet
+{
+ /**
+ * Returns the colorization classes that should be used to recolor
+ * objects in this tileset.
+ */
+ function getColorizations () :Array
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/SwissArmyTileSet.as b/src/as/com/threerings/media/tile/SwissArmyTileSet.as
new file mode 100644
index 00000000..9dd9f65b
--- /dev/null
+++ b/src/as/com/threerings/media/tile/SwissArmyTileSet.as
@@ -0,0 +1,237 @@
+//
+// $Id: SwissArmyTileSet.java 868 2010-01-04 21:47:34Z dhoover $
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+import flash.geom.Point;
+import flash.geom.Rectangle;
+
+import com.threerings.util.StringUtil;
+
+/**
+ * The swiss army tileset supports a diverse variety of tiles in the tileset image. Each row can
+ * contain varying numbers of tiles and each row can have its own width and height. Tiles can be
+ * separated from the edge of the tileset image by some border offset and can be separated from one
+ * another by a gap distance.
+ */
+public class SwissArmyTileSet extends TileSet
+{
+ public override function getTileCount () :int
+ {
+ return _numTiles;
+ }
+
+ public override function computeTileBounds (tileIndex :int) :Rectangle
+ {
+ // find the row number containing the sought-after tile
+ var ridx :int;
+ var tcount :int;
+ var ty :int;
+ var tx :int;
+ ridx = tcount = 0;
+
+ // start tile image position at image start offset
+ tx = _offsetPos.x;
+ ty = _offsetPos.y;
+
+ while ((tcount += _tileCounts[ridx]) < tileIndex + 1) {
+ // increment tile image position by row height and gap distance
+ ty += (_heights[ridx++] + _gapSize.y);
+ }
+
+ // determine the horizontal index of this tile in the row
+ var xidx :int = tileIndex - (tcount - _tileCounts[ridx]);
+
+ // final image x-position is based on tile width and gap distance
+ tx += (xidx * (_widths[ridx] + _gapSize.x));
+
+// Log.info("Computed tile bounds [tileIndex=" + tileIndex +
+// ", ridx=" + ridx + ", xidx=" + xidx + ", tx=" + tx + ", ty=" + ty + "].");
+
+ // crop the tile-sized image chunk from the full image
+ return new Rectangle(tx, ty, _widths[ridx], _heights[ridx]);
+ }
+
+ /**
+ * Sets the tile counts which are the number of tiles in each row of the tileset image. Each
+ * row can have an arbitrary number of tiles.
+ */
+ public function setTileCounts (tileCounts :Array) :void
+ {
+ _tileCounts = tileCounts;
+
+ // compute our total tile count
+ computeTileCount();
+ }
+
+ /**
+ * Returns the tile count settings.
+ */
+ public function getTileCounts () :Array
+ {
+ return _tileCounts;
+ }
+
+ /**
+ * Computes our total tile count from the individual counts for each row.
+ */
+ protected function computeTileCount () :void
+ {
+ // compute our number of tiles
+ _numTiles = 0;
+ for each (var count :int in _tileCounts) {
+ _numTiles += count;
+ }
+ }
+
+ /**
+ * Sets the tile widths for each row. Each row can have tiles of a different width.
+ */
+ public function setWidths (widths :Array) :void
+ {
+ _widths = widths;
+ }
+
+ /**
+ * Returns the width settings.
+ */
+ public function getWidths () :Array
+ {
+ return _widths;
+ }
+
+ /**
+ * Sets the tile heights for each row. Each row can have tiles of a different height.
+ */
+ public function setHeights (heights :Array) :void
+ {
+ _heights = heights;
+ }
+
+ /**
+ * Returns the height settings.
+ */
+ public function getHeights () :Array
+ {
+ return _heights;
+ }
+
+ /**
+ * Sets the offset in pixels of the upper left corner of the first tile in the first row. If
+ * the tileset image has a border, this can be set to account for it.
+ */
+ public function setOffsetPos (offsetPos :Point) :void
+ {
+ _offsetPos = offsetPos;
+ }
+
+ /**
+ * Sets the size of the gap between tiles (in pixels). If the tiles have space between them,
+ * this can be set to account for it.
+ */
+ public function setGapSize (gapSize :Point) :void
+ {
+ _gapSize = gapSize;
+ }
+
+ protected override function toStringBuf (buf :String) :String
+ {
+ buf = super.toStringBuf(buf);
+ buf = buf.concat(", widths=", StringUtil.toString(_widths));
+ buf = buf.concat(", heights=", StringUtil.toString(_heights));
+ buf = buf.concat(", tileCounts=", StringUtil.toString(_tileCounts));
+ buf = buf.concat(", offsetPos=", StringUtil.toString(_offsetPos));
+ buf = buf.concat(", gapSize=", StringUtil.toString(_gapSize));
+ return buf;
+ }
+
+ public static function fromXml (xml :XML) :SwissArmyTileSet
+ {
+ var set :SwissArmyTileSet = new SwissArmyTileSet();
+ set.populateFromXml(xml);
+ return set;
+ }
+
+ protected override function populateFromXml (xml :XML) :void
+ {
+ super.populateFromXml(xml);
+ _tileCounts = toIntArray(xml.tileCounts);
+ _widths = toIntArray(xml.widths);
+ _heights = toIntArray(xml.heights);
+ var offsetPosArr :Array = toIntArray(xml.offsetPos);
+ _offsetPos = new Point(offsetPosArr[0], offsetPosArr[1]);
+ var gapSizeArr :Array = toIntArray(xml.gapSize);
+ _gapSize = new Point(gapSizeArr[0], gapSizeArr[1]);
+ computeTileCount();
+ }
+
+ protected static function toStrArray (str :String) :Array
+ {
+ if (str == null) {
+ return null;
+ }
+
+ return str.split(",");
+ }
+
+ protected static function toBoolArray (str :String) :Array
+ {
+ if (str == null) {
+ return null;
+ }
+
+ return str.split(",").map(function(element :*, index :int, arr :Array) :Boolean {
+ return Boolean(element);
+ });
+ }
+
+ protected static function toIntArray (str :String) :Array
+ {
+ if (str == null) {
+ return null;
+ }
+
+ return str.split(",").map(function(element :*, index :int, arr :Array) :int {
+ return int(element);
+ });
+ }
+
+ /** The number of tiles in each row. */
+ protected var _tileCounts :Array;
+
+ /** The number of tiles in the tileset. */
+ protected var _numTiles :int;
+
+ /** The width of the tiles in each row in pixels. */
+ protected var _widths :Array;
+
+ /** The height of the tiles in each row in pixels. */
+ protected var _heights :Array;
+
+ /** The offset distance (x, y) in pixels from the top-left of the image to the start of the
+ * first tile image. */
+ protected var _offsetPos :Point = new Point();
+
+ /** The distance (x, y) in pixels between each tile in each row horizontally, and between each
+ * row of tiles vertically. */
+ protected var _gapSize :Point = new Point();
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/Tile.as b/src/as/com/threerings/media/tile/Tile.as
new file mode 100644
index 00000000..944978ee
--- /dev/null
+++ b/src/as/com/threerings/media/tile/Tile.as
@@ -0,0 +1,147 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+import flash.display.Bitmap;
+import flash.display.BitmapData;
+import flash.display.DisplayObject;
+
+public class Tile
+{
+ /** The key associated with this tile. */
+ public var key :Tile_Key;
+
+ /**
+ * Configures this tile with its tile image.
+ */
+ public function setImage (image :DisplayObject) :void
+ {
+ _image = image;
+
+ // Notify them all and clear our list.
+ for each (var func :Function in _notifyOnLoad) {
+ func(this);
+ }
+ }
+
+ public function getImage () :DisplayObject
+ {
+ if (_image is Bitmap) {
+ // TODO - handle this more consistently...
+ var data :BitmapData = new BitmapData(_image.width, _image.height, true, 0x00000000);
+ data.draw(_image);
+ return new Bitmap(data);
+ } else {
+ return _image;
+ }
+ }
+
+ /**
+ * Returns the width of this tile.
+ */
+ public function getWidth () :int
+ {
+ return _image.width;
+ }
+
+ /**
+ * Returns the height of this tile.
+ */
+ public function getHeight () :int
+ {
+ return _image.height;
+ }
+
+ /**
+ * Returns true if the specified coordinates within this tile contains
+ * a non-transparent pixel.
+ */
+ public function hitTest (x :int, y :int) :Boolean
+ {
+ return _image.hitTestPoint(x, y, true);
+ }
+
+ public function toString () :String
+ {
+ return "[" + toStringBuf(new String()) + "]";
+ }
+
+ /**
+ * This should be overridden by derived classes (which should be sure
+ * to call super.toString()) to append the derived class
+ * specific tile information to the string buffer.
+ */
+ protected function toStringBuf (buf :String) :String
+ {
+ if (_image == null) {
+ return buf.concat("null-image");
+ } else {
+ return buf.concat(_image.width, "x", _image.height);
+ }
+ }
+
+ public function notifyOnLoad (func :Function) :void
+ {
+ _notifyOnLoad.push(func);
+ }
+
+ /**
+ * Returns the x offset into the tile image of the origin (which will be aligned with the
+ * bottom center of the origin tile) or Integer.MIN_VALUE if the origin is not
+ * explicitly specified and should be computed from the image size and tile footprint.
+ */
+ public function getOriginX () :int
+ {
+ throw new Error("abstract");
+ }
+
+ /**
+ * Returns the y offset into the tile image of the origin (which will be aligned with the
+ * bottom center of the origin tile) or Integer.MIN_VALUE if the origin is not
+ * explicitly specified and should be computed from the image size and tile footprint.
+ */
+ public function getOriginY () :int
+ {
+ throw new Error("abstract")
+ }
+
+ /**
+ * Returns the object footprint width in tile units.
+ */
+ public function getBaseWidth () :int
+ {
+ throw new Error("abstract");
+ }
+
+ /**
+ * Returns the object footprint height in tile units.
+ */
+ public function getBaseHeight () :int
+ {
+ throw new Error("abstract");
+ }
+
+ /** Our tileset image. */
+ protected var _image :DisplayObject;
+
+ /** Everyone who cares when we're loaded. */
+ protected var _notifyOnLoad :Array = [];
+}
+}
diff --git a/src/as/com/threerings/media/tile/TileDataPack.as b/src/as/com/threerings/media/tile/TileDataPack.as
new file mode 100644
index 00000000..1ed6ef66
--- /dev/null
+++ b/src/as/com/threerings/media/tile/TileDataPack.as
@@ -0,0 +1,108 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+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.util.DataPack;
+import com.threerings.util.MultiLoader;
+
+/**
+ * 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 TileDataPack extends DataPack
+ implements ImageProvider
+{
+ public function TileDataPack (
+ source :Object, completeListener :Function = null, errorListener :Function = null)
+ {
+ super(source, completeListener, errorListener);
+ }
+
+ protected override function bytesAvailable (bytes :ByteArray) :void
+ {
+ bytes.position = 0;
+ try {
+ _zip = new ZipFile(bytes);
+ } catch (zipError :ZipError) {
+ dispatchError("Unable to read datapack: " + zipError.message);
+ return;
+ }
+
+ // Put something there so we know we're loaded.
+ _metadata =
Loading tilesets by hand is intended for things like toolbar icons or games with a single
+ * set of tiles (think Stratego, for example). Loading tilesets from a repository supports games
+ * with vast numbers of tiles to which more tiles may be added on the fly (think the tiles for an
+ * isometric-display graphical MUD).
+ */
+public class TileManager
+{
+ private static var log :Log = Log.getLog(TileManager);
+
+ /**
+ * Sets the tileset repository that will be used by the tile manager when tiles are requested
+ * by tileset id.
+ */
+ public function setTileSetRepository (setrep :TileSetRepository) :void
+ {
+ _setrep = setrep;
+ }
+
+ /**
+ * Returns the tileset repository currently in use.
+ */
+ public function getTileSetRepository () :TileSetRepository
+ {
+ return _setrep;
+ }
+
+ /**
+ * Returns the tileset with the specified id. Tilesets are fetched from the tileset repository
+ * supplied via {@link #setTileSetRepository}, and are subsequently cached.
+ *
+ * @param tileSetId the unique identifier for the desired tileset.
+ *
+ * @exception NoSuchTileSetError thrown if no tileset exists with the specified id or if
+ * an underlying error occurs with the tileset repository's persistence mechanism.
+ */
+ public function getTileSet (tileSetId :int) :TileSet
+ {
+ // make sure we have a repository configured
+ if (_setrep == null) {
+ throw new NoSuchTileSetError(tileSetId);
+ }
+
+ try {
+ return _setrep.getTileSet(tileSetId);
+ } catch (pe :Error) {
+ log.warning("Failure loading tileset", "id", tileSetId, pe);
+ throw new NoSuchTileSetError(tileSetId);
+ }
+
+ // Unreachable.
+ return null;
+ }
+
+ /**
+ * Returns the tileset with the specified name.
+ *
+ * @throws NoSuchTileSetError if no tileset with the specified name is available via our
+ * configured tile set repository.
+ */
+ public function getTileSetByName (name :String) :TileSet
+ {
+ // make sure we have a repository configured
+ if (_setrep == null) {
+ throw new NoSuchTileSetError(name);
+ }
+
+ try {
+ return _setrep.getTileSetByName(name);
+ } catch (pe :Error) {
+ log.warning("Failure loading tileset", "name", name, "error", pe);
+ throw new NoSuchTileSetError(name);
+ }
+
+ // Unreachable.
+ return null;
+ }
+
+ /**
+ * Returns the {@link Tile} object with the specified fully qualified tile id. The supplied
+ * colorizer will be used to recolor the tile.
+ *
+ * @see TileUtil#getFQTileId
+ */
+ public function getTile (fqTileId :int, rizer :Colorizer = null) :Tile
+ {
+ return getTileBySet(TileUtil.getTileSetId(fqTileId),
+ TileUtil.getTileIndex(fqTileId), rizer);
+ }
+
+ /**
+ * Returns the {@link Tile} object from the specified tileset at the specified index.
+ *
+ * @param tileSetId the tileset id.
+ * @param tileIndex the index of the tile to be retrieved.
+ *
+ * @return the tile object.
+ */
+ public function getTileBySet (tileSetId :int, tileIndex :int, rizer :Colorizer) :Tile
+ {
+ var set :TileSet = getTileSet(tileSetId);
+ return set.getTile(tileIndex, rizer);
+ }
+
+ /** The tile set repository. */
+ protected var _setrep :TileSetRepository;
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/TileSet.as b/src/as/com/threerings/media/tile/TileSet.as
new file mode 100644
index 00000000..af51d294
--- /dev/null
+++ b/src/as/com/threerings/media/tile/TileSet.as
@@ -0,0 +1,368 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+import flash.display.DisplayObject;
+import flash.geom.Rectangle;
+
+import com.threerings.display.ImageUtil;
+import com.threerings.io.ObjectInputStream;
+import com.threerings.io.ObjectOutputStream;
+import com.threerings.io.Streamable;
+import com.threerings.media.image.Colorization;
+import com.threerings.util.Hashable;
+import com.threerings.util.Log;
+import com.threerings.util.StringUtil;
+import com.threerings.util.Throttle;
+import com.threerings.util.maps.HashMap;
+import com.threerings.util.maps.WeakValueMap;
+
+public /* abstract */ class TileSet
+ implements Streamable, Hashable
+{
+ private static var log :Log = Log.getLog(TileSet);
+
+ public function readObject (oin :ObjectInputStream) :void
+ {
+ _imagePath = oin.readUTF();
+ _name = oin.readUTF();
+ }
+
+ public function writeObject (oout :ObjectOutputStream) :void
+ {
+ oout.writeUTF(_imagePath);
+ oout.writeUTF(_name);
+ }
+
+ /**
+ * Configures this tileset with an image provider that it can use to load its tileset image.
+ * This will be called automatically when the tileset is fetched via the {@link TileManager}.
+ */
+ public function setImageProvider (improv :ImageProvider) :void
+ {
+ _improv = improv;
+
+ if (_improv != null) {
+ for each (var arr :Array in _pending) {
+ initTile(arr[0], arr[1], arr[2]);
+ }
+ }
+ _pending = [];
+ }
+
+ /**
+ * Returns the tileset name.
+ */
+ public function getName () :String
+ {
+ return (_name == null) ? _imagePath : _name;
+ }
+
+ /**
+ * Specifies the tileset name.
+ */
+ public function setName (name :String) :void
+ {
+ _name = name;
+ }
+
+ public function hashCode () :int
+ {
+ return StringUtil.hashCode(getName());
+ }
+
+ public function equals (other :Object) :Boolean
+ {
+ return other === this;
+ }
+
+ /**
+ * Sets the path to the image that will be used by this tileset. This must be called before the
+ * first call to {@link #getTile}.
+ */
+ public function setImagePath (imagePath :String) :void
+ {
+ _imagePath = imagePath;
+ }
+
+ /**
+ * Returns the path to the composite image used by this tileset.
+ */
+ public function getImagePath () :String
+ {
+ return _imagePath;
+ }
+
+ /**
+ * Returns the number of tiles in the tileset.
+ */
+ public function getTileCount () :int
+ {
+ throw new Error("abstract");
+ }
+
+ /**
+ * Computes and fills in the bounds for the specified tile based on the mechanism used by the
+ * derived class to do such things. The width and height of the bounds should be the size of
+ * the tile image and the x and y offset should be the offset in the tileset image for the
+ * image data of the specified tile.
+ *
+ * @param tileIndex the index of the tile whose bounds are to be computed.
+ * @param bounds the rectangle object into which to fill the bounds.
+ *
+ * @return the rectangle passed into the bounds parameter.
+ */
+ public function computeTileBounds (tileIndex :int) :Rectangle
+ {
+ throw new Error("abstract");
+ }
+
+ /**
+ * Creates a {@link Tile} object from this tileset corresponding to the specified tile id and
+ * returns that tile. A null tile will never be returned, but one with an error image may be
+ * returned if a problem occurs loading the underlying tileset image.
+ *
+ * @param tileIndex the index of the tile in the tileset. Tile indexes start with zero as the
+ * upper left tile and increase by one as the tiles move left to right and top to bottom over
+ * the source image.
+ * @param zations colorizations to be applied to the tile image prior to returning it. These
+ * may be null for uncolorized images.
+ *
+ * @return the tile object.
+ */
+ public function getTile (tileIndex :int, zations :* = null) :Tile
+ {
+ // Default to using our default tileset colorizations.
+ if (zations == null) {
+ zations = _zations;
+ } else if (zations is Colorizer) {
+ zations = getTileColorizations(tileIndex, zations);
+ }
+
+ var tile :Tile = null;
+
+ // first look in the active set; if it's in use by anyone or in the cache, it will be in
+ // the active set
+ var key :Tile_Key = new Tile_Key(this, tileIndex, zations);
+ tile = _atiles.get(key);
+
+ // if it's not in the active set, it's not in memory; so load it
+ if (tile == null) {
+ tile = createTile();
+ tile.key = new Tile_Key(this, tileIndex, zations);
+ initTile(tile, tileIndex, zations);
+ _atiles.put(tile.key, tile);
+ }
+
+ // periodically report our image cache performance
+ reportCachePerformance();
+
+ return tile;
+ }
+
+ /**
+ * Returns a prepared version of the image that would be used by the tile at the specified
+ * index. Because tilesets are often used simply to provide access to a collection of uniform
+ * images, this method is provided to bypass the creation of a {@link Tile} object when all
+ * that is desired is access to the underlying image.
+ */
+ public function getTileImage (tileIndex :int, zations :Array, callback :Function)
+ :void
+ {
+ // If they weren't specified, get the default zations for this tile.
+ if (zations == null) {
+ zations = getTileColorizations(tileIndex, null);
+ }
+
+ var bounds :Rectangle = computeTileBounds(tileIndex);
+ var image :DisplayObject = null;
+ if (checkTileIndex(tileIndex)) {
+ if (_improv == null) {
+ log.warning("Aiya! Tile set missing image provider [path=" + _imagePath + "].");
+ callback(ImageUtil.createErrorImage(bounds.width, bounds.height));
+
+ } else {
+ _improv.getTileImage(_imagePath, bounds, zations,
+ function(result :DisplayObject) :void {
+ if (result == null) {
+ result = ImageUtil.createErrorImage(bounds.width, bounds.height);
+ }
+ callback(result);
+ });
+ }
+ } else {
+ callback(ImageUtil.createErrorImage(bounds.width, bounds.height));
+ }
+ }
+
+ /**
+ * Returns colorizations for the specified tile image. The default is to return any
+ * colorizations associated with the tileset via a call to {@link #clone(Colorization[])},
+ * however derived classes may have dynamic colorization policies that look up colorization
+ * assignments from the supplied colorizer.
+ */
+ protected function getTileColorizations (tileIndex :int, rizer :Colorizer) :Array
+ {
+ return _zations;
+ }
+
+ /**
+ * Used to ensure that the specified tile index is valid.
+ */
+ protected function checkTileIndex (tileIndex :int) :Boolean
+ {
+ var tcount :int = getTileCount();
+ if (tileIndex >= 0 && tileIndex < tcount) {
+ return true;
+ } else {
+ log.warning("Requested invalid tile [tset=" + this + ", index=" + tileIndex + "].",
+ new Error());
+ return false;
+ }
+ }
+
+ /**
+ * Creates a blank tile of the appropriate type for this tileset.
+ *
+ * @return a blank tile ready to be populated with its image and metadata.
+ */
+ protected function createTile () :Tile
+ {
+ return new Tile();
+ }
+
+ /**
+ * Initializes the supplied tile. Derived classes can override this method to add
+ * in their own tile information, but should be sure to call super.initTile().
+ *
+ * @param tile the tile to initialize.
+ * @param tileIndex the index of the tile.
+ * @param zations the colorizations to be used when generating the tile image.
+ */
+ protected function initTile (tile :Tile, tileIndex :int, zations :Array) :void
+ {
+ if (_improv != null) {
+ getTileImage(tileIndex, zations, function(result :DisplayObject) :void {
+ tile.setImage(result);
+ });
+ } else {
+ _pending.push([tile, tileIndex, zations]);
+ }
+ }
+
+ /**
+ * Reports statistics detailing the image manager cache performance and the current size of the
+ * cached images.
+ */
+ protected function reportCachePerformance () :void
+ {
+ if (_improv == null ||
+ _cacheStatThrottle.throttleOp()) {
+ return;
+ }
+
+ // compute our estimated memory usage
+ var amem :int = 0;
+ var asize :int = 0;
+ // first total up the active tiles
+ for each (var tile :Tile in _atiles.values()) {
+ if (tile != null) {
+ asize++;
+ }
+ }
+ log.info("Tile caches [seen=" + _atiles.size() + ", asize=" + asize + "].");
+ }
+
+ public function toString () :String
+ {
+ return "[" + toStringBuf(new String()) + "]";
+ }
+
+ /**
+ * Derived classes can override this, calling super.toString(buf) and then
+ * appending additional information to the buffer.
+ */
+ protected function toStringBuf (buf :String) :String
+ {
+ buf = buf.concat("name=", _name);
+ buf = buf.concat(", path=", _imagePath);
+ buf = buf.concat(", tileCount=", getTileCount());
+ return buf;
+ }
+
+ public function isLoaded () :Boolean
+ {
+ return _isLoaded;
+ }
+
+ public function loaded () :void
+ {
+ _isLoaded = true;
+
+ // Notify them all and clear our list.
+ for each (var func :Function in _notifyOnLoad) {
+ func(this);
+ }
+ _notifyOnLoad = [];
+ }
+
+ public function notifyOnLoad (func :Function) :void
+ {
+ if (isLoaded()) {
+ func(this);
+ } else {
+ _notifyOnLoad.push(func);
+ }
+ }
+
+ protected function populateFromXml (xml :XML) :void
+ {
+ _name = xml.@name;
+ _imagePath = xml.imagePath;
+ }
+
+ /** Whether all the media for this tileset is loaded and ready. */
+ protected var _isLoaded :Boolean;
+
+ /** The path to the file containing the tile images. */
+ protected var _imagePath :String;
+
+ /** The tileset name. */
+ protected var _name :String;
+
+ /** Colorizations to be applied to tiles created from this tileset. */
+ protected var _zations :Array; /* of */ Colorization;
+
+ /** The entity from which we obtain our tile image. */
+ protected var _improv :ImageProvider;
+
+ /** Everyone who cares when we're loaded. */
+ protected var _notifyOnLoad :Array = [];
+
+ /** Tiles awaiting an image provider. */
+ protected var _pending :Array = [];
+
+ /** A map containing weak references to all "active" tiles. */
+ protected static var _atiles :WeakValueMap = new WeakValueMap(new HashMap());
+
+ /** Throttle our cache status logging to once every 300 seconds. */
+ protected static var _cacheStatThrottle :Throttle = new Throttle(1, 300000);
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/TileSetIdMap.as b/src/as/com/threerings/media/tile/TileSetIdMap.as
new file mode 100644
index 00000000..81941420
--- /dev/null
+++ b/src/as/com/threerings/media/tile/TileSetIdMap.as
@@ -0,0 +1,29 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+public interface TileSetIdMap
+{
+ /**
+ * Returns the unique identifier for the named tileset.
+ */
+ function getTileSetId (tileSetName :String) :int
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/TileSetRepository.as b/src/as/com/threerings/media/tile/TileSetRepository.as
new file mode 100644
index 00000000..83a2f1bf
--- /dev/null
+++ b/src/as/com/threerings/media/tile/TileSetRepository.as
@@ -0,0 +1,70 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+public interface TileSetRepository {
+
+ /**
+ * Returns an iterator over the identifiers of all {@link TileSet}
+ * objects available.
+ */
+ function enumerateTileSetIds () :Array;
+
+ /**
+ * Returns an iterator over all {@link TileSet} objects available.
+ */
+ function enumerateTileSets () :Array;
+
+ /**
+ * Returns the {@link TileSet} with the specified tile set
+ * identifier. The repository is responsible for configuring the tile
+ * set with an image provider.
+ *
+ * @exception NoSuchTileSetException thrown if no tileset exists with
+ * the specified identifier.
+ * @exception PersistenceException thrown if an error occurs
+ * communicating with the underlying persistence mechanism.
+ */
+ function getTileSet (tileSetId :int) :TileSet;
+
+ /**
+ * Returns the unique identifier of the {@link TileSet} with the
+ * specified tile set name.
+ *
+ * @exception NoSuchTileSetException thrown if no tileset exists with
+ * the specified name.
+ * @exception PersistenceException thrown if an error occurs
+ * communicating with the underlying persistence mechanism.
+ */
+ function getTileSetId (setName :String) :int;
+
+ /**
+ * Returns the {@link TileSet} with the specified tile set name. The
+ * repository is responsible for configuring the tile set with an
+ * image provider.
+ *
+ * @exception NoSuchTileSetException thrown if no tileset exists with
+ * the specified name.
+ * @exception PersistenceException thrown if an error occurs
+ * communicating with the underlying persistence mechanism.
+ */
+ function getTileSetByName (setName :String) :TileSet;
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/Tile_Key.as b/src/as/com/threerings/media/tile/Tile_Key.as
new file mode 100644
index 00000000..8b118e22
--- /dev/null
+++ b/src/as/com/threerings/media/tile/Tile_Key.as
@@ -0,0 +1,65 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile {
+
+import com.threerings.media.image.Colorization;
+import com.threerings.util.ArrayUtil;
+import com.threerings.util.Hashable;
+
+/** Used when caching tiles. */
+public class Tile_Key
+ implements Hashable
+{
+ public var tileSet :TileSet;
+ public var tileIndex :int;
+ public var zations :Array;
+
+ public function Tile_Key (tileSet :TileSet, tileIndex :int, zations :Array) {
+ this.tileSet = tileSet;
+ this.tileIndex = tileIndex;
+ this.zations = zations;
+ }
+
+ public function equals (other :Object) :Boolean
+ {
+ if (other is Tile_Key) {
+ var okey :Tile_Key = Tile_Key(other);
+ return (tileSet == okey.tileSet &&
+ tileIndex == okey.tileIndex &&
+ ArrayUtil.equals(zations, okey.zations));
+ } else {
+ return false;
+ }
+ }
+
+ public function hashCode () :int
+ {
+ var code :int = (tileSet == null) ? tileIndex :
+ (tileSet.hashCode() ^ tileIndex);
+ var zcount :int = (zations == null) ? 0 : zations.length;
+ for each (var zation :Colorization in zations) {
+ if (zation != null) {
+ code ^= zation.hashCode();
+ }
+ }
+ return code;
+ }
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/media/tile/bundle/TileSetBundle.as b/src/as/com/threerings/media/tile/bundle/TileSetBundle.as
new file mode 100644
index 00000000..e626d553
--- /dev/null
+++ b/src/as/com/threerings/media/tile/bundle/TileSetBundle.as
@@ -0,0 +1,66 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.media.tile.bundle {
+
+import flash.display.DisplayObject;
+
+import com.threerings.io.ObjectOutputStream;
+import com.threerings.io.ObjectInputStream;
+import com.threerings.io.Streamable;
+import com.threerings.util.DataPack;
+import com.threerings.util.maps.DictionaryMap;
+import com.threerings.media.tile.BaseTileSet;
+import com.threerings.media.tile.ObjectTileSet;
+import com.threerings.media.tile.TileSet;
+import com.threerings.media.tile.TileSetIdMap;
+
+public class TileSetBundle extends DictionaryMap
+{
+ public function init (pack :DataPack) :void
+ {
+ _pack = pack;
+ }
+
+ public static function fromXml (xml :XML, idMap :TileSetIdMap) :TileSetBundle
+ {
+ var bundle :TileSetBundle = new TileSetBundle();
+ for each (var objXml :XML in xml.object.tileset) {
+ var objId :int = idMap.getTileSetId(objXml.@name);
+ bundle.put(objId, ObjectTileSet.fromXml(objXml));
+ }
+ for each (var baseXml :XML in xml.base.tileset) {
+ var baseId :int = idMap.getTileSetId(baseXml.@name);
+ bundle.put(baseId, BaseTileSet.fromXml(baseXml));
+ }
+ return bundle;
+ }
+
+ /**
+ * Loads the image from our data pack.
+ */
+ public function loadImage (path :String) :DisplayObject
+ {
+ return DisplayObject(_pack.getFile(path));
+ }
+
+ /** The data pack from which we can grab images. */
+ protected var _pack :DataPack;
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/miso/client/BaseTileIsoSprite.as b/src/as/com/threerings/miso/client/BaseTileIsoSprite.as
index 1a078b51..4f60a305 100644
--- a/src/as/com/threerings/miso/client/BaseTileIsoSprite.as
+++ b/src/as/com/threerings/miso/client/BaseTileIsoSprite.as
@@ -1,20 +1,40 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
package com.threerings.miso.client {
-import as3isolib.display.primitive.IsoBox;
-import as3isolib.graphics.SolidColorFill;
+import com.threerings.media.tile.Tile;
+import com.threerings.miso.util.MisoSceneMetrics;
-public class BaseTileIsoSprite extends IsoBox
+public class BaseTileIsoSprite extends TileIsoSprite
{
- public function BaseTileIsoSprite (x :int, y :int, tileId :int)
+ public function BaseTileIsoSprite (x :int, y :int, tileId :int, tile :Tile,
+ metrics :MisoSceneMetrics)
{
- width = 1;
- height = VERT_OFFSET;
- length = 1;
+ super(x, y, tileId, tile, metrics);
+ }
- moveTo(x, y, -VERT_OFFSET);
+ public override function layout (x :int, y :int, tile :Tile) :void
+ {
+ super.layout(x, y, tile);
- // TEMP
- fill = new SolidColorFill(tileId * 1000, 1.0);
+ setSize(1, 1, VERT_OFFSET);
}
protected static const VERT_OFFSET :Number = 0.01;
diff --git a/src/as/com/threerings/miso/client/MisoScenePanel.as b/src/as/com/threerings/miso/client/MisoScenePanel.as
index 67a18800..835c7f87 100644
--- a/src/as/com/threerings/miso/client/MisoScenePanel.as
+++ b/src/as/com/threerings/miso/client/MisoScenePanel.as
@@ -1,9 +1,30 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
package com.threerings.miso.client {
import flash.utils.getTimer;
import flash.display.Sprite;
+import flash.events.Event;
+
import flash.geom.Point;
import flash.geom.Rectangle;
@@ -11,6 +32,9 @@ import flash.events.MouseEvent;
import com.threerings.crowd.client.PlaceView;
import com.threerings.crowd.data.PlaceObject;
+import com.threerings.media.tile.NoSuchTileSetError;
+import com.threerings.media.tile.TileSet;
+import com.threerings.media.tile.TileUtil;
import com.threerings.miso.client.MisoMetricsTransformation;
import com.threerings.miso.data.MisoSceneModel;
import com.threerings.miso.data.ObjectInfo;
@@ -30,10 +54,13 @@ public class MisoScenePanel extends Sprite
{
public function MisoScenePanel (ctx :MisoContext, metrics :MisoSceneMetrics)
{
+ _ctx = ctx;
// Excitingly, we get to override this globally for as3isolib...
IsoMath.transformationObject = new MisoMetricsTransformation(metrics,
metrics.tilehei * 3 / 4);
+ _metrics = metrics;
+
_isoView = new IsoView();
_isoView.setSize(DEF_WIDTH, DEF_HEIGHT);
@@ -42,11 +69,6 @@ public class MisoScenePanel extends Sprite
addChild(_isoView);
}
- public function setSize (width :int, height :int) :void
- {
- _isoView.setSize(width, height);
- }
-
public function onClick (event :MouseEvent) :void
{
var viewPt :Point = _isoView.globalToLocal(new Point(event.stageX, event.stageY));
@@ -78,13 +100,43 @@ public class MisoScenePanel extends Sprite
var time :int = getTimer();
var baseArr :Array = [];
- for (var si :int = -10; si < 3; si++) {
- for (var sj :int = -8; sj < 4; sj++) {
+ for (var si :int = -2; si < 4; si++) {
+ for (var sj :int = -1; sj < 3; sj++) {
var baseScene :IsoScene = new IsoScene();
for (var ii :int = 10*si; ii < 10*si + 10; ii++) {
for (var jj :int = 10*sj; jj < 10*sj + 10; jj++) {
+ var tileId :int = _model.getBaseTileId(ii, jj);
+ if (tileId <= 0) {
+ var defSet :TileSet;
+ try {
+ var setId :int = _model.getDefaultBaseTileSet();
+ defSet = _ctx.getTileManager().getTileSet(setId);
+ tileId = TileUtil.getFQTileId(setId,
+ TileUtil.getTileHash(ii, jj) % defSet.getTileCount());
+ } catch (err :NoSuchTileSetError) {
+ // Someone else already complained...
+ continue;
+ }
+ }
+
+ var tileSet :TileSet;
+ try {
+ tileSet =
+ _ctx.getTileManager().getTileSet(TileUtil.getTileSetId(tileId));
+ } catch (err :NoSuchTileSetError) {
+ // Someone else already complained...
+ continue;
+ }
+
+ if (tileSet == null) {
+ trace("TileManager returned null tilset: " +
+ TileUtil.getTileSetId(tileId));
+ continue;
+ }
+
baseScene.addChild(
- new BaseTileIsoSprite(ii, jj, _model.getBaseTileId(ii, jj)));
+ new BaseTileIsoSprite(ii, jj, tileId,
+ tileSet.getTile(TileUtil.getTileIndex(tileId)), _metrics));
}
}
baseScene.render();
@@ -97,9 +149,24 @@ public class MisoScenePanel extends Sprite
time = getTimer();
for (ii = 0; ii < set.size(); ii++) {
var objInfo :ObjectInfo = set.get(ii);
- if (objInfo.priority == 0) {
- scene.addChild(new ObjectTileIsoSprite(objInfo.x, objInfo.y, objInfo.tileId));
+ var objTileId :int = objInfo.tileId;
+ var objTileSet :TileSet;
+ try {
+ objTileSet =
+ _ctx.getTileManager().getTileSet(TileUtil.getTileSetId(objTileId));
+ } catch (err :NoSuchTileSetError) {
+ // Someone else already complained...
+ continue;
}
+
+ if (objTileSet == null) {
+ trace("TileManager returned null TileSet: " + TileUtil.getTileSetId(objTileId));
+ continue;
+ }
+
+ scene.addChild(
+ new ObjectTileIsoSprite(objInfo.x, objInfo.y, objTileId,
+ objTileSet.getTile(TileUtil.getTileIndex(objTileId)), _metrics));
}
time = getTimer();
@@ -112,6 +179,10 @@ public class MisoScenePanel extends Sprite
protected var _isoView :IsoView;
+ protected var _ctx :MisoContext;
+
+ protected var _metrics :MisoSceneMetrics;
+
protected const DEF_WIDTH :int = 985;
protected const DEF_HEIGHT :int = 560;
}
diff --git a/src/as/com/threerings/miso/client/ObjectTileIsoSprite.as b/src/as/com/threerings/miso/client/ObjectTileIsoSprite.as
index 5820d31e..cc9216a2 100644
--- a/src/as/com/threerings/miso/client/ObjectTileIsoSprite.as
+++ b/src/as/com/threerings/miso/client/ObjectTileIsoSprite.as
@@ -1,20 +1,41 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
package com.threerings.miso.client {
-import as3isolib.display.primitive.IsoBox;
-import as3isolib.graphics.SolidColorFill;
+import com.threerings.media.tile.Tile;
+import com.threerings.miso.util.MisoSceneMetrics;
-public class ObjectTileIsoSprite extends IsoBox
+public class ObjectTileIsoSprite extends TileIsoSprite
{
- public function ObjectTileIsoSprite (x :int, y :int, tileId :int)
+ public function ObjectTileIsoSprite (x :int, y :int, tileId :int, tile :Tile,
+ metrics :MisoSceneMetrics)
{
- width = 1;
- height = 1;
- length = 1;
+ super(x, y, tileId, tile, metrics);
+ }
- moveTo(x, y, 0);
+ public override function layout (x :int, y :int, tile :Tile) :void
+ {
+ super.layout(x, y, tile);
- // TEMP
- fill = new SolidColorFill(tileId * 1000, 1.0);
+ setSize(tile.getBaseWidth(), tile.getBaseHeight(), 1);
+ moveBy(-(tile.getBaseWidth() - 1), -(tile.getBaseHeight() - 1), 0);
}
}
}
diff --git a/src/as/com/threerings/miso/client/TileIsoSprite.as b/src/as/com/threerings/miso/client/TileIsoSprite.as
new file mode 100644
index 00000000..62875e5b
--- /dev/null
+++ b/src/as/com/threerings/miso/client/TileIsoSprite.as
@@ -0,0 +1,100 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.miso.client {
+
+import flash.display.DisplayObject;
+
+import as3isolib.core.IsoDisplayObject;
+import as3isolib.display.IsoSprite;
+import as3isolib.display.primitive.IsoBox;
+import as3isolib.graphics.SolidColorFill;
+
+import com.threerings.media.tile.Tile;
+import com.threerings.media.tile.TileSet;
+import com.threerings.media.tile.TileUtil;
+import com.threerings.util.Log;
+import com.threerings.miso.util.MisoSceneMetrics;
+
+public class TileIsoSprite extends IsoDisplayObject
+{
+ private static var log :Log = Log.getLog(TileIsoSprite);
+
+ public function TileIsoSprite (x :int, y :int, tileId :int, tile :Tile,
+ metrics :MisoSceneMetrics)
+ {
+ _tileId = tileId;
+
+ _metrics = metrics;
+
+ moveTo(x, y, 0);
+
+ layout(x, y, tile);
+
+ if (tile.getImage() == null) {
+ var box :IsoBox = new IsoBox();
+ box.width = width;
+ box.height = height;
+ box.length = length;
+ box.fill = new SolidColorFill(0x808080, 0.5);
+ addChild(box);
+ } else {
+ addSprite(tile);
+ }
+ tile.notifyOnLoad(loaded);
+ }
+
+ public function layout (x :int, y :int, tile :Tile) :void
+ {
+ moveTo(x, y, 0);
+ }
+
+ /**
+ * Our tile was loaded, set ourselves up appropriately to use it.
+ */
+ public function loaded (tile :Tile) :void
+ {
+ removeAllChildren();
+ addSprite(tile);
+ }
+
+ protected function addSprite (tile :Tile) :void
+ {
+ var sprite :IsoSprite = new IsoSprite();
+ var image :DisplayObject = tile.getImage();
+ if (image == null) {
+ log.warning("TileIsoSprite tile image is null", "tileId", _tileId);
+ return;
+ }
+ // as3isolib uses top instead of bottom.
+ image.x = -tile.getOriginX() + _metrics.tilewid *
+ ((tile.getBaseWidth() - tile.getBaseHeight())/2);;
+ image.y = -tile.getOriginY() + _metrics.tilehei *
+ (1 + (tile.getBaseWidth() + tile.getBaseHeight())/2);
+ sprite.sprites = [image];
+ sprite.setSize(tile.getBaseWidth(), tile.getBaseHeight(), 1);
+ addChild(sprite);
+ render();
+ }
+
+ protected var _tileId :int;
+
+ protected var _metrics :MisoSceneMetrics;
+}
+}
\ No newline at end of file
diff --git a/src/as/com/threerings/miso/tile/MisoTileManager.as b/src/as/com/threerings/miso/tile/MisoTileManager.as
new file mode 100644
index 00000000..80143c4d
--- /dev/null
+++ b/src/as/com/threerings/miso/tile/MisoTileManager.as
@@ -0,0 +1,32 @@
+//
+// Nenya library - tools for developing networked games
+// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
+// http://www.threerings.net/code/nenya/
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.threerings.miso.tile {
+
+import com.threerings.media.tile.TileManager;
+
+/**
+ * Extends the basic tile manager and provides support for automatically generating fringes in
+ * between different types of base tiles in a scene.
+ */
+public class MisoTileManager extends TileManager
+{
+ // TODO: Fringing.
+}
+}
diff --git a/src/as/com/threerings/miso/util/MisoContext.as b/src/as/com/threerings/miso/util/MisoContext.as
index 22a30609..4cd8463d 100644
--- a/src/as/com/threerings/miso/util/MisoContext.as
+++ b/src/as/com/threerings/miso/util/MisoContext.as
@@ -18,11 +18,18 @@
package com.threerings.miso.util {
+import com.threerings.miso.tile.MisoTileManager;
+
/**
* Provides Miso code with access to the managers that it needs to do its
* thing. For now it is just a stub.
*/
public interface MisoContext
{
+ /**
+ * Returns a reference to the tile manager. This reference is valid
+ * for the lifetime of the application.
+ */
+ function getTileManager () :MisoTileManager;
}
}