From 2f634e3a60447adac422326f76dce93cd09041c4 Mon Sep 17 00:00:00 2001 From: Mike Thomas Date: Tue, 8 Jun 2010 01:48:12 +0000 Subject: [PATCH] Port of the miso scene model and related bits (so, data only, no rendering of any kind yet). git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@923 ed5b42cb-e716-0410-a449-f6a68f950b19 --- src/as/com/threerings/media/tile/TileUtil.as | 67 ++++ .../threerings/miso/data/MisoSceneModel.as | 101 ++++++ src/as/com/threerings/miso/data/ObjectInfo.as | 201 +++++++++++ .../miso/data/SparseMisoSceneModel.as | 248 ++++++++++++++ .../SparseMisoSceneModel_ObjectVisitor.as | 26 ++ .../miso/data/SparseMisoSceneModel_Section.as | 265 +++++++++++++++ .../miso/data/VirtualMisoSceneModel.as | 56 +++ .../com/threerings/miso/util/MisoContext.as | 28 ++ src/as/com/threerings/miso/util/ObjectSet.as | 160 +++++++++ src/as/com/threerings/util/DirectionCodes.as | 111 ++++++ src/as/com/threerings/util/DirectionUtil.as | 320 ++++++++++++++++++ 11 files changed, 1583 insertions(+) create mode 100644 src/as/com/threerings/media/tile/TileUtil.as create mode 100644 src/as/com/threerings/miso/data/MisoSceneModel.as create mode 100644 src/as/com/threerings/miso/data/ObjectInfo.as create mode 100644 src/as/com/threerings/miso/data/SparseMisoSceneModel.as create mode 100644 src/as/com/threerings/miso/data/SparseMisoSceneModel_ObjectVisitor.as create mode 100644 src/as/com/threerings/miso/data/SparseMisoSceneModel_Section.as create mode 100644 src/as/com/threerings/miso/data/VirtualMisoSceneModel.as create mode 100644 src/as/com/threerings/miso/util/MisoContext.as create mode 100644 src/as/com/threerings/miso/util/ObjectSet.as create mode 100644 src/as/com/threerings/util/DirectionCodes.as create mode 100644 src/as/com/threerings/util/DirectionUtil.as diff --git a/src/as/com/threerings/media/tile/TileUtil.as b/src/as/com/threerings/media/tile/TileUtil.as new file mode 100644 index 00000000..7b3aac24 --- /dev/null +++ b/src/as/com/threerings/media/tile/TileUtil.as @@ -0,0 +1,67 @@ +// Nenya library - tools for developing networked games +// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved +// http://www.threerings.net/code/nenya/ +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.threerings.media.tile { + +public class TileUtil +{ + /** + * Generates a fully-qualified tile id given the supplied tileset id + * and tile index. + */ + public static function getFQTileId (tileSetId :int, tileIndex :int) :int + { + return (tileSetId << 16) | tileIndex; + } + + /** + * Extracts the tile set id from the supplied fully qualified tile id. + */ + public static function getTileSetId (fqTileId :int) :int + { + return (fqTileId >> 16); + } + + /** + * Extracts the tile index from the supplied fully qualified tile id. + */ + public static function getTileIndex (fqTileId :int) :int + { + return (fqTileId & 0xFFFF); + } + + /** + * Compute some hash value for "randomizing" tileset picks + * based on x and y coordinates. + * NOTE: Because actionscript doesn't handle longs well, this does NOT match the implementation + * of the java version of getTileHash() + * + * @return a positive, seemingly random number based on x and y. + */ + public static function getTileHash (x :int, y :int) :int + { + var seed :int = ((x ^ y) ^ MULTIPLIER) & MASK; + var hash :int = (seed * MULTIPLIER + ADDEND) & MASK; + return hash >>> 10; + } + + protected static const MULTIPLIER :int = 0x5E66D; + protected static const ADDEND :int = 0xB; + protected static const MASK :int = (1 << 16) - 1; +} +} diff --git a/src/as/com/threerings/miso/data/MisoSceneModel.as b/src/as/com/threerings/miso/data/MisoSceneModel.as new file mode 100644 index 00000000..54879f26 --- /dev/null +++ b/src/as/com/threerings/miso/data/MisoSceneModel.as @@ -0,0 +1,101 @@ +// +// $Id: MisoSceneModel.java 872 2010-01-13 18:28:28Z ray $ +// +// 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.data { + +import flash.geom.Rectangle; + +import com.threerings.miso.util.ObjectSet; +import com.threerings.io.SimpleStreamableObject; +import com.threerings.miso.data.MisoSceneModel; +import com.threerings.io.ObjectInputStream; +import com.threerings.miso.data.ObjectInfo; +import com.threerings.util.ClassUtil; +import com.threerings.util.Cloneable; +import com.threerings.io.ObjectOutputStream; + +/** + * Contains basic information for a miso scene model that is shared among + * the specialized model implementations. + */ +public /*abstract*/ class MisoSceneModel extends SimpleStreamableObject + implements Cloneable +{ + public function clone () :Object + { + return (ClassUtil.newInstance(this) as MisoSceneModel); + } + + /** + * Returns the fully qualified tile id of the base tile at the + * specified coordinates. -1 will be returned if there is + * no tile at the specified coordinate. + */ + public function getBaseTileId (arg1 :int, arg2 :int) :int + { + throw new Error("abstract"); + } + + public function setBaseTile (arg1 :int, arg2 :int, arg3 :int) :Boolean + { + throw new Error("abstract"); + } + + public function setDefaultBaseTileSet (arg1 :int) :void + { + // nothing doing + } + + /** + * Scene models can return a default tileset to be used when no base + * tile data exists for a particular tile. + */ + public function getDefaultBaseTileSet () :int + { + return 0; + } + + /** + * Populates the supplied object set with info on all objects whose + * origin falls in the requested region. + */ + public function getObjects (region :Rectangle, set :ObjectSet) :void + { + throw new Error("abstract"); + } + + public function addObject (info :ObjectInfo) :Boolean + { + throw new Error("abstract"); + } + + public function updateObject (info :ObjectInfo) :void + { + throw new Error("abstract"); + } + + public function removeObject (info :ObjectInfo) :Boolean + { + throw new Error("abstract"); + } + +} +} diff --git a/src/as/com/threerings/miso/data/ObjectInfo.as b/src/as/com/threerings/miso/data/ObjectInfo.as new file mode 100644 index 00000000..a5ec526b --- /dev/null +++ b/src/as/com/threerings/miso/data/ObjectInfo.as @@ -0,0 +1,201 @@ +// +// $Id: ObjectInfo.java 872 2010-01-13 18:28:28Z ray $ +// +// 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.data { + +import com.threerings.util.Hashable; +import com.threerings.util.ClassUtil; +import com.threerings.util.Cloneable; +import com.threerings.util.StringUtil; + +import com.threerings.io.SimpleStreamableObject; +import com.threerings.io.ObjectInputStream; +import com.threerings.io.ObjectOutputStream; + +import com.threerings.miso.data.ObjectInfo; +import com.threerings.media.tile.TileUtil; + +/** + * Contains information about an object in a Miso scene. + */ +public class ObjectInfo extends SimpleStreamableObject + implements Cloneable, Hashable +{ + /** The fully qualified object tile id. */ + public var tileId :int; + + /** The x and y tile coordinates of the object. */ + public var x :int; + public var y :int; + + /** Don't access this directly unless you are serializing this + * instance. Use {@link #getPriority} instead. */ + public var priority :int = 0; + + /** The action associated with this object or null if it has no + * action. */ + public var action :String; + + /** A "spot" associated with this object (specified as an offset from + * the fine coordinates of the object's origin tile). */ + public var sx :int; + public var sy :int; + + /** The orientation of the "spot" associated with this object. */ + public var sorient :int; + + /** Up to two colorization assignments for this object. */ + public var zations :int; + + public function ObjectInfo (tileId :int = 0, x :int = 0, y :int = 0) + { + this.tileId = tileId; + this.x = x; + this.y = y; + } + + public function hashCode () :int + { + return x ^ y ^ tileId; + } + + public function clone () :Object + { + var info :ObjectInfo = (ClassUtil.newInstance(this) as ObjectInfo); + info.tileId = tileId; + info.x = x; + info.y = y; + info.priority = priority; + info.action = action; + info.sx = sx; + info.sy = sy; + info.sorient = sorient; + info.zations = zations; + return info; + } + + public function equals (other :Object) :Boolean + { + if (other is ObjectInfo) { + var ooi :ObjectInfo = ObjectInfo(other); + return (x == ooi.x && y == ooi.y && tileId == ooi.tileId); + } else { + return false; + } + } + + /** + * Returns the render priority of this object tile. + */ + public function getPriority () :int + { + return priority; + } + + /** + * Returns the primary colorization assignment. + */ + public function getPrimaryZation () :int + { + return (zations & 0xFF); + } + + /** + * Returns the secondary colorization assignment. + */ + public function getSecondaryZation () :int + { + return ((zations >> 16) & 0xFF); + } + + /** + * Returns the tertiary colorization assignment. + */ + public function getTertiaryZation () :int + { + return ((zations >> 24) & 0xFF); + } + + /** + * Returns the quaternary colorization assignment. + */ + public function getQuaternaryZation () :int + { + return ((zations >> 8) & 0xFF); + } + + /** + * Sets the primary and secondary colorization assignments. + */ + public function setZations (primary :int, secondary :int, tertiary :int, quaternary :int) :void + { + zations = (primary | (secondary << 16) | (tertiary << 24) | (quaternary << 8)); + } + + /** + * Returns true if this object info contains non-default data for + * anything other than the tile id and coordinates. + */ + public function isInteresting () :Boolean + { + return (!StringUtil.isBlank(action) || priority != 0 || + sx != 0 || sy != 0 || zations != 0); + } + + /** Enhances our {@link SimpleStreamableObject#toString} output. */ + public function tileIdToString () :String + { + return (TileUtil.getTileSetId(tileId) + ":" + + TileUtil.getTileIndex(tileId)); + } + + // from interface Streamable + override public function readObject (ins :ObjectInputStream) :void + { + super.readObject(ins); + tileId = ins.readInt(); + x = ins.readInt(); + y = ins.readInt(); + priority = ins.readByte(); + action = ins.readField(String); + sx = ins.readByte(); + sy = ins.readByte(); + sorient = ins.readByte(); + zations = ins.readInt(); + } + + // from interface Streamable + override public function writeObject (out :ObjectOutputStream) :void + { + super.writeObject(out); + out.writeInt(tileId); + out.writeInt(x); + out.writeInt(y); + out.writeByte(priority); + out.writeField(action); + out.writeByte(sx); + out.writeByte(sy); + out.writeByte(sorient); + out.writeInt(zations); + } + +} +} diff --git a/src/as/com/threerings/miso/data/SparseMisoSceneModel.as b/src/as/com/threerings/miso/data/SparseMisoSceneModel.as new file mode 100644 index 00000000..400d9add --- /dev/null +++ b/src/as/com/threerings/miso/data/SparseMisoSceneModel.as @@ -0,0 +1,248 @@ +// +// $Id: SparseMisoSceneModel.java 901 2010-03-11 00:51:30Z mthomas $ +// +// 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.data { + +import flash.geom.Rectangle; + +import com.threerings.io.ObjectInputStream; +import com.threerings.io.SimpleStreamableObject; +import com.threerings.io.ObjectOutputStream; + +import com.threerings.util.ArrayIterator; +import com.threerings.util.ClassUtil; +import com.threerings.util.Iterator; +import com.threerings.util.Joiner; +import com.threerings.util.MathUtil; +import com.threerings.util.StreamableHashMap; +import com.threerings.util.StringUtil; + +import com.threerings.miso.util.ObjectSet; +import com.threerings.miso.data.MisoSceneModel; +import com.threerings.miso.data.SparseMisoSceneModel; +import com.threerings.miso.data.ObjectInfo; +import com.threerings.miso.data.SparseMisoSceneModel_ObjectVisitor; +import com.threerings.miso.data.SparseMisoSceneModel_Section; + +/** + * Contains miso scene data that is broken up into NxN tile sections. + */ +public class SparseMisoSceneModel extends MisoSceneModel +{ + public var swidth :int; + public var sheight :int; + /** The dimensions of a section of our scene. */ + + /** The tileset to use when we have no tile data. */ + public var defTileSet :int = 0; + + override public function clone () :Object + { + var model :SparseMisoSceneModel = (ClassUtil.newInstance(this) as SparseMisoSceneModel); + model._sections = new StreamableHashMap(); + for (var iter :Iterator = getSections(); iter.hasNext(); ) { + var sect :SparseMisoSceneModel_Section = SparseMisoSceneModel_Section(iter.next()); + model.setSection(SparseMisoSceneModel_Section(sect.clone())); + } + return model; + } + + override public function getBaseTileId (col :int, row :int) :int + { + var sec :SparseMisoSceneModel_Section = getSection(col, row, false); + return (sec == null) ? -1 : sec.getBaseTileId(col, row); + } + + override public function setBaseTile (fqBaseTileId :int, col :int, row :int) :Boolean + { + getSection(col, row, true).setBaseTile(col, row, fqBaseTileId); + return true; + } + + override public function setDefaultBaseTileSet (tileSetId :int) :void + { + defTileSet = tileSetId; + } + + override public function getDefaultBaseTileSet () :int + { + return defTileSet; + } + + override public function getObjects (region :Rectangle, set :ObjectSet) :void + { + var minx :int = MathUtil.floorDiv(region.x, swidth)*swidth; + var maxx :int = MathUtil.floorDiv(region.x+region.width-1, swidth)*swidth; + var miny :int = MathUtil.floorDiv(region.y, sheight)*sheight; + var maxy :int = MathUtil.floorDiv(region.y+region.height-1, sheight)*sheight; + for (var yy :int = miny; yy <= maxy; yy += sheight) { + for (var xx :int = minx; xx <= maxx; xx += swidth) { + var sec :SparseMisoSceneModel_Section = getSection(xx, yy, false); + if (sec != null) { + sec.getObjects(region, set); + } + } + } + } + + override public function addObject (info :ObjectInfo) :Boolean + { + return getSection(info.x, info.y, true).addObject(info); + } + + override public function updateObject (info :ObjectInfo) :void + { + // not efficient, but this is only done in editing situations + removeObject(info); + addObject(info); + } + + override public function removeObject (info :ObjectInfo) :Boolean + { + var sec :SparseMisoSceneModel_Section = getSection(info.x, info.y, false); + if (sec != null) { + return sec.removeObject(info); + } else { + return false; + } + } + + /** + * Adds all interesting {@link ObjectInfo} records in this scene to + * the supplied list. + */ + public function getInterestingObjects (list :Array) :void + { + for (var iter :Iterator = getSections(); iter.hasNext(); ) { + var sect :SparseMisoSceneModel_Section = SparseMisoSceneModel_Section(iter.next()); + for each (var element :ObjectInfo in sect.objectInfo) { + list.add(element); + } + } + } + + /** + * Don't call this method! This is only public so that the scene + * writer can generate XML from the raw scene data. + */ + public function getSections () :Iterator + { + return new ArrayIterator(_sections.values()); + } + + /** + * Adds all {@link ObjectInfo} records in this scene to the supplied list. + */ + public function getAllObjects (list :Array) :void + { + for (var iter :Iterator = getSections(); iter.hasNext(); ) { + iter.next().getAllObjects(list); + } + } + + /** + * Informs the supplied visitor of each object in this scene. + * + * @param interestingOnly if true, only the interesting objects will + * be visited. + */ + public function visitObjects (visitor :SparseMisoSceneModel_ObjectVisitor, + interestingOnly :Boolean = false) :void + { + for (var iter :Iterator = getSections(); iter.hasNext(); ) { + var sect :SparseMisoSceneModel_Section = SparseMisoSceneModel_Section(iter.next()); + for each (var oinfo :ObjectInfo in sect.objectInfo) { + visitor.visit(oinfo); + } + if (!interestingOnly) { + for (var oo :int = 0; oo < sect.objectTileIds.length; oo++) { + var info :ObjectInfo = new ObjectInfo(sect.objectTileIds[oo], + sect.objectXs[oo], sect.objectYs[oo]); + visitor.visit(info); + } + } + } + } + + /** + * Don't call this method! This is only public so that the scene + * parser can construct a scene from raw data. If only Java supported + * class friendship. + */ + public function setSection (section :SparseMisoSceneModel_Section) :void + { + _sections.put(key(section.x, section.y), section); + } + + // from interface Streamable + override public function readObject (ins :ObjectInputStream) :void + { + super.readObject(ins); + swidth = ins.readShort(); + sheight = ins.readShort(); + defTileSet = ins.readInt(); + _sections = ins.readObject(StreamableHashMap); + } + + // from interface Streamable + override public function writeObject (out :ObjectOutputStream) :void + { + super.writeObject(out); + out.writeShort(swidth); + out.writeShort(sheight); + out.writeInt(defTileSet); + out.writeObject(_sections); + } + + override protected function toStringJoiner (j :Joiner) :void + { + super.toStringJoiner(j); + j.add("sections", StringUtil.toString(_sections.values())); + } + + /** + * Returns the key for the specified section. + */ + protected function key (x :int, y :int) :int + { + var sx :int = MathUtil.floorDiv(x, swidth); + var sy :int = MathUtil.floorDiv(y, sheight); + return (sx << 16) | (sy & 0xFFFF); + } + + /** Returns the section for the specified tile coordinate. */ + protected function getSection (x :int, y :int, create :Boolean) :SparseMisoSceneModel_Section + { + var key :int = key(x, y); + var sect :SparseMisoSceneModel_Section = _sections.get(key); + if (sect == null && create) { + var sx :int = MathUtil.floorDiv(x, swidth)*swidth; + var sy :int = MathUtil.floorDiv(y, sheight)*sheight; + _sections.put(key, sect = new SparseMisoSceneModel_Section(sx, sy, swidth, sheight)); + } + return sect; + } + + /** Contains our sections in row major order. */ + protected var _sections :StreamableHashMap = new StreamableHashMap(); + +} +} diff --git a/src/as/com/threerings/miso/data/SparseMisoSceneModel_ObjectVisitor.as b/src/as/com/threerings/miso/data/SparseMisoSceneModel_ObjectVisitor.as new file mode 100644 index 00000000..f847b299 --- /dev/null +++ b/src/as/com/threerings/miso/data/SparseMisoSceneModel_ObjectVisitor.as @@ -0,0 +1,26 @@ +// 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.data { + +public interface SparseMisoSceneModel_ObjectVisitor +{ + /** Called for each object in the scene, interesting and not. */ + function visit (info :ObjectInfo) :void; +} +} diff --git a/src/as/com/threerings/miso/data/SparseMisoSceneModel_Section.as b/src/as/com/threerings/miso/data/SparseMisoSceneModel_Section.as new file mode 100644 index 00000000..6bf22acc --- /dev/null +++ b/src/as/com/threerings/miso/data/SparseMisoSceneModel_Section.as @@ -0,0 +1,265 @@ +// +// $Id: SparseMisoSceneModel_Section.java 901 2010-03-11 00:51:30Z mthomas $ +// +// 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.data { + +import flash.geom.Rectangle; + +import com.threerings.io.ObjectOutputStream; +import com.threerings.io.ObjectInputStream; +import com.threerings.io.SimpleStreamableObject; +import com.threerings.io.TypedArray; +import com.threerings.miso.util.ObjectSet; +import com.threerings.util.ArrayUtil; +import com.threerings.util.ClassUtil; +import com.threerings.util.Cloneable; +import com.threerings.util.Log; +import com.threerings.util.StringUtil; + +public class SparseMisoSceneModel_Section extends SimpleStreamableObject + implements Cloneable +{ + public static const log :Log = Log.getLog(SparseMisoSceneModel_Section); + + /** The tile coordinate of our upper leftmost tile. */ + public var x :int; + public var y :int; + + /** The width of this section in tiles. */ + public var width :int; + + /** The combined tile ids (tile set id and tile id) for our + * section (in row major order). */ + public var baseTileIds :TypedArray; + + /** The combined tile ids (tile set id and tile id) of the + * "uninteresting" tiles in the object layer. */ + public var objectTileIds :TypedArray = TypedArray.create(int); + + /** The x coordinate of the "uninteresting" tiles in the object + * layer. */ + public var objectXs :TypedArray = TypedArray.createShort(); + + /** The y coordinate of the "uninteresting" tiles in the object + * layer. */ + public var objectYs :TypedArray = TypedArray.createShort(); + + /** Information records for the "interesting" objects in the + * object layer. */ + public var objectInfo :TypedArray = TypedArray.create(ObjectInfo); + + /** + * Creates a new scene section with the specified dimensions. + */ + public function SparseMisoSceneModel_Section ( + x :int = 0, y :int = 0, width :int = 0, height :int = 0) + { + this.x = x; + this.y = y; + this.width = width; + baseTileIds = TypedArray.create(int, ArrayUtil.create(width*height)); + } + + public function getBaseTileId (col :int, row :int) :int + { + if (col < x || col >= (x+width) || row < y || row >= (y+width)) { + log.warning("Requested bogus tile +" + col + "+" + row + + " from " + this + "."); + return -1; + } else { + return baseTileIds[(row-y)*width+(col-x)]; + } + } + + public function setBaseTile (col :int, row :int, fqBaseTileId :int) :void + { + baseTileIds[(row-y)*width+(col-x)] = fqBaseTileId; + } + + public function addObject (info :ObjectInfo) :Boolean + { + // sanity check: see if there is already an object of this + // type at these coordinates + var dupidx :int; + if ((dupidx = ArrayUtil.indexOf(objectInfo, info)) != -1) { + log.warning("Refusing to add duplicate object [ninfo=" + info + + ", oinfo=" + objectInfo[dupidx] + "]."); + return false; + } + if ((dupidx = indexOfUn(info)) != -1) { + log.warning("Refusing to add duplicate object " + + "[info=" + info + "]."); + return false; + } + + if (info.isInteresting()) { + objectInfo = TypedArray(objectInfo.concat(info)); + } else { + objectTileIds = TypedArray(objectTileIds.concat(info.tileId)); + objectXs = TypedArray(objectXs.concat(info.x)); + objectYs = TypedArray(objectYs.concat(info.y)); + } + return true; + } + + public function removeObject (info :ObjectInfo) :Boolean + { + // look for it in the interesting info array + var oidx :int = ArrayUtil.indexOf(objectInfo, info); + if (oidx != -1) { + objectInfo = TypedArray(ArrayUtil.splice(objectInfo, oidx, 1)); + return true; + } + + // look for it in the uninteresting arrays + oidx = indexOfUn(info); + if (oidx != -1) { + objectTileIds = TypedArray(ArrayUtil.splice(objectTileIds, oidx, 1)); + objectXs = TypedArray(ArrayUtil.splice(objectXs, oidx, 1)); + objectYs = TypedArray(ArrayUtil.splice(objectYs, oidx, 1)); + return true; + } + + return false; + } + + /** + * Returns the index of the specified object in the uninteresting + * arrays or -1 if it is not in this section as an uninteresting + * object. + */ + protected function indexOfUn (info :ObjectInfo) :int + { + for (var ii :int = 0; ii < objectTileIds.length; ii++) { + if (objectTileIds[ii] == info.tileId && + objectXs[ii] == info.x && objectYs[ii] == info.y) { + return ii; + } + } + return -1; + } + + public function getAllObjects (list :Array) :void + { + for each (var info :ObjectInfo in objectInfo) { + list.add(info); + } + for (var ii :int= 0; ii < objectTileIds.length; ii++) { + var x :int = objectXs[ii]; + var y :int = objectYs[ii]; + list.add(new ObjectInfo(objectTileIds[ii], x, y)); + } + } + + public function getObjects (region :Rectangle, set :ObjectSet) :void + { + // first look for intersecting interesting objects + for each (var info :ObjectInfo in objectInfo) { + if (region.contains(info.x, info.y)) { + set.insert(info); + } + } + + // now look for intersecting non-interesting objects + for (var ii :int = 0; ii < objectTileIds.length; ii++) { + var x :int = objectXs[ii]; + var y :int = objectYs[ii]; + if (region.contains(x, y)) { + set.insert(new ObjectInfo(objectTileIds[ii], x, y)); + } + } + } + + /** + * Returns true if this section contains no data beyond the default. + * Used when saving a sparse scene: we omit blank sections. + */ + public function isBlank () :Boolean + { + if ((objectTileIds.length != 0) || (objectInfo.length != 0)) { + return false; + } + for each (var baseTileId :int in baseTileIds) { + if (baseTileId != 0) { + return false; + } + } + + return true; + } + + public function clone () :Object + { + var section :SparseMisoSceneModel_Section = + (ClassUtil.newInstance(this) as SparseMisoSceneModel_Section); + section.x = x; + section.y = y; + section.width = width; + section.baseTileIds = TypedArray(ArrayUtil.copyOf(baseTileIds)); + section.objectTileIds = TypedArray(ArrayUtil.copyOf(objectTileIds)); + section.objectXs = TypedArray(ArrayUtil.copyOf(objectXs)); + section.objectYs = TypedArray(ArrayUtil.copyOf(objectYs)); + section.objectInfo = TypedArray.create(ObjectInfo); + for (var ii :int = 0; ii < objectInfo.length; ii++) { + section.objectInfo[ii] = objectInfo[ii].clone(); + } + return section; + } + + override public function toString () :String + { + if (width == 0 || baseTileIds == null) { + return ""; + } else { + return width + "x" + (baseTileIds.length / width) + "+" + x + ":" + y + ":" + + objectInfo.length; + } + } + + // from interface Streamable + override public function readObject (ins :ObjectInputStream) :void + { + super.readObject(ins); + x = ins.readShort(); + y = ins.readShort(); + width = ins.readInt(); + baseTileIds = ins.readField(TypedArray.getJavaType(int)); + objectTileIds = ins.readField(TypedArray.getJavaType(int)); + objectXs = ins.readField(TypedArray.getJavaShortType()); + objectYs = ins.readField(TypedArray.getJavaShortType()); + objectInfo = TypedArray(ins.readObject()); + } + + // from interface Streamable + override public function writeObject (out :ObjectOutputStream) :void + { + super.writeObject(out); + out.writeShort(x); + out.writeShort(y); + out.writeInt(width); + out.writeField(baseTileIds); + out.writeField(objectTileIds); + out.writeField(objectXs); + out.writeField(objectYs); + out.writeField(objectInfo); + } +} +} diff --git a/src/as/com/threerings/miso/data/VirtualMisoSceneModel.as b/src/as/com/threerings/miso/data/VirtualMisoSceneModel.as new file mode 100644 index 00000000..6534ff83 --- /dev/null +++ b/src/as/com/threerings/miso/data/VirtualMisoSceneModel.as @@ -0,0 +1,56 @@ +// +// $Id: VirtualMisoSceneModel.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.miso.data { + +import com.threerings.io.ObjectInputStream; +import com.threerings.miso.data.ObjectInfo; +import com.threerings.io.ObjectOutputStream; + +/** + * A convenient base class for "virtual" scenes which do not allow editing + * and compute the base and object tiles rather than obtain them from some + * data structure. + */ +public /*abstract*/ class VirtualMisoSceneModel extends MisoSceneModel +{ + override public function setBaseTile (arg1 :int, arg2 :int, arg3 :int) :Boolean + { + throw new Error("Unsupported"); + } + + override public function addObject (arg1 :ObjectInfo) :Boolean + { + throw new Error("Unsupported"); + } + + override public function updateObject (arg1 :ObjectInfo) :void + { + throw new Error("Unsupported"); + } + + override public function removeObject (arg1 :ObjectInfo) :Boolean + { + throw new Error("Unsupported"); + } + +} +} diff --git a/src/as/com/threerings/miso/util/MisoContext.as b/src/as/com/threerings/miso/util/MisoContext.as new file mode 100644 index 00000000..22a30609 --- /dev/null +++ b/src/as/com/threerings/miso/util/MisoContext.as @@ -0,0 +1,28 @@ +// 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.util { + +/** + * 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 +{ +} +} diff --git a/src/as/com/threerings/miso/util/ObjectSet.as b/src/as/com/threerings/miso/util/ObjectSet.as new file mode 100644 index 00000000..f6faeac3 --- /dev/null +++ b/src/as/com/threerings/miso/util/ObjectSet.as @@ -0,0 +1,160 @@ +// Nenya library - tools for developing networked games +// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved +// http://www.threerings.net/code/nenya/ +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.threerings.miso.util { + +import com.threerings.miso.data.ObjectInfo; +import com.threerings.util.ArrayUtil; +import com.threerings.util.Log; + +/** + * Used to store an (arbitrarily) ordered, low-impact iteratable (doesn't + * require object creation), set of {@link ObjectInfo} instances. + */ +public class ObjectSet +{ + public static const log :Log = Log.getLog(ObjectSet); + + /** + * Inserts the supplied object into the set. + * + * @return true if it was inserted, false if the object was already in + * the set. + */ + public function insert (info :ObjectInfo) :Boolean + { + // bail if it's already in the set + var ipos :int = indexOf(info); + if (ipos >= 0) { + // log a warning because the caller shouldn't be doing this + log.warning("Requested to add an object to a set that already " + + "contains such an object [ninfo=" + info + + ", oinfo=" + _objs[ipos] + "].", new Error()); + return false; + } + + // otherwise insert it + ipos = -(ipos+1); + _objs = _objs.splice(ipos, 0, info); + return true; + } + + /** + * Returns true if the specified object is in the set, false if it is + * not. + */ + public function contains (info :ObjectInfo) :Boolean + { + return (indexOf(info) >= 0); + } + + /** + * Returns the number of objects in this set. + */ + public function size () :int + { + return _objs.length; + } + + /** + * Returns the object with the specified index. The index must & be + * between 0 and {@link #size}-1. + */ + public function get (index :int) :ObjectInfo + { + return ObjectInfo(_objs[index]); + } + + /** + * Removes the object at the specified index. + */ + public function removeAt (index :int) :void + { + ArrayUtil.removeFirst(_objs, index); + } + + /** + * Removes the specified object from the set. + * + * @return true if it was removed, false if it was not in the set. + */ + public function remove (info :ObjectInfo) :Boolean + { + var opos :int = indexOf(info); + if (opos >= 0) { + removeAt(opos); + return true; + } else { + return false; + } + } + + /** + * Clears out the contents of this set. + */ + public function clear () :void + { + _objs.length = 0; + } + + /** + * Converts the contents of this object set to an array. + */ + public function toArray () :Array + { + return ArrayUtil.copyOf(_objs); + } + + public function toString () :String + { + var result :String = "["; + for (var ii :int = 0; ii < _objs.length; ii++) { + if (ii > 0) { + result += ", "; + } + result += _objs[ii]; + } + return result += "]"; + } + + /** We simply sort the objects in order of their hash code. We don't + * care about their order, it exists only to support binary search. */ + protected function searchCompare (o1 :Object, o2 :Object) :int + { + var do1 :ObjectInfo = ObjectInfo(o1); + var do2 :ObjectInfo = ObjectInfo(o2); + if (do1.tileId == do2.tileId) { + return ((do1.x << 16) + do1.y) - ((do2.x << 16) + do2.y); + } else { + return do1.tileId - do2.tileId; + } + }; + + /** + * Returns the index of the object or it's insertion index if it is + * not in the set. + */ + protected function indexOf (info :ObjectInfo) :int + { + return ArrayUtil.binarySearch(_objs, 0, _objs.length, info, searchCompare); + } + + /** Our sorted array of objects. */ + protected var _objs :Array = new Array(); +} +} diff --git a/src/as/com/threerings/util/DirectionCodes.as b/src/as/com/threerings/util/DirectionCodes.as new file mode 100644 index 00000000..c0e66301 --- /dev/null +++ b/src/as/com/threerings/util/DirectionCodes.as @@ -0,0 +1,111 @@ +// 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.util { + +/** + * A single, top-level location for the definition of compass direction + * constants, which are used by a variety of Narya services. + */ +public class DirectionCodes +{ + /** A direction code indicating no direction. */ + public static const NONE :int = -1; + + /** A direction code indicating moving left. */ + public static const LEFT :int = 0; + + /** A direction code indicating moving right. */ + public static const RIGHT :int = 1; + + /** A direction code indicating moving up. */ + public static const UP :int = 2; + + /** A direction code indicating moving down. */ + public static const DOWN :int = 3; + + /** A direction code indicating a counter-clockwise rotation. */ + public static const CCW :int = 0; + + /** A direction code indicating a clockwise rotation. */ + public static const CW :int = 1; + + /** A direction code indicating horizontal movement. */ + public static const HORIZONTAL :int = 0; + + /** A direction code indicating vertical movement. */ + public static const VERTICAL :int = 1; + + /** A direction code indicating southwest. */ + public static const SOUTHWEST :int = 0; + + /** A direction code indicating west. */ + public static const WEST :int = 1; + + /** A direction code indicating northwest. */ + public static const NORTHWEST :int = 2; + + /** A direction code indicating north. */ + public static const NORTH :int = 3; + + /** A direction code indicating northeast. */ + public static const NORTHEAST :int = 4; + + /** A direction code indicating east. */ + public static const EAST :int = 5; + + /** A direction code indicating southeast. */ + public static const SOUTHEAST :int = 6; + + /** A direction code indicating south. */ + public static const SOUTH :int = 7; + + /** The number of basic compass directions. */ + public static const DIRECTION_COUNT :int = 8; + + /** A direction code indicating west by southwest. */ + public static const WESTSOUTHWEST :int = 8; + + /** A direction code indicating west by northwest. */ + public static const WESTNORTHWEST :int = 9; + + /** A direction code indicating north by northwest. */ + public static const NORTHNORTHWEST :int = 10; + + /** A direction code indicating north by northeast. */ + public static const NORTHNORTHEAST :int = 11; + + /** A direction code indicating east by northeast. */ + public static const EASTNORTHEAST :int = 12; + + /** A direction code indicating east by southeast. */ + public static const EASTSOUTHEAST :int = 13; + + /** A direction code indicating south by southeast. */ + public static const SOUTHSOUTHEAST :int = 14; + + /** A direction code indicating south by southwest. */ + public static const SOUTHSOUTHWEST :int = 15; + + /** The number of fine compass directions. */ + public static const FINE_DIRECTION_COUNT :int = 16; + + /** The four points of the compass. */ + public static const CARDINAL_DIRECTIONS :Array = [ NORTH, EAST, SOUTH, WEST ]; +} +} diff --git a/src/as/com/threerings/util/DirectionUtil.as b/src/as/com/threerings/util/DirectionUtil.as new file mode 100644 index 00000000..c0dbd018 --- /dev/null +++ b/src/as/com/threerings/util/DirectionUtil.as @@ -0,0 +1,320 @@ +// 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.util { + +import flash.geom.Point; + +import com.threerings.util.ArrayUtil; + +public class DirectionUtil extends DirectionCodes +{ + /** + * Returns an array of names corresponding to each direction constant. + */ + public static function getDirectionNames () :Array + { + return DIR_STRINGS; + } + + /** + * Returns a string representation of the supplied direction code. + */ + public static function toString (direction :int) :String + { + return ((direction >= 0) && (direction < FINE_DIRECTION_COUNT)) ? + DIR_STRINGS[direction] : "INVALID"; + } + + /** + * Returns an abbreviated string representation of the supplied + * direction code. + */ + public static function toShortString (direction :int) :String + { + return ((direction >= 0) && (direction < FINE_DIRECTION_COUNT)) ? + SHORT_DIR_STRINGS[direction] : "?"; + } + + /** + * Returns the direction code that corresponds to the supplied string + * or {@link #NONE} if the string does not correspond to a known + * direction code. + */ + public static function fromString (dirstr :String) :int + { + for (var ii :int = 0; ii < FINE_DIRECTION_COUNT; ii++) { + if (DIR_STRINGS[ii].equals(dirstr)) { + return ii; + } + } + return NONE; + } + + /** + * Returns the direction code that corresponds to the supplied short + * string or {@link #NONE} if the string does not correspond to a + * known direction code. + */ + public static function fromShortString (dirstr :String) :int + { + for (var ii :int = 0; ii < FINE_DIRECTION_COUNT; ii++) { + if (SHORT_DIR_STRINGS[ii].equals(dirstr)) { + return ii; + } + } + return NONE; + } + + /** + * Returns a string representation of an array of direction codes. The + * directions are represented by the abbreviated names. + */ + public static function dirsToString (directions :Array) :String + { + var result :String = "{"; + for (var ii :int = 0; ii < directions.length; ii++) { + if (ii > 0) { + result += ", "; + } + result += toShortString(directions[ii]); + } + result += "}"; + return result; + } + + /** + * Rotates the requested fine direction constant clockwise by + * the requested number of ticks. + */ + public static function rotateCW (direction :int, ticks :int) :int + { + for (var ii :int = 0; ii < ticks; ii++) { + direction = FINE_CW_ROTATE[direction]; + } + return direction; + } + + /** + * Rotates the requested fine direction constant + * counter-clockwise by the requested number of ticks. + */ + public static function rotateCCW (direction :int, ticks :int) :int + { + for (var ii :int = 0; ii < ticks; ii++) { + direction = FINE_CCW_ROTATE[direction]; + } + return direction; + } + + /** + * Returns the opposite of the specified direction. + */ + public static function getOpposite (direction :int) :int + { + return rotateCW(direction, FINE_CW_ROTATE.length/2); + } + + /** + * Get the cardinal direction closest to the specified direction (preferring a clockwise match). + */ + public static function getClosestCardinal (direction :int) :int + { + return getClosest(direction, CARDINAL_DIRECTIONS, true); + } + + /** + * Get the direction closest to the specified direction, out of + * the directions in the possible list. + * + * @param preferCW whether to prefer a clockwise match or a + * counter-clockwise match. + */ + public static function getClosest (direction :int, possible :Array, + preferCW :Boolean = true) :int + { + // rotate a tick at a time, looking for matches + var first :int = direction; + var second :int = direction; + for (var ii :int = 0; ii <= FINE_DIRECTION_COUNT / 2; ii++) { + if (ArrayUtil.contains(possible, first)) { + return first; + } + + if (ii != 0 && ArrayUtil.contains(possible, second)) { + return second; + } + + first = preferCW ? rotateCW(first, 1) : rotateCCW(first, 1); + second = preferCW ? rotateCCW(second, 1) : rotateCW(second, 1); + } + + return NONE; + } + + /** + * Returns which of the eight compass directions that point + * b lies in from point a as one of the + * {@link DirectionCodes} direction constants. Note: that the + * coordinates supplied are assumed to be logical (screen) rather than + * cartesian coordinates and NORTH is considered to point + * toward the top of the screen. + */ + public static function getDirectionForPts (a :Point, b :Point) :int + { + return getDirection(a.x, a.y, b.x, b.y); + } + + /** + * Returns which of the eight compass directions that point + * b lies in from point a as one of the + * {@link DirectionCodes} direction constants. Note: that the + * coordinates supplied are assumed to be logical (screen) rather than + * cartesian coordinates and NORTH is considered to point + * toward the top of the screen. + */ + public static function getDirection (ax :Number, ay :Number, bx :Number, by :Number) :int + { + return getDirectionForAngle(Math.atan2(by-ay, bx-ax)); + } + + /** + * Returns which of the eight compass directions is associated with + * the specified angle theta. Note: that the angle supplied + * is assumed to increase clockwise around the origin (which screen + * angles do) rather than counter-clockwise around the origin (which + * cartesian angles do) and NORTH is considered to point + * toward the top of the screen. + */ + public static function getDirectionForAngle (theta :Number) :int + { + theta = ((theta + Math.PI) * 4) / Math.PI; + return (int)(Math.round(theta) + WEST) % 8; + } + + /** + * Returns which of the sixteen compass directions that point + * b lies in from point a as one of the + * {@link DirectionCodes} direction constants. Note: that the + * coordinates supplied are assumed to be logical (screen) rather than + * cartesian coordinates and NORTH is considered to point + * toward the top of the screen. + */ + public static function getFineDirectionForPts (a :Point, b :Point) :int + { + return getFineDirection(a.x, a.y, b.x, b.y); + } + + /** + * Returns which of the sixteen compass directions that point + * b lies in from point a as one of the + * {@link DirectionCodes} direction constants. Note: that the + * coordinates supplied are assumed to be logical (screen) rather than + * cartesian coordinates and NORTH is considered to point + * toward the top of the screen. + */ + public static function getFineDirection (ax :Number, ay :Number, bx :Number, by :Number) :int + { + return getFineDirectionForAngle(Math.atan2(by-ay, bx-ax)); + } + + /** + * Returns which of the sixteen compass directions is associated with + * the specified angle theta. Note: that the angle supplied + * is assumed to increase clockwise around the origin (which screen + * angles do) rather than counter-clockwise around the origin (which + * cartesian angles do) and NORTH is considered to point + * toward the top of the screen. + */ + public static function getFineDirectionForAngle (theta :Number) :int + { + theta = ((theta + Math.PI) * 8) / Math.PI; + return ANGLE_MAP[int(Math.round(theta)) % FINE_DIRECTION_COUNT]; + } + + /** + * Move the specified point in the specified screen direction, + * adjusting by the specified adjustments. Fine directions are + * not supported. + */ + public static function moveDirection (p :Point, direction :int, dx :int, dy :int) :void + { + if (direction >= DIRECTION_COUNT) { + throw new ArgumentError( + "Fine coordinates not supported."); + } + + switch (direction) { + case NORTH: case NORTHWEST: case NORTHEAST: p.y -= dy; + } + switch (direction) { + case SOUTH: case SOUTHWEST: case SOUTHEAST: p.y += dy; + } + switch (direction) { + case WEST: case SOUTHWEST: case NORTHWEST: p.x -= dx; + } + switch (direction) { + case EAST: case SOUTHEAST: case NORTHEAST: p.x += dx; + } + } + + /** Direction constant string names. */ + protected static const DIR_STRINGS :Array = [ + "SOUTHWEST", "WEST", "NORTHWEST", "NORTH", + "NORTHEAST", "EAST", "SOUTHEAST", "SOUTH", + "WESTSOUTHWEST", "WESTNORTHWEST", "NORTHNORTHWEST", "NORTHNORTHEAST", + "EASTNORTHEAST", "EASTSOUTHEAST", "SOUTHSOUTHEAST", "SOUTHSOUTHWEST", + ]; + + /** Abbreviated direction constant string names. */ + protected static const SHORT_DIR_STRINGS :Array = [ + "SW", "W", "NW", "N", "NE", "E", "SE", "S", + "WSW", "WNW", "NNW", "NNE", "ENE", "ESE", "SSE", "SSW", + ]; + + /** Used to rotate a fine compass direction clockwise. */ + protected static const FINE_CW_ROTATE :Array = [ + /* SW -> */ WESTSOUTHWEST, /* W -> */ WESTNORTHWEST, + /* NW -> */ NORTHNORTHWEST, /* N -> */ NORTHNORTHEAST, + /* NE -> */ EASTNORTHEAST, /* E -> */ EASTSOUTHEAST, + /* SE -> */ SOUTHSOUTHEAST, /* S -> */ SOUTHSOUTHWEST, + /* WSW -> */ WEST, /* WNW -> */ NORTHWEST, + /* NNW -> */ NORTH, /* NNE -> */ NORTHEAST, + /* ENE -> */ EAST, /* ESE -> */ SOUTHEAST, + /* SSE -> */ SOUTH, /* SSW -> */ SOUTHWEST + ]; + + /** Used to rotate a fine compass direction counter-clockwise. */ + protected static const FINE_CCW_ROTATE :Array = [ + /* SW -> */ SOUTHSOUTHWEST, /* W -> */ WESTSOUTHWEST, + /* NW -> */ WESTNORTHWEST, /* N -> */ NORTHNORTHWEST, + /* NE -> */ NORTHNORTHEAST, /* E -> */ EASTNORTHEAST, + /* SE -> */ EASTSOUTHEAST, /* S -> */ SOUTHSOUTHEAST, + /* WSW -> */ SOUTHWEST, /* WNW -> */ WEST, + /* NNW -> */ NORTHWEST, /* NNE -> */ NORTH, + /* ENE -> */ NORTHEAST, /* ESE -> */ EAST, + /* SSE -> */ SOUTHEAST, /* SSW -> */ SOUTH + ]; + + /** Used to map an angle to a fine compass direction. */ + protected static const ANGLE_MAP :Array = [ + WEST, WESTNORTHWEST, NORTHWEST, NORTHNORTHWEST, NORTH, NORTHNORTHEAST, + NORTHEAST, EASTNORTHEAST, EAST, EASTSOUTHEAST, SOUTHEAST, + SOUTHSOUTHEAST, SOUTH, SOUTHSOUTHWEST, SOUTHWEST, WESTSOUTHWEST ]; +} +}