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
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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. <code>-1</code> 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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 "<no bounds>";
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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 <code>0</code> and {@link #size}<code>-1</code>.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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 ];
|
||||
}
|
||||
}
|
||||
@@ -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 <em>fine</em> 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 <em>fine</em> 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
|
||||
* <code>b</code> lies in from point <code>a</code> as one of the
|
||||
* {@link DirectionCodes} direction constants. <em>Note:</em> that the
|
||||
* coordinates supplied are assumed to be logical (screen) rather than
|
||||
* cartesian coordinates and <code>NORTH</code> 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
|
||||
* <code>b</code> lies in from point <code>a</code> as one of the
|
||||
* {@link DirectionCodes} direction constants. <em>Note:</em> that the
|
||||
* coordinates supplied are assumed to be logical (screen) rather than
|
||||
* cartesian coordinates and <code>NORTH</code> 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. <em>Note:</em> 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 <code>NORTH</code> 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
|
||||
* <code>b</code> lies in from point <code>a</code> as one of the
|
||||
* {@link DirectionCodes} direction constants. <em>Note:</em> that the
|
||||
* coordinates supplied are assumed to be logical (screen) rather than
|
||||
* cartesian coordinates and <code>NORTH</code> 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
|
||||
* <code>b</code> lies in from point <code>a</code> as one of the
|
||||
* {@link DirectionCodes} direction constants. <em>Note:</em> that the
|
||||
* coordinates supplied are assumed to be logical (screen) rather than
|
||||
* cartesian coordinates and <code>NORTH</code> 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. <em>Note:</em> 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 <code>NORTH</code> 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 ];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user