Behold, Nenya, Ring of Water and repository for our media and animation related
goodies, both Java 2D and LWJGL/JME 3D. git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@1 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// $Id: Log.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* A placeholder class that contains a reference to the log object used by
|
||||
* the miso package.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
public static com.samskivert.util.Log log =
|
||||
new com.samskivert.util.Log("miso");
|
||||
|
||||
/** Convenience function. */
|
||||
public static void debug (String message)
|
||||
{
|
||||
log.debug(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void info (String message)
|
||||
{
|
||||
log.info(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void warning (String message)
|
||||
{
|
||||
log.warning(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void logStackTrace (Throwable t)
|
||||
{
|
||||
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// $Id: MisoConfig.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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;
|
||||
|
||||
import com.samskivert.util.Config;
|
||||
|
||||
import com.threerings.miso.util.MisoSceneMetrics;
|
||||
|
||||
/**
|
||||
* Provides access to the Miso configuration.
|
||||
*/
|
||||
public class MisoConfig
|
||||
{
|
||||
/** Provides access to configuration data for this package. */
|
||||
public static Config config = new Config("rsrc/config/miso/miso");
|
||||
|
||||
/**
|
||||
* Creates scene metrics with information obtained from the deployed
|
||||
* config file.
|
||||
*/
|
||||
public static MisoSceneMetrics getSceneMetrics ()
|
||||
{
|
||||
return new MisoSceneMetrics(
|
||||
config.getValue(TILE_WIDTH_KEY, DEF_TILE_WIDTH),
|
||||
config.getValue(TILE_HEIGHT_KEY, DEF_TILE_HEIGHT),
|
||||
config.getValue(FINE_GRAN_KEY, DEF_FINE_GRAN));
|
||||
}
|
||||
|
||||
/** The config key for tile width in pixels. */
|
||||
protected static final String TILE_WIDTH_KEY = "tile_width";
|
||||
|
||||
/** The config key for tile height in pixels. */
|
||||
protected static final String TILE_HEIGHT_KEY = "tile_height";
|
||||
|
||||
/** The config key for tile fine coordinate granularity. */
|
||||
protected static final String FINE_GRAN_KEY = "fine_granularity";
|
||||
|
||||
/** Default scene view parameters. */
|
||||
protected static final int DEF_TILE_WIDTH = 64;
|
||||
protected static final int DEF_TILE_HEIGHT = 48;
|
||||
protected static final int DEF_FINE_GRAN = 4;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// $Id: MisoPrefs.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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;
|
||||
|
||||
import com.samskivert.util.Config;
|
||||
|
||||
/**
|
||||
* Provides access to runtime configuration parameters for the miso
|
||||
* package and its subpackages.
|
||||
*/
|
||||
public class MisoPrefs
|
||||
{
|
||||
/** Used to load our preferences from a properties file and map them
|
||||
* to the persistent Java preferences repository. */
|
||||
public static Config config = new Config("rsrc/config/miso");
|
||||
}
|
||||
@@ -0,0 +1,680 @@
|
||||
//
|
||||
// $Id: DirtyItemList.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.client;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
|
||||
import com.samskivert.util.SortableArrayList;
|
||||
|
||||
import com.threerings.media.Log;
|
||||
import com.threerings.media.sprite.Sprite;
|
||||
import com.threerings.media.tile.ObjectTile;
|
||||
|
||||
/**
|
||||
* The dirty item list keeps track of dirty sprites and object tiles
|
||||
* in a scene.
|
||||
*/
|
||||
public class DirtyItemList
|
||||
{
|
||||
/**
|
||||
* Creates a dirt item list that will handle dirty items for the
|
||||
* specified view.
|
||||
*/
|
||||
public DirtyItemList ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the dirty sprite at the given coordinates to the dirty item
|
||||
* list.
|
||||
*
|
||||
* @param sprite the dirty sprite itself.
|
||||
* @param tx the sprite's x tile position.
|
||||
* @param ty the sprite's y tile position.
|
||||
*/
|
||||
public void appendDirtySprite (Sprite sprite, int tx, int ty)
|
||||
{
|
||||
DirtyItem item = getDirtyItem();
|
||||
item.init(sprite, tx, ty);
|
||||
_items.add(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the dirty object tile at the given coordinates to the dirty
|
||||
* item list.
|
||||
*
|
||||
* @param scobj the scene object that is dirty.
|
||||
*/
|
||||
public void appendDirtyObject (SceneObject scobj)
|
||||
{
|
||||
DirtyItem item = getDirtyItem();
|
||||
item.init(scobj, scobj.info.x, scobj.info.y);
|
||||
_items.add(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dirty item at the given index in the list.
|
||||
*/
|
||||
public DirtyItem get (int idx)
|
||||
{
|
||||
return (DirtyItem)_items.get(idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the {@link DirtyItem} objects in the list
|
||||
* sorted in proper rendering order.
|
||||
*/
|
||||
public void sort ()
|
||||
{
|
||||
int size = size();
|
||||
|
||||
if (DEBUG_SORT) {
|
||||
Log.info("Sorting dirty item list [size=" + size + "].");
|
||||
}
|
||||
|
||||
// if we've only got one item, we need to do no sorting
|
||||
if (size > 1) {
|
||||
// get items sorted by increasing origin x-coordinate
|
||||
_xitems.addAll(_items);
|
||||
_xitems.sort(ORIGIN_X_COMP);
|
||||
if (DEBUG_SORT) {
|
||||
Log.info("Sorted by x-origin " +
|
||||
"[items=" + toString(_xitems) + "].");
|
||||
}
|
||||
|
||||
// get items sorted by increasing origin y-coordinate
|
||||
_yitems.addAll(_items);
|
||||
_yitems.sort(ORIGIN_Y_COMP);
|
||||
if (DEBUG_SORT) {
|
||||
Log.info("Sorted by y-origin " +
|
||||
"[items=" + toString(_yitems) + "].");
|
||||
}
|
||||
|
||||
// sort the items according to the depth of the rear-most tile
|
||||
_ditems.addAll(_items);
|
||||
_ditems.sort(REAR_DEPTH_COMP);
|
||||
|
||||
// now insertion sort the items from back to front into the
|
||||
// render-sorted array
|
||||
_items.clear();
|
||||
POS_LOOP:
|
||||
for (int ii = 0; ii < size; ii++) {
|
||||
DirtyItem item = (DirtyItem)_ditems.get(ii);
|
||||
for (int rr = _items.size()-1; rr >= 0; rr--) {
|
||||
DirtyItem pitem = (DirtyItem)_items.get(rr);
|
||||
// if we render in front of this item, insert
|
||||
// ourselves immediately following it
|
||||
if (_rcomp.compare(item, pitem) > 0) {
|
||||
_items.add(rr+1, item);
|
||||
continue POS_LOOP;
|
||||
}
|
||||
}
|
||||
// we don't render in front of anyone, so we go at the
|
||||
// front of the list
|
||||
_items.add(0, item);
|
||||
}
|
||||
|
||||
// clear out our temporary arrays
|
||||
_xitems.clear();
|
||||
_yitems.clear();
|
||||
_ditems.clear();
|
||||
}
|
||||
|
||||
if (DEBUG_SORT) {
|
||||
Log.info("Sorted for render [items=" + toString(_items) + "].");
|
||||
for (int ii = 0, ll = _items.size()-1; ii < ll; ii++) {
|
||||
DirtyItem a = (DirtyItem)_items.get(ii);
|
||||
DirtyItem b = (DirtyItem)_items.get(ii+1);
|
||||
if (_rcomp.compare(a, b) > 0) {
|
||||
Log.warning("Invalid ordering [a=" + a + ", b=" + b + "].");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints all the dirty items in this list using the supplied graphics
|
||||
* context. The items are removed from the dirty list after being
|
||||
* painted and the dirty list ends up empty.
|
||||
*/
|
||||
public void paintAndClear (Graphics2D gfx)
|
||||
{
|
||||
int icount = _items.size();
|
||||
for (int ii = 0; ii < icount; ii++) {
|
||||
DirtyItem item = (DirtyItem)_items.get(ii);
|
||||
item.paint(gfx);
|
||||
item.clear();
|
||||
_freelist.add(item);
|
||||
}
|
||||
_items.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out any items that were in this list.
|
||||
*/
|
||||
public void clear ()
|
||||
{
|
||||
for (int icount = _items.size(); icount > 0; icount--) {
|
||||
DirtyItem item = (DirtyItem)_items.remove(0);
|
||||
item.clear();
|
||||
_freelist.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of items in the dirty item list.
|
||||
*/
|
||||
public int size ()
|
||||
{
|
||||
return _items.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a new dirty item instance, reusing an old one if possible
|
||||
* or creating a new one otherwise.
|
||||
*/
|
||||
protected DirtyItem getDirtyItem ()
|
||||
{
|
||||
if (_freelist.size() > 0) {
|
||||
return (DirtyItem)_freelist.remove(0);
|
||||
} else {
|
||||
return new DirtyItem();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an abbreviated string representation of the given dirty
|
||||
* item describing only its origin coordinates and render priority.
|
||||
* Intended for debugging purposes.
|
||||
*/
|
||||
protected static String toString (DirtyItem a)
|
||||
{
|
||||
StringBuilder buf = new StringBuilder("[");
|
||||
toString(buf, a);
|
||||
return buf.append("]").toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an abbreviated string representation of the two given dirty
|
||||
* items. See {@link #toString(DirtyItem)}.
|
||||
*/
|
||||
protected static String toString (DirtyItem a, DirtyItem b)
|
||||
{
|
||||
StringBuilder buf = new StringBuilder("[");
|
||||
toString(buf, a);
|
||||
toString(buf, b);
|
||||
return buf.append("]").toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an abbreviated string representation of the given dirty
|
||||
* items. See {@link #toString(DirtyItem)}.
|
||||
*/
|
||||
protected static String toString (SortableArrayList items)
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[");
|
||||
for (int ii = 0; ii < items.size(); ii++) {
|
||||
DirtyItem item = (DirtyItem)items.get(ii);
|
||||
toString(buf, item);
|
||||
if (ii < (items.size() - 1)) {
|
||||
buf.append(", ");
|
||||
}
|
||||
}
|
||||
return buf.append("]").toString();
|
||||
}
|
||||
|
||||
/** Helper function for {@link #toString(DirtyItem)}. */
|
||||
protected static void toString (StringBuilder buf, DirtyItem item)
|
||||
{
|
||||
buf.append("(o:+").append(item.ox).append("+").append(item.oy);
|
||||
buf.append(" p:").append(item.getRenderPriority()).append(")");
|
||||
}
|
||||
|
||||
/**
|
||||
* A class to hold the items inserted in the dirty list along with
|
||||
* all of the information necessary to render their dirty regions
|
||||
* to the target graphics context when the time comes to do so.
|
||||
*/
|
||||
public class DirtyItem
|
||||
{
|
||||
/** The dirtied object; one of either a sprite or an object tile. */
|
||||
public Object obj;
|
||||
|
||||
/** The origin tile coordinates. */
|
||||
public int ox, oy;
|
||||
|
||||
/** The leftmost tile coordinates. */
|
||||
public int lx, ly;
|
||||
|
||||
/** The rightmost tile coordinates. */
|
||||
public int rx, ry;
|
||||
|
||||
/**
|
||||
* Initializes a dirty item.
|
||||
*/
|
||||
public void init (Object obj, int x, int y)
|
||||
{
|
||||
this.obj = obj;
|
||||
this.ox = x;
|
||||
this.oy = y;
|
||||
|
||||
// calculate the item's leftmost and rightmost tiles; note
|
||||
// that sprites occupy only a single tile, so leftmost and
|
||||
// rightmost tiles are equivalent
|
||||
lx = rx = ox;
|
||||
ly = ry = oy;
|
||||
if (obj instanceof SceneObject) {
|
||||
ObjectTile tile = ((SceneObject)obj).tile;
|
||||
lx -= (tile.getBaseWidth() - 1);
|
||||
ry -= (tile.getBaseHeight() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the dirty item to the given graphics context. Only
|
||||
* the portion of the item that falls within the given dirty
|
||||
* rectangle is actually drawn.
|
||||
*/
|
||||
public void paint (Graphics2D gfx)
|
||||
{
|
||||
if (obj instanceof Sprite) {
|
||||
((Sprite)obj).paint(gfx);
|
||||
} else {
|
||||
((SceneObject)obj).paint(gfx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "depth" of our rear-most tile.
|
||||
*/
|
||||
public int getRearDepth ()
|
||||
{
|
||||
return ry + lx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the render priority for this dirty item. It will be
|
||||
* zero unless this is a display object which may have a custom
|
||||
* render priority.
|
||||
*/
|
||||
public int getRenderPriority ()
|
||||
{
|
||||
if (obj instanceof SceneObject) {
|
||||
return ((SceneObject)obj).getPriority();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases all references held by this dirty item so that it
|
||||
* doesn't inadvertently hold on to any objects while waiting to
|
||||
* be reused.
|
||||
*/
|
||||
public void clear ()
|
||||
{
|
||||
obj = null;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean equals (Object other)
|
||||
{
|
||||
// we're never equal to something that's not our kind
|
||||
if (!(other instanceof DirtyItem)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// sprites are equivalent if they're the same sprite
|
||||
DirtyItem b = (DirtyItem)other;
|
||||
return obj.equals(b.obj);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public int hashCode ()
|
||||
{
|
||||
return obj.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the dirty item.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[obj=").append(obj);
|
||||
buf.append(", ox=").append(ox);
|
||||
buf.append(", oy=").append(oy);
|
||||
buf.append(", lx=").append(lx);
|
||||
buf.append(", ly=").append(ly);
|
||||
buf.append(", rx=").append(rx);
|
||||
buf.append(", ry=").append(ry);
|
||||
return buf.append("]").toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A comparator class for use in sorting dirty items in ascending
|
||||
* origin x- or y-axis coordinate order.
|
||||
*/
|
||||
protected static class OriginComparator implements Comparator
|
||||
{
|
||||
/**
|
||||
* Constructs an origin comparator that sorts dirty items in
|
||||
* ascending order based on their origin coordinate on the given
|
||||
* axis.
|
||||
*/
|
||||
public OriginComparator (int axis)
|
||||
{
|
||||
_axis = axis;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public int compare (Object a, Object b)
|
||||
{
|
||||
DirtyItem da = (DirtyItem)a;
|
||||
DirtyItem db = (DirtyItem)b;
|
||||
|
||||
// if they don't overlap, sort them normally
|
||||
if (_axis == X_AXIS) {
|
||||
if (da.ox != db.ox) {
|
||||
return da.ox - db.ox;
|
||||
}
|
||||
} else {
|
||||
if (da.oy != db.oy) {
|
||||
return da.oy - db.oy;
|
||||
}
|
||||
}
|
||||
|
||||
// if they do overlap, incorporate render priority; assume
|
||||
// non-display objects have a render priority of zero
|
||||
return da.getRenderPriority() - db.getRenderPriority();
|
||||
}
|
||||
|
||||
/** The axis this comparator sorts on. */
|
||||
protected int _axis;
|
||||
}
|
||||
|
||||
/**
|
||||
* A comparator class for use in sorting the dirty sprites and
|
||||
* objects in a scene in ascending x- and y-coordinate order
|
||||
* suitable for rendering in the isometric view with proper visual
|
||||
* results.
|
||||
*/
|
||||
protected class RenderComparator implements Comparator
|
||||
{
|
||||
// documentation inherited
|
||||
public int compare (Object a, Object b)
|
||||
{
|
||||
DirtyItem da = (DirtyItem)a;
|
||||
DirtyItem db = (DirtyItem)b;
|
||||
|
||||
// if the two objects are scene objects and they overlap, we
|
||||
// compare them solely based on their human assigned priority
|
||||
if ((da.obj instanceof SceneObject) &&
|
||||
(db.obj instanceof SceneObject)) {
|
||||
SceneObject soa = (SceneObject)da.obj;
|
||||
SceneObject sob = (SceneObject)db.obj;
|
||||
if (soa.objectFootprintOverlaps(sob)) {
|
||||
int result = soa.getPriority() - sob.getPriority();
|
||||
if (DEBUG_COMPARE) {
|
||||
String items = DirtyItemList.toString(da, db);
|
||||
Log.info("compare: overlapping [result=" + result +
|
||||
", items=" + items + "].");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// check for partitioning objects on the y-axis
|
||||
int result = comparePartitioned(Y_AXIS, da, db);
|
||||
if (result != 0) {
|
||||
if (DEBUG_COMPARE) {
|
||||
String items = DirtyItemList.toString(da, db);
|
||||
Log.info("compare: Y-partitioned " +
|
||||
"[result=" + result + ", items=" + items + "].");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// check for partitioning objects on the x-axis
|
||||
result = comparePartitioned(X_AXIS, da, db);
|
||||
if (result != 0) {
|
||||
if (DEBUG_COMPARE) {
|
||||
String items = DirtyItemList.toString(da, db);
|
||||
Log.info("compare: X-partitioned " +
|
||||
"[result=" + result + ", items=" + items + "].");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// use normal iso-ordering check
|
||||
result = compareNonPartitioned(da, db);
|
||||
if (DEBUG_COMPARE) {
|
||||
String items = DirtyItemList.toString(da, db);
|
||||
Log.info("compare: non-partitioned " +
|
||||
"[result=" + result + ", items=" + items + "].");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether two dirty items have a partitioning object
|
||||
* between them on the given axis.
|
||||
*/
|
||||
protected int comparePartitioned (
|
||||
int axis, DirtyItem da, DirtyItem db)
|
||||
{
|
||||
// prepare for the partitioning check
|
||||
SortableArrayList sitems;
|
||||
Comparator comp;
|
||||
boolean swapped = false;
|
||||
switch (axis) {
|
||||
case X_AXIS:
|
||||
if (da.ox == db.ox) {
|
||||
// can't be partitioned if there's no space between
|
||||
return 0;
|
||||
}
|
||||
|
||||
// order items for proper comparison
|
||||
if (da.ox > db.ox) {
|
||||
DirtyItem temp = da;
|
||||
da = db;
|
||||
db = temp;
|
||||
swapped = true;
|
||||
}
|
||||
|
||||
// use the axis-specific sorted array
|
||||
sitems = _xitems;
|
||||
comp = ORIGIN_X_COMP;
|
||||
break;
|
||||
|
||||
case Y_AXIS:
|
||||
default:
|
||||
if (da.oy == db.oy) {
|
||||
// can't be partitioned if there's no space between
|
||||
return 0;
|
||||
}
|
||||
|
||||
// order items for proper comparison
|
||||
if (da.oy > db.oy) {
|
||||
DirtyItem temp = da;
|
||||
da = db;
|
||||
db = temp;
|
||||
swapped = true;
|
||||
}
|
||||
|
||||
// use the axis-specific sorted array
|
||||
sitems = _yitems;
|
||||
comp = ORIGIN_Y_COMP;
|
||||
break;
|
||||
}
|
||||
|
||||
// get the bounding item indices and the number of
|
||||
// potentially-partitioning dirty items
|
||||
int aidx = sitems.binarySearch(da, comp);
|
||||
int bidx = sitems.binarySearch(db, comp);
|
||||
int size = bidx - aidx - 1;
|
||||
|
||||
// check each potentially partitioning item
|
||||
int startidx = aidx + 1, endidx = startidx + size;
|
||||
for (int pidx = startidx; pidx < endidx; pidx++) {
|
||||
DirtyItem dp = (DirtyItem)sitems.get(pidx);
|
||||
if (dp.obj instanceof Sprite) {
|
||||
// sprites can't partition things
|
||||
continue;
|
||||
} else if ((dp.obj == da.obj) ||
|
||||
(dp.obj == db.obj)) {
|
||||
// can't be partitioned by ourselves
|
||||
continue;
|
||||
}
|
||||
|
||||
// perform the actual partition check for this object
|
||||
switch (axis) {
|
||||
case X_AXIS:
|
||||
if (dp.ly >= da.ry &&
|
||||
dp.ry <= db.ly &&
|
||||
dp.lx >= da.rx &&
|
||||
dp.rx <= db.lx) {
|
||||
return (swapped) ? 1 : -1;
|
||||
}
|
||||
|
||||
case Y_AXIS:
|
||||
default:
|
||||
if (dp.lx <= db.ox &&
|
||||
dp.rx >= da.lx &&
|
||||
dp.ry >= da.oy &&
|
||||
dp.oy <= db.ry) {
|
||||
return (swapped) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no partitioning object found
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the two dirty items assuming there are no partitioning
|
||||
* objects between them.
|
||||
*/
|
||||
protected int compareNonPartitioned (DirtyItem da, DirtyItem db)
|
||||
{
|
||||
if (da.ox == db.ox &&
|
||||
da.oy == db.oy) {
|
||||
if (da.equals(db)) {
|
||||
// render level is equal if we're the same sprite
|
||||
// or an object at the same location
|
||||
return 0;
|
||||
}
|
||||
|
||||
boolean aIsSprite = (da.obj instanceof Sprite);
|
||||
boolean bIsSprite = (db.obj instanceof Sprite);
|
||||
|
||||
if (aIsSprite && bIsSprite) {
|
||||
Sprite as = (Sprite)da.obj, bs = (Sprite)db.obj;
|
||||
// we're comparing two sprites co-existing on the same
|
||||
// tile, first check their render order
|
||||
int rocomp = as.getRenderOrder() - bs.getRenderOrder();
|
||||
if (rocomp != 0) {
|
||||
return rocomp;
|
||||
}
|
||||
// next sort them by y-position
|
||||
int ydiff = as.getY() - bs.getY();
|
||||
if (ydiff != 0) {
|
||||
return ydiff;
|
||||
}
|
||||
// if they're at the same height, just use hashCode()
|
||||
// to establish a consistent arbitrary ordering
|
||||
return (as.hashCode() - bs.hashCode());
|
||||
|
||||
// otherwise, always put a sprite on top of a non-sprite
|
||||
} else if (aIsSprite) {
|
||||
return 1;
|
||||
|
||||
} else if (bIsSprite) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise use a consistent ordering for non-overlappers;
|
||||
// see narya/docs/miso/render_sort_diagram.png for more info
|
||||
if (db.lx <= da.ox && db.ry <= da.oy) {
|
||||
return 1;
|
||||
} else if (db.rx >= da.lx && db.ly >= da.ry) {
|
||||
return -1;
|
||||
} else {
|
||||
return da.oy - db.oy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The list of dirty items. */
|
||||
protected SortableArrayList _items = new SortableArrayList();
|
||||
|
||||
/** The list of dirty items sorted by x-position. */
|
||||
protected SortableArrayList _xitems = new SortableArrayList();
|
||||
|
||||
/** The list of dirty items sorted by y-position. */
|
||||
protected SortableArrayList _yitems = new SortableArrayList();
|
||||
|
||||
/** The list of dirty items sorted by rear-depth. */
|
||||
protected SortableArrayList _ditems = new SortableArrayList();
|
||||
|
||||
/** The render comparator we'll use for our final, magical sort. */
|
||||
protected Comparator _rcomp = new RenderComparator();
|
||||
|
||||
/** Unused dirty items. */
|
||||
protected ArrayList _freelist = new ArrayList();
|
||||
|
||||
/** Whether to log debug info when comparing pairs of dirty items. */
|
||||
protected static final boolean DEBUG_COMPARE = false;
|
||||
|
||||
/** Whether to log debug info for the main dirty item sorting algorithm. */
|
||||
protected static final boolean DEBUG_SORT = false;
|
||||
|
||||
/** Constants used to denote axis sorting constraints. */
|
||||
protected static final int X_AXIS = 0;
|
||||
protected static final int Y_AXIS = 1;
|
||||
|
||||
/** The comparator used to sort dirty items in ascending origin
|
||||
* x-coordinate order. */
|
||||
protected static final Comparator ORIGIN_X_COMP =
|
||||
new OriginComparator(X_AXIS);
|
||||
|
||||
/** The comparator used to sort dirty items in ascending origin
|
||||
* y-coordinate order. */
|
||||
protected static final Comparator ORIGIN_Y_COMP =
|
||||
new OriginComparator(Y_AXIS);
|
||||
|
||||
/** The comparator used to sort dirty items in ascending "rear-depth"
|
||||
* order. */
|
||||
protected static final Comparator REAR_DEPTH_COMP = new Comparator() {
|
||||
public int compare (Object o1, Object o2) {
|
||||
return (((DirtyItem)o1).getRearDepth() -
|
||||
((DirtyItem)o2).getRearDepth());
|
||||
}
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// $Id: ObjectActionHandler.java 3917 2006-03-06 23:45:12Z mthomas $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.client;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.HashMap;
|
||||
import javax.swing.Icon;
|
||||
|
||||
import com.samskivert.swing.RadialMenu;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.miso.Log;
|
||||
import com.threerings.miso.client.SceneObject;
|
||||
|
||||
/**
|
||||
* Objects in scenes can be configured to generate action events. Those
|
||||
* events are grouped into types and an object action handler can be
|
||||
* registered to handle all actions of a particular type.
|
||||
*/
|
||||
public class ObjectActionHandler
|
||||
{
|
||||
/**
|
||||
* Returns true if we should allow this object action, false if we
|
||||
* should not.
|
||||
*/
|
||||
public boolean actionAllowed (String action)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we should display the text for the action. By default
|
||||
* this returns whether the action is allowed or not, but can be
|
||||
* overridden by subclasses. This is used to completely hide actions that
|
||||
* should not be visible without the proper privileges.
|
||||
*/
|
||||
public boolean isVisible (String action)
|
||||
{
|
||||
return actionAllowed(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the human readable object tip for the specified action.
|
||||
*/
|
||||
public String getTipText (String action)
|
||||
{
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tooltip icon for the specified action or null if the
|
||||
* action has no tooltip icon.
|
||||
*/
|
||||
public Icon getTipIcon (String action)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link RadialMenu} or null if no menu needed.
|
||||
*/
|
||||
public RadialMenu handlePressed (SceneObject sourceObject)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an action is generated for an object.
|
||||
*/
|
||||
public void handleAction (SceneObject scobj, ActionEvent event)
|
||||
{
|
||||
Log.warning("Unknown object action [scobj=" + scobj +
|
||||
", action=" + event + "].");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type associated with this action command (which is
|
||||
* mapped to a registered object action handler) or the empty string
|
||||
* if it has no type.
|
||||
*/
|
||||
public static String getType (String command)
|
||||
{
|
||||
int cidx = StringUtil.isBlank(command) ? -1 : command.indexOf(':');
|
||||
return (cidx == -1) ? "" : command.substring(0, cidx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unqualified object action (minus the type, see {@link
|
||||
* #getType}).
|
||||
*/
|
||||
public static String getAction (String command)
|
||||
{
|
||||
int cidx = StringUtil.isBlank(command) ? -1 : command.indexOf(':');
|
||||
return (cidx == -1) ? command : command.substring(cidx+1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the object action handler associated with the specified
|
||||
* command.
|
||||
*/
|
||||
public static ObjectActionHandler lookup (String command)
|
||||
{
|
||||
return (ObjectActionHandler)_oahandlers.get(getType(command));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an object action handler which will be called when a user
|
||||
* clicks on an object in a scene that has an associated action.
|
||||
*/
|
||||
public static void register (String prefix, ObjectActionHandler handler)
|
||||
{
|
||||
// make sure we know about potential funny business
|
||||
if (_oahandlers.containsKey(prefix)) {
|
||||
Log.warning("Warning! Overwriting previous object action " +
|
||||
"handler registration, all hell could shortly " +
|
||||
"break loose [prefix=" + prefix +
|
||||
", handler=" + handler + "].");
|
||||
}
|
||||
_oahandlers.put(prefix, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an object action handler registration.
|
||||
*/
|
||||
public static void unregister (String prefix)
|
||||
{
|
||||
_oahandlers.remove(prefix);
|
||||
}
|
||||
|
||||
/** Our registered object action handlers. */
|
||||
protected static HashMap _oahandlers = new HashMap();
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
//
|
||||
// $Id: ResolutionView.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.client;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Polygon;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.samskivert.util.IntTuple;
|
||||
|
||||
import com.threerings.media.util.MathUtil;
|
||||
import com.threerings.miso.util.MisoSceneMetrics;
|
||||
import com.threerings.miso.util.MisoUtil;
|
||||
|
||||
/**
|
||||
* Used to debug scene block resolution visually.
|
||||
*/
|
||||
public class ResolutionView extends JPanel
|
||||
{
|
||||
public ResolutionView (MisoScenePanel panel)
|
||||
{
|
||||
_panel = panel;
|
||||
_metrics = panel.getSceneMetrics();
|
||||
}
|
||||
|
||||
public Dimension getPreferredSize ()
|
||||
{
|
||||
return new Dimension(TILE_SIZE*MAX_WIDTH, TILE_SIZE*MAX_HEIGHT);
|
||||
}
|
||||
|
||||
public synchronized void queuedBlock (SceneBlock block)
|
||||
{
|
||||
assignColor(block, Color.yellow);
|
||||
repaint();
|
||||
}
|
||||
|
||||
public synchronized void resolvingBlock (SceneBlock block)
|
||||
{
|
||||
IntTuple key = blockKey(block);
|
||||
if (_blocks.containsKey(key)) {
|
||||
assignColor(block, Color.red);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void resolvedBlock (SceneBlock block)
|
||||
{
|
||||
IntTuple key = blockKey(block);
|
||||
if (_blocks.containsKey(key)) {
|
||||
assignColor(block, Color.green);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void blockCleared (SceneBlock block)
|
||||
{
|
||||
_blocks.remove(blockKey(block));
|
||||
repaint();
|
||||
}
|
||||
|
||||
public synchronized void newScene ()
|
||||
{
|
||||
_blocks.clear();
|
||||
repaint();
|
||||
}
|
||||
|
||||
protected void assignColor (SceneBlock block, Color color)
|
||||
{
|
||||
IntTuple key = blockKey(block);
|
||||
BlockGlyph glyph = (BlockGlyph)_blocks.get(key);
|
||||
if (glyph == null) {
|
||||
glyph = new BlockGlyph(_metrics, key.left, key.right);
|
||||
_blocks.put(key, glyph);
|
||||
}
|
||||
glyph.color = color;
|
||||
}
|
||||
|
||||
public synchronized void paintComponent (Graphics g)
|
||||
{
|
||||
super.paintComponent(g);
|
||||
Graphics2D gfx = (Graphics2D)g;
|
||||
|
||||
Rectangle vbounds = _panel.getViewBounds();
|
||||
gfx.translate((getWidth()-vbounds.width/16)/2 - vbounds.x/16,
|
||||
(getHeight()-vbounds.height/16)/2 - vbounds.y/16);
|
||||
|
||||
AffineTransform xform = gfx.getTransform();
|
||||
gfx.scale(0.25, 0.25);
|
||||
|
||||
// draw our block glyphs
|
||||
for (Iterator iter = _blocks.values().iterator(); iter.hasNext(); ) {
|
||||
((BlockGlyph)iter.next()).paint(gfx);
|
||||
}
|
||||
|
||||
// draw the view bounds
|
||||
gfx.scale(0.25, 0.25);
|
||||
gfx.draw(vbounds);
|
||||
gfx.setColor(Color.red);
|
||||
gfx.draw(_panel.getInfluentialBounds());
|
||||
gfx.setTransform(xform);
|
||||
}
|
||||
|
||||
protected final IntTuple blockKey (SceneBlock block)
|
||||
{
|
||||
Rectangle bounds = block.getBounds();
|
||||
return new IntTuple(MathUtil.floorDiv(bounds.x, bounds.width),
|
||||
MathUtil.floorDiv(bounds.y, bounds.height));
|
||||
}
|
||||
|
||||
protected static class BlockGlyph
|
||||
{
|
||||
public Color color;
|
||||
|
||||
public BlockGlyph (MisoSceneMetrics metrics, int bx, int by)
|
||||
{
|
||||
_bpoly = MisoUtil.getTilePolygon(metrics, bx, by);
|
||||
}
|
||||
|
||||
public void paint (Graphics2D gfx)
|
||||
{
|
||||
gfx.setColor(color);
|
||||
gfx.fill(_bpoly);
|
||||
gfx.setColor(Color.black);
|
||||
gfx.draw(_bpoly);
|
||||
}
|
||||
|
||||
protected Polygon _bpoly;
|
||||
}
|
||||
|
||||
protected MisoScenePanel _panel;
|
||||
protected MisoSceneMetrics _metrics;
|
||||
protected HashMap _blocks = new HashMap();
|
||||
|
||||
protected static final int TILE_SIZE = 10;
|
||||
protected static final int MAX_WIDTH = 30;
|
||||
protected static final int MAX_HEIGHT = 30;
|
||||
}
|
||||
@@ -0,0 +1,596 @@
|
||||
//
|
||||
// $Id: SceneBlock.java 3628 2005-06-28 17:42:55Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.client;
|
||||
|
||||
import java.awt.Polygon;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.geom.GeomUtil;
|
||||
import com.threerings.media.tile.NoSuchTileSetException;
|
||||
import com.threerings.media.tile.ObjectTile;
|
||||
import com.threerings.media.tile.TileSet;
|
||||
import com.threerings.media.tile.TileUtil;
|
||||
import com.threerings.media.util.MathUtil;
|
||||
|
||||
import com.threerings.miso.Log;
|
||||
import com.threerings.miso.data.MisoSceneModel;
|
||||
import com.threerings.miso.data.ObjectInfo;
|
||||
import com.threerings.miso.tile.BaseTile;
|
||||
import com.threerings.miso.util.MisoUtil;
|
||||
import com.threerings.miso.util.ObjectSet;
|
||||
|
||||
/**
|
||||
* Contains the base and object tile information on a particular
|
||||
* rectangular region of a scene.
|
||||
*/
|
||||
public class SceneBlock
|
||||
{
|
||||
/**
|
||||
* Creates a scene block and resolves the base and object tiles that
|
||||
* reside therein.
|
||||
*/
|
||||
public SceneBlock (
|
||||
MisoScenePanel panel, int tx, int ty, int width, int height)
|
||||
{
|
||||
_panel = panel;
|
||||
_bounds = new Rectangle(tx, ty, width, height);
|
||||
_base = new BaseTile[width*height];
|
||||
_fringe = new BaseTile[width*height];
|
||||
_covered = new boolean[width*height];
|
||||
|
||||
// compute our screen-coordinate footprint polygon
|
||||
_footprint = MisoUtil.getFootprintPolygon(
|
||||
panel.getSceneMetrics(), tx, ty, width, height);
|
||||
|
||||
// the rest of our resolution will happen in resolve()
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a note that this block was considered to be visible at the
|
||||
* time it was created. This is purely for debugging purposes.
|
||||
*/
|
||||
public void setVisiBlock (boolean visi)
|
||||
{
|
||||
_visi = visi;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by the {@link SceneBlockResolver} on the
|
||||
* block resolution thread to allow us to load up our image data
|
||||
* without blocking the AWT thread.
|
||||
*/
|
||||
protected boolean resolve ()
|
||||
{
|
||||
// if we got canned before we were resolved, go ahead and bail now
|
||||
if (_panel.getBlock(_bounds.x, _bounds.y) != this) {
|
||||
// Log.info("Not resolving abandoned block " + this + ".");
|
||||
_panel.blockAbandoned(this);
|
||||
return false;
|
||||
}
|
||||
_panel.blockResolving(this);
|
||||
|
||||
// start with the bounds of the footprint polygon
|
||||
Rectangle sbounds = new Rectangle(_footprint.getBounds());
|
||||
Rectangle obounds = null;
|
||||
|
||||
// resolve our base tiles
|
||||
long now = System.currentTimeMillis();
|
||||
int baseCount = 0, fringeCount = 0;
|
||||
MisoSceneModel model = _panel.getSceneModel();
|
||||
for (int yy = 0; yy < _bounds.height; yy++) {
|
||||
for (int xx = 0; xx < _bounds.width; xx++) {
|
||||
int x = _bounds.x + xx, y = _bounds.y + yy;
|
||||
int fqTileId = model.getBaseTileId(x, y);
|
||||
if (fqTileId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// load up this base tile
|
||||
updateBaseTile(fqTileId, x, y);
|
||||
baseCount++;
|
||||
|
||||
// if there's no tile here, we don't need no fringe
|
||||
int tidx = index(x, y);
|
||||
if (_base[tidx] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// compute the fringe for this tile
|
||||
_fringe[tidx] = _panel.computeFringeTile(x, y);
|
||||
fringeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG: check for long resolution times
|
||||
long stamp = System.currentTimeMillis();
|
||||
long elapsed = stamp - now;
|
||||
if (elapsed > 500L) {
|
||||
Log.warning("Base and fringe resolution took long time " +
|
||||
"[block=" + this + ", baseCount=" + baseCount +
|
||||
", fringeCount=" + fringeCount +
|
||||
", elapsed=" + elapsed + "].");
|
||||
}
|
||||
|
||||
// resolve our objects
|
||||
ObjectSet set = new ObjectSet();
|
||||
model.getObjects(_bounds, set);
|
||||
ArrayList scobjs = new ArrayList();
|
||||
now = System.currentTimeMillis();
|
||||
for (int ii = 0, ll = set.size(); ii < ll; ii++) {
|
||||
SceneObject scobj = new SceneObject(_panel, set.get(ii));
|
||||
// ignore this object if it failed to resolve
|
||||
if (scobj.bounds == null) {
|
||||
continue;
|
||||
}
|
||||
sbounds.add(scobj.bounds);
|
||||
obounds = GeomUtil.grow(obounds, scobj.bounds);
|
||||
scobjs.add(scobj);
|
||||
|
||||
// DEBUG: check for long resolution times
|
||||
stamp = System.currentTimeMillis();
|
||||
elapsed = stamp - now;
|
||||
now = stamp;
|
||||
if (elapsed > 250L) {
|
||||
Log.warning("Scene object took look time to resolve " +
|
||||
"[block=" + this + ", scobj=" + scobj +
|
||||
", elapsed=" + elapsed + "].");
|
||||
}
|
||||
}
|
||||
_objects = (SceneObject[])scobjs.toArray(
|
||||
new SceneObject[scobjs.size()]);
|
||||
|
||||
// resolve our default tileset
|
||||
int bsetid = model.getDefaultBaseTileSet();
|
||||
try {
|
||||
if (bsetid > 0) {
|
||||
_defset = _panel.getTileManager().getTileSet(bsetid);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.warning("Unable to fetch default base tileset [tsid=" + bsetid +
|
||||
", error=" + e + "].");
|
||||
}
|
||||
|
||||
// this both marks us as resolved and makes all our other updated
|
||||
// fields visible
|
||||
synchronized (this) {
|
||||
_obounds = obounds;
|
||||
_sbounds = sbounds;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called by the {@link SceneBlockResolver} on the AWT thread
|
||||
* when our resolution has completed. We inform our containing panel.
|
||||
*/
|
||||
protected void wasResolved ()
|
||||
{
|
||||
_panel.blockResolved(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this block has been resolved, false if not.
|
||||
*/
|
||||
protected synchronized boolean isResolved ()
|
||||
{
|
||||
return _sbounds != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounds of this block, in tile coordinates.
|
||||
*/
|
||||
public Rectangle getBounds ()
|
||||
{
|
||||
return _bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounds of the screen coordinate rectangle that contains
|
||||
* all pixels that are drawn on by all tiles and objects in this
|
||||
* block.
|
||||
*/
|
||||
public Rectangle getScreenBounds ()
|
||||
{
|
||||
return _sbounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounds of the screen coordinate rectangle that contains
|
||||
* all pixels that are drawn on by all objects (but not base tiles) in
|
||||
* this block. <em>Note:</em> this will return <code>null</code> if
|
||||
* the block has no objects.
|
||||
*/
|
||||
public Rectangle getObjectBounds ()
|
||||
{
|
||||
return _obounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the screen-coordinate polygon bounding the footprint of
|
||||
* this block.
|
||||
*/
|
||||
public Polygon getFootprint ()
|
||||
{
|
||||
return _footprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all resolved scene objects in this block.
|
||||
*/
|
||||
public SceneObject[] getObjects ()
|
||||
{
|
||||
return _objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base tile at the specified coordinates or null if
|
||||
* there's no tile at said coordinates.
|
||||
*/
|
||||
public BaseTile getBaseTile (int tx, int ty)
|
||||
{
|
||||
BaseTile tile = _base[index(tx, ty)];
|
||||
if (tile == null && _defset != null) {
|
||||
tile = (BaseTile)_defset.getTile(
|
||||
TileUtil.getTileHash(tx, ty) % _defset.getTileCount());
|
||||
}
|
||||
return tile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fringe tile at the specified coordinates or null if
|
||||
* there's no tile at said coordinates.
|
||||
*/
|
||||
public BaseTile getFringeTile (int tx, int ty)
|
||||
{
|
||||
return _fringe[index(tx, ty)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs this scene block that the specified base tile has been
|
||||
* changed.
|
||||
*/
|
||||
public void updateBaseTile (int fqTileId, int tx, int ty)
|
||||
{
|
||||
String errmsg = null;
|
||||
int tidx = index(tx, ty);
|
||||
|
||||
// this is a bit magical: we pass the fully qualified tile id to
|
||||
// the tile manager which loads up from the configured tileset
|
||||
// repository the appropriate tileset (which should be a
|
||||
// BaseTileSet) and then extracts the appropriate base tile (the
|
||||
// index of which is also in the fqTileId)
|
||||
try {
|
||||
if (fqTileId <= 0) {
|
||||
_base[tidx] = null;
|
||||
} else {
|
||||
_base[tidx] = (BaseTile)
|
||||
_panel.getTileManager().getTile(fqTileId);
|
||||
}
|
||||
// clear out the fringe (it must be recomputed by the caller)
|
||||
_fringe[tidx] = null;
|
||||
|
||||
} catch (ClassCastException cce) {
|
||||
errmsg = "Scene contains non-base tile in base layer";
|
||||
} catch (NoSuchTileSetException nste) {
|
||||
errmsg = "Scene contains non-existent tileset";
|
||||
}
|
||||
|
||||
if (errmsg != null) {
|
||||
Log.warning(errmsg + " [fqtid=" + fqTileId +
|
||||
", x=" + tx + ", y=" + ty + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs this block to recompute its fringe at the specified
|
||||
* location.
|
||||
*/
|
||||
public void updateFringe (int tx, int ty)
|
||||
{
|
||||
int tidx = index(tx, ty);
|
||||
if (_base[tidx] != null) {
|
||||
_fringe[tidx] = _panel.computeFringeTile(tx, ty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the supplied object to this block. Coverage is not computed
|
||||
* for the added object, a subsequent call to {@link #update} will be
|
||||
* needed.
|
||||
*
|
||||
* @return true if the object was added, false if it was not because
|
||||
* another object of the same type already occupies that location.
|
||||
*/
|
||||
public boolean addObject (ObjectInfo info)
|
||||
{
|
||||
// make sure we don't already have this same object at these
|
||||
// coordinates
|
||||
for (int ii = 0; ii < _objects.length; ii++) {
|
||||
if (_objects[ii].info.equals(info)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_objects = (SceneObject[])
|
||||
ArrayUtil.append(_objects, new SceneObject(_panel, info));
|
||||
|
||||
// clear out our neighbors array so that the subsequent update
|
||||
// causes us to recompute our coverage
|
||||
Arrays.fill(_neighbors, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified object from this block. Coverage is not
|
||||
* recomputed, so a subsequent call to {@link #update} will be needed.
|
||||
*
|
||||
* @return true if the object was deleted, false if it was not found
|
||||
* in our object list.
|
||||
*/
|
||||
public boolean deleteObject (ObjectInfo info)
|
||||
{
|
||||
int oidx = -1;
|
||||
for (int ii = 0; ii < _objects.length; ii++) {
|
||||
if (_objects[ii].info.equals(info)) {
|
||||
oidx = ii;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (oidx == -1) {
|
||||
return false;
|
||||
}
|
||||
_objects = (SceneObject[])ArrayUtil.splice(_objects, oidx, 1);
|
||||
|
||||
// clear out our neighbors array so that the subsequent update
|
||||
// causes us to recompute our coverage
|
||||
Arrays.fill(_neighbors, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified traverser can traverse the specified
|
||||
* tile (which is assumed to be in the bounds of this scene block).
|
||||
*/
|
||||
public boolean canTraverse (Object traverser, int tx, int ty)
|
||||
{
|
||||
if (_covered[index(tx, ty)]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// null base or impassable base kills traversal
|
||||
BaseTile base = getBaseTile(tx, ty);
|
||||
if ((base == null) || !base.isPassable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// fringe can only kill traversal if it is present
|
||||
BaseTile fringe = getFringeTile(tx, ty);
|
||||
return (fringe == null) || fringe.isPassable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the memory usage of the base and object tiles in this
|
||||
* scene block; registering counted tiles in the hash map so that
|
||||
* other blocks can be sure not to double count them. Base tile usage
|
||||
* is placed into the zeroth array element, fringe tile usage into the
|
||||
* first and object tile usage into the second.
|
||||
*/
|
||||
public void computeMemoryUsage (
|
||||
HashMap bases, HashSet fringes, HashMap objects, long[] usage)
|
||||
{
|
||||
// account for our base tiles
|
||||
MisoSceneModel model = _panel.getSceneModel();
|
||||
for (int yy = 0; yy < _bounds.height; yy++) {
|
||||
for (int xx = 0; xx < _bounds.width; xx++) {
|
||||
int x = _bounds.x + xx, y = _bounds.y + yy;
|
||||
int tidx = index(x, y);
|
||||
BaseTile base = _base[tidx];
|
||||
if (base == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BaseTile sbase = (BaseTile)bases.get(base.key);
|
||||
if (sbase == null) {
|
||||
bases.put(base.key, base);
|
||||
usage[0] += base.getEstimatedMemoryUsage();
|
||||
} else if (base != _base[tidx]) {
|
||||
Log.warning("Multiple instances of same base tile " +
|
||||
"[base=" + base +
|
||||
", x=" + xx + ", y=" + yy + "].");
|
||||
usage[0] += base.getEstimatedMemoryUsage();
|
||||
}
|
||||
|
||||
// now account for the fringe
|
||||
if (_fringe[tidx] == null) {
|
||||
continue;
|
||||
} else if (!fringes.contains(_fringe[tidx])) {
|
||||
fringes.add(_fringe[tidx]);
|
||||
usage[1] += _fringe[tidx].getEstimatedMemoryUsage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now get the object tiles
|
||||
int ocount = (_objects == null) ? 0 : _objects.length;
|
||||
for (int ii = 0; ii < ocount; ii++) {
|
||||
SceneObject scobj = _objects[ii];
|
||||
ObjectTile tile = (ObjectTile)objects.get(scobj.tile.key);
|
||||
if (tile == null) {
|
||||
objects.put(scobj.tile.key, scobj.tile);
|
||||
usage[2] += scobj.tile.getEstimatedMemoryUsage();
|
||||
} else if (tile != scobj.tile) {
|
||||
Log.warning("Multiple instances of same object tile: " +
|
||||
scobj.info + ".");
|
||||
usage[2] += scobj.tile.getEstimatedMemoryUsage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this instance.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
int bx = MathUtil.floorDiv(_bounds.x, _bounds.width);
|
||||
int by = MathUtil.floorDiv(_bounds.y, _bounds.height);
|
||||
return StringUtil.coordsToString(bx, by) + ":" +
|
||||
StringUtil.toString(_bounds) + ":" +
|
||||
((_objects == null) ? 0 : _objects.length) +
|
||||
(_visi ? ":v" : ":i");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index into our arrays of the specified tile.
|
||||
*/
|
||||
protected final int index (int tx, int ty)
|
||||
{
|
||||
// if (!_bounds.contains(tx, ty)) {
|
||||
// String errmsg = "Coordinates out of bounds: +" + tx + "+" + ty +
|
||||
// " not in " + StringUtil.toString(_bounds);
|
||||
// throw new IllegalArgumentException(errmsg);
|
||||
// }
|
||||
return (ty-_bounds.y)*_bounds.width + (tx-_bounds.x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Links this block to its neighbors; informs neighboring blocks of
|
||||
* object coverage.
|
||||
*/
|
||||
protected void update (HashIntMap blocks)
|
||||
{
|
||||
boolean recover = false;
|
||||
|
||||
// link up to our neighbors
|
||||
for (int ii = 0; ii < DX.length; ii++) {
|
||||
SceneBlock neigh = (SceneBlock)
|
||||
blocks.get(neighborKey(DX[ii], DY[ii]));
|
||||
if (neigh != _neighbors[ii]) {
|
||||
_neighbors[ii] = neigh;
|
||||
// if we're linking up to a neighbor for the first time;
|
||||
// we need to recalculate our coverage
|
||||
recover = recover || (neigh != null);
|
||||
// Log.info(this + " was introduced to " + neigh + ".");
|
||||
}
|
||||
}
|
||||
|
||||
// if we need to regenerate the set of tiles covered by our
|
||||
// objects, do so
|
||||
if (recover) {
|
||||
for (int ii = 0; ii < _objects.length; ii++) {
|
||||
setCovered(blocks, _objects[ii]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Computes the key of our neighbor. */
|
||||
protected final int neighborKey (int dx, int dy)
|
||||
{
|
||||
int nx = MathUtil.floorDiv(_bounds.x, _bounds.width)+dx;
|
||||
int ny = MathUtil.floorDiv(_bounds.y, _bounds.height)+dy;
|
||||
return MisoScenePanel.compose(nx, ny);
|
||||
}
|
||||
|
||||
/** Computes the key for the block that holds the specified tile. */
|
||||
protected final int blockKey (int tx, int ty)
|
||||
{
|
||||
int bx = MathUtil.floorDiv(tx, _bounds.width);
|
||||
int by = MathUtil.floorDiv(ty, _bounds.height);
|
||||
return MisoScenePanel.compose(bx, by);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the footprint of this object tile
|
||||
*/
|
||||
protected void setCovered (HashIntMap blocks, SceneObject scobj)
|
||||
{
|
||||
int endx = scobj.info.x - scobj.tile.getBaseWidth() + 1;
|
||||
int endy = scobj.info.y - scobj.tile.getBaseHeight() + 1;
|
||||
|
||||
for (int xx = scobj.info.x; xx >= endx; xx--) {
|
||||
for (int yy = scobj.info.y; yy >= endy; yy--) {
|
||||
SceneBlock block = (SceneBlock)blocks.get(blockKey(xx, yy));
|
||||
if (block != null) {
|
||||
block.setCovered(xx, yy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log.info("Updated coverage " + scobj.info + ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that this tile is covered by an object footprint.
|
||||
*/
|
||||
protected void setCovered (int tx, int ty)
|
||||
{
|
||||
_covered[index(tx, ty)] = true;
|
||||
}
|
||||
|
||||
/** The panel for which we contain a block. */
|
||||
protected MisoScenePanel _panel;
|
||||
|
||||
/** The bounds of (in tile coordinates) of this block. */
|
||||
protected Rectangle _bounds;
|
||||
|
||||
/** The bounds (in screen coords) of all images rendered by this block. */
|
||||
protected Rectangle _sbounds;
|
||||
|
||||
/** The bounds (in screen coords) of all objects rendered by this block. */
|
||||
protected Rectangle _obounds;
|
||||
|
||||
/** A polygon bounding the footprint of this block. */
|
||||
protected Polygon _footprint;
|
||||
|
||||
/** Used to return a tile where we have none. */
|
||||
protected TileSet _defset;
|
||||
|
||||
/** Our base tiles. */
|
||||
protected BaseTile[] _base;
|
||||
|
||||
/** Our fringe tiles. */
|
||||
protected BaseTile[] _fringe;
|
||||
|
||||
/** Indicates whether our tiles are covered by an object. */
|
||||
protected boolean[] _covered;
|
||||
|
||||
/** Info on our objects. */
|
||||
protected SceneObject[] _objects;
|
||||
|
||||
/** Our neighbors in the eight cardinal directions. */
|
||||
protected SceneBlock[] _neighbors = new SceneBlock[DX.length];
|
||||
|
||||
/** A debug flag indicating whether we were visible at creation. */
|
||||
protected boolean _visi;
|
||||
|
||||
// used to link up to our neighbors
|
||||
protected static final int[] DX = { -1, -1, 0, 1, 1, 1, 0, -1 };
|
||||
protected static final int[] DY = { 0, -1, -1, -1, 0, 1, 1, 1 };
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
//
|
||||
// $Id: SceneBlockResolver.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.client;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
|
||||
import com.samskivert.util.Histogram;
|
||||
import com.samskivert.util.LoopingThread;
|
||||
import com.samskivert.util.Queue;
|
||||
|
||||
import com.threerings.miso.Log;
|
||||
|
||||
/**
|
||||
* A separate thread for resolving miso scene blocks.
|
||||
*/
|
||||
public class SceneBlockResolver extends LoopingThread
|
||||
{
|
||||
/**
|
||||
* Queues up a scene block for resolution.
|
||||
*/
|
||||
public void resolveBlock (SceneBlock block, boolean hipri)
|
||||
{
|
||||
Log.debug("Queueing block for resolution " + block +
|
||||
" (" + hipri + ").");
|
||||
if (hipri) {
|
||||
_queue.prepend(block);
|
||||
} else {
|
||||
_queue.append(block);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily suspends the scene block resolution thread.
|
||||
*/
|
||||
public synchronized void suspendResolution ()
|
||||
{
|
||||
_resolving = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the operation of the scene block resolution thread after a
|
||||
* previous call to {@link #suspendResolution}.
|
||||
*/
|
||||
public synchronized void restoreResolution ()
|
||||
{
|
||||
_resolving = true;
|
||||
notify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of scene blocks on the resolution queue.
|
||||
*/
|
||||
public int queueSize ()
|
||||
{
|
||||
return _queue.size();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void iterate ()
|
||||
{
|
||||
final SceneBlock block = (SceneBlock)_queue.get();
|
||||
|
||||
while (!_resolving) {
|
||||
synchronized (this) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException ie) {
|
||||
Log.info("Resolver interrupted.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
long start = System.currentTimeMillis();
|
||||
Log.debug("Resolving block " + block + ".");
|
||||
if (block.resolve()) {
|
||||
Log.debug("Resolved block " + block + ".");
|
||||
}
|
||||
long elapsed = System.currentTimeMillis() - start;
|
||||
_histo.addValue((int)elapsed);
|
||||
|
||||
// warn if a block takes a long time to resolve
|
||||
if (elapsed > LONG_RESOLVE_TIME) {
|
||||
Log.warning("Block took long time to resolve [block=" + block +
|
||||
", elapsed=" + elapsed + "ms].");
|
||||
}
|
||||
|
||||
// queue it up on the AWT thread to complete its resolution
|
||||
final boolean report = (_queue.size() == 0);
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run () {
|
||||
// let the block's panel know that it is resolved
|
||||
block.wasResolved();
|
||||
// report statistics
|
||||
// if (report) {
|
||||
// Log.info("Resolution histogram " +
|
||||
// _histo.summarize() + ".");
|
||||
// }
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Block failed during resolution " + block + ".");
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** The invoker's queue of units to be executed. */
|
||||
protected Queue _queue = new Queue();
|
||||
|
||||
/** Indicates whether or not we are resolving or suspended. */
|
||||
protected boolean _resolving = true;
|
||||
|
||||
/** Used to time block loading. */
|
||||
protected Histogram _histo = new Histogram(0, 25, 100);
|
||||
|
||||
/** Blocks shouldn't take too long to resolve. */
|
||||
protected static final long LONG_RESOLVE_TIME = 500L;
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
//
|
||||
// $Id: SceneObject.java 4007 2006-04-10 08:59:30Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.client;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.Polygon;
|
||||
import java.awt.Rectangle;
|
||||
|
||||
import com.samskivert.swing.RuntimeAdjust;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.media.tile.NoSuchTileSetException;
|
||||
import com.threerings.media.tile.ObjectTile;
|
||||
import com.threerings.media.tile.TileUtil;
|
||||
|
||||
import com.threerings.miso.Log;
|
||||
import com.threerings.miso.MisoPrefs;
|
||||
import com.threerings.miso.data.ObjectInfo;
|
||||
import com.threerings.miso.util.MisoSceneMetrics;
|
||||
import com.threerings.miso.util.MisoUtil;
|
||||
|
||||
/**
|
||||
* Contains resolved information on an object in a scene.
|
||||
*/
|
||||
public class SceneObject
|
||||
{
|
||||
/** The object's info record. */
|
||||
public ObjectInfo info;
|
||||
|
||||
/** The object tile used to display this object. */
|
||||
public ObjectTile tile;
|
||||
|
||||
/** The screen coordinate bounds of our object tile given its position
|
||||
* in the scene. */
|
||||
public Rectangle bounds;
|
||||
|
||||
/**
|
||||
* Creates a scene object for display by the specified panel. The
|
||||
* appropriate object tile is resolved and the object's in-situ bounds
|
||||
* are computed.
|
||||
*/
|
||||
public SceneObject (MisoScenePanel panel, ObjectInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
|
||||
// resolve our object tile
|
||||
refreshObjectTile(panel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a scene object for display by the specified panel.
|
||||
*/
|
||||
public SceneObject (MisoScenePanel panel, ObjectInfo info, ObjectTile tile)
|
||||
{
|
||||
this(panel.getSceneMetrics(), info, tile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a scene object for display according to the supplied metrics.
|
||||
*/
|
||||
public SceneObject (
|
||||
MisoSceneMetrics metrics, ObjectInfo info, ObjectTile tile)
|
||||
{
|
||||
this.info = info;
|
||||
this.tile = tile;
|
||||
computeInfo(metrics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to flag overlapping scene objects that have no resolving
|
||||
* object priorities.
|
||||
*/
|
||||
public void setWarning (boolean warning)
|
||||
{
|
||||
_warning = warning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that this scene object render itself.
|
||||
*/
|
||||
public void paint (Graphics2D gfx)
|
||||
{
|
||||
if (_hideObjects.getValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're rendering footprints, paint that
|
||||
boolean footpaint = _fprintDebug.getValue();
|
||||
if (footpaint) {
|
||||
gfx.setColor(Color.black);
|
||||
gfx.draw(_footprint);
|
||||
}
|
||||
|
||||
// if we have a warning, render an alpha'd red rectangle over our
|
||||
// bounds
|
||||
if (_warning) {
|
||||
Composite ocomp = gfx.getComposite();
|
||||
gfx.setComposite(ALPHA_WARN);
|
||||
gfx.fill(bounds);
|
||||
gfx.setComposite(ocomp);
|
||||
}
|
||||
|
||||
// paint our tile
|
||||
tile.paint(gfx, bounds.x, bounds.y);
|
||||
|
||||
// and possibly paint the object's spot
|
||||
if (footpaint && _sspot != null) {
|
||||
// translate the origin to center on the portal
|
||||
gfx.translate(_sspot.x, _sspot.y);
|
||||
|
||||
// rotate to reflect the spot orientation
|
||||
double rot = (Math.PI / 4.0f) * tile.getSpotOrient();
|
||||
gfx.rotate(rot);
|
||||
|
||||
// draw the spot triangle
|
||||
gfx.setColor(Color.green);
|
||||
gfx.fill(_spotTri);
|
||||
|
||||
// outline the triangle in black
|
||||
gfx.setColor(Color.black);
|
||||
gfx.draw(_spotTri);
|
||||
|
||||
// restore the original transform
|
||||
gfx.rotate(-rot);
|
||||
gfx.translate(-_sspot.x, -_sspot.y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location associated with this object's "spot" in fine
|
||||
* coordinates or null if it has no spot.
|
||||
*/
|
||||
public Point getObjectSpot ()
|
||||
{
|
||||
return _fspot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location associated with this object's "spot" in screen
|
||||
* coordinates or null if it has no spot.
|
||||
*/
|
||||
public Point getObjectScreenSpot ()
|
||||
{
|
||||
return _sspot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this object's footprint overlaps that of the
|
||||
* specified other object.
|
||||
*/
|
||||
public boolean objectFootprintOverlaps (SceneObject so)
|
||||
{
|
||||
return _frect.intersects(so._frect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this object's footprint overlaps the supplied tile
|
||||
* coordinate rectangle.
|
||||
*/
|
||||
public boolean objectFootprintOverlaps (Rectangle rect)
|
||||
{
|
||||
return _frect.intersects(rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a polygon bounding all footprint tiles of this scene
|
||||
* object.
|
||||
*
|
||||
* @return the bounding polygon.
|
||||
*/
|
||||
public Polygon getObjectFootprint ()
|
||||
{
|
||||
return _footprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the render priority of this scene object.
|
||||
*/
|
||||
public int getPriority ()
|
||||
{
|
||||
// if we have no overridden priority, return our object tile's
|
||||
// default priority
|
||||
return (info.priority == 0 && tile != null) ?
|
||||
tile.getPriority() : info.priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the render priority of this object.
|
||||
*/
|
||||
public void setPriority (byte priority)
|
||||
{
|
||||
info.priority = (byte)Math.max(
|
||||
Byte.MIN_VALUE, Math.min(Byte.MAX_VALUE, priority));
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs this scene object that the mouse is now hovering over it.
|
||||
* Custom objects may wish to adjust some internal state and return
|
||||
* true from this method indicating that they should be repainted.
|
||||
*/
|
||||
public boolean setHovered (boolean hovered)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this object's origin tile coordinate. It's bounds and other
|
||||
* cached screen coordinate information are updated.
|
||||
*/
|
||||
public void relocateObject (MisoSceneMetrics metrics, int tx, int ty)
|
||||
{
|
||||
// Log.info("Relocating object " + this + " to " +
|
||||
// StringUtil.coordsToString(tx, ty));
|
||||
info.x = tx;
|
||||
info.y = ty;
|
||||
computeInfo(metrics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads and recolorizes our object tile. It is not intended for the
|
||||
* actual object tile used by a scene object to change in its
|
||||
* lifetime, only attributes of that object like its colorizations. So
|
||||
* don't do anything crazy like change our {@link ObjectInfo}'s
|
||||
* <code>tileId</code> and call this method or things might break.
|
||||
*/
|
||||
public void refreshObjectTile (MisoScenePanel panel)
|
||||
{
|
||||
int tsid = TileUtil.getTileSetId(info.tileId);
|
||||
int tidx = TileUtil.getTileIndex(info.tileId);
|
||||
try {
|
||||
tile = (ObjectTile)panel.getTileManager().getTile(
|
||||
tsid, tidx, panel.getColorizer(info));
|
||||
computeInfo(panel.getSceneMetrics());
|
||||
|
||||
} catch (NoSuchTileSetException te) {
|
||||
Log.warning("Scene contains non-existent object tileset " +
|
||||
"[info=" + info + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes our screen bounds, tile footprint and other useful cached
|
||||
* metrics.
|
||||
*/
|
||||
protected void computeInfo (MisoSceneMetrics metrics)
|
||||
{
|
||||
// start with the screen coordinates of our origin tile
|
||||
Point tpos = MisoUtil.tileToScreen(
|
||||
metrics, info.x, info.y, new Point());
|
||||
|
||||
// if the tile has an origin coordinate, use that, otherwise
|
||||
// compute it from the tile footprint
|
||||
int tox = tile.getOriginX(), toy = tile.getOriginY();
|
||||
if (tox == Integer.MIN_VALUE) {
|
||||
tox = tile.getBaseWidth() * metrics.tilehwid;
|
||||
}
|
||||
if (toy == Integer.MIN_VALUE) {
|
||||
toy = tile.getHeight();
|
||||
}
|
||||
|
||||
bounds = new Rectangle(tpos.x + metrics.tilehwid - tox,
|
||||
tpos.y + metrics.tilehei - toy,
|
||||
tile.getWidth(), tile.getHeight());
|
||||
|
||||
// compute our object footprint as well
|
||||
_frect = new Rectangle(info.x - tile.getBaseWidth() + 1,
|
||||
info.y - tile.getBaseHeight() + 1,
|
||||
tile.getBaseWidth(), tile.getBaseHeight());
|
||||
_footprint = MisoUtil.getFootprintPolygon(
|
||||
metrics, _frect.x, _frect.y, _frect.width, _frect.height);
|
||||
|
||||
// compute our object spot if we've got one
|
||||
if (tile.hasSpot()) {
|
||||
_fspot = MisoUtil.tilePlusFineToFull(
|
||||
metrics, info.x, info.y, tile.getSpotX(), tile.getSpotY(),
|
||||
new Point());
|
||||
_sspot = MisoUtil.fullToScreen(
|
||||
metrics, _fspot.x, _fspot.y, new Point());
|
||||
}
|
||||
|
||||
// Log.info("Computed object metrics " +
|
||||
// "[tpos=" + StringUtil.coordsToString(tx, ty) +
|
||||
// ", info=" + info +
|
||||
// ", sbounds=" + StringUtil.toString(bounds) + "].");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this instance.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
return info + "[" + StringUtil.toString(bounds) + "]";
|
||||
}
|
||||
|
||||
/** Our object as a tile coordinate rectangle. */
|
||||
protected Rectangle _frect;
|
||||
|
||||
/** Our object footprint as a polygon. */
|
||||
protected Polygon _footprint;
|
||||
|
||||
/** The full-coordinates of our object spot; or null if we have none. */
|
||||
protected Point _fspot;
|
||||
|
||||
/** The screen-coordinates of our object spot; or null if we have none. */
|
||||
protected Point _sspot;
|
||||
|
||||
/** Used to mark objects with errors. */
|
||||
protected boolean _warning;
|
||||
|
||||
/** A debug hook that toggles rendering of objects. */
|
||||
protected static RuntimeAdjust.BooleanAdjust _hideObjects =
|
||||
new RuntimeAdjust.BooleanAdjust(
|
||||
"Toggles rendering of objects in the scene view.",
|
||||
"narya.miso.hide_objects", MisoPrefs.config, false);
|
||||
|
||||
/** A debug hook that toggles rendering of object footprints. */
|
||||
protected static RuntimeAdjust.BooleanAdjust _fprintDebug =
|
||||
new RuntimeAdjust.BooleanAdjust(
|
||||
"Toggles rendering of object footprints in the scene view.",
|
||||
"narya.miso.iso_fprint_debug_render", MisoPrefs.config, false);
|
||||
|
||||
/** The triangle used to render an object's spot. */
|
||||
protected static Polygon _spotTri;
|
||||
|
||||
static {
|
||||
_spotTri = new Polygon();
|
||||
_spotTri.addPoint(-3, -3);
|
||||
_spotTri.addPoint(3, -3);
|
||||
_spotTri.addPoint(0, 3);
|
||||
};
|
||||
|
||||
/** The alpha used to fill our bounds for warning purposes. */
|
||||
protected static final Composite ALPHA_WARN =
|
||||
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// $Id: SceneObjectActionEvent.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.client;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
/**
|
||||
* An {@link ActionEvent} derivation that is fired when a scene object is
|
||||
* clicked or menu item selected.
|
||||
*/
|
||||
public class SceneObjectActionEvent extends ActionEvent
|
||||
{
|
||||
public SceneObjectActionEvent (Object source, int id, String action,
|
||||
int modifiers, SceneObject scobj)
|
||||
{
|
||||
super(source, id, action, modifiers);
|
||||
_scobj = scobj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scene object that was the source of this action.
|
||||
*/
|
||||
public SceneObject getSceneObject ()
|
||||
{
|
||||
return _scobj;
|
||||
}
|
||||
|
||||
protected SceneObject _scobj;
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
//
|
||||
// $Id: SceneObjectTip.java 4007 2006-04-10 08:59:30Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.client;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import com.samskivert.swing.Label;
|
||||
import com.samskivert.swing.LabelSausage;
|
||||
import com.samskivert.util.ComparableArrayList;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* A lightweight tooltip used by the {@link MisoScenePanel}. The tip
|
||||
* foreground and background are controlled by the following {@link
|
||||
* UIManager} properties:
|
||||
*
|
||||
* <pre>
|
||||
* SceneObjectTip.background
|
||||
* SceneObjectTip.foreground
|
||||
* SceneObjectTip.font (falls back to Label.font)
|
||||
* </pre>
|
||||
*/
|
||||
public class SceneObjectTip extends LabelSausage
|
||||
{
|
||||
/**
|
||||
* Used to position a scene tip in relation to the object with which
|
||||
* it is associated.
|
||||
*/
|
||||
public static interface TipLayout
|
||||
{
|
||||
/**
|
||||
* Position the supplied tip relative to the supplied scene object.
|
||||
*/
|
||||
public void layout (Graphics2D gfx, Rectangle boundary,
|
||||
SceneObject tipFor, SceneObjectTip tip);
|
||||
}
|
||||
|
||||
/** The bounding box of this tip, or null prior to layout(). */
|
||||
public Rectangle bounds;
|
||||
|
||||
/**
|
||||
* Construct a SceneObjectTip.
|
||||
*/
|
||||
public SceneObjectTip (String text, Icon icon)
|
||||
{
|
||||
super(new Label(text, _foreground, _font), icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to initialize the tip so that it can be painted.
|
||||
*
|
||||
* @param tipFor the scene object that we're a tip for.
|
||||
* @param boundary the boundary of all displayable space.
|
||||
* @param othertips other tip boundaries that we should avoid.
|
||||
*/
|
||||
public void layout (Graphics2D gfx, SceneObject tipFor, Rectangle boundary,
|
||||
Collection othertips)
|
||||
{
|
||||
layout(gfx, ICON_PAD, EXTRA_PAD);
|
||||
bounds = new Rectangle(_size);
|
||||
|
||||
// locate the most appropriate tip layout
|
||||
for (int ii = 0, ll = _layouts.size(); ii < ll; ii++) {
|
||||
LayoutReg reg = (LayoutReg)_layouts.get(ii);
|
||||
String act = tipFor.info.action == null ? "" : tipFor.info.action;
|
||||
if (act.startsWith(reg.prefix)) {
|
||||
reg.layout.layout(gfx, boundary, tipFor, this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paint this tip at it's location.
|
||||
*/
|
||||
public void paint (Graphics2D gfx)
|
||||
{
|
||||
paint(gfx, bounds.x, bounds.y, _background, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a string representation of this instance.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
return _label.getText() + "[" + StringUtil.toString(bounds) + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* It may be desirable to layout object tips specially depending on
|
||||
* what sort of actions they represent, so we allow different tip
|
||||
* layout algorithms to be registered for particular object prefixes.
|
||||
* The registration is simply a list searched from longest string to
|
||||
* shortest string for the first match to an object's action.
|
||||
*/
|
||||
public static void registerTipLayout (String prefix, TipLayout layout)
|
||||
{
|
||||
LayoutReg reg = new LayoutReg();
|
||||
reg.prefix = prefix;
|
||||
reg.layout = layout;
|
||||
_layouts.insertSorted(reg);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void drawBase (Graphics2D gfx, int x, int y)
|
||||
{
|
||||
Composite ocomp = gfx.getComposite();
|
||||
gfx.setComposite(ALPHA);
|
||||
super.drawBase(gfx, x, y);
|
||||
gfx.setComposite(ocomp);
|
||||
}
|
||||
|
||||
/** The alpha we use for our base. */
|
||||
protected static final Composite ALPHA = AlphaComposite.getInstance(
|
||||
AlphaComposite.SRC_OVER, .75f);
|
||||
|
||||
/** Colors to use when rendering the tip. */
|
||||
protected static Color _background, _foreground;
|
||||
|
||||
/** The font to use when rendering the tip. */
|
||||
protected static Font _font;
|
||||
|
||||
// initialize resources shared by all tips
|
||||
static {
|
||||
_background = UIManager.getColor("SceneObjectTip.background");
|
||||
_foreground = UIManager.getColor("SceneObjectTip.foreground");
|
||||
_font = UIManager.getFont("SceneObjectTip.font");
|
||||
if (_font == null) {
|
||||
_font = UIManager.getFont("Label.font");
|
||||
}
|
||||
}
|
||||
|
||||
/** Used to store {@link TipLayout} registrations. */
|
||||
protected static class LayoutReg implements Comparable
|
||||
{
|
||||
/** The prefix that defines our applicability. */
|
||||
public String prefix;
|
||||
|
||||
/** The layout to use for objects matching our prefix. */
|
||||
public TipLayout layout;
|
||||
|
||||
// documentation inherited from interface
|
||||
public int compareTo (Object o) {
|
||||
LayoutReg or = (LayoutReg)o;
|
||||
if (or.prefix.length() == prefix.length()) {
|
||||
return or.prefix.compareTo(prefix);
|
||||
} else {
|
||||
return or.prefix.length() - prefix.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Our default tip layout algorithm which centers the tip in the
|
||||
* bounds of the object in question. */
|
||||
protected static class DefaultLayout implements TipLayout
|
||||
{
|
||||
public void layout (Graphics2D gfx, Rectangle boundary,
|
||||
SceneObject tipFor, SceneObjectTip tip) {
|
||||
tip.bounds.setLocation(
|
||||
tipFor.bounds.x + (tipFor.bounds.width-tip.bounds.width) / 2,
|
||||
tipFor.bounds.y + (tipFor.bounds.height-tip.bounds.height) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
/** Contains a sorted list of layout registrations. */
|
||||
protected static ComparableArrayList _layouts = new ComparableArrayList();
|
||||
|
||||
/** The number of pixels to pad around the icon. */
|
||||
protected static final int ICON_PAD = 4;
|
||||
|
||||
/** The number of pixels to pad between the icon and text. */
|
||||
protected static final int EXTRA_PAD = 2;
|
||||
|
||||
static {
|
||||
registerTipLayout("", new DefaultLayout());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
//
|
||||
// $Id: TilePath.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.client;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.List;
|
||||
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
import com.threerings.media.sprite.Sprite;
|
||||
import com.threerings.media.util.LineSegmentPath;
|
||||
import com.threerings.media.util.MathUtil;
|
||||
|
||||
import com.threerings.miso.util.MisoSceneMetrics;
|
||||
import com.threerings.miso.util.MisoUtil;
|
||||
|
||||
/**
|
||||
* The tile path represents a path of tiles through a scene. The path is
|
||||
* traversed by treating each pair of connected tiles as a line segment.
|
||||
* Only ambulatory sprites can follow a tile path, and their tile
|
||||
* coordinates are updated as the path is traversed.
|
||||
*/
|
||||
public class TilePath extends LineSegmentPath
|
||||
implements DirectionCodes
|
||||
{
|
||||
/**
|
||||
* Constructs a tile path.
|
||||
*
|
||||
* @param metrics the metrics for the scene the with which the path is
|
||||
* associated.
|
||||
* @param sprite the sprite to follow the path.
|
||||
* @param tiles the tiles to be traversed during the path.
|
||||
* @param destx the destination x-position in screen pixel
|
||||
* coordinates.
|
||||
* @param desty the destination y-position in screen pixel
|
||||
* coordinates.
|
||||
*/
|
||||
public TilePath (MisoSceneMetrics metrics, Sprite sprite,
|
||||
List tiles, int destx, int desty)
|
||||
{
|
||||
// constrain destination pixels to fine coordinates
|
||||
Point fpos = new Point();
|
||||
MisoUtil.screenToFull(metrics, destx, desty, fpos);
|
||||
|
||||
// add the starting path node
|
||||
int sx = sprite.getX(), sy = sprite.getY();
|
||||
Point ipos = MisoUtil.screenToTile(metrics, sx, sy, new Point());
|
||||
addNode(sx, sy, NORTH);
|
||||
|
||||
// TODO: make more visually appealing path segments from start to
|
||||
// second tile, and penultimate to ultimate tile
|
||||
|
||||
// add all remaining path nodes excepting the last one
|
||||
Point prev = new Point(ipos.x, ipos.y);
|
||||
Point spos = new Point();
|
||||
int size = tiles.size();
|
||||
for (int ii = 1; ii < size - 1; ii++) {
|
||||
Point next = (Point)tiles.get(ii);
|
||||
|
||||
// determine the direction from previous to next node
|
||||
int dir = MisoUtil.getIsoDirection(prev.x, prev.y, next.x, next.y);
|
||||
|
||||
// determine the node's position in screen pixel coordinates
|
||||
MisoUtil.tileToScreen(metrics, next.x, next.y, spos);
|
||||
|
||||
// add the node to the path, wandering through the middle
|
||||
// of each tile in the path for now
|
||||
int dsx = spos.x + metrics.tilehwid;
|
||||
int dsy = spos.y + metrics.tilehhei;
|
||||
addNode(dsx, dsy, dir);
|
||||
|
||||
prev = next;
|
||||
}
|
||||
|
||||
// get the final destination point's screen coordinates
|
||||
// constrained to the closest full coordinate
|
||||
MisoUtil.fullToScreen(metrics, fpos.x, fpos.y, spos);
|
||||
|
||||
// get the tile coordinates for the final destination tile
|
||||
int tdestx = MisoUtil.fullToTile(fpos.x);
|
||||
int tdesty = MisoUtil.fullToTile(fpos.y);
|
||||
|
||||
// get the facing direction for the final node
|
||||
int dir;
|
||||
if (prev.x == ipos.x && prev.y == ipos.y) {
|
||||
// if destination is within starting tile, direction is
|
||||
// determined by studying the fine coordinates
|
||||
dir = MisoUtil.getDirection(metrics, sx, sy, spos.x, spos.y);
|
||||
|
||||
} else {
|
||||
// else it's based on the last tile we traversed
|
||||
dir = MisoUtil.getIsoDirection(prev.x, prev.y, tdestx, tdesty);
|
||||
}
|
||||
|
||||
// add the final destination path node
|
||||
addNode(spos.x, spos.y, dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the estimated number of millis that we'll be traveling
|
||||
* along this path.
|
||||
*/
|
||||
public long getEstimTravelTime ()
|
||||
{
|
||||
return (long)(_estimPixels / _vel);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void addNode (int x, int y, int dir)
|
||||
{
|
||||
super.addNode(x, y, dir);
|
||||
if (_last == null) {
|
||||
_last = new Point();
|
||||
} else {
|
||||
_estimPixels += MathUtil.distance(_last.x, _last.y, x, y);
|
||||
}
|
||||
_last.setLocation(x, y);
|
||||
}
|
||||
|
||||
/** Used to compute estimated travel time. */
|
||||
protected Point _last;
|
||||
|
||||
/** Estimated pixels traveled. */
|
||||
protected int _estimPixels;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// $Id: MisoSceneModel.java 3726 2005-10-11 19:17:43Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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 java.awt.Rectangle;
|
||||
import java.util.Random;
|
||||
|
||||
import com.threerings.io.SimpleStreamableObject;
|
||||
|
||||
import com.threerings.miso.util.ObjectSet;
|
||||
|
||||
/**
|
||||
* Contains basic information for a miso scene model that is shared among
|
||||
* the specialized model implementations.
|
||||
*/
|
||||
public abstract class MisoSceneModel extends SimpleStreamableObject
|
||||
implements Cloneable
|
||||
{
|
||||
/**
|
||||
* Creates a completely uninitialized model suitable for little more
|
||||
* than unserialization.
|
||||
*/
|
||||
public 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 abstract int getBaseTileId (int x, int y);
|
||||
|
||||
/**
|
||||
* Updates the tile at the specified location in the base layer.
|
||||
*
|
||||
* <p> Note that if there are fringe tiles associated with this scene,
|
||||
* calling this method may result in the surrounding fringe tiles
|
||||
* being cleared and subsequently recalculated. This should not be
|
||||
* called on a displaying scene unless you know what you are doing.
|
||||
*
|
||||
* @param fqTileId the fully-qualified tile id (@see
|
||||
* TileUtil#getFQTileId}) of the tile to set.
|
||||
* @param x the x-coordinate of the tile to set.
|
||||
* @param y the y-coordinate of the tile to set.
|
||||
*
|
||||
* @return false if the specified tile coordinates are outside of the
|
||||
* scene and the tile was not saved, true otherwise.
|
||||
*/
|
||||
public abstract boolean setBaseTile (int fqTileId, int x, int y);
|
||||
|
||||
/**
|
||||
* Updates the default base tileset id for this scene.
|
||||
*/
|
||||
public void setDefaultBaseTileSet (int tileSetId)
|
||||
{
|
||||
// nothing doing
|
||||
}
|
||||
|
||||
/**
|
||||
* Scene models can return a default tileset to be used when no base
|
||||
* tile data exists for a particular tile.
|
||||
*/
|
||||
public int getDefaultBaseTileSet ()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the supplied object set with info on all objects whose
|
||||
* origin falls in the requested region.
|
||||
*/
|
||||
public abstract void getObjects (Rectangle region, ObjectSet set);
|
||||
|
||||
/**
|
||||
* Adds an object to this scene.
|
||||
*
|
||||
* @return true if the object was added, false if the add was rejected
|
||||
* due to being a duplicate.
|
||||
*/
|
||||
public abstract boolean addObject (ObjectInfo info);
|
||||
|
||||
/**
|
||||
* Updates an object in this scene.
|
||||
*/
|
||||
public abstract void updateObject (ObjectInfo info);
|
||||
|
||||
/**
|
||||
* Removes the specified object from the scene.
|
||||
*
|
||||
* @return true if it was removed, false if the object was not in the
|
||||
* scene.
|
||||
*/
|
||||
public abstract boolean removeObject (ObjectInfo info);
|
||||
|
||||
/**
|
||||
* Creates a copy of this scene model.
|
||||
*/
|
||||
public Object clone ()
|
||||
{
|
||||
try {
|
||||
return (MisoSceneModel)super.clone();
|
||||
} catch (CloneNotSupportedException cnse) {
|
||||
throw new RuntimeException("MisoSceneModel.clone: " + cnse);
|
||||
}
|
||||
}
|
||||
|
||||
/** A random number generator for filling random base tiles. */
|
||||
protected transient Random _rando = new Random();
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// $Id: ObjectInfo.java 3749 2005-11-09 04:00:16Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.io.SimpleStreamableObject;
|
||||
import com.threerings.media.tile.TileUtil;
|
||||
|
||||
/**
|
||||
* Contains information about an object in a Miso scene.
|
||||
*/
|
||||
public class ObjectInfo extends SimpleStreamableObject
|
||||
implements Cloneable
|
||||
{
|
||||
/** The fully qualified object tile id. */
|
||||
public int tileId;
|
||||
|
||||
/** The x and y tile coordinates of the object. */
|
||||
public int x, y;
|
||||
|
||||
/** Don't access this directly unless you are serializing this
|
||||
* instance. Use {@link #getPriority} instead. */
|
||||
public byte priority = 0;
|
||||
|
||||
/** The action associated with this object or null if it has no
|
||||
* action. */
|
||||
public String action;
|
||||
|
||||
/** A "spot" associated with this object (specified as an offset from
|
||||
* the fine coordinates of the object's origin tile). */
|
||||
public byte sx, sy;
|
||||
|
||||
/** The orientation of the "spot" associated with this object. */
|
||||
public byte sorient;
|
||||
|
||||
/** Up to two colorization assignments for this object. */
|
||||
public int zations;
|
||||
|
||||
/**
|
||||
* Convenience constructor.
|
||||
*/
|
||||
public ObjectInfo (int tileId, int x, int y)
|
||||
{
|
||||
this.tileId = tileId;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an object info that is a copy of the supplied info.
|
||||
*/
|
||||
public ObjectInfo (ObjectInfo other)
|
||||
{
|
||||
this.tileId = other.tileId;
|
||||
this.x = other.x;
|
||||
this.y = other.y;
|
||||
this.priority = other.priority;
|
||||
this.action = other.action;
|
||||
this.sx = other.sx;
|
||||
this.sy = other.sy;
|
||||
this.sorient = other.sorient;
|
||||
this.zations = other.zations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zero argument constructor needed for unserialization.
|
||||
*/
|
||||
public ObjectInfo ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the render priority of this object tile.
|
||||
*/
|
||||
public int getPriority ()
|
||||
{
|
||||
return priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the primary colorization assignment.
|
||||
*/
|
||||
public int getPrimaryZation ()
|
||||
{
|
||||
return (zations & 0xFFFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the secondary colorization assignment.
|
||||
*/
|
||||
public int getSecondaryZation ()
|
||||
{
|
||||
return ((zations >> 16) & 0xFFFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the primary and secondary colorization assignments.
|
||||
*/
|
||||
public void setZations (short primary, short secondary)
|
||||
{
|
||||
zations = ((secondary << 16) | primary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this object info contains non-default data for
|
||||
* anything other than the tile id and coordinates.
|
||||
*/
|
||||
public boolean isInteresting ()
|
||||
{
|
||||
return (!StringUtil.isBlank(action) || priority != 0 ||
|
||||
sx != 0 || sy != 0 || zations != 0);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean equals (Object other)
|
||||
{
|
||||
if (other instanceof ObjectInfo) {
|
||||
ObjectInfo ooi = (ObjectInfo)other;
|
||||
return (x == ooi.x && y == ooi.y && tileId == ooi.tileId);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public int hashCode ()
|
||||
{
|
||||
return x ^ y ^ tileId;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Object clone ()
|
||||
{
|
||||
try {
|
||||
return super.clone();
|
||||
} catch (CloneNotSupportedException cnse) {
|
||||
// notgunnahappen.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Enhances our {@link SimpleStreamableObject#toString} output. */
|
||||
public String tileIdToString ()
|
||||
{
|
||||
return (TileUtil.getTileSetId(tileId) + ":" +
|
||||
TileUtil.getTileIndex(tileId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
//
|
||||
// $Id: SimpleMisoSceneModel.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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 java.awt.Rectangle;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.util.IntListUtil;
|
||||
import com.samskivert.util.ListUtil;
|
||||
|
||||
import com.threerings.media.util.MathUtil;
|
||||
import com.threerings.miso.util.ObjectSet;
|
||||
|
||||
/**
|
||||
* Contains miso scene data for a scene that is assumed to be reasonably
|
||||
* simple and small, such that all base tile data for the entire scene can
|
||||
* be stored in a single contiguous array.
|
||||
*
|
||||
* <p> Additionally, it makes the assumption that the single model will be
|
||||
* used to display a scene on a rectangular screen (dimensions defined by
|
||||
* {@link #vwidth} and {@link #vheight}) and further optimizes the base
|
||||
* tile array to obviate the need to store tile data for things that fall
|
||||
* outside the bounds of the screen.
|
||||
*/
|
||||
public class SimpleMisoSceneModel extends MisoSceneModel
|
||||
{
|
||||
/** The width of this scene in tiles. */
|
||||
public short width;
|
||||
|
||||
/** The height of this scene in tiles. */
|
||||
public short height;
|
||||
|
||||
/** The viewport width in tiles. */
|
||||
public int vwidth;
|
||||
|
||||
/** The viewport height in tiles. */
|
||||
public int vheight;
|
||||
|
||||
/** The combined tile ids (tile set id and tile id) in compressed
|
||||
* format. Don't go poking around in here, use the accessor
|
||||
* methods. */
|
||||
public int[] baseTileIds;
|
||||
|
||||
/** The combined tile ids (tile set id and tile id) of the
|
||||
* "uninteresting" tiles in the object layer. */
|
||||
public int[] objectTileIds;
|
||||
|
||||
/** The x coordinate of the "uninteresting" tiles in the object
|
||||
* layer. */
|
||||
public short[] objectXs;
|
||||
|
||||
/** The y coordinate of the "uninteresting" tiles in the object
|
||||
* layer. */
|
||||
public short[] objectYs;
|
||||
|
||||
/** Information records for the "interesting" objects in the object
|
||||
* layer. */
|
||||
public ObjectInfo[] objectInfo;
|
||||
|
||||
/**
|
||||
* Creates a completely uninitialized model suitable for little more
|
||||
* than unserialization.
|
||||
*/
|
||||
public SimpleMisoSceneModel ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a blank scene model with the specified dimensions.
|
||||
*/
|
||||
public SimpleMisoSceneModel (int width, int height, int vwidth, int vheight)
|
||||
{
|
||||
this.width = (short)MathUtil.bound(
|
||||
Short.MIN_VALUE, width, Short.MAX_VALUE);
|
||||
this.height = (short)MathUtil.bound(
|
||||
Short.MIN_VALUE, height, Short.MAX_VALUE);
|
||||
this.vwidth = vwidth;
|
||||
this.vheight = vheight;
|
||||
allocateBaseTileArray();
|
||||
|
||||
// start with zero-length object arrays
|
||||
objectTileIds = new int[0];
|
||||
objectXs = new short[0];
|
||||
objectYs = new short[0];
|
||||
objectInfo = new ObjectInfo[0];
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public int getBaseTileId (int col, int row)
|
||||
{
|
||||
int index = getIndex(col, row);
|
||||
return (index == -1) ? 0 : baseTileIds[index];
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean setBaseTile (int fqBaseTileId, int col, int row)
|
||||
{
|
||||
int index = getIndex(col, row);
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
baseTileIds[index] = fqBaseTileId;
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void getObjects (Rectangle region, ObjectSet set)
|
||||
{
|
||||
// first look for intersecting interesting objects
|
||||
for (int ii = 0; ii < objectInfo.length; ii++) {
|
||||
ObjectInfo info = objectInfo[ii];
|
||||
if (region.contains(info.x, info.y)) {
|
||||
set.insert(info);
|
||||
}
|
||||
}
|
||||
|
||||
// now look for intersecting non-interesting objects
|
||||
for (int ii = 0; ii < objectTileIds.length; ii++) {
|
||||
int x = objectXs[ii], y = objectYs[ii];
|
||||
if (region.contains(x, y)) {
|
||||
set.insert(new ObjectInfo(objectTileIds[ii], x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean addObject (ObjectInfo info)
|
||||
{
|
||||
if (info.isInteresting()) {
|
||||
objectInfo = (ObjectInfo[])ArrayUtil.append(objectInfo, info);
|
||||
} else {
|
||||
objectTileIds = ArrayUtil.append(objectTileIds, info.tileId);
|
||||
objectXs = ArrayUtil.append(objectXs, (short)info.x);
|
||||
objectYs = ArrayUtil.append(objectYs, (short)info.y);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void updateObject (ObjectInfo info)
|
||||
{
|
||||
// not efficient, but this is only done in editing situations
|
||||
removeObject(info);
|
||||
addObject(info);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean removeObject (ObjectInfo info)
|
||||
{
|
||||
// look for it in the interesting info array
|
||||
int oidx = ListUtil.indexOf(objectInfo, info);
|
||||
if (oidx != -1) {
|
||||
objectInfo = (ObjectInfo[])ArrayUtil.splice(objectInfo, oidx, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// look for it in the uninteresting arrays
|
||||
oidx = IntListUtil.indexOf(objectTileIds, info.tileId);
|
||||
if (oidx != -1) {
|
||||
objectTileIds = ArrayUtil.splice(objectTileIds, oidx, 1);
|
||||
objectXs = ArrayUtil.splice(objectXs, oidx, 1);
|
||||
objectYs = ArrayUtil.splice(objectYs, oidx, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Object clone ()
|
||||
{
|
||||
SimpleMisoSceneModel model = (SimpleMisoSceneModel)super.clone();
|
||||
model.baseTileIds = (int[])baseTileIds.clone();
|
||||
model.objectTileIds = (int[])objectTileIds.clone();
|
||||
model.objectXs = (short[])objectXs.clone();
|
||||
model.objectYs = (short[])objectYs.clone();
|
||||
model.objectInfo = (ObjectInfo[])objectInfo.clone();
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index into the baseTileIds[] for the specified
|
||||
* x and y coordinates, or return -1 if the specified coordinates
|
||||
* are outside of the viewable area.
|
||||
*
|
||||
* Assumption: The viewable area is centered and aligned as far
|
||||
* to the top of the isometric scene as possible, such that
|
||||
* the upper-left corner is at the point where the tiles
|
||||
* (0, vwid) and (0, vwid-1) touch. The upper-right corner
|
||||
* is at the point where the tiles (vwid-1, 0) and (vwid, 0)
|
||||
* touch.
|
||||
*
|
||||
* The viewable area is made up of "fat" rows and "thin" rows. The
|
||||
* fat rows display one more tile than the thin rows because their
|
||||
* first and last tiles are halfway off the viewable area. The thin
|
||||
* rows are fully contained within the viewable area except for the
|
||||
* first and last thin rows, which display only their bottom and top
|
||||
* halves, respectively. Note that #fatrows == #thinrows - 1;
|
||||
*/
|
||||
protected int getIndex (int x, int y)
|
||||
{
|
||||
// check to see if the index lies in one of the "fat" rows
|
||||
if (((x + y) & 1) == (vwidth & 1)) {
|
||||
|
||||
int col = (vwidth + x - y) >> 1;
|
||||
int row = x - col;
|
||||
if ((col < 0) || (col > vwidth) ||
|
||||
(row < 0) || (row >= vheight)) {
|
||||
return -1; // out of view
|
||||
}
|
||||
|
||||
return (vwidth + 1) * row + col;
|
||||
|
||||
} else {
|
||||
// the index must be in a "thin" row
|
||||
int col = (vwidth + x - y - 1) >> 1;
|
||||
int row = x - col;
|
||||
if ((col < 0) || (col >= vwidth) ||
|
||||
(row < 0) || (row > vheight)) {
|
||||
return -1; // out of view
|
||||
}
|
||||
|
||||
// we store the all the fat rows first, then all the thin
|
||||
// rows, the '(vwidth + 1) * vheight' is the size of all
|
||||
// the fat rows.
|
||||
return row * vwidth + col + (vwidth + 1) * vheight;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate the base tile array.
|
||||
*/
|
||||
protected void allocateBaseTileArray ()
|
||||
{
|
||||
baseTileIds = new int[vwidth + vheight + ((vwidth * vheight) << 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the interesting and uninteresting parts of a miso scene
|
||||
* model given lists of {@link ObjectInfo} records for each.
|
||||
*/
|
||||
public static void populateObjects (SimpleMisoSceneModel model,
|
||||
ArrayList ilist, ArrayList ulist)
|
||||
{
|
||||
// set up the uninteresting arrays
|
||||
int ucount = ulist.size();
|
||||
model.objectTileIds = new int[ucount];
|
||||
model.objectXs = new short[ucount];
|
||||
model.objectYs = new short[ucount];
|
||||
for (int ii = 0; ii < ucount; ii++) {
|
||||
ObjectInfo info = (ObjectInfo)ulist.get(ii);
|
||||
model.objectTileIds[ii] = info.tileId;
|
||||
model.objectXs[ii] = (short)info.x;
|
||||
model.objectYs[ii] = (short)info.y;
|
||||
}
|
||||
|
||||
// set up the interesting array
|
||||
int icount = ilist.size();
|
||||
model.objectInfo = new ObjectInfo[icount];
|
||||
ilist.toArray(model.objectInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,445 @@
|
||||
//
|
||||
// $Id: SparseMisoSceneModel.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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 java.awt.Rectangle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.util.ListUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.io.SimpleStreamableObject;
|
||||
|
||||
import com.threerings.media.util.MathUtil;
|
||||
import com.threerings.util.StreamableHashIntMap;
|
||||
|
||||
import com.threerings.miso.Log;
|
||||
import com.threerings.miso.util.ObjectSet;
|
||||
|
||||
/**
|
||||
* Contains miso scene data that is broken up into NxN tile sections.
|
||||
*/
|
||||
public class SparseMisoSceneModel extends MisoSceneModel
|
||||
{
|
||||
/** An interface that allows external entities to "visit" and inspect
|
||||
* every object in this scene. */
|
||||
public static interface ObjectVisitor
|
||||
{
|
||||
/** Called for each object in the scene, interesting and not. */
|
||||
public void visit (ObjectInfo info);
|
||||
}
|
||||
|
||||
/** Contains information on a section of this scene. This is only
|
||||
* public so that the scene model parser can do its job, so don't go
|
||||
* poking around in here. */
|
||||
public static class Section extends SimpleStreamableObject
|
||||
implements Cloneable
|
||||
{
|
||||
/** The tile coordinate of our upper leftmost tile. */
|
||||
public short x, y;
|
||||
|
||||
/** The width of this section in tiles. */
|
||||
public int width;
|
||||
|
||||
/** The combined tile ids (tile set id and tile id) for our
|
||||
* section (in row major order). */
|
||||
public int[] baseTileIds;
|
||||
|
||||
/** The combined tile ids (tile set id and tile id) of the
|
||||
* "uninteresting" tiles in the object layer. */
|
||||
public int[] objectTileIds = new int[0];
|
||||
|
||||
/** The x coordinate of the "uninteresting" tiles in the object
|
||||
* layer. */
|
||||
public short[] objectXs = new short[0];
|
||||
|
||||
/** The y coordinate of the "uninteresting" tiles in the object
|
||||
* layer. */
|
||||
public short[] objectYs = new short[0];
|
||||
|
||||
/** Information records for the "interesting" objects in the
|
||||
* object layer. */
|
||||
public ObjectInfo[] objectInfo = new ObjectInfo[0];
|
||||
|
||||
/**
|
||||
* Creates a blank section instance, suitable for unserialization
|
||||
* or configuration by the XML scene parser.
|
||||
*/
|
||||
public Section ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new scene section with the specified dimensions.
|
||||
*/
|
||||
public Section (short x, short y, short width, short height)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
baseTileIds = new int[width*height];
|
||||
}
|
||||
|
||||
public int getBaseTileId (int col, int row) {
|
||||
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 void setBaseTile (int col, int row, int fqBaseTileId) {
|
||||
baseTileIds[(row-y)*width+(col-x)] = fqBaseTileId;
|
||||
}
|
||||
|
||||
public boolean addObject (ObjectInfo info) {
|
||||
// sanity check: see if there is already an object of this
|
||||
// type at these coordinates
|
||||
int dupidx;
|
||||
if ((dupidx = ListUtil.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 = (ObjectInfo[])ArrayUtil.append(objectInfo, info);
|
||||
} else {
|
||||
objectTileIds = ArrayUtil.append(objectTileIds, info.tileId);
|
||||
objectXs = ArrayUtil.append(objectXs, (short)info.x);
|
||||
objectYs = ArrayUtil.append(objectYs, (short)info.y);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean removeObject (ObjectInfo info) {
|
||||
// look for it in the interesting info array
|
||||
int oidx = ListUtil.indexOf(objectInfo, info);
|
||||
if (oidx != -1) {
|
||||
objectInfo = (ObjectInfo[])
|
||||
ArrayUtil.splice(objectInfo, oidx, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// look for it in the uninteresting arrays
|
||||
oidx = indexOfUn(info);
|
||||
if (oidx != -1) {
|
||||
objectTileIds = ArrayUtil.splice(objectTileIds, oidx, 1);
|
||||
objectXs = ArrayUtil.splice(objectXs, oidx, 1);
|
||||
objectYs = 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 int indexOfUn (ObjectInfo info)
|
||||
{
|
||||
for (int ii = 0; ii < objectTileIds.length; ii++) {
|
||||
if (objectTileIds[ii] == info.tileId &&
|
||||
objectXs[ii] == info.x && objectYs[ii] == info.y) {
|
||||
return ii;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void getObjects (Rectangle region, ObjectSet set) {
|
||||
// first look for intersecting interesting objects
|
||||
for (int ii = 0; ii < objectInfo.length; ii++) {
|
||||
ObjectInfo info = objectInfo[ii];
|
||||
if (region.contains(info.x, info.y)) {
|
||||
set.insert(info);
|
||||
}
|
||||
}
|
||||
|
||||
// now look for intersecting non-interesting objects
|
||||
for (int ii = 0; ii < objectTileIds.length; ii++) {
|
||||
int x = objectXs[ii], y = 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 boolean isBlank ()
|
||||
{
|
||||
if ((objectTileIds.length != 0) || (objectInfo.length != 0)) {
|
||||
return false;
|
||||
}
|
||||
for (int ii=0, nn=baseTileIds.length; ii < nn; ii++) {
|
||||
if (baseTileIds[ii] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Object clone () {
|
||||
try {
|
||||
Section section = (Section)super.clone();
|
||||
section.baseTileIds = (int[])baseTileIds.clone();
|
||||
section.objectTileIds = (int[])objectTileIds.clone();
|
||||
section.objectXs = (short[])objectXs.clone();
|
||||
section.objectYs = (short[])objectYs.clone();
|
||||
section.objectInfo = (ObjectInfo[])objectInfo.clone();
|
||||
return section;
|
||||
} catch (CloneNotSupportedException cnse) {
|
||||
throw new RuntimeException(
|
||||
"SparseMisoSceneModel.Section.clone: " + cnse);
|
||||
}
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return ((width == 0) ? "<no bounds>" :
|
||||
(width + "x" + (baseTileIds.length/width))) +
|
||||
"+" + x + "+" + y +
|
||||
":" + objectInfo.length + ":" + objectTileIds.length;
|
||||
}
|
||||
}
|
||||
|
||||
/** The dimensions of a section of our scene. */
|
||||
public short swidth, sheight;
|
||||
|
||||
/** The tileset to use when we have no tile data. */
|
||||
public int defTileSet = 0;
|
||||
|
||||
/**
|
||||
* Creates a scene model with the specified bounds.
|
||||
*
|
||||
* @param swidth the width of a single section (in tiles).
|
||||
* @param sheight the height of a single section (in tiles).
|
||||
*/
|
||||
public SparseMisoSceneModel (int swidth, int sheight)
|
||||
{
|
||||
this.swidth = (short)swidth;
|
||||
this.sheight = (short)sheight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a blank model suitable for unserialization.
|
||||
*/
|
||||
public SparseMisoSceneModel ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all interesting {@link ObjectInfo} records in this scene to
|
||||
* the supplied list.
|
||||
*/
|
||||
public void getInterestingObjects (ArrayList list)
|
||||
{
|
||||
for (Iterator iter = getSections(); iter.hasNext(); ) {
|
||||
Section sect = (Section)iter.next();
|
||||
for (int oo = 0; oo < sect.objectInfo.length; oo++) {
|
||||
list.add(sect.objectInfo[oo]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs the supplied visitor of each object in this scene.
|
||||
*/
|
||||
public void visitObjects (ObjectVisitor visitor)
|
||||
{
|
||||
visitObjects(visitor, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs the supplied visitor of each object in this scene.
|
||||
*
|
||||
* @param interestingOnly if true, only the interesting objects will
|
||||
* be visited.
|
||||
*/
|
||||
public void visitObjects (ObjectVisitor visitor, boolean interestingOnly)
|
||||
{
|
||||
for (Iterator iter = getSections(); iter.hasNext(); ) {
|
||||
Section sect = (Section)iter.next();
|
||||
for (int oo = 0; oo < sect.objectInfo.length; oo++) {
|
||||
ObjectInfo oinfo = sect.objectInfo[oo];
|
||||
visitor.visit(oinfo);
|
||||
}
|
||||
if (!interestingOnly) {
|
||||
ObjectInfo info = new ObjectInfo();
|
||||
for (int oo = 0; oo < sect.objectTileIds.length; oo++) {
|
||||
info.tileId = sect.objectTileIds[oo];
|
||||
info.x = sect.objectXs[oo];
|
||||
info.y = sect.objectYs[oo];
|
||||
visitor.visit(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public int getBaseTileId (int col, int row)
|
||||
{
|
||||
Section sec = getSection(col, row, false);
|
||||
return (sec == null) ? -1 : sec.getBaseTileId(col, row);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean setBaseTile (int fqBaseTileId, int col, int row)
|
||||
{
|
||||
getSection(col, row, true).setBaseTile(col, row, fqBaseTileId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void setDefaultBaseTileSet (int tileSetId)
|
||||
{
|
||||
defTileSet = tileSetId;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public int getDefaultBaseTileSet ()
|
||||
{
|
||||
return defTileSet;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void getObjects (Rectangle region, ObjectSet set)
|
||||
{
|
||||
int minx = MathUtil.floorDiv(region.x, swidth)*swidth;
|
||||
int maxx = MathUtil.floorDiv(region.x+region.width-1, swidth)*swidth;
|
||||
int miny = MathUtil.floorDiv(region.y, sheight)*sheight;
|
||||
int maxy = MathUtil.floorDiv(region.y+region.height-1, sheight)*sheight;
|
||||
for (int yy = miny; yy <= maxy; yy += sheight) {
|
||||
for (int xx = minx; xx <= maxx; xx += swidth) {
|
||||
Section sec = getSection(xx, yy, false);
|
||||
if (sec != null) {
|
||||
sec.getObjects(region, set);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean addObject (ObjectInfo info)
|
||||
{
|
||||
return getSection(info.x, info.y, true).addObject(info);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void updateObject (ObjectInfo info)
|
||||
{
|
||||
// not efficient, but this is only done in editing situations
|
||||
removeObject(info);
|
||||
addObject(info);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean removeObject (ObjectInfo info)
|
||||
{
|
||||
Section sec = getSection(info.x, info.y, false);
|
||||
if (sec != null) {
|
||||
return sec.removeObject(info);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 void setSection (Section section)
|
||||
{
|
||||
_sections.put(key(section.x, section.y), section);
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't call this method! This is only public so that the scene
|
||||
* writer can generate XML from the raw scene data.
|
||||
*/
|
||||
public Iterator getSections ()
|
||||
{
|
||||
return _sections.values().iterator();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void toString (StringBuilder buf)
|
||||
{
|
||||
super.toString(buf);
|
||||
buf.append(", sections=" +
|
||||
StringUtil.toString(_sections.values().iterator()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key for the specified section.
|
||||
*/
|
||||
protected final int key (int x, int y)
|
||||
{
|
||||
int sx = MathUtil.floorDiv(x, swidth);
|
||||
int sy = MathUtil.floorDiv(y, sheight);
|
||||
return (sx << 16) | (sy & 0xFFFF);
|
||||
}
|
||||
|
||||
/** Returns the section for the specified tile coordinate. */
|
||||
protected final Section getSection (int x, int y, boolean create)
|
||||
{
|
||||
int key = key(x, y);
|
||||
Section sect = (Section)_sections.get(key);
|
||||
if (sect == null && create) {
|
||||
short sx = (short)(MathUtil.floorDiv(x, swidth)*swidth);
|
||||
short sy = (short)(MathUtil.floorDiv(y, sheight)*sheight);
|
||||
_sections.put(key, sect = new Section(sx, sy, swidth, sheight));
|
||||
// Log.info("Created new section " + sect + ".");
|
||||
}
|
||||
return sect;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Object clone ()
|
||||
{
|
||||
SparseMisoSceneModel model = (SparseMisoSceneModel)super.clone();
|
||||
model._sections = new StreamableHashIntMap();
|
||||
for (Iterator iter = getSections(); iter.hasNext(); ) {
|
||||
Section sect = (Section)iter.next();
|
||||
model.setSection((Section)sect.clone());
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
/** Contains our sections in row major order. */
|
||||
protected StreamableHashIntMap _sections = new StreamableHashIntMap();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// $Id: VirtualMisoSceneModel.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
public VirtualMisoSceneModel ()
|
||||
{
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean setBaseTile (int fqTileId, int x, int y)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean addObject (ObjectInfo info)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void updateObject (ObjectInfo info)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean removeObject (ObjectInfo info)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
//
|
||||
// $Id: AutoFringer.java 4145 2006-05-24 01:24:24Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.tile;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Transparency;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.samskivert.util.CheapIntMap;
|
||||
import com.samskivert.util.QuickSort;
|
||||
|
||||
import com.threerings.media.image.BufferedMirage;
|
||||
import com.threerings.media.image.ImageManager;
|
||||
import com.threerings.media.image.ImageUtil;
|
||||
|
||||
import com.threerings.media.tile.NoSuchTileSetException;
|
||||
import com.threerings.media.tile.Tile;
|
||||
import com.threerings.media.tile.TileManager;
|
||||
import com.threerings.media.tile.TileSet;
|
||||
import com.threerings.media.tile.TileUtil;
|
||||
|
||||
import com.threerings.miso.Log;
|
||||
import com.threerings.miso.data.MisoSceneModel;
|
||||
|
||||
/**
|
||||
* Automatically fringes a scene according to the rules in the supplied
|
||||
* fringe configuration.
|
||||
*/
|
||||
public class AutoFringer
|
||||
{
|
||||
/**
|
||||
* Constructs an instance that will fringe according to the rules in
|
||||
* the supplied fringe configuration.
|
||||
*/
|
||||
public AutoFringer (FringeConfiguration fringeconf, ImageManager imgr,
|
||||
TileManager tmgr)
|
||||
{
|
||||
_fringeconf = fringeconf;
|
||||
_imgr = imgr;
|
||||
_tmgr = tmgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and return the fringe tile to be inserted at the specified
|
||||
* location.
|
||||
*/
|
||||
public BaseTile getFringeTile (MisoSceneModel scene, int col, int row,
|
||||
HashMap masks)
|
||||
{
|
||||
// get the tileset id of the base tile we are considering
|
||||
int underset = scene.getBaseTileId(col, row) >> 16;
|
||||
|
||||
// start with a clean temporary fringer map
|
||||
_fringers.clear();
|
||||
boolean passable = true;
|
||||
|
||||
// walk through our influence tiles
|
||||
for (int y = row - 1, maxy = row + 2; y < maxy; y++) {
|
||||
for (int x = col - 1, maxx = col + 2; x < maxx; x++) {
|
||||
// we sensibly do not consider ourselves
|
||||
if ((x == col) && (y == row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// determine the tileset for this tile
|
||||
int btid = scene.getBaseTileId(x, y);
|
||||
int baseset = (btid <= 0) ?
|
||||
scene.getDefaultBaseTileSet() : (btid >> 16);
|
||||
|
||||
// determine if it fringes on our tile
|
||||
int pri = _fringeconf.fringesOn(baseset, underset);
|
||||
if (pri == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FringerRec fringer = (FringerRec)_fringers.get(baseset);
|
||||
if (fringer == null) {
|
||||
fringer = new FringerRec(baseset, pri);
|
||||
_fringers.put(baseset, fringer);
|
||||
}
|
||||
|
||||
// now turn on the appropriate fringebits
|
||||
fringer.bits |= FLAGMATRIX[y - row + 1][x - col + 1];
|
||||
|
||||
// See if a tile that fringes on us kills our passability,
|
||||
// but don't count the default base tile against us, as
|
||||
// we allow users to splash in the water.
|
||||
if (passable && (btid > 0)) {
|
||||
try {
|
||||
BaseTile bt = (BaseTile) _tmgr.getTile(btid);
|
||||
passable = bt.isPassable();
|
||||
} catch (NoSuchTileSetException nstse) {
|
||||
Log.warning("Autofringer couldn't find a base " +
|
||||
"set while attempting to figure passability " +
|
||||
"[error=" + nstse + "].");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if nothing fringed, we're done
|
||||
int numfringers = _fringers.size();
|
||||
if (numfringers == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// otherwise compose a FringeTile from the specified fringes
|
||||
FringerRec[] frecs = new FringerRec[numfringers];
|
||||
for (int ii = 0, pp = 0; ii < 16; ii++) {
|
||||
FringerRec rec = (FringerRec)_fringers.getValue(ii);
|
||||
if (rec != null) {
|
||||
frecs[pp++] = rec;
|
||||
}
|
||||
}
|
||||
|
||||
BaseTile frTile = new BaseTile();
|
||||
frTile.setPassable(passable);
|
||||
composeFringeTile(frTile, frecs, masks, TileUtil.getTileHash(col, row));
|
||||
return frTile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a FringeTile out of the various fringe images needed.
|
||||
*/
|
||||
protected void composeFringeTile (
|
||||
Tile frTile, FringerRec[] fringers, HashMap masks, int hashValue)
|
||||
{
|
||||
// sort the array so that higher priority fringers get drawn first
|
||||
QuickSort.sort(fringers);
|
||||
|
||||
BufferedImage ftimg = null;
|
||||
for (int ii = 0; ii < fringers.length; ii++) {
|
||||
int[] indexes = getFringeIndexes(fringers[ii].bits);
|
||||
for (int jj = 0; jj < indexes.length; jj++) {
|
||||
try {
|
||||
ftimg = getTileImage(ftimg, fringers[ii].baseset,
|
||||
indexes[jj], masks, hashValue);
|
||||
} catch (NoSuchTileSetException nstse) {
|
||||
Log.warning("Autofringer couldn't find a needed tileset " +
|
||||
"[error=" + nstse + "].");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frTile.setImage(new BufferedMirage(ftimg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve or compose an image for the specified fringe.
|
||||
*/
|
||||
protected BufferedImage getTileImage (
|
||||
BufferedImage ftimg, int baseset, int index,
|
||||
HashMap masks, int hashValue)
|
||||
throws NoSuchTileSetException
|
||||
{
|
||||
FringeConfiguration.FringeTileSetRecord tsr =
|
||||
_fringeconf.getFringe(baseset, hashValue);
|
||||
int fringeset = tsr.fringe_tsid;
|
||||
TileSet fset = _tmgr.getTileSet(fringeset);
|
||||
|
||||
if (!tsr.mask) {
|
||||
// oh good, this is easy
|
||||
Tile stamp = fset.getTile(index);
|
||||
return stampTileImage(stamp, ftimg, stamp.getWidth(),
|
||||
stamp.getHeight());
|
||||
}
|
||||
|
||||
// otherwise, it's a mask.. look for it in the cache..
|
||||
Long maskkey = Long.valueOf((((long) baseset) << 32) +
|
||||
(fringeset << 16) + index);
|
||||
BufferedImage img = (BufferedImage)masks.get(maskkey);
|
||||
if (img == null) {
|
||||
BufferedImage fsrc = fset.getRawTileImage(index);
|
||||
BufferedImage bsrc = _tmgr.getTileSet(baseset).getRawTileImage(0);
|
||||
img = ImageUtil.composeMaskedImage(_imgr, fsrc, bsrc);
|
||||
masks.put(maskkey, img);
|
||||
}
|
||||
ftimg = stampTileImage(img, ftimg, img.getWidth(null),
|
||||
img.getHeight(null));
|
||||
|
||||
return ftimg;
|
||||
}
|
||||
|
||||
/** Helper function for {@link #getTileImage}. */
|
||||
protected BufferedImage stampTileImage (Object stamp, BufferedImage ftimg,
|
||||
int width, int height)
|
||||
{
|
||||
// create the target image if necessary
|
||||
if (ftimg == null) {
|
||||
ftimg = _imgr.createImage(width, height, Transparency.BITMASK);
|
||||
}
|
||||
Graphics2D gfx = (Graphics2D)ftimg.getGraphics();
|
||||
try {
|
||||
if (stamp instanceof Tile) {
|
||||
((Tile)stamp).paint(gfx, 0, 0);
|
||||
} else {
|
||||
gfx.drawImage((BufferedImage)stamp, 0, 0, null);
|
||||
}
|
||||
} finally {
|
||||
gfx.dispose();
|
||||
}
|
||||
return ftimg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fringe index specified by the fringebits. If no index
|
||||
* is available, try breaking down the bits into contiguous regions of
|
||||
* bits and look for indexes for those.
|
||||
*/
|
||||
protected int[] getFringeIndexes (int bits)
|
||||
{
|
||||
int index = BITS_TO_INDEX[bits];
|
||||
if (index != -1) {
|
||||
int[] ret = new int[1];
|
||||
ret[0] = index;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// otherwise, split the bits into contiguous components
|
||||
|
||||
// look for a zero and start our first split
|
||||
int start = 0;
|
||||
while ((((1 << start) & bits) != 0) && (start < NUM_FRINGEBITS)) {
|
||||
start++;
|
||||
}
|
||||
|
||||
if (start == NUM_FRINGEBITS) {
|
||||
// we never found an empty fringebit, and since index (above)
|
||||
// was already -1, we have no fringe tile for these bits.. sad.
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
ArrayList indexes = new ArrayList();
|
||||
int weebits = 0;
|
||||
for (int ii=(start + 1) % NUM_FRINGEBITS; ii != start;
|
||||
ii = (ii + 1) % NUM_FRINGEBITS) {
|
||||
|
||||
if (((1 << ii) & bits) != 0) {
|
||||
weebits |= (1 << ii);
|
||||
} else if (weebits != 0) {
|
||||
index = BITS_TO_INDEX[weebits];
|
||||
if (index != -1) {
|
||||
indexes.add(Integer.valueOf(index));
|
||||
}
|
||||
weebits = 0;
|
||||
}
|
||||
}
|
||||
if (weebits != 0) {
|
||||
index = BITS_TO_INDEX[weebits];
|
||||
if (index != -1) {
|
||||
indexes.add(Integer.valueOf(index));
|
||||
}
|
||||
}
|
||||
|
||||
int[] ret = new int[indexes.size()];
|
||||
for (int ii=0; ii < ret.length; ii++) {
|
||||
ret[ii] = ((Integer) indexes.get(ii)).intValue();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* A record for holding information about a particular fringe as we're
|
||||
* computing what it will look like.
|
||||
*/
|
||||
static protected class FringerRec implements Comparable
|
||||
{
|
||||
int baseset;
|
||||
int priority;
|
||||
int bits;
|
||||
|
||||
public FringerRec (int base, int pri)
|
||||
{
|
||||
baseset = base;
|
||||
priority = pri;
|
||||
}
|
||||
|
||||
public int compareTo (Object o)
|
||||
{
|
||||
return priority - ((FringerRec) o).priority;
|
||||
}
|
||||
|
||||
public String toString ()
|
||||
{
|
||||
return "[base=" + baseset + ", pri=" + priority +
|
||||
", bits=" + Integer.toString(bits, 16) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
// fringe bits
|
||||
// see docs/miso/fringebits.png
|
||||
//
|
||||
protected static final int NORTH = 1 << 0;
|
||||
protected static final int NORTHEAST = 1 << 1;
|
||||
protected static final int EAST = 1 << 2;
|
||||
protected static final int SOUTHEAST = 1 << 3;
|
||||
protected static final int SOUTH = 1 << 4;
|
||||
protected static final int SOUTHWEST = 1 << 5;
|
||||
protected static final int WEST = 1 << 6;
|
||||
protected static final int NORTHWEST = 1 << 7;
|
||||
|
||||
protected static final int NUM_FRINGEBITS = 8;
|
||||
|
||||
// A matrix mapping adjacent tiles to which fringe bits
|
||||
// they affect.
|
||||
// (x and y are offset by +1, since we can't have -1 as an array index)
|
||||
// again, see docs/miso/fringebits.png
|
||||
//
|
||||
protected static final int[][] FLAGMATRIX = {
|
||||
{ NORTHEAST, (NORTHEAST | EAST | SOUTHEAST), SOUTHEAST },
|
||||
{ (NORTHWEST | NORTH | NORTHEAST), 0, (SOUTHEAST | SOUTH | SOUTHWEST) },
|
||||
{ NORTHWEST, (NORTHWEST | WEST | SOUTHWEST), SOUTHWEST }
|
||||
};
|
||||
|
||||
/**
|
||||
* The fringe tiles we use. These are the 17 possible tiles made
|
||||
* up of continuous fringebits sections.
|
||||
* Huh? see docs/miso/fringebits.png
|
||||
*/
|
||||
protected static final int[] FRINGETILES = {
|
||||
SOUTHEAST,
|
||||
SOUTHWEST | SOUTH | SOUTHEAST,
|
||||
SOUTHWEST,
|
||||
NORTHEAST | EAST | SOUTHEAST,
|
||||
NORTHWEST | WEST | SOUTHWEST,
|
||||
NORTHEAST,
|
||||
NORTHWEST | NORTH | NORTHEAST,
|
||||
NORTHWEST,
|
||||
|
||||
SOUTHWEST | WEST | NORTHWEST | NORTH | NORTHEAST,
|
||||
NORTHWEST | NORTH | NORTHEAST | EAST | SOUTHEAST,
|
||||
NORTHWEST | WEST | SOUTHWEST | SOUTH | SOUTHEAST,
|
||||
SOUTHWEST | SOUTH | SOUTHEAST | EAST | NORTHEAST,
|
||||
|
||||
NORTHEAST | NORTH | NORTHWEST | WEST | SOUTHWEST | SOUTH | SOUTHEAST,
|
||||
SOUTHEAST | EAST | NORTHEAST | NORTH | NORTHWEST | WEST | SOUTHWEST,
|
||||
SOUTHWEST | SOUTH | SOUTHEAST | EAST | NORTHEAST | NORTH | NORTHWEST,
|
||||
NORTHWEST | WEST | SOUTHWEST | SOUTH | SOUTHEAST | EAST | NORTHEAST,
|
||||
|
||||
// all the directions!
|
||||
NORTH | NORTHEAST | EAST | SOUTHEAST |
|
||||
SOUTH | SOUTHWEST | WEST | NORTHWEST
|
||||
};
|
||||
|
||||
// A reverse map of the above array, for quickly looking up which tile
|
||||
// we want.
|
||||
protected static final int[] BITS_TO_INDEX;
|
||||
|
||||
// Construct the BITS_TO_INDEX array.
|
||||
static {
|
||||
int num = (1 << NUM_FRINGEBITS);
|
||||
BITS_TO_INDEX = new int[num];
|
||||
|
||||
// first clear everything to -1 (meaning there is no tile defined)
|
||||
for (int ii=0; ii < num; ii++) {
|
||||
BITS_TO_INDEX[ii] = -1;
|
||||
}
|
||||
|
||||
// then fill in with the defined tiles.
|
||||
for (int ii=0; ii < FRINGETILES.length; ii++) {
|
||||
BITS_TO_INDEX[FRINGETILES[ii]] = ii;
|
||||
}
|
||||
}
|
||||
|
||||
protected ImageManager _imgr;
|
||||
protected TileManager _tmgr;
|
||||
protected FringeConfiguration _fringeconf;
|
||||
protected CheapIntMap _fringers = new CheapIntMap(16);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// $Id: BaseTile.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.tile;
|
||||
|
||||
import com.threerings.media.tile.Tile;
|
||||
|
||||
/**
|
||||
* Extends the tile class to add support for tile passability.
|
||||
*
|
||||
* @see BaseTileSet
|
||||
*/
|
||||
public class BaseTile extends Tile
|
||||
{
|
||||
/**
|
||||
* Returns whether or not this tile can be walked upon by character
|
||||
* sprites.
|
||||
*/
|
||||
public boolean isPassable ()
|
||||
{
|
||||
return _passable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures this base tile as passable or impassable.
|
||||
*/
|
||||
public void setPassable (boolean passable)
|
||||
{
|
||||
_passable = passable;
|
||||
}
|
||||
|
||||
/** Whether the tile is passable. */
|
||||
protected boolean _passable = true;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// $Id: BaseTileSet.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.tile;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.media.image.Colorization;
|
||||
import com.threerings.media.tile.SwissArmyTileSet;
|
||||
import com.threerings.media.tile.Tile;
|
||||
|
||||
/**
|
||||
* The base tileset extends the swiss army tileset to add support for tile
|
||||
* passability. Passability is used to determine whether traverser objects
|
||||
* (generally sprites made to "walk" around the scene) can traverse a
|
||||
* particular tile in a scene.
|
||||
*/
|
||||
public class BaseTileSet extends SwissArmyTileSet
|
||||
{
|
||||
/**
|
||||
* Sets the passability information for the tiles in this tileset.
|
||||
* Each entry in the array corresponds to the tile at that tile index.
|
||||
*/
|
||||
public void setPassability (boolean[] passable)
|
||||
{
|
||||
_passable = passable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the passability information for the tiles in this tileset.
|
||||
*/
|
||||
public boolean[] getPassability ()
|
||||
{
|
||||
return _passable;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected Tile createTile ()
|
||||
{
|
||||
return new BaseTile();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void initTile (Tile tile, int tileIndex, Colorization[] zations)
|
||||
{
|
||||
super.initTile(tile, tileIndex, zations);
|
||||
((BaseTile)tile).setPassable(_passable[tileIndex]);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void toString (StringBuilder buf)
|
||||
{
|
||||
super.toString(buf);
|
||||
buf.append(", passable=").append(StringUtil.toString(_passable));
|
||||
}
|
||||
|
||||
/** Whether each tile is passable. */
|
||||
protected boolean[] _passable;
|
||||
|
||||
/** Increase this value when object's serialized state is impacted by
|
||||
* a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
//
|
||||
// $Id: FringeConfiguration.java 3403 2005-03-14 23:58:02Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.tile;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Used to manage data about which base tilesets fringe on which others
|
||||
* and how they fringe.
|
||||
*/
|
||||
public class FringeConfiguration implements Serializable
|
||||
{
|
||||
/**
|
||||
* The path (relative to the resource directory) at which the fringe
|
||||
* configuration should be loaded and stored.
|
||||
*/
|
||||
public static final String CONFIG_PATH = "config/miso/tile/fringeconf.dat";
|
||||
|
||||
public static class FringeRecord implements Serializable
|
||||
{
|
||||
/** The tileset id of the base tileset to which this applies. */
|
||||
public int base_tsid;
|
||||
|
||||
/** The fringe priority of this base tileset. */
|
||||
public int priority;
|
||||
|
||||
/** A list of the possible tilesets that can be used for fringing. */
|
||||
public ArrayList tilesets = new ArrayList();
|
||||
|
||||
/** Used when parsing the tilesets definitions. */
|
||||
public void addTileset (FringeTileSetRecord record)
|
||||
{
|
||||
tilesets.add(record);
|
||||
}
|
||||
|
||||
/** Did everything parse well? */
|
||||
public boolean isValid ()
|
||||
{
|
||||
return ((base_tsid != 0) && (priority > 0));
|
||||
}
|
||||
|
||||
/** Generates a string representation of this instance. */
|
||||
public String toString ()
|
||||
{
|
||||
return "[base_tsid=" + base_tsid + ", priority=" + priority +
|
||||
", tilesets=" + StringUtil.toString(tilesets) + "]";
|
||||
}
|
||||
|
||||
/** Increase this value when object's serialized state is impacted
|
||||
* by a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to parse the tileset fringe definitions.
|
||||
*/
|
||||
public static class FringeTileSetRecord implements Serializable
|
||||
{
|
||||
/** The tileset id of the fringe tileset. */
|
||||
public int fringe_tsid;
|
||||
|
||||
/** Is this a mask? */
|
||||
public boolean mask;
|
||||
|
||||
/** Did everything parse well? */
|
||||
public boolean isValid ()
|
||||
{
|
||||
return (fringe_tsid != 0);
|
||||
}
|
||||
|
||||
/** Generates a string representation of this instance. */
|
||||
public String toString ()
|
||||
{
|
||||
return "[fringe_tsid=" + fringe_tsid + ", mask=" + mask + "]";
|
||||
}
|
||||
|
||||
/** Increase this value when object's serialized state is impacted
|
||||
* by a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a parsed FringeRecord to this instance. This is used when parsing
|
||||
* the fringerecords from xml.
|
||||
*/
|
||||
public void addFringeRecord (FringeRecord frec)
|
||||
{
|
||||
_frecs.put(frec.base_tsid, frec);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the first base tileset fringes upon the second, return the
|
||||
* fringe priority of the first base tileset, otherwise return -1.
|
||||
*/
|
||||
public int fringesOn (int first, int second)
|
||||
{
|
||||
FringeRecord f1 = (FringeRecord) _frecs.get(first);
|
||||
|
||||
// we better have a fringe record for the first
|
||||
if (null != f1) {
|
||||
|
||||
// it had better have some tilesets defined
|
||||
if (f1.tilesets.size() > 0) {
|
||||
|
||||
FringeRecord f2 = (FringeRecord) _frecs.get(second);
|
||||
|
||||
// and we only fringe if second doesn't exist or has a lower
|
||||
// priority
|
||||
if ((null == f2) || (f1.priority > f2.priority)) {
|
||||
return f1.priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random FringeTileSetRecord from amongst the ones
|
||||
* listed for the specified base tileset.
|
||||
*/
|
||||
public FringeTileSetRecord getFringe (int baseset, int hashValue)
|
||||
{
|
||||
FringeRecord f = (FringeRecord) _frecs.get(baseset);
|
||||
return (FringeTileSetRecord) f.tilesets.get(
|
||||
hashValue % f.tilesets.size());
|
||||
}
|
||||
|
||||
/** The mapping from base tileset id to fringerecord. */
|
||||
protected HashIntMap _frecs = new HashIntMap();
|
||||
|
||||
/** Increase this value when object's serialized state is impacted by
|
||||
* a class change (modification of fields, inheritance). */
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// $Id: MisoTileManager.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.tile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import com.samskivert.io.StreamUtil;
|
||||
|
||||
import com.threerings.resource.ResourceManager;
|
||||
import com.threerings.util.CompiledConfig;
|
||||
|
||||
import com.threerings.media.image.ImageManager;
|
||||
import com.threerings.media.tile.TileManager;
|
||||
|
||||
import com.threerings.miso.Log;
|
||||
|
||||
/**
|
||||
* Extends the basic tile manager and provides support for automatically
|
||||
* generating fringes in between different types of base tiles in a scene.
|
||||
*/
|
||||
public class MisoTileManager extends TileManager
|
||||
{
|
||||
/**
|
||||
* Creates a tile manager and provides it with a reference to the
|
||||
* image manager from which it will load tileset images.
|
||||
*
|
||||
* @param imgr the image manager via which the tile manager will
|
||||
* decode and cache images.
|
||||
*/
|
||||
public MisoTileManager (ResourceManager rmgr, ImageManager imgr)
|
||||
{
|
||||
super(imgr);
|
||||
|
||||
// look for a fringe configuration in the appropriate place
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = rmgr.getResource(FRINGE_CONFIG_PATH);
|
||||
FringeConfiguration config = (FringeConfiguration)
|
||||
CompiledConfig.loadConfig(in);
|
||||
|
||||
// if we've found it, create our auto fringer with it
|
||||
_fringer = new AutoFringer(config, imgr, this);
|
||||
|
||||
} catch (IOException ioe) {
|
||||
Log.warning("Unable to load fringe configuration " +
|
||||
"[path=" + FRINGE_CONFIG_PATH +
|
||||
", error=" + ioe + "].");
|
||||
|
||||
} finally {
|
||||
StreamUtil.close(in);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the auto fringer that has been configured for use by this
|
||||
* tile manager. This will only be valid if this tile manager has been
|
||||
* provided with a miso tileset repository via {@link
|
||||
* #setTileSetRepository}.
|
||||
*/
|
||||
public AutoFringer getAutoFringer ()
|
||||
{
|
||||
return _fringer;
|
||||
}
|
||||
|
||||
/** The entity that performs the automatic fringe layer generation. */
|
||||
protected AutoFringer _fringer;
|
||||
|
||||
/** The path (in the classpath) to the serialized fringe
|
||||
* configuration. */
|
||||
protected static final String FRINGE_CONFIG_PATH =
|
||||
"config/miso/tile/fringeconf.dat";
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// $Id: CompileFringeConfigurationTask.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.tile.tools;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Task;
|
||||
|
||||
import com.samskivert.io.PersistenceException;
|
||||
|
||||
import com.threerings.util.CompiledConfig;
|
||||
|
||||
import com.threerings.media.tile.tools.MapFileTileSetIDBroker;
|
||||
|
||||
import com.threerings.miso.tile.tools.xml.FringeConfigurationParser;
|
||||
|
||||
/**
|
||||
* Compile fringe configuration.
|
||||
*/
|
||||
public class CompileFringeConfigurationTask extends Task
|
||||
{
|
||||
public void setTileSetMap (File tsetmap)
|
||||
{
|
||||
_tsetmap = tsetmap;
|
||||
}
|
||||
|
||||
public void setFringeDef (File fringedef)
|
||||
{
|
||||
_fringedef = fringedef;
|
||||
}
|
||||
|
||||
public void setTarget (File target)
|
||||
{
|
||||
_target = target;
|
||||
}
|
||||
|
||||
public void execute () throws BuildException
|
||||
{
|
||||
// make sure the source file exists
|
||||
if (!_fringedef.exists()) {
|
||||
throw new BuildException("Fringe definition file not found " +
|
||||
"[path=" + _fringedef.getPath() + "].");
|
||||
}
|
||||
|
||||
// set up the tileid broker
|
||||
MapFileTileSetIDBroker broker;
|
||||
try {
|
||||
broker = new MapFileTileSetIDBroker(_tsetmap);
|
||||
} catch (PersistenceException pe) {
|
||||
throw new BuildException("Couldn't set up tileset mapping " +
|
||||
"[path=" + _tsetmap.getPath() +
|
||||
", error=" + pe.getCause() + "].");
|
||||
}
|
||||
|
||||
FringeConfigurationParser parser = new FringeConfigurationParser(
|
||||
broker);
|
||||
Serializable config;
|
||||
try {
|
||||
config = parser.parseConfig(_fringedef);
|
||||
} catch (Exception e) {
|
||||
throw new BuildException("Failure parsing config definition", e);
|
||||
}
|
||||
|
||||
try {
|
||||
// and write it on out
|
||||
CompiledConfig.saveConfig(_target, config);
|
||||
} catch (Exception e) {
|
||||
throw new BuildException("Failure writing serialized config", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected File _tsetmap;
|
||||
protected File _fringedef;
|
||||
protected File _target;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// $Id: BaseTileSetRuleSet.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.tile.tools.xml;
|
||||
|
||||
import org.apache.commons.digester.Digester;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.samskivert.xml.CallMethodSpecialRule;
|
||||
|
||||
import com.threerings.media.tile.tools.xml.SwissArmyTileSetRuleSet;
|
||||
|
||||
import com.threerings.miso.Log;
|
||||
import com.threerings.miso.tile.BaseTileSet;
|
||||
|
||||
/**
|
||||
* Parses {@link BaseTileSet} instances from a tileset description. Base
|
||||
* tilesets extend swiss army tilesets with the addition of a passability
|
||||
* flag for each tile.
|
||||
*
|
||||
* @see SwissArmyTileSetRuleSet
|
||||
*/
|
||||
public class BaseTileSetRuleSet extends SwissArmyTileSetRuleSet
|
||||
{
|
||||
// documentation inherited
|
||||
public void addRuleInstances (Digester digester)
|
||||
{
|
||||
super.addRuleInstances(digester);
|
||||
|
||||
digester.addRule(
|
||||
_prefix + TILESET_PATH + "/passable", new CallMethodSpecialRule() {
|
||||
public void parseAndSet (String bodyText, Object target)
|
||||
{
|
||||
int[] values = StringUtil.parseIntArray(bodyText);
|
||||
boolean[] passable = new boolean[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
passable[i] = (values[i] != 0);
|
||||
}
|
||||
BaseTileSet starget = (BaseTileSet)target;
|
||||
starget.setPassability(passable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean isValid (Object target)
|
||||
{
|
||||
BaseTileSet set = (BaseTileSet)target;
|
||||
boolean valid = super.isValid(target);
|
||||
|
||||
// check for a <passable> element
|
||||
if (set.getPassability() == null) {
|
||||
Log.warning("Tile set definition missing valid <passable> " +
|
||||
"element [set=" + set + "].");
|
||||
valid = false;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected Class getTileSetClass ()
|
||||
{
|
||||
return BaseTileSet.class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// $Id: FringeConfigurationParser.java 3749 2005-11-09 04:00:16Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.miso.tile.tools.xml;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.xml.sax.Attributes;
|
||||
import org.apache.commons.digester.Digester;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.samskivert.xml.SetPropertyFieldsRule;
|
||||
import com.samskivert.xml.ValidatedSetNextRule;
|
||||
|
||||
import com.threerings.tools.xml.CompiledConfigParser;
|
||||
|
||||
import com.threerings.media.tile.TileSetIDBroker;
|
||||
|
||||
import com.threerings.miso.Log;
|
||||
import com.threerings.miso.tile.FringeConfiguration.FringeRecord;
|
||||
import com.threerings.miso.tile.FringeConfiguration.FringeTileSetRecord;
|
||||
import com.threerings.miso.tile.FringeConfiguration;
|
||||
|
||||
/**
|
||||
* Parses fringe config definitions.
|
||||
*/
|
||||
public class FringeConfigurationParser extends CompiledConfigParser
|
||||
{
|
||||
public FringeConfigurationParser (TileSetIDBroker broker)
|
||||
{
|
||||
_idBroker = broker;
|
||||
}
|
||||
|
||||
|
||||
// documentation inherited
|
||||
protected Serializable createConfigObject ()
|
||||
{
|
||||
return new FringeConfiguration();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void addRules (Digester digest)
|
||||
{
|
||||
// configure top-level constraints
|
||||
String prefix = "fringe";
|
||||
digest.addRule(prefix, new SetPropertyFieldsRule());
|
||||
|
||||
// create and configure fringe config instances
|
||||
prefix += "/base";
|
||||
digest.addObjectCreate(prefix, FringeRecord.class.getName());
|
||||
|
||||
ValidatedSetNextRule.Validator val;
|
||||
val = new ValidatedSetNextRule.Validator() {
|
||||
public boolean isValid (Object target) {
|
||||
if (((FringeRecord) target).isValid()) {
|
||||
return true;
|
||||
} else {
|
||||
Log.warning("A FringeRecord was not added because it was " +
|
||||
"improperly specified [rec=" + target + "].");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
ValidatedSetNextRule vrule;
|
||||
vrule = new ValidatedSetNextRule("addFringeRecord", val) {
|
||||
// parse the fringe record, converting tileset names to
|
||||
// tileset ids
|
||||
public void begin (String namespace, String lname, Attributes attrs)
|
||||
throws Exception
|
||||
{
|
||||
FringeRecord frec = (FringeRecord) digester.peek();
|
||||
|
||||
for (int ii=0; ii < attrs.getLength(); ii++) {
|
||||
String name = attrs.getLocalName(ii);
|
||||
if (StringUtil.isBlank(name)) {
|
||||
name = attrs.getQName(ii);
|
||||
}
|
||||
String value = attrs.getValue(ii);
|
||||
|
||||
if ("name".equals(name)) {
|
||||
if (_idBroker.tileSetMapped(value)) {
|
||||
frec.base_tsid = _idBroker.getTileSetID(value);
|
||||
} else {
|
||||
Log.warning("Skipping unknown base " +
|
||||
"tileset [name=" + value + "].");
|
||||
}
|
||||
|
||||
} else if ("priority".equals(name)) {
|
||||
frec.priority = Integer.parseInt(value);
|
||||
} else {
|
||||
Log.warning("Skipping unknown attribute " +
|
||||
"[name=" + name + "].");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
digest.addRule(prefix, vrule);
|
||||
|
||||
// create the tileset records in each fringe record
|
||||
prefix += "/tileset";
|
||||
digest.addObjectCreate(prefix, FringeTileSetRecord.class.getName());
|
||||
|
||||
val = new ValidatedSetNextRule.Validator() {
|
||||
public boolean isValid (Object target) {
|
||||
if (((FringeTileSetRecord) target).isValid()) {
|
||||
return true;
|
||||
} else {
|
||||
Log.warning("A FringeTileSetRecord was not added because " +
|
||||
"it was improperly specified " +
|
||||
"[rec=" + target + "].");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
vrule = new ValidatedSetNextRule("addTileset", val) {
|
||||
// parse the fringe tilesetrecord, converting tileset names to ids
|
||||
public void begin (String namespace, String lname, Attributes attrs)
|
||||
throws Exception
|
||||
{
|
||||
FringeTileSetRecord f = (FringeTileSetRecord) digester.peek();
|
||||
|
||||
for (int ii=0; ii < attrs.getLength(); ii++) {
|
||||
String name = attrs.getLocalName(ii);
|
||||
if (StringUtil.isBlank(name)) {
|
||||
name = attrs.getQName(ii);
|
||||
}
|
||||
String value = attrs.getValue(ii);
|
||||
|
||||
if ("name".equals(name)) {
|
||||
if (_idBroker.tileSetMapped(value)) {
|
||||
f.fringe_tsid = _idBroker.getTileSetID(value);
|
||||
} else {
|
||||
Log.warning("Skipping unknown fringe " +
|
||||
"tileset [name=" + value + "].");
|
||||
}
|
||||
|
||||
} else if ("mask".equals(name)) {
|
||||
f.mask = Boolean.valueOf(value).booleanValue();
|
||||
} else {
|
||||
Log.warning("Skipping unknown attribute " +
|
||||
"[name=" + name + "].");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
digest.addRule(prefix, vrule);
|
||||
}
|
||||
|
||||
protected TileSetIDBroker _idBroker;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// $Id: SimpleMisoSceneParser.java 3749 2005-11-09 04:00:16Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.tools.xml;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.FileInputStream;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
import org.apache.commons.digester.Digester;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.miso.data.SimpleMisoSceneModel;
|
||||
|
||||
/**
|
||||
* A simple class for parsing simple miso scene models.
|
||||
*/
|
||||
public class SimpleMisoSceneParser
|
||||
{
|
||||
/**
|
||||
* Constructs a scene parser that parses scenes with the specified XML
|
||||
* path prefix.
|
||||
*/
|
||||
public SimpleMisoSceneParser (String prefix)
|
||||
{
|
||||
// create and configure our digester
|
||||
_digester = new Digester();
|
||||
|
||||
// create our scene rule set
|
||||
SimpleMisoSceneRuleSet set = new SimpleMisoSceneRuleSet();
|
||||
|
||||
// configure our top-level path prefix
|
||||
if (StringUtil.isBlank(prefix)) {
|
||||
_prefix = set.getOuterElement();
|
||||
} else {
|
||||
_prefix = prefix + "/" + set.getOuterElement();
|
||||
}
|
||||
|
||||
// add the scene rules
|
||||
set.addRuleInstances(_prefix, _digester);
|
||||
|
||||
// add a rule to grab the finished scene model
|
||||
_digester.addSetNext(
|
||||
_prefix, "setScene", SimpleMisoSceneModel.class.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the XML file at the specified path into a scene model
|
||||
* instance.
|
||||
*/
|
||||
public SimpleMisoSceneModel parseScene (String path)
|
||||
throws IOException, SAXException
|
||||
{
|
||||
_model = null;
|
||||
_digester.push(this);
|
||||
_digester.parse(new FileInputStream(path));
|
||||
return _model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the parser once the scene is parsed.
|
||||
*/
|
||||
public void setScene (SimpleMisoSceneModel model)
|
||||
{
|
||||
_model = model;
|
||||
}
|
||||
|
||||
protected String _prefix;
|
||||
protected Digester _digester;
|
||||
protected SimpleMisoSceneModel _model;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// $Id: SimpleMisoSceneRuleSet.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.tools.xml;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.xml.sax.Attributes;
|
||||
|
||||
import org.apache.commons.digester.Digester;
|
||||
import org.apache.commons.digester.Rule;
|
||||
|
||||
import com.samskivert.xml.CallMethodSpecialRule;
|
||||
import com.samskivert.xml.SetFieldRule;
|
||||
import com.samskivert.xml.SetPropertyFieldsRule;
|
||||
|
||||
import com.threerings.tools.xml.NestableRuleSet;
|
||||
|
||||
import com.threerings.miso.data.ObjectInfo;
|
||||
import com.threerings.miso.data.SimpleMisoSceneModel;
|
||||
|
||||
/**
|
||||
* Used to parse a {@link SimpleMisoSceneModel} from XML.
|
||||
*/
|
||||
public class SimpleMisoSceneRuleSet implements NestableRuleSet
|
||||
{
|
||||
// documentation inherited from interface
|
||||
public String getOuterElement ()
|
||||
{
|
||||
return SimpleMisoSceneWriter.OUTER_ELEMENT;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void addRuleInstances (String prefix, Digester dig)
|
||||
{
|
||||
// this creates the appropriate instance when we encounter our
|
||||
// prefix tag
|
||||
dig.addRule(prefix, new Rule() {
|
||||
public void begin (String namespace, String name,
|
||||
Attributes attributes) throws Exception {
|
||||
digester.push(createMisoSceneModel());
|
||||
}
|
||||
public void end (String namespace, String name) throws Exception {
|
||||
digester.pop();
|
||||
}
|
||||
});
|
||||
|
||||
// set up rules to parse and set our fields
|
||||
dig.addRule(prefix + "/width", new SetFieldRule("width"));
|
||||
dig.addRule(prefix + "/height", new SetFieldRule("height"));
|
||||
dig.addRule(prefix + "/viewwidth", new SetFieldRule("vwidth"));
|
||||
dig.addRule(prefix + "/viewheight", new SetFieldRule("vheight"));
|
||||
dig.addRule(prefix + "/base", new SetFieldRule("baseTileIds"));
|
||||
|
||||
dig.addObjectCreate(prefix + "/objects", ArrayList.class.getName());
|
||||
dig.addObjectCreate(prefix + "/objects/object",
|
||||
ObjectInfo.class.getName());
|
||||
dig.addSetNext(prefix + "/objects/object", "add",
|
||||
Object.class.getName());
|
||||
|
||||
dig.addRule(prefix + "/objects/object", new SetPropertyFieldsRule());
|
||||
|
||||
dig.addRule(prefix + "/objects", new CallMethodSpecialRule() {
|
||||
public void parseAndSet (String bodyText, Object target)
|
||||
throws Exception
|
||||
{
|
||||
ArrayList ilist = (ArrayList)target;
|
||||
ArrayList ulist = new ArrayList();
|
||||
SimpleMisoSceneModel model = (SimpleMisoSceneModel)
|
||||
digester.peek(1);
|
||||
|
||||
// filter interesting and uninteresting into two lists
|
||||
for (int ii = 0; ii < ilist.size(); ii++) {
|
||||
ObjectInfo info = (ObjectInfo)ilist.get(ii);
|
||||
if (!info.isInteresting()) {
|
||||
ilist.remove(ii--);
|
||||
ulist.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
// now populate the model
|
||||
SimpleMisoSceneModel.populateObjects(model, ilist, ulist);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected SimpleMisoSceneModel createMisoSceneModel ()
|
||||
{
|
||||
return new SimpleMisoSceneModel(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
//
|
||||
// $Id: SimpleMisoSceneWriter.java 3749 2005-11-09 04:00:16Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.tools.xml;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.AttributesImpl;
|
||||
|
||||
import com.megginson.sax.DataWriter;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.threerings.tools.xml.NestableWriter;
|
||||
|
||||
import com.threerings.miso.data.ObjectInfo;
|
||||
import com.threerings.miso.data.SimpleMisoSceneModel;
|
||||
|
||||
/**
|
||||
* Generates an XML representation of a {@link SimpleMisoSceneModel}.
|
||||
*/
|
||||
public class SimpleMisoSceneWriter implements NestableWriter
|
||||
{
|
||||
/** The element used to enclose scene models written with this
|
||||
* writer. */
|
||||
public static final String OUTER_ELEMENT = "miso";
|
||||
|
||||
// documentation inherited from interface
|
||||
public void write (Object object, DataWriter writer)
|
||||
throws SAXException
|
||||
{
|
||||
SimpleMisoSceneModel model = (SimpleMisoSceneModel)object;
|
||||
writer.startElement(OUTER_ELEMENT);
|
||||
writeSceneData(model, writer);
|
||||
writer.endElement(OUTER_ELEMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes just the scene data which is handy for derived classes which
|
||||
* may wish to add their own scene data to the scene output.
|
||||
*/
|
||||
protected void writeSceneData (SimpleMisoSceneModel model,
|
||||
DataWriter writer)
|
||||
throws SAXException
|
||||
{
|
||||
writer.dataElement("width", Integer.toString(model.width));
|
||||
writer.dataElement("height", Integer.toString(model.height));
|
||||
writer.dataElement("viewwidth", Integer.toString(model.vwidth));
|
||||
writer.dataElement("viewheight", Integer.toString(model.vheight));
|
||||
writer.dataElement("base",
|
||||
StringUtil.toString(model.baseTileIds, "", ""));
|
||||
|
||||
// write our uninteresting object tile information
|
||||
writer.startElement("objects");
|
||||
for (int ii = 0; ii < model.objectTileIds.length; ii++) {
|
||||
AttributesImpl attrs = new AttributesImpl();
|
||||
attrs.addAttribute("", "tileId", "", "",
|
||||
String.valueOf(model.objectTileIds[ii]));
|
||||
attrs.addAttribute("", "x", "", "",
|
||||
String.valueOf(model.objectXs[ii]));
|
||||
attrs.addAttribute("", "y", "", "",
|
||||
String.valueOf(model.objectYs[ii]));
|
||||
writer.emptyElement("", "object", "", attrs);
|
||||
}
|
||||
|
||||
// write our uninteresting object tile information
|
||||
for (int ii = 0; ii < model.objectInfo.length; ii++) {
|
||||
ObjectInfo info = model.objectInfo[ii];
|
||||
AttributesImpl attrs = new AttributesImpl();
|
||||
attrs.addAttribute("", "tileId", "", "",
|
||||
String.valueOf(info.tileId));
|
||||
attrs.addAttribute("", "x", "", "", String.valueOf(info.x));
|
||||
attrs.addAttribute("", "y", "", "", String.valueOf(info.y));
|
||||
|
||||
if (!StringUtil.isBlank(info.action)) {
|
||||
attrs.addAttribute("", "action", "", "", info.action);
|
||||
}
|
||||
|
||||
if (info.priority != 0) {
|
||||
attrs.addAttribute("", "priority", "", "",
|
||||
String.valueOf(info.priority));
|
||||
}
|
||||
|
||||
if (info.sx != 0 || info.sy != 0) {
|
||||
attrs.addAttribute("", "sx", "", "",
|
||||
String.valueOf(info.sx));
|
||||
attrs.addAttribute("", "sy", "", "",
|
||||
String.valueOf(info.sy));
|
||||
attrs.addAttribute("", "sorient", "", "",
|
||||
String.valueOf(info.sorient));
|
||||
}
|
||||
|
||||
if (info.zations != 0) {
|
||||
attrs.addAttribute("", "zations", "", "",
|
||||
String.valueOf(info.zations));
|
||||
}
|
||||
writer.emptyElement("", "object", "", attrs);
|
||||
}
|
||||
writer.endElement("objects");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// $Id: SparseMisoSceneParser.java 3749 2005-11-09 04:00:16Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.tools.xml;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.FileInputStream;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
import org.apache.commons.digester.Digester;
|
||||
|
||||
import com.samskivert.io.StreamUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.miso.data.SparseMisoSceneModel;
|
||||
|
||||
/**
|
||||
* A simple class for parsing simple miso scene models.
|
||||
*/
|
||||
public class SparseMisoSceneParser
|
||||
{
|
||||
/**
|
||||
* Constructs a scene parser that parses scenes with the specified XML
|
||||
* path prefix.
|
||||
*/
|
||||
public SparseMisoSceneParser (String prefix)
|
||||
{
|
||||
// create and configure our digester
|
||||
_digester = new Digester();
|
||||
|
||||
// create our scene rule set
|
||||
SparseMisoSceneRuleSet set = new SparseMisoSceneRuleSet();
|
||||
|
||||
// configure our top-level path prefix
|
||||
if (StringUtil.isBlank(prefix)) {
|
||||
_prefix = set.getOuterElement();
|
||||
} else {
|
||||
_prefix = prefix + "/" + set.getOuterElement();
|
||||
}
|
||||
|
||||
// add the scene rules
|
||||
set.addRuleInstances(_prefix, _digester);
|
||||
|
||||
// add a rule to grab the finished scene model
|
||||
_digester.addSetNext(
|
||||
_prefix, "setScene", SparseMisoSceneModel.class.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the XML file at the specified path into a scene model
|
||||
* instance.
|
||||
*/
|
||||
public SparseMisoSceneModel parseScene (String path)
|
||||
throws IOException, SAXException
|
||||
{
|
||||
_model = null;
|
||||
_digester.push(this);
|
||||
FileInputStream stream = null;
|
||||
try {
|
||||
stream = new FileInputStream(path);
|
||||
_digester.parse(stream);
|
||||
} finally {
|
||||
StreamUtil.close(stream);
|
||||
}
|
||||
return _model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the parser once the scene is parsed.
|
||||
*/
|
||||
public void setScene (SparseMisoSceneModel model)
|
||||
{
|
||||
_model = model;
|
||||
}
|
||||
|
||||
protected String _prefix;
|
||||
protected Digester _digester;
|
||||
protected SparseMisoSceneModel _model;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// $Id: SparseMisoSceneRuleSet.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.tools.xml;
|
||||
|
||||
import org.xml.sax.Attributes;
|
||||
|
||||
import org.apache.commons.digester.Digester;
|
||||
import org.apache.commons.digester.Rule;
|
||||
|
||||
import com.samskivert.xml.SetFieldRule;
|
||||
import com.samskivert.xml.SetPropertyFieldsRule;
|
||||
|
||||
import com.threerings.tools.xml.NestableRuleSet;
|
||||
|
||||
import com.threerings.miso.data.ObjectInfo;
|
||||
import com.threerings.miso.data.SparseMisoSceneModel.Section;
|
||||
import com.threerings.miso.data.SparseMisoSceneModel;
|
||||
|
||||
/**
|
||||
* Used to parse a {@link SparseMisoSceneModel} from XML.
|
||||
*/
|
||||
public class SparseMisoSceneRuleSet implements NestableRuleSet
|
||||
{
|
||||
// documentation inherited from interface
|
||||
public String getOuterElement ()
|
||||
{
|
||||
return SparseMisoSceneWriter.OUTER_ELEMENT;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void addRuleInstances (String prefix, Digester dig)
|
||||
{
|
||||
// this creates the appropriate instance when we encounter our
|
||||
// prefix tag
|
||||
dig.addRule(prefix, new Rule() {
|
||||
public void begin (String namespace, String name,
|
||||
Attributes attributes) throws Exception {
|
||||
digester.push(createMisoSceneModel());
|
||||
}
|
||||
public void end (String namespace, String name) throws Exception {
|
||||
digester.pop();
|
||||
}
|
||||
});
|
||||
|
||||
// set up rules to parse and set our fields
|
||||
dig.addRule(prefix + "/swidth", new SetFieldRule("swidth"));
|
||||
dig.addRule(prefix + "/sheight", new SetFieldRule("sheight"));
|
||||
dig.addRule(prefix + "/defTileSet", new SetFieldRule("defTileSet"));
|
||||
|
||||
String sprefix = prefix + "/sections/section";
|
||||
dig.addObjectCreate(sprefix, Section.class.getName());
|
||||
dig.addRule(sprefix, new SetPropertyFieldsRule());
|
||||
dig.addRule(sprefix + "/base", new SetFieldRule("baseTileIds"));
|
||||
dig.addObjectCreate(sprefix + "/objects/object",
|
||||
ObjectInfo.class.getName());
|
||||
dig.addRule(sprefix + "/objects/object", new SetPropertyFieldsRule());
|
||||
dig.addSetNext(sprefix + "/objects/object", "addObject",
|
||||
ObjectInfo.class.getName());
|
||||
dig.addSetNext(sprefix, "setSection", Section.class.getName());
|
||||
}
|
||||
|
||||
protected SparseMisoSceneModel createMisoSceneModel ()
|
||||
{
|
||||
return new SparseMisoSceneModel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// $Id: SparseMisoSceneWriter.java 3749 2005-11-09 04:00:16Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.tools.xml;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.AttributesImpl;
|
||||
|
||||
import com.megginson.sax.DataWriter;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.threerings.tools.xml.NestableWriter;
|
||||
|
||||
import com.threerings.miso.data.ObjectInfo;
|
||||
import com.threerings.miso.data.SparseMisoSceneModel.Section;
|
||||
import com.threerings.miso.data.SparseMisoSceneModel;
|
||||
|
||||
/**
|
||||
* Generates an XML representation of a {@link SparseMisoSceneModel}.
|
||||
*/
|
||||
public class SparseMisoSceneWriter implements NestableWriter
|
||||
{
|
||||
/** The element used to enclose scene models written with this
|
||||
* writer. */
|
||||
public static final String OUTER_ELEMENT = "miso";
|
||||
|
||||
// documentation inherited from interface
|
||||
public void write (Object object, DataWriter writer)
|
||||
throws SAXException
|
||||
{
|
||||
SparseMisoSceneModel model = (SparseMisoSceneModel)object;
|
||||
writer.startElement(OUTER_ELEMENT);
|
||||
writeSceneData(model, writer);
|
||||
writer.endElement(OUTER_ELEMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes just the scene data which is handy for derived classes which
|
||||
* may wish to add their own scene data to the scene output.
|
||||
*/
|
||||
protected void writeSceneData (SparseMisoSceneModel model,
|
||||
DataWriter writer)
|
||||
throws SAXException
|
||||
{
|
||||
writer.dataElement("swidth", Integer.toString(model.swidth));
|
||||
writer.dataElement("sheight", Integer.toString(model.sheight));
|
||||
writer.dataElement("defTileSet", Integer.toString(model.defTileSet));
|
||||
|
||||
writer.startElement("sections");
|
||||
for (Iterator iter = model.getSections(); iter.hasNext(); ) {
|
||||
Section sect = (Section)iter.next();
|
||||
if (sect.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
AttributesImpl attrs = new AttributesImpl();
|
||||
attrs.addAttribute("", "x", "", "", String.valueOf(sect.x));
|
||||
attrs.addAttribute("", "y", "", "", String.valueOf(sect.y));
|
||||
attrs.addAttribute("", "width", "", "", String.valueOf(sect.width));
|
||||
writer.startElement("", "section", "", attrs);
|
||||
|
||||
writer.dataElement(
|
||||
"base", StringUtil.toString(sect.baseTileIds, "", ""));
|
||||
|
||||
// write our uninteresting object tile information
|
||||
writer.startElement("objects");
|
||||
for (int ii = 0; ii < sect.objectTileIds.length; ii++) {
|
||||
attrs = new AttributesImpl();
|
||||
attrs.addAttribute("", "tileId", "", "",
|
||||
String.valueOf(sect.objectTileIds[ii]));
|
||||
attrs.addAttribute("", "x", "", "",
|
||||
String.valueOf(sect.objectXs[ii]));
|
||||
attrs.addAttribute("", "y", "", "",
|
||||
String.valueOf(sect.objectYs[ii]));
|
||||
writer.emptyElement("", "object", "", attrs);
|
||||
}
|
||||
|
||||
// write our interesting object tile information
|
||||
for (int ii = 0; ii < sect.objectInfo.length; ii++) {
|
||||
ObjectInfo info = sect.objectInfo[ii];
|
||||
attrs = new AttributesImpl();
|
||||
attrs.addAttribute("", "tileId", "", "",
|
||||
String.valueOf(info.tileId));
|
||||
attrs.addAttribute("", "x", "", "", String.valueOf(info.x));
|
||||
attrs.addAttribute("", "y", "", "", String.valueOf(info.y));
|
||||
|
||||
if (!StringUtil.isBlank(info.action)) {
|
||||
attrs.addAttribute("", "action", "", "", info.action);
|
||||
}
|
||||
if (info.priority != 0) {
|
||||
attrs.addAttribute("", "priority", "", "",
|
||||
String.valueOf(info.priority));
|
||||
}
|
||||
if (info.sx != 0 || info.sy != 0) {
|
||||
attrs.addAttribute("", "sx", "", "",
|
||||
String.valueOf(info.sx));
|
||||
attrs.addAttribute("", "sy", "", "",
|
||||
String.valueOf(info.sy));
|
||||
attrs.addAttribute("", "sorient", "", "",
|
||||
String.valueOf(info.sorient));
|
||||
}
|
||||
if (info.zations != 0) {
|
||||
attrs.addAttribute("", "zations", "", "",
|
||||
String.valueOf(info.zations));
|
||||
}
|
||||
writer.emptyElement("", "object", "", attrs);
|
||||
}
|
||||
writer.endElement("objects");
|
||||
writer.endElement("section");
|
||||
}
|
||||
writer.endElement("sections");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// $Id: MisoContext.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.media.FrameManager;
|
||||
import com.threerings.miso.tile.MisoTileManager;
|
||||
|
||||
/**
|
||||
* Provides Miso code with access to the managers that it needs to do its
|
||||
* thing.
|
||||
*/
|
||||
public interface MisoContext
|
||||
{
|
||||
/**
|
||||
* Returns the frame manager that our scene panel will interact with.
|
||||
*/
|
||||
public FrameManager getFrameManager ();
|
||||
|
||||
/**
|
||||
* Returns a reference to the tile manager. This reference is valid
|
||||
* for the lifetime of the application.
|
||||
*/
|
||||
public MisoTileManager getTileManager ();
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// $Id: MisoSceneMetrics.java 3310 2005-01-24 23:08:21Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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.client.MisoScenePanel;
|
||||
|
||||
/**
|
||||
* Contains information on the configuration of a particular isometric
|
||||
* view. The member data are public to facilitate convenient referencing
|
||||
* by the {@link MisoScenePanel} class, the values should not be modified
|
||||
* once the metrics are constructed.
|
||||
*/
|
||||
public class MisoSceneMetrics
|
||||
{
|
||||
/** Tile dimensions and half-dimensions in the view. */
|
||||
public int tilewid, tilehei, tilehwid, tilehhei;
|
||||
|
||||
/** Fine coordinate dimensions. */
|
||||
public int finehwid, finehhei;
|
||||
|
||||
/** Number of fine coordinates on each axis within a tile. */
|
||||
public int finegran;
|
||||
|
||||
/** Dimensions of our scene blocks in tile count. */
|
||||
public short blockwid = 4, blockhei = 4;
|
||||
|
||||
/** The length of a tile edge in pixels. */
|
||||
public float tilelen;
|
||||
|
||||
/** The slope of the x- and y-axis lines. */
|
||||
public float slopeX, slopeY;
|
||||
|
||||
/** The length between fine coordinates in pixels. */
|
||||
public float finelen;
|
||||
|
||||
/** The y-intercept of the x-axis line within a tile. */
|
||||
public float fineBX;
|
||||
|
||||
/** The slope of the x- and y-axis lines within a tile. */
|
||||
public float fineSlopeX, fineSlopeY;
|
||||
|
||||
/**
|
||||
* Constructs scene metrics by directly specifying the desired config
|
||||
* parameters.
|
||||
*
|
||||
* @param tilewid the width in pixels of the tiles.
|
||||
* @param tilehei the height in pixels of the tiles.
|
||||
* @param finegran the number of sub-tile divisions to use for fine
|
||||
* coordinates.
|
||||
*/
|
||||
public MisoSceneMetrics (int tilewid, int tilehei, int finegran)
|
||||
{
|
||||
// keep track of this stuff
|
||||
this.tilewid = tilewid;
|
||||
this.tilehei = tilehei;
|
||||
this.finegran = finegran;
|
||||
|
||||
// halve the dimensions
|
||||
tilehwid = (tilewid / 2);
|
||||
tilehhei = (tilehei / 2);
|
||||
|
||||
// calculate the length of a tile edge in pixels
|
||||
tilelen = (float) Math.sqrt(
|
||||
(tilehwid * tilehwid) + (tilehhei * tilehhei));
|
||||
|
||||
// calculate the slope of the x- and y-axis lines
|
||||
slopeX = (float)tilehei / (float)tilewid;
|
||||
slopeY = -slopeX;
|
||||
|
||||
// calculate the edge length separating each fine coordinate
|
||||
finelen = tilelen / (float)finegran;
|
||||
|
||||
// calculate the fine-coordinate x-axis line
|
||||
fineSlopeX = (float)tilehei / (float)tilewid;
|
||||
fineBX = -(fineSlopeX * (float)tilehwid);
|
||||
fineSlopeY = -fineSlopeX;
|
||||
|
||||
// calculate the fine coordinate dimensions
|
||||
finehwid = (int)((float)tilehwid / (float)finegran);
|
||||
finehhei = (int)((float)tilehhei / (float)finegran);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,518 @@
|
||||
//
|
||||
// $Id: MisoUtil.java 3628 2005-06-28 17:42:55Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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 java.awt.Point;
|
||||
import java.awt.Polygon;
|
||||
|
||||
import com.samskivert.swing.SmartPolygon;
|
||||
|
||||
import com.threerings.util.DirectionCodes;
|
||||
import com.threerings.util.DirectionUtil;
|
||||
|
||||
import com.threerings.media.util.MathUtil;
|
||||
|
||||
/**
|
||||
* Miscellaneous isometric-display-related utility routines.
|
||||
*/
|
||||
public class MisoUtil
|
||||
implements DirectionCodes
|
||||
{
|
||||
/**
|
||||
* Given two points in screen pixel coordinates, return the
|
||||
* compass direction that point B lies in from point A from an
|
||||
* isometric perspective.
|
||||
*
|
||||
* @param ax the x-position of point A.
|
||||
* @param ay the y-position of point A.
|
||||
* @param bx the x-position of point B.
|
||||
* @param by the y-position of point B.
|
||||
*
|
||||
* @return the direction specified as one of the <code>Sprite</code>
|
||||
* class's direction constants.
|
||||
*/
|
||||
public static int getDirection (
|
||||
MisoSceneMetrics metrics, int ax, int ay, int bx, int by)
|
||||
{
|
||||
Point afpos = new Point(), bfpos = new Point();
|
||||
|
||||
// convert screen coordinates to full coordinates to get both
|
||||
// tile coordinates and fine coordinates
|
||||
screenToFull(metrics, ax, ay, afpos);
|
||||
screenToFull(metrics, bx, by, bfpos);
|
||||
|
||||
// pull out the tile coordinates for each point
|
||||
int tax = fullToTile(afpos.x);
|
||||
int tay = fullToTile(afpos.y);
|
||||
|
||||
int tbx = fullToTile(bfpos.x);
|
||||
int tby = fullToTile(bfpos.y);
|
||||
|
||||
// compare tile coordinates to determine direction
|
||||
int dir = getIsoDirection(tax, tay, tbx, tby);
|
||||
if (dir != DirectionCodes.NONE) {
|
||||
return dir;
|
||||
}
|
||||
|
||||
// destination point is in the same tile as the
|
||||
// origination point, so consider fine coordinates
|
||||
|
||||
// pull out the fine coordinates for each point
|
||||
int fax = afpos.x - (tax * FULL_TILE_FACTOR);
|
||||
int fay = afpos.y - (tay * FULL_TILE_FACTOR);
|
||||
|
||||
int fbx = bfpos.x - (tbx * FULL_TILE_FACTOR);
|
||||
int fby = bfpos.y - (tby * FULL_TILE_FACTOR);
|
||||
|
||||
// compare fine coordinates to determine direction
|
||||
dir = getIsoDirection(fax, fay, fbx, fby);
|
||||
|
||||
// arbitrarily return southwest if fine coords were also equivalent
|
||||
return (dir == -1) ? SOUTHWEST : dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two points in an isometric coordinate system (in which {@link
|
||||
* #NORTH} is in the direction of the negative x-axis and {@link
|
||||
* #WEST} in the direction of the negative y-axis), return the compass
|
||||
* direction that point B lies in from point A. This method is used
|
||||
* to determine direction for both tile coordinates and fine
|
||||
* coordinates within a tile, since the coordinate systems are the
|
||||
* same.
|
||||
*
|
||||
* @param ax the x-position of point A.
|
||||
* @param ay the y-position of point A.
|
||||
* @param bx the x-position of point B.
|
||||
* @param by the y-position of point B.
|
||||
*
|
||||
* @return the direction specified as one of the <code>Sprite</code>
|
||||
* class's direction constants, or <code>DirectionCodes.NONE</code> if
|
||||
* point B is equivalent to point A.
|
||||
*/
|
||||
public static int getIsoDirection (int ax, int ay, int bx, int by)
|
||||
{
|
||||
// head off a div by 0 at the pass..
|
||||
if (bx == ax) {
|
||||
if (by == ay) {
|
||||
return DirectionCodes.NONE;
|
||||
}
|
||||
return (by < ay) ? EAST : WEST;
|
||||
}
|
||||
|
||||
// figure direction base on the slope of the line
|
||||
float slope = ((float) (ay - by)) / ((float) Math.abs(ax - bx));
|
||||
if (slope > 2f) {
|
||||
return EAST;
|
||||
}
|
||||
if (slope > .5f) {
|
||||
return (bx < ax) ? NORTHEAST : SOUTHEAST;
|
||||
}
|
||||
if (slope > -.5f) {
|
||||
return (bx < ax) ? NORTH : SOUTH;
|
||||
}
|
||||
if (slope > -2f) {
|
||||
return (bx < ax) ? NORTHWEST : SOUTHWEST;
|
||||
}
|
||||
return WEST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two points in screen coordinates, return the isometrically
|
||||
* projected compass direction that point B lies in from point A.
|
||||
*
|
||||
* @param ax the x-position of point A.
|
||||
* @param ay the y-position of point A.
|
||||
* @param bx the x-position of point B.
|
||||
* @param by the y-position of point B.
|
||||
*
|
||||
* @return the direction specified as one of the <code>Sprite</code>
|
||||
* class's direction constants, or <code>DirectionCodes.NONE</code> if
|
||||
* point B is equivalent to point A.
|
||||
*/
|
||||
public static int getProjectedIsoDirection (int ax, int ay, int bx, int by)
|
||||
{
|
||||
return toIsoDirection(DirectionUtil.getDirection(ax, ay, bx, by));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a non-isometric orientation (where north points toward the
|
||||
* top of the screen) to an isometric orientation where north points
|
||||
* toward the upper-left corner of the screen.
|
||||
*/
|
||||
public static int toIsoDirection (int dir)
|
||||
{
|
||||
if (dir != DirectionCodes.NONE) {
|
||||
// rotate the direction clockwise (ie. change SOUTHEAST to
|
||||
// SOUTH)
|
||||
dir = DirectionUtil.rotateCW(dir, 2);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tile coordinate of the given full coordinate.
|
||||
*/
|
||||
public static int fullToTile (int val)
|
||||
{
|
||||
return MathUtil.floorDiv(val, FULL_TILE_FACTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fine coordinate of the given full coordinate.
|
||||
*/
|
||||
public static int fullToFine (int val)
|
||||
{
|
||||
return (val - (fullToTile(val) * FULL_TILE_FACTOR));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given screen-based pixel coordinates to their
|
||||
* corresponding tile-based coordinates. Converted coordinates
|
||||
* are placed in the given point object.
|
||||
*
|
||||
* @param sx the screen x-position pixel coordinate.
|
||||
* @param sy the screen y-position pixel coordinate.
|
||||
* @param tpos the point object to place coordinates in.
|
||||
*
|
||||
* @return the point instance supplied via the <code>tpos</code>
|
||||
* parameter.
|
||||
*/
|
||||
public static Point screenToTile (
|
||||
MisoSceneMetrics metrics, int sx, int sy, Point tpos)
|
||||
{
|
||||
// determine the upper-left of the quadrant that contains our
|
||||
// point
|
||||
int zx = (int)Math.floor((float)sx / metrics.tilewid);
|
||||
int zy = (int)Math.floor((float)sy / metrics.tilehei);
|
||||
|
||||
// these are the screen coordinates of the tile's top
|
||||
int ox = (zx * metrics.tilewid), oy = (zy * metrics.tilehei);
|
||||
|
||||
// these are the tile coordinates
|
||||
tpos.x = zy + zx; tpos.y = zy - zx;
|
||||
|
||||
// now determine which of the four tiles our point occupies
|
||||
int dx = sx - ox, dy = sy - oy;
|
||||
|
||||
if (Math.round(metrics.slopeY * dx + metrics.tilehei) <= dy) {
|
||||
tpos.x += 1;
|
||||
}
|
||||
|
||||
if (Math.round(metrics.slopeX * dx) > dy) {
|
||||
tpos.y -= 1;
|
||||
}
|
||||
|
||||
// Log.info("Converted [sx=" + sx + ", sy=" + sy +
|
||||
// ", zx=" + zx + ", zy=" + zy +
|
||||
// ", ox=" + ox + ", oy=" + oy +
|
||||
// ", dx=" + dx + ", dy=" + dy +
|
||||
// ", tpos.x=" + tpos.x + ", tpos.y=" + tpos.y + "].");
|
||||
return tpos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given tile-based coordinates to their corresponding
|
||||
* screen-based pixel coordinates. The screen coordinate for a tile is
|
||||
* the upper-left coordinate of the rectangle that bounds the tile
|
||||
* polygon. Converted coordinates are placed in the given point
|
||||
* object.
|
||||
*
|
||||
* @param x the tile x-position coordinate.
|
||||
* @param y the tile y-position coordinate.
|
||||
* @param spos the point object to place coordinates in.
|
||||
*
|
||||
* @return the point instance supplied via the <code>spos</code>
|
||||
* parameter.
|
||||
*/
|
||||
public static Point tileToScreen (
|
||||
MisoSceneMetrics metrics, int x, int y, Point spos)
|
||||
{
|
||||
spos.x = (x - y - 1) * metrics.tilehwid;
|
||||
spos.y = (x + y) * metrics.tilehhei;
|
||||
return spos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given fine coordinates to pixel coordinates within
|
||||
* the containing tile. Converted coordinates are placed in the
|
||||
* given point object.
|
||||
*
|
||||
* @param x the x-position fine coordinate.
|
||||
* @param y the y-position fine coordinate.
|
||||
* @param ppos the point object to place coordinates in.
|
||||
*/
|
||||
public static void fineToPixel (
|
||||
MisoSceneMetrics metrics, int x, int y, Point ppos)
|
||||
{
|
||||
ppos.x = metrics.tilehwid + ((x - y) * metrics.finehwid);
|
||||
ppos.y = (x + y) * metrics.finehhei;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given pixel coordinates, whose origin is at the
|
||||
* top-left of a tile's containing rectangle, to fine coordinates
|
||||
* within that tile. Converted coordinates are placed in the
|
||||
* given point object.
|
||||
*
|
||||
* @param x the x-position pixel coordinate.
|
||||
* @param y the y-position pixel coordinate.
|
||||
* @param fpos the point object to place coordinates in.
|
||||
*/
|
||||
public static void pixelToFine (
|
||||
MisoSceneMetrics metrics, int x, int y, Point fpos)
|
||||
{
|
||||
// calculate line parallel to the y-axis (from the given
|
||||
// x/y-pos to the x-axis)
|
||||
float bY = y - (metrics.fineSlopeY * x);
|
||||
|
||||
// determine intersection of x- and y-axis lines
|
||||
int crossx = (int)((bY - metrics.fineBX) /
|
||||
(metrics.fineSlopeX - metrics.fineSlopeY));
|
||||
int crossy = (int)((metrics.fineSlopeY * crossx) + bY);
|
||||
|
||||
// TODO: final position should check distance between our
|
||||
// position and the surrounding fine coords and return the
|
||||
// actual closest fine coord, rather than just dividing.
|
||||
|
||||
// determine distance along the x-axis
|
||||
float xdist = MathUtil.distance(metrics.tilehwid, 0, crossx, crossy);
|
||||
fpos.x = (int)(xdist / metrics.finelen);
|
||||
|
||||
// determine distance along the y-axis
|
||||
float ydist = MathUtil.distance(x, y, crossx, crossy);
|
||||
fpos.y = (int)(ydist / metrics.finelen);
|
||||
|
||||
// Log.info("Pixel to fine " + StringUtil.coordsToString(x, y) +
|
||||
// " -> " + StringUtil.toString(fpos) + ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given screen-based pixel coordinates to full
|
||||
* scene-based coordinates that include both the tile coordinates
|
||||
* and the fine coordinates in each dimension. Converted
|
||||
* coordinates are placed in the given point object.
|
||||
*
|
||||
* @param sx the screen x-position pixel coordinate.
|
||||
* @param sy the screen y-position pixel coordinate.
|
||||
* @param fpos the point object to place coordinates in.
|
||||
*
|
||||
* @return the point passed in to receive the coordinates.
|
||||
*/
|
||||
public static Point screenToFull (
|
||||
MisoSceneMetrics metrics, int sx, int sy, Point fpos)
|
||||
{
|
||||
// get the tile coordinates
|
||||
Point tpos = new Point();
|
||||
screenToTile(metrics, sx, sy, tpos);
|
||||
|
||||
// get the screen coordinates for the containing tile
|
||||
Point spos = tileToScreen(metrics, tpos.x, tpos.y, new Point());
|
||||
|
||||
// Log.info("Screen to full " +
|
||||
// "[screen=" + StringUtil.coordsToString(sx, sy) +
|
||||
// ", tpos=" + StringUtil.toString(tpos) +
|
||||
// ", spos=" + StringUtil.toString(spos) +
|
||||
// ", fpix=" + StringUtil.coordsToString(
|
||||
// sx-spos.x, sy-spos.y) + "].");
|
||||
|
||||
// get the fine coordinates within the containing tile
|
||||
pixelToFine(metrics, sx - spos.x, sy - spos.y, fpos);
|
||||
|
||||
// toss in the tile coordinates for good measure
|
||||
fpos.x += (tpos.x * FULL_TILE_FACTOR);
|
||||
fpos.y += (tpos.y * FULL_TILE_FACTOR);
|
||||
|
||||
return fpos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given full coordinates to screen-based pixel
|
||||
* coordinates. Converted coordinates are placed in the given
|
||||
* point object.
|
||||
*
|
||||
* @param x the x-position full coordinate.
|
||||
* @param y the y-position full coordinate.
|
||||
* @param spos the point object to place coordinates in.
|
||||
*
|
||||
* @return the point passed in to receive the coordinates.
|
||||
*/
|
||||
public static Point fullToScreen (
|
||||
MisoSceneMetrics metrics, int x, int y, Point spos)
|
||||
{
|
||||
// get the tile screen position
|
||||
int tx = fullToTile(x), ty = fullToTile(y);
|
||||
Point tspos = tileToScreen(metrics, tx, ty, new Point());
|
||||
|
||||
// get the pixel position of the fine coords within the tile
|
||||
Point ppos = new Point();
|
||||
int fx = x - (tx * FULL_TILE_FACTOR), fy = y - (ty * FULL_TILE_FACTOR);
|
||||
fineToPixel(metrics, fx, fy, ppos);
|
||||
|
||||
// final position is tile position offset by fine position
|
||||
spos.x = tspos.x + ppos.x;
|
||||
spos.y = tspos.y + ppos.y;
|
||||
|
||||
return spos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given fine coordinate to a full coordinate (a tile
|
||||
* coordinate plus a fine coordinate remainder). The fine coordinate
|
||||
* is assumed to be relative to tile <code>(0, 0)</code>.
|
||||
*/
|
||||
public static int fineToFull (MisoSceneMetrics metrics, int fine)
|
||||
{
|
||||
return toFull(fine / metrics.finegran, fine % metrics.finegran);
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes the supplied tile coordinate and fine coordinate offset
|
||||
* into a full coordinate.
|
||||
*/
|
||||
public static int toFull (int tile, int fine)
|
||||
{
|
||||
return tile * FULL_TILE_FACTOR + fine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a polygon framing the specified tile.
|
||||
*
|
||||
* @param x the tile x-position coordinate.
|
||||
* @param y the tile y-position coordinate.
|
||||
*/
|
||||
public static Polygon getTilePolygon (
|
||||
MisoSceneMetrics metrics, int x, int y)
|
||||
{
|
||||
return getFootprintPolygon(metrics, x, y, 1, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a screen-coordinates polygon framing the two specified
|
||||
* tile-coordinate points.
|
||||
*/
|
||||
public static Polygon getMultiTilePolygon (MisoSceneMetrics metrics,
|
||||
Point sp1, Point sp2)
|
||||
{
|
||||
int x = Math.min(sp1.x, sp2.x), y = Math.min(sp1.y, sp2.y);
|
||||
int width = Math.abs(sp1.x-sp2.x)+1, height = Math.abs(sp1.y-sp2.y)+1;
|
||||
return getFootprintPolygon(metrics, x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a polygon framing the specified scene footprint.
|
||||
*
|
||||
* @param x the x tile coordinate of the "upper-left" of the footprint.
|
||||
* @param y the y tile coordinate of the "upper-left" of the footprint.
|
||||
* @param width the width in tiles of the footprint.
|
||||
* @param height the height in tiles of the footprint.
|
||||
*/
|
||||
public static Polygon getFootprintPolygon (
|
||||
MisoSceneMetrics metrics, int x, int y, int width, int height)
|
||||
{
|
||||
SmartPolygon footprint = new SmartPolygon();
|
||||
Point tpos = MisoUtil.tileToScreen(metrics, x, y, new Point());
|
||||
|
||||
// start with top-center point
|
||||
int rx = tpos.x + metrics.tilehwid, ry = tpos.y;
|
||||
footprint.addPoint(rx, ry);
|
||||
// right point
|
||||
rx += width * metrics.tilehwid;
|
||||
ry += width * metrics.tilehhei;
|
||||
footprint.addPoint(rx, ry);
|
||||
// bottom-center point
|
||||
rx -= height * metrics.tilehwid;
|
||||
ry += height * metrics.tilehhei;
|
||||
footprint.addPoint(rx, ry);
|
||||
// left point
|
||||
rx -= width * metrics.tilehwid;
|
||||
ry -= width * metrics.tilehhei;
|
||||
footprint.addPoint(rx, ry);
|
||||
// end with top-center point
|
||||
rx += height * metrics.tilehwid;
|
||||
ry -= height * metrics.tilehhei;
|
||||
footprint.addPoint(rx, ry);
|
||||
|
||||
return footprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the supplied fine coordinates to the supplied tile coordinates
|
||||
* to compute full coordinates.
|
||||
*
|
||||
* @return the point object supplied as <code>full</code>.
|
||||
*/
|
||||
public static Point tilePlusFineToFull (MisoSceneMetrics metrics,
|
||||
int tileX, int tileY,
|
||||
int fineX, int fineY,
|
||||
Point full)
|
||||
{
|
||||
int dtx = fineX / metrics.finegran;
|
||||
int dty = fineY / metrics.finegran;
|
||||
int fx = fineX - dtx * metrics.finegran;
|
||||
if (fx < 0) {
|
||||
dtx--;
|
||||
fx += metrics.finegran;
|
||||
}
|
||||
int fy = fineY - dty * metrics.finegran;
|
||||
if (fy < 0) {
|
||||
dty--;
|
||||
fy += metrics.finegran;
|
||||
}
|
||||
|
||||
full.x = toFull(tileX + dtx, fx);
|
||||
full.y = toFull(tileY + dty, fy);
|
||||
return full;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns x and y scene coordinates into an integer key.
|
||||
*
|
||||
* @return the hash key, given x and y.
|
||||
*/
|
||||
public static final int coordsToKey (int x, int y)
|
||||
{
|
||||
return ((y << 16) & (0xFFFF0000)) | (x & 0xFFFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the x coordinate from an integer hash key.
|
||||
*
|
||||
* @return the x coordinate.
|
||||
*/
|
||||
public static final int xCoordFromKey (int key)
|
||||
{
|
||||
return (key & 0xFFFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the y coordinate from an integer hash key.
|
||||
*
|
||||
* @return the y coordinate from the hash key.
|
||||
*/
|
||||
public static final int yCoordFromKey (int key)
|
||||
{
|
||||
return ((key >> 16) & 0xFFFF);
|
||||
}
|
||||
|
||||
/** Multiplication factor to embed tile coords in full coords. */
|
||||
protected static final int FULL_TILE_FACTOR = 100;
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
//
|
||||
// $Id: ObjectSet.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// 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 java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.util.ListUtil;
|
||||
|
||||
import com.threerings.miso.Log;
|
||||
import com.threerings.miso.data.ObjectInfo;
|
||||
|
||||
/**
|
||||
* Used to store an (arbitrarily) ordered, low-impact iteratable (doesn't
|
||||
* require object creation), set of {@link ObjectInfo} instances.
|
||||
*/
|
||||
public class ObjectSet
|
||||
{
|
||||
/**
|
||||
* Inserts the supplied object into the set.
|
||||
*
|
||||
* @return true if it was inserted, false if the object was already in
|
||||
* the set.
|
||||
*/
|
||||
public boolean insert (ObjectInfo info)
|
||||
{
|
||||
// bail if it's already in the set
|
||||
int ipos = 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] + "].");
|
||||
Thread.dumpStack();
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise insert it
|
||||
ipos = -(ipos+1);
|
||||
_objs = ListUtil.insert(_objs, ipos, info);
|
||||
_size++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified object is in the set, false if it is
|
||||
* not.
|
||||
*/
|
||||
public boolean contains (ObjectInfo info)
|
||||
{
|
||||
return (indexOf(info) >= 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of objects in this set.
|
||||
*/
|
||||
public int size ()
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object with the specified index. The index must & be
|
||||
* between <code>0</code> and {@link #size}<code>-1</code>.
|
||||
*/
|
||||
public ObjectInfo get (int index)
|
||||
{
|
||||
return (ObjectInfo)_objs[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the object at the specified index.
|
||||
*/
|
||||
public void remove (int index)
|
||||
{
|
||||
ListUtil.remove(_objs, index);
|
||||
_size--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified object from the set.
|
||||
*
|
||||
* @return true if it was removed, false if it was not in the set.
|
||||
*/
|
||||
public boolean remove (ObjectInfo info)
|
||||
{
|
||||
int opos = indexOf(info);
|
||||
if (opos >= 0) {
|
||||
remove(opos);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out the contents of this set.
|
||||
*/
|
||||
public void clear ()
|
||||
{
|
||||
_size = 0;
|
||||
Arrays.fill(_objs, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the contents of this object set to an array.
|
||||
*/
|
||||
public ObjectInfo[] toArray ()
|
||||
{
|
||||
ObjectInfo[] info = new ObjectInfo[_size];
|
||||
System.arraycopy(_objs, 0, info, 0, _size);
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this instance.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
StringBuilder buf = new StringBuilder("[");
|
||||
for (int ii = 0; ii < _size; ii++) {
|
||||
if (ii > 0) {
|
||||
buf.append(", ");
|
||||
}
|
||||
buf.append(_objs[ii]);
|
||||
}
|
||||
return buf.append("]").toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the object or it's insertion index if it is
|
||||
* not in the set.
|
||||
*/
|
||||
protected final int indexOf (ObjectInfo info)
|
||||
{
|
||||
return ArrayUtil.binarySearch(_objs, 0, _size, info, INFO_COMP);
|
||||
}
|
||||
|
||||
/** Our sorted array of objects. */
|
||||
protected Object[] _objs = new Object[DEFAULT_SIZE];
|
||||
|
||||
/** The number of objects in the set. */
|
||||
protected int _size;
|
||||
|
||||
/** 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 static final Comparator INFO_COMP = new Comparator() {
|
||||
public int compare (Object o1, Object o2) {
|
||||
ObjectInfo do1 = (ObjectInfo)o1;
|
||||
ObjectInfo do2 = (ObjectInfo)o2;
|
||||
if (do1.tileId == do2.tileId) {
|
||||
return ((do1.x << 16) + do1.y) - ((do2.x << 16) + do2.y);
|
||||
} else {
|
||||
return do1.tileId - do2.tileId;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** We start big because we know these will in general contain at
|
||||
* least in the tens of objects. */
|
||||
protected static final int DEFAULT_SIZE = 16;
|
||||
}
|
||||
Reference in New Issue
Block a user