Step one in the fun and exciting extraction of our Miso + Whirled + Cast

juicy goodness into something reusable by SOY.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3436 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2005-03-29 20:12:34 +00:00
parent e47f950695
commit a62f9621b2
13 changed files with 1849 additions and 0 deletions
+3
View File
@@ -192,6 +192,9 @@
<jar destfile="${deploy.dir}/${app.name}-cast.jar"> <jar destfile="${deploy.dir}/${app.name}-cast.jar">
<fileset dir="${classes.dir}" includes="com/threerings/cast/**"/> <fileset dir="${classes.dir}" includes="com/threerings/cast/**"/>
</jar> </jar>
<jar destfile="${deploy.dir}/${app.name}-stage.jar">
<fileset dir="${classes.dir}" includes="com/threerings/stage/**"/>
</jar>
</target> </target>
<!-- generate a class hierarchy diagram --> <!-- generate a class hierarchy diagram -->
+38
View File
@@ -0,0 +1,38 @@
//
// $Id: Log.java 49 2001-08-09 00:32:53Z mdb $
package com.threerings.stage;
/**
* A placeholder class that contains a reference to the log object used by
* this package.
*/
public class Log
{
public static com.samskivert.util.Log log =
new com.samskivert.util.Log("stage");
/** 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,130 @@
//
// $Id: SceneColorizer.java 17027 2004-09-10 00:10:46Z ray $
package com.threerings.stage.client;
import java.util.HashMap;
import java.util.Iterator;
import com.threerings.media.image.ColorPository;
import com.threerings.media.image.Colorization;
import com.threerings.media.tile.TileSet;
import com.threerings.miso.data.ObjectInfo;
import com.threerings.stage.Log;
import com.threerings.stage.data.StageScene;
/**
* Handles colorization of object tiles in a scene.
*/
public class SceneColorizer implements TileSet.Colorizer
{
/**
* Creates a scene colorizer for the supplied scene.
*/
public SceneColorizer (ColorPository cpos, StageScene scene)
{
_cpos = cpos;
_scene = scene;
// enumerate the color ids for all possible colorization classes
for (Iterator iter = _cpos.enumerateClasses(); iter.hasNext(); ) {
String cname = ((ColorPository.ClassRecord)iter.next()).name;
_cids.put(cname, _cpos.enumerateColorIds(cname));
}
}
/**
* Set an auxiliary colorizer that overrides our colorizations.
*/
public void setAuxiliary (TileSet.Colorizer aux)
{
_aux = aux;
}
/**
* Obtains a colorizer for the supplied scene object.
*/
public TileSet.Colorizer getColorizer (final ObjectInfo oinfo)
{
// if the object has no custom colorizations, return the default
// colorizer
if (oinfo.zations == 0) {
return this;
}
// otherwise create a custom colorizer that returns this object's
// custom colorization assignments
return new TileSet.Colorizer() {
public Colorization getColorization (int index, String zation) {
int colorId = 0;
switch (index) {
case 0: colorId = oinfo.getPrimaryZation(); break;
case 1: colorId = oinfo.getSecondaryZation(); break;
}
if (colorId == 0) {
return SceneColorizer.this.getColorization(index, zation);
} else {
return _cpos.getColorization(zation, colorId);
}
}
};
}
// documentation inherited from interface TileSet.Colorizer
public Colorization getColorization (int index, String zation)
{
// This method is called when an object in the scene has no colorization
// of its own defined for a particular color class.
if (_aux != null) {
Colorization c = _aux.getColorization(index, zation);
if (c != null) {
return c;
}
}
return _cpos.getColorization(zation, getColorId(zation));
}
/**
* Get the colorId to use for the specified colorization.
*/
public int getColorId (String zation)
{
// 1. We see if the scene contains a default color we should use.
ColorPository.ClassRecord rec = _cpos.getClassRecord(zation);
int colorId = _scene.getDefaultColor(rec.classId);
if (colorId == -1) {
// 2. If the scene does not contain a color, see if a default
// is defined for that color class.
ColorPository.ColorRecord def = rec.getDefault();
if (def != null) {
return def.colorId;
}
// 3. If there are no defaults whatsoever, just hash on the sceneId.
int[] cids = (int[])_cids.get(zation);
if (cids == null) {
Log.warning("Zoiks, have no colorizations for '" +
zation + "'.");
return -1;
} else {
colorId = cids[_scene.getZoneId() % cids.length];
}
}
return colorId;
}
/** An auxiliary colorizer which may temporarily return
* non-standard colorizations. */
protected TileSet.Colorizer _aux;
/** The entity from which we obtain colorization info. */
protected ColorPository _cpos;
/** The scene for which we're providing zations. */
protected StageScene _scene;
/** Contains our colorization class information. */
protected HashMap _cids = new HashMap();
}
@@ -0,0 +1,33 @@
//
// $Id: WorldSceneController.java 9625 2003-06-11 04:17:18Z mdb $
package com.threerings.stage.client;
import com.threerings.crowd.client.PlaceView;
import com.threerings.whirled.data.SceneUpdate;
import com.threerings.whirled.spot.client.SpotSceneController;
import com.threerings.stage.util.StageContext;
/**
* Extends the {@link SpotSceneController} with functionality specific to
* displaying Stage scenes.
*/
public class StageSceneController extends SpotSceneController
{
// documentation inherited
protected PlaceView createPlaceView ()
{
return new StageScenePanel((StageContext)_ctx, this);
}
// documentation inherited
protected void sceneUpdated (SceneUpdate update)
{
super.sceneUpdated(update);
// let the scene panel know to rethink everything
((StageScenePanel)_view).sceneUpdated(update);
}
}
@@ -0,0 +1,659 @@
//
// $Id: WorldScenePanel.java 18366 2004-12-15 22:56:58Z ray $
package com.threerings.stage.client;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import com.samskivert.swing.Controller;
import com.samskivert.swing.ControllerProvider;
import com.samskivert.swing.util.SwingUtil;
import com.samskivert.util.RuntimeAdjust;
import com.samskivert.util.Tuple;
import com.threerings.util.StreamableArrayList;
import com.threerings.media.tile.ObjectTile;
import com.threerings.media.tile.TileSet;
import com.threerings.media.tile.UniformTileSet;
import com.threerings.miso.client.MisoScenePanel;
import com.threerings.miso.client.SceneObject;
import com.threerings.miso.client.SceneObjectTip;
import com.threerings.miso.data.ObjectInfo;
import com.threerings.miso.util.MisoUtil;
import com.threerings.crowd.client.PlaceView;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.whirled.data.SceneUpdate;
import com.threerings.whirled.spot.data.Cluster;
import com.threerings.whirled.spot.data.Location;
import com.threerings.whirled.spot.data.Portal;
import com.threerings.stage.Log;
import com.threerings.stage.data.StageMisoSceneModel;
import com.threerings.stage.data.StageScene;
import com.threerings.stage.data.StageSceneModel;
import com.threerings.stage.util.StageContext;
import com.threerings.stage.util.StageSceneUtil;
/**
* Extends the basic Miso scene panel with Stage fun stuff like portals,
* clusters and locations.
*/
public class StageScenePanel extends MisoScenePanel
implements ControllerProvider, KeyListener, PlaceView
{
/** An action command generated when the user clicks on a location
* within the scene. */
public static final String LOCATION_CLICKED = "LocationClicked";
/** An action command generated when something besides the user wants
* us to move to a location. */
public static final String LOCATION_REQUESTED = "LocationRequested";
/** An action command generated when a cluster is clicked. */
public static final String CLUSTER_CLICKED = "ClusterClicked";
/** Show flag that indicates we should show all clusters. */
public static final int SHOW_CLUSTERS = (1 << 1);
/** Show flag that indicates we should render known land plots
* (expensive, don't turn this on willy nilly). */
public static final int SHOW_PLOTS = (1 << 2);
/**
* Constructs a stage scene view panel.
*/
public StageScenePanel (StageContext ctx, StageSceneController ctrl)
{
super(ctx, StageSceneUtil.getMetrics());
// keep these around for later
_ctx = ctx;
_ctrl = ctrl;
_ctrl.setControlledPanel(this);
// no layout manager
setLayout(null);
}
/**
* Get the tileset colorizer in use in this scene.
*/
public SceneColorizer getColorizer ()
{
return _rizer;
}
// documentation inherited
protected TileSet.Colorizer getColorizer (ObjectInfo oinfo)
{
return _rizer.getColorizer(oinfo);
}
/**
* Returns the scene being displayed by this panel. Do not modify it.
*/
public StageScene getScene ()
{
return _scene;
}
/**
* Sets the scene managed by the panel.
*/
public void setScene (StageScene scene)
{
_scene = scene;
if (_scene != null) {
recomputePortals();
setSceneModel(StageMisoSceneModel.getSceneModel(
scene.getSceneModel()));
_rizer = new SceneColorizer(_ctx.getColorPository(), scene);
} else {
Log.warning("Zoiks! We can't display a null scene!");
// TODO: display something to the user letting them know that
// we're so hosed that we don't even know what time it is
}
}
/**
* Called when we have received a scene update from the server.
*/
public void sceneUpdated (SceneUpdate update)
{
// recompute the portals as those may well have changed
recomputePortals();
// we go aheand and completely replace our scene model which will
// reload the whole good goddamned business; it is a little
// shocking to the user, but it's guaranteed to work
refreshScene();
}
/**
* Computes a set of display objects for the portals in this scene.
*/
protected void recomputePortals ()
{
// create scene objects for our portals
UniformTileSet ots = loadPortalTileSet();
_portobjs.clear();
for (Iterator iter = _scene.getPortals(); iter.hasNext(); ) {
Portal portal = (Portal) iter.next();
Point p = getScreenCoords(portal.x, portal.y);
int tx = MisoUtil.fullToTile(portal.x);
int ty = MisoUtil.fullToTile(portal.y);
Point ts = MisoUtil.tileToScreen(_metrics, tx, ty, new Point());
// Log.info("Added portal " + portal +
// " [screen=" + StringUtil.toString(p) +
// ", tile=" + StringUtil.coordsToString(tx, ty) +
// ", tscreen=" + StringUtil.toString(ts) + "].");
ObjectInfo info = new ObjectInfo(0, tx, ty);
info.action = "portal:" + portal.portalId;
// TODO: cache me
ObjectTile tile = new PortalObjectTile(
ts.x + _metrics.tilehwid - p.x + (PORTAL_ICON_WIDTH / 2),
ts.y + _metrics.tilehei - p.y + (PORTAL_ICON_HEIGHT / 2));
tile.setImage(ots.getTileMirage(portal.orient));
_portobjs.add(new SceneObject(this, info, tile) {
public boolean setHovered (boolean hovered) {
((PortalObjectTile)this.tile).hovered = hovered;
return isResponsive();
}
});
}
}
// documentation inherited
protected void recomputeVisible ()
{
super.recomputeVisible();
// add our visible portal objects to the list of visible objects
for (int ii = 0, ll = _portobjs.size(); ii < ll; ii++) {
SceneObject pobj = (SceneObject)_portobjs.get(ii);
if (pobj.bounds != null && _vbounds.intersects(pobj.bounds)) {
_vizobjs.add(pobj);
}
}
}
// documentation inherited from interface ControllerProvider
public Controller getController ()
{
return _ctrl;
}
// documentation inherited from interface KeyListener
public void keyPressed (KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_ALT) {
// display all tooltips
setShowFlags(SHOW_TIPS, true);
}
}
// documentation inherited from interface KeyListener
public void keyReleased (KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_ALT) {
// stop displaying all tooltips
setShowFlags(SHOW_TIPS, defaultShowTips());
}
}
// documentation inherited from interface PlaceView
public void willEnterPlace (PlaceObject plobj)
{
}
// documentation inherited from interface PlaceView
public void didLeavePlace (PlaceObject plobj)
{
}
/**
* Returns true if we should always show the object tooltips by
* default, false if they should only be shown while the 'Alt' key is
* depressed.
*/
protected boolean defaultShowTips ()
{
return false;
}
// documentation inherited
public void keyTyped (KeyEvent e)
{
// nothing
}
// documentation inherited
protected boolean handleMousePressed (Object hobject, MouseEvent event)
{
// let our parent have a crack at the old mouse press
if (super.handleMousePressed(hobject, event)) {
return true;
}
// if the hover object is a cluster, we clicked it!
if (event.getButton() == MouseEvent.BUTTON1) {
if (hobject instanceof Cluster) {
int mx = event.getX(), my = event.getY();
Object actarg = new Tuple(hobject, new Point(mx, my));
Controller.postAction(this, CLUSTER_CLICKED, actarg);
} else {
// post an action indicating that we've clicked on a location
Point lc = MisoUtil.screenToFull(
_metrics, event.getX(), event.getY(), new Point());
Controller.postAction(this, LOCATION_CLICKED,
new Location(lc.x, lc.y, (byte)0));
}
return true;
}
return false;
}
/**
* Called when our show flags have changed.
*/
protected void showFlagsDidChange (int oldflags)
{
super.showFlagsDidChange(oldflags);
if ((oldflags & SHOW_CLUSTERS) != (_showFlags & SHOW_CLUSTERS)) {
// dirty every cluster rectangle
Iterator iter = _clusters.values().iterator();
while (iter.hasNext()) {
dirtyCluster((Shape)iter.next());
}
}
}
/**
* Called when a real cluster is created or updated in the scene.
*/
protected void clusterUpdated (Cluster cluster)
{
// compute a screen rectangle that contains all possible "spots"
// in this cluster
ArrayList spots = StageSceneUtil.getClusterLocs(cluster);
Rectangle cbounds = null;
for (int ii = 0, ll = spots.size(); ii < ll; ii++) {
Location loc = (Location)spots.get(ii);
Point sp = getScreenCoords(loc.x, loc.y);
if (cbounds == null) {
cbounds = new Rectangle(sp.x, sp.y, 0, 0);
} else {
cbounds.add(sp.x, sp.y);
}
}
if (cbounds == null) {
// if we found no one actually in this cluster, nix it
removeCluster(cluster.clusterOid);
} else {
// otherwise have the view update the cluster
updateCluster(cluster, cbounds);
}
}
/**
* Adds or updates the specified cluster in the view. Metrics will be
* created that allow the cluster to be rendered and hovered over
* (which would make it the active cluster as indicated by {@link
* #getActiveCluster}).
*
* @param cluster the cluster record to be added.
* @param bounds the screen coordinates that bound the occupants of
* the cluster.
*/
public void updateCluster (Cluster cluster, Rectangle bounds)
{
// dirty any old bounds
dirtyCluster(cluster);
// compute the screen coordinate bounds of this cluster
Shape shape = new Ellipse2D.Float(
bounds.x, bounds.y, bounds.width, bounds.height);
_clusters.put(cluster, shape);
// if the mouse is inside these bounds, we highlight this cluster
Shape mshape = new Ellipse2D.Float(
bounds.x-CLUSTER_SLOP, bounds.y-CLUSTER_SLOP,
bounds.width+2*CLUSTER_SLOP, bounds.height+2*CLUSTER_SLOP);
_clusterWells.put(cluster, mshape);
// dirty our new bounds
dirtyCluster(shape);
}
/**
* Removes the specified cluster from the view.
*
* @return true if such a cluster existed and was removed.
*/
public boolean removeCluster (int clusterOid)
{
Cluster key = new Cluster();
key.clusterOid = clusterOid;
_clusterWells.remove(key);
Shape shape = (Shape)_clusters.remove(key);
if (shape == null) {
return false;
}
dirtyCluster(shape);
// clear out the hover object if this cluster was it
if (_hobject instanceof Cluster &&
((Cluster)_hobject).clusterOid == clusterOid) {
_hobject = null;
}
return true;
}
/**
* A place for subclasses to react to the hover object changing.
* One of the supplied arguments may be null.
*/
protected void hoverObjectChanged (Object oldHover, Object newHover)
{
super.hoverObjectChanged(oldHover, newHover);
if (oldHover instanceof Cluster) {
dirtyCluster((Cluster)oldHover);
}
if (newHover instanceof Cluster) {
dirtyCluster((Cluster)newHover);
}
}
/**
* Gives derived classes a chance to compute a hover object that takes
* precedence over sprites and actionable objects. If this method
* returns non-null, no sprite or object hover calculations will be
* performed and the object returned will become the new hover object.
*/
protected Object computeOverHover (int mx, int my)
{
return null;
}
/**
* Gives derived classes a chance to compute a hover object that is
* used if the mouse is not hovering over a sprite or actionable
* object. If this method is called, it means that there are no
* sprites or objects under the mouse. Thus if it returns non-null,
* the object returned will become the new hover object.
*/
protected Object computeUnderHover (int mx, int my)
{
if (!isResponsive()) {
return null;
}
// if the current hover object is a cluster, see if we're still in
// that cluster
if (_hobject instanceof Cluster) {
Cluster cluster = (Cluster)_hobject;
if (containsPoint(cluster, mx, my)) {
return cluster;
}
}
// otherwise, check to see if the mouse is in some new cluster
Iterator iter = _clusters.keySet().iterator();
while (iter.hasNext()) {
Cluster cclust = (Cluster)iter.next();
if (containsPoint(cclust, mx, my)) {
return cclust;
}
}
return null;
}
/**
* Returns true if the specified cluster contains the supplied screen
* coordinate.
*/
protected boolean containsPoint (Cluster cluster, int mx, int my)
{
Shape shape = (Shape)_clusterWells.get(cluster);
return (shape == null) ? false : shape.contains(mx, my);
}
/**
* Dirties the supplied cluster.
*/
protected void dirtyCluster (Cluster cluster)
{
if (cluster != null) {
dirtyCluster((Shape)_clusters.get(cluster));
}
}
/**
* Dirties the supplied cluster rectangle.
*/
protected void dirtyCluster (Shape shape)
{
if (shape != null) {
Rectangle r = shape.getBounds();
_remgr.invalidateRegion(
r.x - (CLUSTER_PAD / 2),
r.y - (CLUSTER_PAD / 2),
r.width + (CLUSTER_PAD * 3 / 2),
r.height + (CLUSTER_PAD * 3 / 2));
}
}
/**
* Returns the portal at the specified full coordinates or null if no
* portal exists at said coordinates.
*/
public Portal getPortal (int fullX, int fullY)
{
Iterator iter = _scene.getPortals();
while (iter.hasNext()) {
Portal portal = (Portal)iter.next();
if (portal.x == fullX && portal.y == fullY) {
return portal;
}
}
return null;
}
// documentation inherited
protected void paintBaseDecorations (Graphics2D gfx, Rectangle clip)
{
super.paintBaseDecorations(gfx, clip);
paintClusters(gfx, clip);
}
/**
* Paints any visible clusters.
*/
protected void paintClusters (Graphics2D gfx, Rectangle clip)
{
// remember how daddy's things were arranged
Object oalias = SwingUtil.activateAntiAliasing(gfx);
Composite ocomp = gfx.getComposite();
Stroke ostroke = gfx.getStroke();
// get ready to draw clusters
gfx.setStroke(CLUSTER_STROKE);
gfx.setColor(CLUSTER_COLOR);
if (checkShowFlag(SHOW_CLUSTERS)
/* || // _alwaysShowClusters.getValue() */) {
// draw all clusters
Iterator iter = _clusters.keySet().iterator();
while (iter.hasNext()) {
drawCluster(gfx, clip, (Cluster)iter.next());
}
} else if (_hobject instanceof Cluster) {
// or just draw the active cluster
drawCluster(gfx, clip, (Cluster)_hobject);
}
// put back daddy's things
gfx.setComposite(ocomp);
gfx.setStroke(ostroke);
SwingUtil.restoreAntiAliasing(gfx, oalias);
}
/**
* Draw the cluster specified by the rectangle.
*/
protected void drawCluster (Graphics2D gfx, Rectangle clip, Cluster cluster)
{
Shape shape = (Shape)_clusters.get(cluster);
if ((shape != null) && shape.intersects(clip)) {
if (_hobject == cluster) {
gfx.setComposite(HIGHLIGHT_ALPHA);
} else {
gfx.setComposite(SHOWN_ALPHA);
}
gfx.draw(shape);
}
}
/**
* Returns true if the specified location is associated with a portal.
*/
protected boolean isPortal (Location loc)
{
Iterator iter = _scene.getPortals();
while (iter.hasNext()) {
Portal p = (Portal)iter.next();
if (p.x == loc.x && p.y == loc.y) {
return true;
}
}
return false;
}
/**
* Loads up the tileset used to render the portal arrows.
*/
protected UniformTileSet loadPortalTileSet ()
{
// return YoUI.client.loadTileSet(
// "media/yohoho/icons/portal_arrows.png",
// PORTAL_ICON_WIDTH, PORTAL_ICON_HEIGHT);
return null;
}
/** Used to render portals as objects in a scene. */
protected class PortalObjectTile extends ObjectTile
{
public boolean hovered = false;
public PortalObjectTile (int ox, int oy)
{
setOrigin(ox, oy);
}
public void paint (Graphics2D gfx, int x, int y)
{
Composite ocomp = gfx.getComposite();
if (!isResponsive() || !hovered) {
gfx.setComposite(INACTIVE_PORTAL_ALPHA);
}
super.paint(gfx, x, y);
gfx.setComposite(ocomp);
}
}
/** A reference to our client context. */
protected StageContext _ctx;
/** The controller with which we work in tandem. */
protected StageSceneController _ctrl;
/** Our currently displayed scene. */
protected StageScene _scene;
/** Contains scene objects for our portals. */
protected ArrayList _portobjs = new ArrayList();
/** Shapes describing the clusters, indexed by cluster. */
protected HashMap _clusters = new HashMap();
/** Shapes describing the clusters, indexed by cluster. */
protected HashMap _clusterWells = new HashMap();
/** Handles scene object colorization. */
protected SceneColorizer _rizer;
// /** A debug hook that toggles always-on rendering of clusters. */
// protected static RuntimeAdjust.BooleanAdjust _alwaysShowClusters =
// new RuntimeAdjust.BooleanAdjust(
// "Causes all clusters to always be rendered.",
// "yohoho.miso.always_show_clusters",
// ClientPrefs.config, false);
/** The width of the portal icons. */
protected static final int PORTAL_ICON_WIDTH = 48;
/** The height of the portal icons. */
protected static final int PORTAL_ICON_HEIGHT = 48;
/** The distance within which the mouse must be from a location
* in order to highlight it. */
protected static final int MAX_LOCATION_DIST = 25;
/** The amount the stroke a cluster. */
protected static final int CLUSTER_PAD = 4;
/** The width with which to draw the cluster. */
protected static final Stroke CLUSTER_STROKE = new BasicStroke(CLUSTER_PAD);
/** The color used to render clusters. */
protected static final Color CLUSTER_COLOR = Color.ORANGE;
/** Alpha level used to hightlight locations or clusters. */
protected static final Composite HIGHLIGHT_ALPHA =
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
/** Alpha level used to render clusters when they're not selected. */
protected static final Composite SHOWN_ALPHA =
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.15f);
/** The alpha with which to render inactive portals. */
protected static final Composite INACTIVE_PORTAL_ALPHA = HIGHLIGHT_ALPHA;
/** The number of pixels outside a cluster when we assume the mouse is
* "over" that cluster. */
protected static final int CLUSTER_SLOP = 25;
}
@@ -0,0 +1,21 @@
//
// $Id: WorldSceneService.java 14426 2004-03-12 12:12:32Z mdb $
package com.threerings.stage.client;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
import com.threerings.miso.data.ObjectInfo;
/**
* Provides services relating to Stage scenes.
*/
public interface StageSceneService extends InvocationService
{
/**
* Requests to add the supplied object to the current scene.
*/
public void addObject (Client client, ObjectInfo info,
ConfirmListener listener);
}
@@ -0,0 +1,53 @@
//
// $Id: AddObjectUpdate.java 15953 2004-06-11 23:40:47Z ray $
package com.threerings.stage.data;
import com.threerings.miso.data.ObjectInfo;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.data.SceneUpdate;
/**
* A scene update that is broadcast when an object has been added to a
* scene.
*/
public class AddObjectUpdate extends SceneUpdate
{
/** The info on the object. */
public ObjectInfo info;
/** If non-null, a list of objects to remove from the scene. */
public ObjectInfo[] casualties;
/**
* Initializes this update with all necessary data.
*
* @param casualties optional, a list of objects to remove.
*/
public void init (int targetId, int targetVersion, ObjectInfo info,
ObjectInfo[] casualties)
{
init(targetId, targetVersion);
this.info = info;
this.casualties = casualties;
}
// documentation inherited
public void apply (SceneModel model)
{
super.apply(model);
StageMisoSceneModel mmodel = StageMisoSceneModel.getSceneModel(model);
// wipe out the objects that need to go
if (casualties != null) {
for (int ii = 0; ii < casualties.length; ii++) {
mmodel.removeObject(casualties[ii]);
}
}
// add the new object
mmodel.addObject(info);
}
}
@@ -0,0 +1,39 @@
//
// $Id: DefaultColorUpdate.java 16551 2004-07-27 20:53:28Z ray $
package com.threerings.stage.data;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.data.SceneUpdate;
/**
* Update to change the default colorization for objects in a scene which
* do not define their own colorization.
*/
public class DefaultColorUpdate extends SceneUpdate
{
/** The class id of the colorization we're changing. */
public int classId;
/** The color id to set as the new default, or -1 to remove the default. */
public int colorId;
/**
* Initializes this update.
*/
public void init (int targetId, int targetVersion, int classId, int colorId)
{
init(targetId, targetVersion);
this.classId = classId;
this.colorId = colorId;
}
// documentation inherited
public void apply (SceneModel model)
{
super.apply(model);
StageSceneModel smodel = (StageSceneModel)model;
smodel.setDefaultColor(classId, colorId);
}
}
@@ -0,0 +1,64 @@
//
// $Id: YoMisoSceneModel.java 9680 2003-06-13 02:16:00Z mdb $
package com.threerings.stage.data;
import com.threerings.miso.data.SparseMisoSceneModel;
import com.threerings.whirled.data.AuxModel;
import com.threerings.whirled.data.SceneModel;
/**
* Extends the {@link SparseMisoSceneModel} with the necessary interface
* to wire it up to the Whirled auxiliary model system.
*/
public class StageMisoSceneModel extends SparseMisoSceneModel
implements AuxModel
{
/** The width (in tiles) of a scene section. */
public static final int SECTION_WIDTH = 9;
/** The height (in tiles) of a scene section. */
public static final int SECTION_HEIGHT = 9;
/**
* Creates a completely uninitialized scene model.
*/
public StageMisoSceneModel ()
{
super(SECTION_WIDTH, SECTION_HEIGHT);
}
/**
* Locates and returns the {@link StageMisoSceneModel} among the
* auxiliary scene models associated with the supplied scene model.
* <code>null</code> is returned if no miso scene model could be
* found.
*/
public static StageMisoSceneModel getSceneModel (SceneModel model)
{
for (int ii = 0; ii < model.auxModels.length; ii++) {
if (model.auxModels[ii] instanceof StageMisoSceneModel) {
return (StageMisoSceneModel)model.auxModels[ii];
}
}
return null;
}
/**
* Returns the section key for the specified tile coordinate.
*/
public int getSectionKey (int x, int y)
{
return key(x, y);
}
/**
* Returns the section identified by the specified key, or null if no
* section exists for that key.
*/
public Section getSection (int key)
{
return (Section)_sections.get(key);
}
}
@@ -0,0 +1,235 @@
//
// $Id: YoScene.java 17013 2004-09-08 02:39:09Z ray $
package com.threerings.stage.data;
import java.util.ArrayList;
import java.util.Iterator;
import com.threerings.miso.data.ObjectInfo;
import com.threerings.miso.util.MisoUtil;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.whirled.data.Scene;
import com.threerings.whirled.data.SceneImpl;
import com.threerings.whirled.data.SceneUpdate;
import com.threerings.whirled.spot.data.Portal;
import com.threerings.whirled.spot.data.SpotScene;
import com.threerings.whirled.spot.data.SpotSceneImpl;
import com.threerings.whirled.spot.data.SpotSceneModel;
import com.threerings.stage.Log;
/**
* The implementation of the Stage scene interface.
*/
public class StageScene extends SceneImpl
implements Scene, SpotScene, Cloneable
{
/**
* Creates an instance that will obtain data from the supplied scene
* model and place config.
*/
public StageScene (StageSceneModel model, PlaceConfig config)
{
super(model, config);
_model = model;
_sdelegate = new SpotSceneImpl(SpotSceneModel.getSceneModel(_model));
readInterestingObjects();
}
protected void readInterestingObjects ()
{
_objects.clear();
StageMisoSceneModel mmodel = StageMisoSceneModel.getSceneModel(_model);
if (mmodel != null) {
mmodel.getInterestingObjects(_objects);
}
}
// documentation inherited
public void updateReceived (SceneUpdate update)
{
super.updateReceived(update);
// update our spot scene delegate
_sdelegate.updateReceived();
// re-read our interesting objects
readInterestingObjects();
}
/**
* Returns the scene type (e.g. "world", "port", "bank", etc.).
*/
public String getType ()
{
return _model.type;
}
/**
* Returns the zone id to which this scene belongs.
*/
public int getZoneId ()
{
return _model.zoneId;
}
/**
* Sets the type of this scene.
*/
public void setType (String type)
{
_model.type = type;
}
/**
* Get the default color id to use for the specified colorization class,
* or -1 if no default is set.
*/
public int getDefaultColor (int classId)
{
return _model.getDefaultColor(classId);
}
/**
* Set the default color to use for the specified colorization class id.
* Setting the colorId to -1 disables the default.
*/
public void setDefaultColor (int classId, int colorId)
{
_model.setDefaultColor(classId, colorId);
}
/**
* Adds a new object to this scene.
*/
public void addObject (ObjectInfo info)
{
_objects.add(info);
// add it to the underlying scene model
StageMisoSceneModel mmodel = StageMisoSceneModel.getSceneModel(_model);
if (mmodel != null) {
if (!mmodel.addObject(info)) {
Log.warning("Scene model rejected object add " +
"[scene=" + this + ", object=" + info + "].");
}
}
}
/**
* Removes an object from this scene.
*/
public boolean removeObject (ObjectInfo info)
{
boolean removed = _objects.remove(info);
// remove it from the underlying scene model
StageMisoSceneModel mmodel = StageMisoSceneModel.getSceneModel(_model);
if (mmodel != null) {
removed = mmodel.removeObject(info) || removed;
}
return removed;
}
/**
* Iterates over all of the interesting objects in this scene.
*/
public Iterator enumerateObjects ()
{
return _objects.iterator();
}
/**
* Applies the supplied scene update to this scene.
*/
public void applyUpdate (SceneUpdate update)
{
if (update instanceof AddObjectUpdate) {
AddObjectUpdate aou = (AddObjectUpdate) update;
// remove any occluded objects
if (aou.casualties != null) {
for (int ii = 0; ii < aou.casualties.length; ii++) {
removeObject(aou.casualties[ii]);
}
}
// add the object
addObject(aou.info);
} else if (update instanceof DefaultColorUpdate) {
DefaultColorUpdate dcu = (DefaultColorUpdate) update;
setDefaultColor(dcu.classId, dcu.colorId);
} else {
Log.warning("Unknown scene update applied to StageScene: " + update);
}
// increment our scene version
setVersion(getVersion() + 1);
}
// documentation inherited
public Object clone ()
throws CloneNotSupportedException
{
// create a new scene with a clone of our model
return new StageScene((StageSceneModel)_model.clone(), _config);
}
// documentation inherited from interface
public Portal getPortal (int portalId)
{
return _sdelegate.getPortal(portalId);
}
// documentation inherited from interface
public int getPortalCount ()
{
return _sdelegate.getPortalCount();
}
// documentation inherited from interface
public Iterator getPortals ()
{
return _sdelegate.getPortals();
}
// documentation inherited from interface
public Portal getDefaultEntrance ()
{
return _sdelegate.getDefaultEntrance();
}
// documentation inherited from interface
public void addPortal (Portal portal)
{
_sdelegate.addPortal(portal);
}
// documentation inherited from interface
public void removePortal (Portal portal)
{
_sdelegate.removePortal(portal);
}
// documentation inherited from interface
public void setDefaultEntrance (Portal portal)
{
_sdelegate.setDefaultEntrance(portal);
}
/** A reference to our scene model. */
protected StageSceneModel _model;
/** Our spot scene delegate. */
protected SpotSceneImpl _sdelegate;
/** A list of all interesting scene objects. */
protected ArrayList _objects = new ArrayList();
}
@@ -0,0 +1,85 @@
//
// $Id: YoSceneModel.java 17643 2004-10-28 22:58:30Z mdb $
package com.threerings.stage.data;
import com.threerings.util.StreamableArrayList;
import com.threerings.util.StreamableIntIntMap;
import com.threerings.whirled.data.SceneModel;
import com.threerings.whirled.spot.data.SpotSceneModel;
/**
* Extends the basic scene model with the notion of a scene type and
* incorporates the necessary auxiliary models used by the Stage system.
*/
public class StageSceneModel extends SceneModel
{
/** A scene type code. */
public static final String WORLD = "world";
/** This scene's type which is a string identifier used to later
* construct a specific controller to handle this scene. */
public String type;
/** The zone id to which this scene belongs. */
public int zoneId;
/** If non-null, contains default colorizations to use for objects
* that do not have colorizations defined. */
public StreamableIntIntMap defaultColors;
/**
* Get the default color to use for the specified colorization
* classId, or -1 if no default is set for that colorization.
*/
public int getDefaultColor (int classId)
{
if (defaultColors != null) {
return defaultColors.get(classId);
}
return -1;
}
/**
* Set the default colorId to use for a specified colorization
* classId, or -1 to clear the default for that colorization.
*/
public void setDefaultColor (int classId, int colorId)
{
if (colorId == -1) {
if (defaultColors != null) {
defaultColors.remove(classId);
if (defaultColors.size() == 0) {
defaultColors = null;
}
}
} else {
if (defaultColors == null) {
defaultColors = new StreamableIntIntMap();
}
defaultColors.put(classId, colorId);
}
}
/**
* Creates and returns a blank scene model.
*/
public static StageSceneModel blankStageSceneModel ()
{
StageSceneModel model = new StageSceneModel();
populateBlankStageSceneModel(model);
return model;
}
/**
* Populates a blank scene model with blank values.
*/
protected static void populateBlankStageSceneModel (StageSceneModel model)
{
populateBlankSceneModel(model);
model.addAuxModel(new SpotSceneModel());
model.addAuxModel(new StageMisoSceneModel());
}
}
@@ -0,0 +1,88 @@
//
// $Id: BasicYoContext.java 19661 2005-03-09 02:40:29Z andrzej $
package com.threerings.stage.util;
import com.threerings.resource.ResourceManager;
import com.threerings.util.KeyDispatcher;
import com.threerings.util.KeyboardManager;
import com.threerings.util.MessageManager;
import com.threerings.media.FrameManager;
import com.threerings.media.IconManager;
import com.threerings.media.image.ColorPository;
import com.threerings.media.image.ImageManager;
import com.threerings.media.sound.SoundManager;
import com.threerings.cast.CharacterManager;
import com.threerings.cast.ComponentRepository;
import com.threerings.miso.util.MisoContext;
/**
* A context that provides for the myriad requirements of the Stage
* system.
*/
public interface StageContext
extends MisoContext
{
/**
* Returns the frame manager driving our interface.
*/
public FrameManager getFrameManager ();
/**
* Returns the resource manager via which all client resources are
* loaded.
*/
public ResourceManager getResourceManager ();
/**
* Access to the image manager.
*/
public ImageManager getImageManager ();
/**
* Provides access to the key dispatcher.
*/
public KeyDispatcher getKeyDispatcher ();
/**
* Returns a reference to the message manager used by the client.
*/
public MessageManager getMessageManager ();
/**
* Returns a reference to the icon manager used by the client.
*/
public IconManager getIconManager ();
/**
* Returns a reference to the sound manager used by the client.
*/
public SoundManager getSoundManager();
/**
* Returns a reference to the keyboard manager.
*/
public KeyboardManager getKeyboardManager();
/**
* Returns the component repository in use by this client.
*/
public ComponentRepository getComponentRepository ();
/**
* Returns a reference to the colorization repository.
*/
public ColorPository getColorPository ();
/**
* Translates the specified message using the default bundle.
*/
public String xlate (String message);
/**
* Translates the specified message using the specified bundle.
*/
public String xlate (String bundle, String message);
}
@@ -0,0 +1,401 @@
//
// $Id: YoSceneUtil.java 19769 2005-03-17 07:38:31Z mdb $
package com.threerings.stage.util;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Comparator;
import com.samskivert.util.SortableArrayList;
import com.samskivert.util.StringUtil;
import com.threerings.util.DirectionCodes;
import com.threerings.util.DirectionUtil;
import com.threerings.media.tile.TileManager;
import com.threerings.media.tile.TileUtil;
import com.threerings.media.tile.TrimmedObjectTileSet;
import com.threerings.media.util.AStarPathUtil;
import com.threerings.media.util.MathUtil;
import com.threerings.miso.MisoConfig;
import com.threerings.miso.data.ObjectInfo;
import com.threerings.miso.tile.BaseTileSet;
import com.threerings.miso.util.MisoSceneMetrics;
import com.threerings.miso.util.MisoUtil;
import com.threerings.miso.util.ObjectSet;
import com.threerings.whirled.spot.data.Cluster;
import com.threerings.whirled.spot.data.Location;
import com.threerings.whirled.spot.data.SceneLocation;
import com.threerings.stage.Log;
import com.threerings.stage.data.StageMisoSceneModel;
import com.threerings.stage.data.StageSceneModel;
/**
* Provides scene related utility functions.
*/
public class StageSceneUtil
{
/**
* Returns the scene metrics we use to do our calculations.
*/
public static MisoSceneMetrics getMetrics ()
{
return _metrics;
}
/**
* Does the necessary jiggery pokery to figure out where the specified
* object's associated location is.
*/
public static Location locationForObject (
TileManager tilemgr, ObjectInfo info)
{
return locationForObject(tilemgr, info.tileId, info.x, info.y);
}
/**
* Does the necessary jiggery pokery to figure out where the specified
* object's associated location is.
*
* @param tilemgr a tile manager that can be used to look up the tile
* information.
* @param tileId the fully qualified tile id of the object tile.
* @param tx the object's x tile coordinate.
* @param ty the object's y tile coordinate.
*/
public static Location locationForObject (
TileManager tilemgr, int tileId, int tx, int ty)
{
try {
int tsid = TileUtil.getTileSetId(tileId);
int tidx = TileUtil.getTileIndex(tileId);
TrimmedObjectTileSet tset = (TrimmedObjectTileSet)
tilemgr.getTileSet(tsid);
if (tset == null || tset.getSpotOrient(tidx) < 0) {
return null;
}
Point opos = MisoUtil.tilePlusFineToFull(
_metrics, tx, ty, tset.getXSpot(tidx), tset.getYSpot(tidx),
new Point());
// Log.info("Computed location [set=" + tset.getName() +
// ", tidx=" + tidx + ", tx=" + tx + ", ty=" + ty +
// ", sx=" + tset.getXSpot(tidx) +
// ", sy=" + tset.getYSpot(tidx) +
// ", lx=" + opos.x + ", ly=" + opos.y +
// ", fg=" + _metrics.finegran + "].");
return new Location(opos.x, opos.y, (byte)tset.getSpotOrient(tidx));
} catch (Exception e) {
Log.warning("Unable to look up object tile for scene object " +
"[tileId=" + tileId + ", error=" + e + "].");
}
return null;
}
/**
* Converts full coordinates to Cartesian coordinates.
*/
public static void locationToCoords (int lx, int ly, Point coords)
{
int tx = MisoUtil.fullToTile(lx), fx = MisoUtil.fullToFine(lx);
int ty = MisoUtil.fullToTile(ly), fy = MisoUtil.fullToFine(ly);
coords.x = tx*_metrics.finegran+fx;
coords.y = ty*_metrics.finegran+fy;
}
/**
* Converts Cartesian coordinates back to full coordinates.
*/
public static void coordsToLocation (int cx, int cy, Point loc)
{
loc.x = MisoUtil.toFull(cx/_metrics.finegran, cx%_metrics.finegran);
loc.y = MisoUtil.toFull(cy/_metrics.finegran, cy%_metrics.finegran);
}
/**
* Returns the footprint, in absolute tile coordinates, for the
* specified object with origin as specified.
*/
public static Rectangle getObjectFootprint (
TileManager tilemgr, int tileId, int ox, int oy)
{
Rectangle foot = new Rectangle();
getObjectFootprint(tilemgr, tileId, ox, oy, foot);
return foot;
}
/**
* Fills in the footprint, in absolute tile coordinates, for the
* specified object with origin as specified.
*
* @return true if the object was successfully looked up and the
* footprint filled in, false if an error occurred trying to look up
* the associated object tile.
*/
public static boolean getObjectFootprint (
TileManager tilemgr, int tileId, int ox, int oy, Rectangle foot)
{
try {
int tsid = TileUtil.getTileSetId(tileId);
int tidx = TileUtil.getTileIndex(tileId);
TrimmedObjectTileSet tset = (TrimmedObjectTileSet)
tilemgr.getTileSet(tsid);
if (tset == null) {
return false;
}
int bwidth = tset.getBaseWidth(tidx);
int bheight = tset.getBaseHeight(tidx);
foot.setBounds(ox - bwidth + 1, oy - bheight + 1, bwidth, bheight);
return true;
} catch (Exception e) {
Log.warning("Unable to look up object tile for scene object " +
"[tileId=" + tileId + ", error=" + e + "].");
return false;
}
}
/**
* Looks up the base tile set for the specified fully qualified tile
* identifier and returns true if the associated tile is passable.
*/
public static boolean isPassable (TileManager tilemgr, int tileId)
{
// non-existent tiles are not passable
if (tileId <= 0) {
return false;
}
try {
int tsid = TileUtil.getTileSetId(tileId);
int tidx = TileUtil.getTileIndex(tileId);
BaseTileSet tset = (BaseTileSet)tilemgr.getTileSet(tsid);
return tset.getPassability()[tidx];
} catch (Exception e) {
Log.warning("Unable to look up base tile [tileId=" + tileId +
", error=" + e + "].");
return true;
}
}
/**
* Computes a list of the valid locations in this cluster.
*/
public static ArrayList getClusterLocs (Cluster cluster)
{
ArrayList list = new ArrayList();
// convert our tile coordinates into a cartesian coordinate system
// with units equal to one fine coordinate in size
int fx = cluster.x*_metrics.finegran+1,
fy = cluster.y*_metrics.finegran+1;
int fwid = cluster.width*_metrics.finegran-2,
fhei = cluster.height*_metrics.finegran-2;
int cx = fx + fwid/2, cy = fy + fhei/2;
// if it's a 1x1 cluster, return one location in the center of the
// cluster
if (cluster.width == 1) {
list.add(new SceneLocation(MisoUtil.toFull(cluster.x, 2),
MisoUtil.toFull(cluster.y, 2),
(byte)DirectionCodes.SOUTHWEST, 0));
return list;
}
double radius = (double)fwid/2;
int clidx = cluster.width-2;
if (clidx >= CLUSTER_METRICS.length/2 || clidx < 0) {
Log.warning("Requested locs from invalid cluster " + cluster + ".");
Thread.dumpStack();
return list;
}
for (double angle = CLUSTER_METRICS[clidx*2]; angle < Math.PI*2;
angle += CLUSTER_METRICS[clidx*2+1]) {
int sx = cx + (int)Math.round(Math.cos(angle) * radius);
int sy = cy + (int)Math.round(Math.sin(angle) * radius);
// obtain the orientation facing toward the center
int orient = 2*(int)(Math.round(angle/(Math.PI/4))%8);
orient = DirectionUtil.rotateCW(DirectionCodes.SOUTH, orient);
orient = DirectionUtil.getOpposite(orient);
// convert them back to full coordinates for the location
int tx = MathUtil.floorDiv(sx, _metrics.finegran);
sx = MisoUtil.toFull(tx, sx-(tx*_metrics.finegran));
int ty = MathUtil.floorDiv(sy, _metrics.finegran);
sy = MisoUtil.toFull(ty, sy-(ty*_metrics.finegran));
list.add(new SceneLocation(sx, sy, (byte)orient, 0));
}
return list;
}
// /**
// * Returns true if this user is available to be clustered with.
// */
// public static boolean isClusterable (YoOccupantInfo info)
// {
// switch (info.activity) {
// case ActivityCodes.NONE:
// case ActivityCodes.READING:
// case ActivityCodes.IDLE:
// case ActivityCodes.DISCONNECTED:
// return true;
// default:
// return false;
// }
// }
/**
* Locates a spot to stand near the supplied rectangular footprint.
* First a spot will be sought in a tile immediately next to the
* footprint, then one tile removed, then two, up to the maximum
* distance specified by <code>dist</code>.
*
* @param foot the tile coordinate footprint around which we are
* attempting to stand.
* @param dist the maximum number of tiles away from the footprint to
* search before giving up.
* @param pred a predicate that will be used to determine whether a
* particular spot can be stood upon (we're hijacking the meaning of
* "traverse" in this case, but the interface is otherwise so nice).
* @param traverser the object that will be passed to the traversal
* predicate.
* @param nearto a point (in tile coordinates) which will be used to
* select from among the valid standing spots, the one nearest to the
* supplied point will be returned.
* @param orient if not {@link DirectionCodes#NONE} this orientation
* will be used to override the "natural" orientation of the spot
* which is facing toward the footprint.
*
* @return the closest spot to the
*/
public static Location findStandingSpot (
Rectangle foot, int dist, AStarPathUtil.TraversalPred pred,
Object traverser, final Point nearto, int orient)
{
// generate a list of the tile coordinates of all squares around
// this footprint
SortableArrayList spots = new SortableArrayList();
for (int dd = 1; dd <= dist; dd++) {
int yy1 = foot.y-dd, yy2 = foot.y+foot.height+dd-1;
int xx1 = foot.x-dd, xx2 = foot.x+foot.width+dd-1;
// get the corners
spots.add(new Location(xx1, yy1, (byte)DirectionCodes.SOUTHWEST));
spots.add(new Location(xx1, yy2, (byte)DirectionCodes.SOUTHEAST));
spots.add(new Location(xx2, yy1, (byte)DirectionCodes.NORTHWEST));
spots.add(new Location(xx2, yy2, (byte)DirectionCodes.NORTHEAST));
// then the sides
for (int xx = xx1+1; xx < xx2; xx++) {
spots.add(new Location(xx, yy1, (byte)DirectionCodes.WEST));
spots.add(new Location(xx, yy2, (byte)DirectionCodes.EAST));
}
for (int yy = yy1+1; yy < yy2; yy++) {
spots.add(new Location(xx1, yy, (byte)DirectionCodes.SOUTH));
spots.add(new Location(xx2, yy, (byte)DirectionCodes.NORTH));
}
// sort them in order of closeness to the players current
// coordinate
spots.sort(new Comparator() {
public int compare (Object o1, Object o2) {
return dist((Location)o1) - dist((Location)o2);
}
private final int dist (Location l) {
return Math.round(100*MathUtil.distance(
l.x, l.y, nearto.x, nearto.y));
}
});
// return the first spot that can be "traversed" which we're
// taking to mean "stood upon"
for (int ii = 0, ll = spots.size(); ii < ll; ii++) {
Location loc = (Location)spots.get(ii);
if (pred.canTraverse(traverser, loc.x, loc.y)) {
// convert to full coordinates
loc.x = MisoUtil.toFull(loc.x, 2);
loc.y = MisoUtil.toFull(loc.y, 2);
// see if we need to override the orientation
if (DirectionCodes.NONE != orient) {
loc.orient = (byte) orient;
}
return loc;
}
}
// clear this list and try one further out
spots.clear();
}
return null;
}
/**
* Returns an array of the objects intersected by the supplied tile
* coordinate rectangle.
*/
public static ObjectInfo[] getIntersectedObjects (
TileManager tmgr, StageSceneModel model, Rectangle rect)
{
// first get all objects whose origin is in an expanded version of
// our intersection rect, any object that is *so* large that its
// origin falls outside of this rectangle but that still
// intersects this rectangle can go to hell; it's either this or
// we iterate over every object in the whole goddamned scene which
// is so hairily inefficient i can't even bear to contemplate it
ObjectSet objs = new ObjectSet();
Rectangle orect = new Rectangle(rect);
orect.grow(MAX_OBJECT_SIZE, MAX_OBJECT_SIZE);
StageMisoSceneModel mmodel = StageMisoSceneModel.getSceneModel(model);
mmodel.getObjects(orect, objs);
// now prune from this set any and all objects that don't actually
// overlap the specified rectangle
Rectangle foot = new Rectangle();
for (int ii = 0; ii < objs.size(); ii++) {
ObjectInfo info = objs.get(ii);
if (getObjectFootprint(tmgr, info.tileId, info.x, info.y, foot)) {
if (!foot.intersects(rect)) {
objs.remove(ii--);
}
} else {
Log.warning("Unknown potentially intersecting object?! " +
"[scene=" + model.name + " (" + model.sceneId +
"), info=" + info + "].");
}
}
return objs.toArray();
}
/** Our default scene metrics. */
protected static MisoSceneMetrics _metrics = MisoConfig.getSceneMetrics();
/** Contains the starting offset from zero radians for the first
* occupant and the radial distance between occupants. */
protected static final double[] CLUSTER_METRICS = {
Math.PI/4, Math.PI/2, // 2x
Math.PI/4, Math.PI/2, // 3x
0, Math.PI/4, // 4x
Math.PI/12, Math.PI/6, // 5x
0, Math.PI/8, // 6x
Math.PI/24, Math.PI/12, // 7x
};
/** The maximum footprint width or height for which we will account in
* {@link #getIntersectedObjects}. */
protected static final int MAX_OBJECT_SIZE = 15;
}