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 = ; + + // yay, we're completely loaded! + dispatchEvent(new Event(Event.COMPLETE)); + } + + /** + * We have no metadata, so everything is undefined. + */ + public override function getData (name :String, formatType :String = null) :* + { + return undefined; + } + + /** + * We have no XML metadata, so our filename is exactly the original name. + */ + protected override function getFileName (name :String) :String + { + return name; + } + + public function getTileSetImage (path :String, zations :Array, callback :Function) :void + { + // TODO - DO SOMETHING WITH ZATIONS + getDisplayObjects(path, callback); + } + + public function getTileImage (path :String, bounds :Rectangle, zations :Array, + callback :Function) :void + { + getTileSetImage(path, zations, function(result :DisplayObject) :void { + // TODO - DO SOMETHING TO SUB_REGION THIS + if (result is Bitmap) { + var data :BitmapData = + new BitmapData(bounds.width, bounds.height, true, 0x00000000); + data.copyPixels(Bitmap(result).bitmapData, bounds, new Point(0, 0)); + callback(new Bitmap(data)); + } else { + callback(result); + } + }); + } +} +} \ No newline at end of file diff --git a/src/as/com/threerings/media/tile/TileManager.as b/src/as/com/threerings/media/tile/TileManager.as new file mode 100644 index 00000000..783c5716 --- /dev/null +++ b/src/as/com/threerings/media/tile/TileManager.as @@ -0,0 +1,138 @@ +// +// 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.Log; + +/** + * The tile manager provides a simplified interface for retrieving and caching tiles. Tiles can be + * loaded in two different ways. An application can load a tileset by hand, specifying the path to + * the tileset image and all of the tileset metadata necessary for extracting the image tiles, or + * it can provide a tileset repository which loads up tilesets using whatever repository mechanism + * is implemented by the supplied repository. In the latter case, tilesets are loaded by a unique + * identifier. + * + *

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; } }