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:
Mike Thomas
2010-06-08 01:48:12 +00:00
parent e3de56d12a
commit 2f634e3a60
11 changed files with 1583 additions and 0 deletions
@@ -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 ];
}
}
+320
View File
@@ -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 ];
}
}