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:
@@ -192,6 +192,9 @@
|
||||
<jar destfile="${deploy.dir}/${app.name}-cast.jar">
|
||||
<fileset dir="${classes.dir}" includes="com/threerings/cast/**"/>
|
||||
</jar>
|
||||
<jar destfile="${deploy.dir}/${app.name}-stage.jar">
|
||||
<fileset dir="${classes.dir}" includes="com/threerings/stage/**"/>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<!-- generate a class hierarchy diagram -->
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user