Files
nenya/src/as/com/threerings/miso/client/MisoScenePanel.as
T
Mike Thomas 0b55ca7817 This is kinda a magical number anyway but it appears I need a little more buffer for some of the taller sprites in question.
git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@959 ed5b42cb-e716-0410-a449-f6a68f950b19
2010-07-16 00:24:24 +00:00

340 lines
11 KiB
ActionScript

//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/nenya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.miso.client {
import flash.utils.getTimer;
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.events.MouseEvent;
import mx.core.ClassFactory;
import as3isolib.display.primitive.IsoBox;
import as3isolib.geom.Pt;
import as3isolib.geom.IsoMath;
import as3isolib.display.scene.IsoScene;
import as3isolib.display.IsoView;
import com.threerings.crowd.client.PlaceView;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.util.DelayUtil;
import com.threerings.util.Log;
import com.threerings.util.Map;
import com.threerings.util.Maps;
import com.threerings.util.MathUtil;
import com.threerings.util.Set;
import com.threerings.util.Sets;
import com.threerings.util.StringUtil;
import com.threerings.util.maps.WeakValueMap;
import com.threerings.media.tile.BaseTile;
import com.threerings.media.tile.Colorizer;
import com.threerings.media.tile.NoSuchTileSetError;
import com.threerings.media.tile.TileSet;
import com.threerings.media.tile.TileUtil;
import com.threerings.miso.client.MisoMetricsTransformation;
import com.threerings.miso.client.PrioritizedSceneLayoutRenderer;
import com.threerings.miso.data.MisoSceneModel;
import com.threerings.miso.data.ObjectInfo;
import com.threerings.miso.tile.FringeTile;
import com.threerings.miso.util.MisoContext;
import com.threerings.miso.util.ObjectSet;
import com.threerings.miso.util.MisoSceneMetrics;
public class MisoScenePanel extends Sprite
implements PlaceView
{
private var log :Log = Log.getLog(MisoScenePanel);
public function MisoScenePanel (ctx :MisoContext, metrics :MisoSceneMetrics)
{
_ctx = ctx;
// Excitingly, we get to override this globally for as3isolib...
IsoMath.transformationObject = new MisoMetricsTransformation(metrics,
metrics.tilehei * 3 / 4);
_metrics = metrics;
_isoView = new IsoView();
_isoView.setSize(DEF_WIDTH, DEF_HEIGHT);
_isoView.addEventListener(MouseEvent.CLICK, onClick);
addChild(_loading = createLoadingPanel());
}
/**
* Creates whatever we want to show while we're waiting to load our exciting scene tile data.
*/
protected function createLoadingPanel () :DisplayObject
{
// By default we just stick up a basic label for this...
var loadingText :TextField = new TextField();
loadingText.textColor = 0xFFFFFF;
var loadingFmt :TextFormat = new TextFormat();
loadingFmt.size = 24;
loadingText.defaultTextFormat = loadingFmt;
loadingText.autoSize = TextFieldAutoSize.LEFT;
_loadingProgressFunc = function (progress :Number) :void {
loadingText.text = "Loading... " + int(progress*100) + "%";
};
_loadingProgressFunc(0.0);
return loadingText;
}
protected function loadingProgress (progress :Number) :void
{
if (_loadingProgressFunc != null) {
_loadingProgressFunc(progress);
}
}
public function onClick (event :MouseEvent) :void
{
var viewPt :Point = _isoView.globalToLocal(new Point(event.stageX, event.stageY));
moveBy(new Point(viewPt.x - _isoView.width / 2, viewPt.y - _isoView.height / 2));
}
public function moveBy (pt :Point) :void
{
if (_pendingMoveBy != null) {
log.info("Already performing a move...");
return;
}
_pendingMoveBy = pt;
refreshBaseBlockScenes();
}
public function setSceneModel (model :MisoSceneModel) :void
{
_model = model;
_ctx.getTileManager().ensureLoaded(_model.getAllTilesets(), function() :void {
DelayUtil.delayFrame(function() :void {
refreshScene();
removeChild(_loading);
addChild(_isoView);
});
}, loadingProgress);
}
public function willEnterPlace (plobj :PlaceObject) :void
{
}
public function didLeavePlace (plobj :PlaceObject) :void
{
}
/** Computes the fringe tile for the specified coordinate. */
public function computeFringeTile (tx :int, ty :int) :BaseTile
{
return _ctx.getTileManager().getFringer().getFringeTile(_model, tx, ty, _fringes,
_masks);
}
protected function getBaseBlocks () :Set
{
var blocks :Set = Sets.newSetOf(int);
var size :Point = _isoView.size;
var xMove :int = (_pendingMoveBy == null ? 0 : _pendingMoveBy.x);
var yMove :int = (_pendingMoveBy == null ? 0 : _pendingMoveBy.y);
var minX :int = xMove;
var maxX :int = size.x + xMove;
var minY :int = yMove;
var maxY :int = BOTTOM_BUFFER + size.y + yMove;
var topLeft :Point = _isoView.localToIso(new Point(minX, minY));
var topRight :Point = _isoView.localToIso(new Point(maxX, minY));
var btmLeft :Point = _isoView.localToIso(new Point(minX, maxY));
var btmRight :Point = _isoView.localToIso(new Point(maxX, maxY));
for (var yy :int = topRight.y - 1; yy <= btmLeft.y + 1; yy++) {
for (var xx :int = topLeft.x - 1; xx <= btmRight.x + 1; xx++) {
// Toss out any that aren't actually in our view.
var blkTop :Point = _isoView.isoToLocal(new Pt(xx, yy, 0));
var blkRight :Point =
_isoView.isoToLocal(new Pt(xx + SceneBlock.BLOCK_SIZE, yy, 0));
var blkLeft :Point =
_isoView.isoToLocal(new Pt(xx, yy + SceneBlock.BLOCK_SIZE, 0));
var blkBtm :Point =
_isoView.isoToLocal(new Pt(xx + SceneBlock.BLOCK_SIZE,
yy + SceneBlock.BLOCK_SIZE, 0));
if (blkTop.y < maxY &&
blkBtm.y > minY &&
blkLeft.x < maxX &&
blkRight.x > minX) {
blocks.add(SceneBlock.getBlockKey(xx, yy));
}
}
}
return blocks;
}
protected function refreshBaseBlockScenes () :void
{
_resStartTime = getTimer();
var blocks :Set = getBaseBlocks();
// Keep this to use in our function...
var thisRef :MisoScenePanel = this;
// Postpone calling complete til they're all queued up.
_skipComplete = true;
blocks.forEach(function(blockKey :int) :void {
if (!_blocks.containsKey(blockKey)) {
var sceneBlock :SceneBlock =
new SceneBlock(blockKey, _objScene, _isoView, _metrics);
_pendingBlocks.add(sceneBlock);
sceneBlock.resolve(_ctx, _model, thisRef, blockResolved);
}
});
_skipComplete = false;
if (_pendingBlocks.size() == 0) {
resolutionComplete();
}
}
protected function blockResolved (resolved :SceneBlock) :void
{
if (!_pendingBlocks.contains(resolved)) {
log.info("Trying to resolve non-pending block???: " + resolved.getKey());
}
// Move that guy from pending to ready...
_readyBlocks.add(resolved);
_pendingBlocks.remove(resolved);
if (_pendingBlocks.size() == 0 && !_skipComplete) {
resolutionComplete();
}
}
protected function resolutionComplete () :void
{
// First, we add in our new blocks...
for each (var newBlock :SceneBlock in _readyBlocks.toArray()) {
newBlock.render();
_blocks.put(newBlock.getKey(), newBlock);
}
_readyBlocks = Sets.newSetOf(SceneBlock);
// Force to end of list so it's on top...
_isoView.removeScene(_objScene);
_isoView.addScene(_objScene);
_objScene.render();
// Then we let the scene finally move if it's trying to...
if (_pendingMoveBy != null) {
_isoView.centerOnPt(new Pt(_pendingMoveBy.x + _isoView.currentX,
_pendingMoveBy.y + _isoView.currentY), false);
_pendingMoveBy = null;
}
// Now, take out any old blocks no longer in our valid blocks.
var blocks :Set = getBaseBlocks();
for each (var baseKey :int in _blocks.keys()) {
if (!blocks.contains(baseKey)) {
_blocks.remove(baseKey).release();
}
}
trace("Scene Block Resolution took: " + (getTimer() - _resStartTime) + "ms");
}
protected function refreshScene () :void
{
// Clear it out...
_isoView.removeAllScenes();
_objScene = new IsoScene();
_objScene.layoutRenderer = new ClassFactory(PrioritizedSceneLayoutRenderer);
refreshBaseBlockScenes();
}
/**
* Derived classes can override this method and provide a colorizer that will be used to
* colorize the supplied scene object when rendering.
*/
public function getColorizer (oinfo :ObjectInfo) :Colorizer
{
return null;
}
protected var _model :MisoSceneModel;
protected var _isoView :IsoView;
protected var _ctx :MisoContext;
protected var _metrics :MisoSceneMetrics;
/** What we display while we're loading up our tilesets. */
protected var _loading :DisplayObject;
/** If we should do something when we hear about progress updates, this is it. */
protected var _loadingProgressFunc :Function;
protected var _objScene :IsoScene;
protected var _blocks :Map = Maps.newMapOf(int);
protected var _pendingMoveBy :Point;
protected var _pendingBlocks :Set = Sets.newSetOf(SceneBlock);
protected var _readyBlocks :Set = Sets.newSetOf(SceneBlock);
protected var _masks :Map = Maps.newMapOf(int);
protected var _fringes :Map = new WeakValueMap(Maps.newMapOf(FringeTile));
protected var _resStartTime :int;
protected var _skipComplete :Boolean
protected const DEF_WIDTH :int = 985;
protected const DEF_HEIGHT :int = 560;
protected const BOTTOM_BUFFER :int = 300;
}
}