PageRenderTime 59ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/as/com/threerings/miso/client/MisoScenePanel.as

https://github.com/dosage/nenya
ActionScript | 732 lines | 487 code | 136 blank | 109 comment | 57 complexity | 87ad67beb7e067e22619e2f476aef650 MD5 | raw file
  1. //
  2. // $Id$
  3. //
  4. // Nenya library - tools for developing networked games
  5. // Copyright (C) 2002-2011 Three Rings Design, Inc., All Rights Reserved
  6. // http://code.google.com/p/nenya/
  7. //
  8. // This library is free software; you can redistribute it and/or modify it
  9. // under the terms of the GNU Lesser General Public License as published
  10. // by the Free Software Foundation; either version 2.1 of the License, or
  11. // (at your option) any later version.
  12. //
  13. // This library is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. // Lesser General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Lesser General Public
  19. // License along with this library; if not, write to the Free Software
  20. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21. package com.threerings.miso.client {
  22. import flash.utils.getTimer;
  23. import flash.display.DisplayObject;
  24. import flash.display.Sprite;
  25. import flash.events.Event;
  26. import flash.geom.Point;
  27. import flash.geom.Rectangle;
  28. import flash.text.TextField;
  29. import flash.text.TextFieldAutoSize;
  30. import flash.text.TextFormat;
  31. import flash.events.MouseEvent;
  32. import mx.core.ClassFactory;
  33. import as3isolib.display.IsoSprite;
  34. import as3isolib.display.primitive.IsoBox;
  35. import as3isolib.core.IsoDisplayObject;
  36. import as3isolib.geom.Pt;
  37. import as3isolib.geom.IsoMath;
  38. import as3isolib.display.scene.IsoScene;
  39. import as3isolib.display.IsoView;
  40. import com.threerings.crowd.client.PlaceView;
  41. import com.threerings.crowd.data.PlaceObject;
  42. import com.threerings.util.ClassUtil;
  43. import com.threerings.util.DelayUtil;
  44. import com.threerings.util.Log;
  45. import com.threerings.util.Map;
  46. import com.threerings.util.Maps;
  47. import com.threerings.util.MathUtil;
  48. import com.threerings.util.Set;
  49. import com.threerings.util.Sets;
  50. import com.threerings.util.StringUtil;
  51. import com.threerings.util.maps.WeakValueMap;
  52. import com.threerings.media.Tickable;
  53. import com.threerings.media.tile.BaseTile;
  54. import com.threerings.media.tile.Colorizer;
  55. import com.threerings.media.tile.NoSuchTileSetError;
  56. import com.threerings.media.tile.TileSet;
  57. import com.threerings.media.tile.TileUtil;
  58. import com.threerings.media.util.AStarPathSearch;
  59. import com.threerings.media.util.AStarPathSearch_Stepper;
  60. import com.threerings.media.util.LineSegmentPath;
  61. import com.threerings.media.util.Path;
  62. import com.threerings.media.util.Pathable;
  63. import com.threerings.media.util.TraversalPred;
  64. import com.threerings.miso.client.MisoMetricsTransformation;
  65. import com.threerings.miso.client.PrioritizedSceneLayoutRenderer;
  66. import com.threerings.miso.data.MisoSceneModel;
  67. import com.threerings.miso.data.ObjectInfo;
  68. import com.threerings.miso.tile.FringeTile;
  69. import com.threerings.miso.util.MisoContext;
  70. import com.threerings.miso.util.ObjectSet;
  71. import com.threerings.miso.util.MisoSceneMetrics;
  72. public class MisoScenePanel extends Sprite
  73. implements PlaceView, TraversalPred, Tickable
  74. {
  75. private var log :Log = Log.getLog(MisoScenePanel);
  76. public function MisoScenePanel (ctx :MisoContext, metrics :MisoSceneMetrics)
  77. {
  78. _ctx = ctx;
  79. // Excitingly, we get to override this globally for as3isolib...
  80. IsoMath.transformationObject = new MisoMetricsTransformation(metrics,
  81. metrics.tilehei * 3 / 4);
  82. _metrics = metrics;
  83. _isoView = new IsoView();
  84. _isoView.setSize(DEF_WIDTH, DEF_HEIGHT);
  85. _centerer = new Centerer(_isoView);
  86. _vbounds = new Rectangle(0, 0,
  87. _isoView.size.x, _isoView.size.y);
  88. _isoView.addEventListener(MouseEvent.CLICK, onClick);
  89. _isoView.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoved);
  90. _isoView.addEventListener(MouseEvent.ROLL_OUT, mouseExited);
  91. addObjectScenes();
  92. addChild(_loading = createLoadingPanel());
  93. addEventListener(Event.ADDED_TO_STAGE, addedToStage);
  94. addEventListener(Event.REMOVED_FROM_STAGE, removedFromStage);
  95. }
  96. /**
  97. * Handles Event.ADDED_TO_STAGE.
  98. */
  99. protected function addedToStage (event :Event) :void
  100. {
  101. _ctx.getTicker().registerTickable(this);
  102. }
  103. /**
  104. * Handles Event.REMOVED_FROM_STAGE.
  105. */
  106. protected function removedFromStage (event :Event) :void
  107. {
  108. _ctx.getTicker().removeTickable(this);
  109. }
  110. public function tick (tickStamp :int) :void
  111. {
  112. _centerer.tick(tickStamp);
  113. }
  114. /**
  115. * Creates whatever we want to show while we're waiting to load our exciting scene tile data.
  116. */
  117. protected function createLoadingPanel () :DisplayObject
  118. {
  119. // By default we just stick up a basic label for this...
  120. var loadingText :TextField = new TextField();
  121. loadingText.textColor = 0xFFFFFF;
  122. var loadingFmt :TextFormat = new TextFormat();
  123. loadingFmt.size = 24;
  124. loadingText.defaultTextFormat = loadingFmt;
  125. loadingText.autoSize = TextFieldAutoSize.LEFT;
  126. _loadingProgressFunc = function (progress :Number) :void {
  127. loadingText.text = "Loading... " + int(progress*100) + "%";
  128. loadingText.x = (_isoView.size.x - loadingText.width)/2;
  129. loadingText.y = (_isoView.size.y - loadingText.height)/2;
  130. };
  131. _loadingProgressFunc(0.0);
  132. return loadingText;
  133. }
  134. protected function loadingProgress (progress :Number) :void
  135. {
  136. if (_loadingProgressFunc != null) {
  137. _loadingProgressFunc(progress);
  138. }
  139. }
  140. public function onClick (event :MouseEvent) :void
  141. {
  142. handleMousePressed(_hobject, event);
  143. }
  144. public function mouseMoved (event :MouseEvent) :void
  145. {
  146. var viewPt :Point = _isoView.globalToLocal(new Point(event.stageX, event.stageY));
  147. var x :int = event.stageX;
  148. var y :int = event.stageY;
  149. // give derived classes a chance to start with a hover object
  150. var hobject :Object = computeOverHover(x, y);
  151. // if they came up with nothing, compute the list of objects over
  152. // which the mouse is hovering
  153. if (hobject == null) {
  154. var hits :Array =
  155. _objScene.displayListChildren.filter(
  156. function(val :Object, idx :int, arr :Array) :Boolean {
  157. if (val is PriorityIsoDisplayObject) {
  158. return PriorityIsoDisplayObject(val).hitTest(x, y);
  159. } else {
  160. return false;
  161. }
  162. });
  163. hits.sort(function (v1 :PriorityIsoDisplayObject, v2 :PriorityIsoDisplayObject) :int {
  164. // We want reverse order, highest prio first...
  165. return v2.getPriority() - v1.getPriority();
  166. });
  167. if (hits.length > 0) {
  168. hobject = hits[0];
  169. }
  170. }
  171. // if the user isn't hovering over a sprite or object with an
  172. // action, allow derived classes to provide some other hover
  173. if (hobject == null) {
  174. hobject = computeUnderHover(x, y);
  175. }
  176. changeHoverObject(hobject);
  177. }
  178. /**
  179. * Gives derived classes a chance to compute a hover object that takes precedence over sprites
  180. * and actionable objects. If this method returns non-null, no sprite or object hover
  181. * calculations will be performed and the object returned will become the new hover object.
  182. */
  183. protected function computeOverHover (mx :int, my :int) :Object
  184. {
  185. return null;
  186. }
  187. /**
  188. * Gives derived classes a chance to compute a hover object that is used if the mouse is not
  189. * hovering over a sprite or actionable object. If this method is called, it means that there
  190. * are no sprites or objects under the mouse. Thus if it returns non-null, the object returned
  191. * will become the new hover object.
  192. */
  193. protected function computeUnderHover (mx :int, my :int) :Object
  194. {
  195. return null;
  196. }
  197. /**
  198. * Change the hover object to the new object.
  199. */
  200. protected function changeHoverObject (newHover :Object) :void
  201. {
  202. if (newHover == _hobject) {
  203. return;
  204. }
  205. var oldHover :Object = _hobject;
  206. _hobject = newHover;
  207. hoverObjectChanged(oldHover, newHover);
  208. }
  209. /**
  210. * A place for subclasses to react to the hover object changing.
  211. * One of the supplied arguments may be null.
  212. */
  213. protected function hoverObjectChanged (oldHover :Object, newHover :Object) :void
  214. {
  215. // Nothing by default.
  216. }
  217. public function mouseExited (event :MouseEvent) :void
  218. {
  219. // clear the highlight tracking data
  220. changeHoverObject(null);
  221. }
  222. public function handleMousePressed (hobject :Object, event :MouseEvent) :Boolean
  223. {
  224. var viewPt :Point = _isoView.globalToLocal(new Point(event.stageX, event.stageY));
  225. moveBy(new Point(viewPt.x - _isoView.width / 2, viewPt.y - _isoView.height / 2), false);
  226. return true;
  227. }
  228. public function moveBy (pt :Point, immediate :Boolean) :void
  229. {
  230. // No scene model yet, just do it...
  231. if (_model == null) {
  232. _centerer.moveTo(pt.x + _isoView.currentX, pt.y + _isoView.currentY, immediate);
  233. return;
  234. }
  235. if (_pendingMoveBy != null) {
  236. log.info("Already performing a move...");
  237. return;
  238. }
  239. _pendingMoveBy = pt;
  240. refreshBaseBlockScenes();
  241. }
  242. public function setSceneModel (model :MisoSceneModel) :void
  243. {
  244. _model = model;
  245. _ctx.getTileManager().ensureLoaded(_model.getAllTilesets(), function() :void {
  246. DelayUtil.delayFrame(function() :void {
  247. refreshScene();
  248. DelayUtil.delayFrame(function() :void {
  249. removeChild(_loading);
  250. addChild(_isoView);
  251. });
  252. });
  253. }, loadingProgress);
  254. }
  255. public function willEnterPlace (plobj :PlaceObject) :void
  256. {
  257. }
  258. public function didLeavePlace (plobj :PlaceObject) :void
  259. {
  260. }
  261. /** Computes the fringe tile for the specified coordinate. */
  262. public function computeFringeTile (tx :int, ty :int) :BaseTile
  263. {
  264. return _ctx.getTileManager().getFringer().getFringeTile(_model, tx, ty, _fringes,
  265. _masks);
  266. }
  267. protected function getBaseBlocks (buffer :Boolean) :Set
  268. {
  269. var blocks :Set = Sets.newSetOf(int);
  270. var size :Point = _isoView.size;
  271. var xMove :int = (_pendingMoveBy == null ? 0 : _pendingMoveBy.x);
  272. var yMove :int = (_pendingMoveBy == null ? 0 : _pendingMoveBy.y);
  273. var minX :int = xMove - (buffer ? size.x/2 : 0);
  274. var maxX :int = size.x + xMove + (buffer ? size.x/2 : 0);
  275. var minY :int = yMove - (buffer ? size.y/2 : 0);
  276. var maxY :int = BOTTOM_BUFFER + size.y + yMove + (buffer ? size.y/2 : 0);
  277. var topLeft :Point = _isoView.localToIso(new Point(minX, minY));
  278. var topRight :Point = _isoView.localToIso(new Point(maxX, minY));
  279. var btmLeft :Point = _isoView.localToIso(new Point(minX, maxY));
  280. var btmRight :Point = _isoView.localToIso(new Point(maxX, maxY));
  281. for (var yy :int = topRight.y - 1; yy <= btmLeft.y + 1; yy++) {
  282. for (var xx :int = topLeft.x - 1; xx <= btmRight.x + 1; xx++) {
  283. // Toss out any that aren't actually in our view.
  284. var blkTop :Point = _isoView.isoToLocal(new Pt(xx, yy, 0));
  285. var blkRight :Point =
  286. _isoView.isoToLocal(new Pt(xx + SceneBlock.BLOCK_SIZE, yy, 0));
  287. var blkLeft :Point =
  288. _isoView.isoToLocal(new Pt(xx, yy + SceneBlock.BLOCK_SIZE, 0));
  289. var blkBtm :Point =
  290. _isoView.isoToLocal(new Pt(xx + SceneBlock.BLOCK_SIZE,
  291. yy + SceneBlock.BLOCK_SIZE, 0));
  292. if (blkTop.y < maxY &&
  293. blkBtm.y > minY &&
  294. blkLeft.x < maxX &&
  295. blkRight.x > minX) {
  296. blocks.add(SceneBlock.getBlockKey(xx, yy));
  297. }
  298. }
  299. }
  300. return blocks;
  301. }
  302. protected function createSceneBlock (blockKey :int) :SceneBlock
  303. {
  304. return new SceneBlock(blockKey, _objScene, _isoView, _metrics);
  305. }
  306. protected function refreshBaseBlockScenes () :void
  307. {
  308. _resStartTime = getTimer();
  309. // Clear em til we're done resolving.
  310. _pendingPrefetchBlocks = [];
  311. var blocks :Set = getBaseBlocks(false);
  312. // Keep this to use in our function...
  313. var thisRef :MisoScenePanel = this;
  314. // Postpone calling complete til they're all queued up.
  315. _skipComplete = true;
  316. blocks.forEach(function(blockKey :int) :void {
  317. if (!_blocks.containsKey(blockKey)) {
  318. if (_prefetchedBlocks.containsKey(blockKey)) {
  319. _readyBlocks.add(_prefetchedBlocks.remove(blockKey));
  320. } else {
  321. var sceneBlock :SceneBlock = createSceneBlock(blockKey);
  322. _pendingBlocks.add(sceneBlock);
  323. sceneBlock.resolve(_ctx, _model, thisRef, blockResolved);
  324. }
  325. }
  326. });
  327. _skipComplete = false;
  328. if (_pendingBlocks.size() == 0) {
  329. resolutionComplete();
  330. }
  331. }
  332. protected function blockResolved (resolved :SceneBlock) :void
  333. {
  334. if (!_pendingBlocks.contains(resolved)) {
  335. log.info("Trying to resolve non-pending block???: " + resolved.getKey());
  336. }
  337. // Move that guy from pending to ready...
  338. _readyBlocks.add(resolved);
  339. _pendingBlocks.remove(resolved);
  340. if (_pendingBlocks.size() == 0 && !_skipComplete) {
  341. resolutionComplete();
  342. }
  343. }
  344. protected function renderObjectScenes () :void
  345. {
  346. _objScene.render();
  347. }
  348. protected function addObjectScenes () :void
  349. {
  350. _objScene = new IsoScene();
  351. _objScene.layoutRenderer = new ClassFactory(PrioritizedSceneLayoutRenderer);
  352. _isoView.addScene(_objScene);
  353. }
  354. protected function resolutionComplete () :void
  355. {
  356. // First, we add in our new blocks...
  357. for each (var newBlock :SceneBlock in _readyBlocks.toArray()) {
  358. newBlock.render();
  359. newBlock.addToCovered(_covered);
  360. _blocks.put(newBlock.getKey(), newBlock);
  361. }
  362. _readyBlocks = Sets.newSetOf(SceneBlock);
  363. renderObjectScenes();
  364. // Then we let the scene finally move if it's trying to...
  365. if (_pendingMoveBy != null) {
  366. _centerer.moveTo(_pendingMoveBy.x + _isoView.currentX,
  367. _pendingMoveBy.y + _isoView.currentY, false);
  368. _pendingMoveBy = null;
  369. }
  370. // Now, take out any old blocks no longer in our valid blocks.
  371. var blocks :Set = getBaseBlocks(true);
  372. for each (var baseKey :int in _blocks.keys()) {
  373. if (!blocks.contains(baseKey)) {
  374. _blocks.remove(baseKey).release();
  375. }
  376. }
  377. // And any prefetch blocks that are bogus can go away too.
  378. for each (var preKey :int in _prefetchedBlocks.keys()) {
  379. if (!blocks.contains(preKey)) {
  380. _prefetchedBlocks.remove(preKey);
  381. }
  382. }
  383. log.info("Scene Block Resolution took: " + (getTimer() - _resStartTime) + "ms");
  384. // Let's setup some prefetching...
  385. blocks.forEach(function(blockKey :int) :void {
  386. if (!_blocks.containsKey(blockKey)) {
  387. var sceneBlock :SceneBlock = createSceneBlock(blockKey);
  388. _pendingPrefetchBlocks.push(sceneBlock);
  389. }
  390. });
  391. maybePrefetchABlock();
  392. }
  393. protected function maybePrefetchABlock () :void
  394. {
  395. if (_pendingPrefetchBlocks.length != 0) {
  396. _pendingPrefetchBlocks.shift().resolve(_ctx, _model, this, prefetchBlockResolved);
  397. }
  398. }
  399. protected function prefetchBlockResolved (resolved :SceneBlock) :void
  400. {
  401. _prefetchedBlocks.put(resolved.getKey(), resolved);
  402. // If we haven't moved on to resolve a new region, let's try another block.
  403. if (_pendingBlocks.size() == 0) {
  404. DelayUtil.delayFrame(function() :void {
  405. maybePrefetchABlock();
  406. });
  407. }
  408. }
  409. protected function refreshScene () :void
  410. {
  411. // Clear it out...
  412. _isoView.removeAllScenes();
  413. refreshBaseBlockScenes();
  414. }
  415. /**
  416. * Derived classes can override this method and provide a colorizer that will be used to
  417. * colorize the supplied scene object when rendering.
  418. */
  419. public function getColorizer (oinfo :ObjectInfo) :Colorizer
  420. {
  421. return null;
  422. }
  423. // documentation inherited
  424. public function canTraverse (traverser :Object, tx :int, ty :int) :Boolean
  425. {
  426. var block :SceneBlock = _blocks.get(SceneBlock.getBlockKey(tx, ty));
  427. var baseTraversable :Boolean = (block == null) ? canTraverseUnresolved(traverser, tx, ty) :
  428. block.canTraverseBase(traverser, tx, ty);
  429. return baseTraversable && !_covered.contains(StringUtil.toCoordsString(tx, ty));
  430. }
  431. /**
  432. * Derived classes can control whether or not we consider unresolved tiles to be traversable
  433. * or not.
  434. */
  435. protected function canTraverseUnresolved (traverser :Object, tx :int, ty :int) :Boolean
  436. {
  437. return false;
  438. }
  439. /**
  440. * Computes a path for the specified sprite to the specified tile coordinates.
  441. *
  442. * @param loose if true, an approximate path will be returned if a complete path cannot be
  443. * located. This path will navigate the sprite "legally" as far as possible and then walk the
  444. * sprite in a straight line to its final destination. This is generally only useful if the
  445. * the path goes "off screen".
  446. */
  447. public function getPath (sprite :IsoDisplayObject, x :int, y :int, loose :Boolean) :Path
  448. {
  449. // sanity check
  450. if (sprite == null) {
  451. throw new Error("Can't get path for null sprite [x=" + x + ", y=" + y + ".");
  452. }
  453. // compute our longest path from the screen size
  454. var longestPath :int = 3 * (width / _metrics.tilewid);
  455. // get a reasonable tile path through the scene
  456. var start :int = getTimer();
  457. var search :AStarPathSearch = new AStarPathSearch(this, new AStarPathSearch_Stepper());
  458. var points :Array = search.getPath(sprite, longestPath, int(Math.round(sprite.x)),
  459. int(Math.round(sprite.y)), x, y, loose);
  460. // Replace the starting point with the Number values rather than the rounded version...
  461. if (points != null) {
  462. points[0] = new Point(sprite.x, sprite.y);
  463. }
  464. var duration :int = getTimer() - start;
  465. // sanity check the number of nodes searched so that we can keep an eye out for bogosity
  466. if (duration > 500) {
  467. log.warning("Considered a lot of nodes for path from " +
  468. StringUtil.toCoordsString(sprite.x, sprite.y) + " to " +
  469. StringUtil.toCoordsString(x, y) +
  470. " [duration=" + duration + "].");
  471. }
  472. // construct a path object to guide the sprite on its merry way
  473. return (points == null) ? null : LineSegmentPath.createWithList(points);
  474. }
  475. protected var _model :MisoSceneModel;
  476. protected var _isoView :IsoView;
  477. protected var _ctx :MisoContext;
  478. protected var _metrics :MisoSceneMetrics;
  479. /** What we display while we're loading up our tilesets. */
  480. protected var _loading :DisplayObject;
  481. /** If we should do something when we hear about progress updates, this is it. */
  482. protected var _loadingProgressFunc :Function;
  483. protected var _objScene :IsoScene;
  484. /** All of the active blocks that are part of the scene. */
  485. protected var _blocks :Map = Maps.newMapOf(int);
  486. /** If we're resolving in preparation for moving, this is how much we'll move by when ready. */
  487. protected var _pendingMoveBy :Point;
  488. /** Required blocks we're working on resolving. */
  489. protected var _pendingBlocks :Set = Sets.newSetOf(SceneBlock);
  490. /** Blocks that are resolved and ready for adding to the scene. */
  491. protected var _readyBlocks :Set = Sets.newSetOf(SceneBlock);
  492. /** The queue of blocks we'd like to prefetch when we have a chance. */
  493. protected var _pendingPrefetchBlocks :Array = [];
  494. /** The blocks already prefetched and ready to go if we need them. */
  495. protected var _prefetchedBlocks :Map = Maps.newMapOf(int);
  496. protected var _masks :Map = Maps.newMapOf(int);
  497. protected var _fringes :Map = new WeakValueMap(Maps.newMapOf(FringeTile));
  498. /** What time did we start the current scene resolution. */
  499. protected var _resStartTime :int;
  500. /** If any block happens to resolve should we currently skip calling completion. */
  501. protected var _skipComplete :Boolean
  502. /** Info on the object that the mouse is currently hovering over. */
  503. protected var _hobject :Object;
  504. protected var _vbounds :Rectangle;
  505. protected var _covered :Set = Sets.newSetOf(String);
  506. protected var _centerer :Centerer;
  507. protected const DEF_WIDTH :int = 985;
  508. protected const DEF_HEIGHT :int = 560;
  509. protected const BOTTOM_BUFFER :int = 300;
  510. }
  511. }
  512. import as3isolib.display.IsoView;
  513. import as3isolib.geom.Pt;
  514. import com.threerings.media.util.LineSegmentPath;
  515. import com.threerings.media.util.Path;
  516. import com.threerings.media.util.Pathable;
  517. import com.threerings.media.Tickable;
  518. import com.threerings.util.DirectionCodes;
  519. class Centerer
  520. implements Pathable, Tickable
  521. {
  522. public function Centerer (isoView :IsoView)
  523. {
  524. _isoView = isoView;
  525. }
  526. public function moveTo (x :int, y:int, immediate :Boolean) :void
  527. {
  528. if (immediate) {
  529. cancelMove();
  530. setLocation(x, y);
  531. } else {
  532. var path :LineSegmentPath =
  533. LineSegmentPath.createWithInts(_isoView.currentX, _isoView.currentY, x, y);
  534. path.setVelocity(SCROLL_VELOCITY);
  535. move(path);
  536. }
  537. }
  538. public function move (path :Path) :void
  539. {
  540. // if there's a previous path, let it know that it's going away
  541. cancelMove();
  542. // save off this path
  543. _path = path;
  544. // we'll initialize it on our next tick thanks to a zero path stamp
  545. _pathStamp = 0;
  546. }
  547. public function cancelMove () :void
  548. {
  549. if (_path != null) {
  550. var oldpath :Path = _path;
  551. _path = null;
  552. oldpath.wasRemoved(this);
  553. }
  554. }
  555. public function tick (tickStamp :int) :void
  556. {
  557. if (_path != null) {
  558. if (_pathStamp == 0) {
  559. _pathStamp = tickStamp
  560. _path.init(this, _pathStamp);
  561. }
  562. _path.tick(this, tickStamp);
  563. }
  564. }
  565. public function getX () :Number
  566. {
  567. return _isoView.currentX;
  568. }
  569. public function getY () :Number
  570. {
  571. return _isoView.currentY;
  572. }
  573. public function setLocation (x :Number, y :Number) :void
  574. {
  575. _isoView.centerOnPt(new Pt(x, y), false);
  576. }
  577. public function setOrientation (orient :int) :void
  578. {
  579. }
  580. public function getOrientation () :int
  581. {
  582. return DirectionCodes.NONE;
  583. }
  584. public function pathBeginning () :void
  585. {
  586. }
  587. public function pathCompleted (timestamp :int) :void
  588. {
  589. _path = null;
  590. }
  591. protected var _isoView :IsoView;
  592. protected var _path :Path;
  593. protected var _pathStamp :int;
  594. /** Our scroll path velocity. */
  595. protected static const SCROLL_VELOCITY :Number = 0.4;
  596. }