PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/org/flixel/system/debug/VCR.as

https://github.com/taitehughes/flixel
ActionScript | 600 lines | 388 code | 66 blank | 146 comment | 93 complexity | b676fb5b4ed5d42b35100bc7cb7c0f70 MD5 | raw file
  1. package org.flixel.system.debug
  2. {
  3. import flash.display.Bitmap;
  4. import flash.display.BitmapData;
  5. import flash.display.Sprite;
  6. import flash.events.Event;
  7. import flash.events.IOErrorEvent;
  8. import flash.events.MouseEvent;
  9. import flash.net.FileFilter;
  10. import flash.net.FileReference;
  11. import flash.text.TextField;
  12. import flash.text.TextFormat;
  13. import flash.utils.ByteArray;
  14. import org.flixel.FlxG;
  15. import org.flixel.FlxU;
  16. import org.flixel.system.FlxReplay;
  17. import org.flixel.system.replay.FrameRecord;
  18. import org.flixel.system.replay.MouseRecord;
  19. /**
  20. * This class contains the record, stop, play, and step 1 frame buttons seen on the top edge of the debugger overlay.
  21. *
  22. * @author Adam Atomic
  23. */
  24. public class VCR extends Sprite
  25. {
  26. [Embed(source="../../data/vcr/open.png")] protected var ImgOpen:Class;
  27. [Embed(source="../../data/vcr/record_off.png")] protected var ImgRecordOff:Class;
  28. [Embed(source="../../data/vcr/record_on.png")] protected var ImgRecordOn:Class;
  29. [Embed(source="../../data/vcr/stop.png")] protected var ImgStop:Class;
  30. [Embed(source="../../data/vcr/flixel.png")] protected var ImgFlixel:Class;
  31. [Embed(source="../../data/vcr/restart.png")] protected var ImgRestart:Class;
  32. [Embed(source="../../data/vcr/pause.png")] protected var ImgPause:Class;
  33. [Embed(source="../../data/vcr/play.png")] protected var ImgPlay:Class;
  34. [Embed(source="../../data/vcr/step.png")] protected var ImgStep:Class;
  35. static protected const FILE_TYPES:Array = [new FileFilter("Flixel Game Recording", "*.fgr")];
  36. static protected const DEFAULT_FILE_NAME:String = "replay.fgr";
  37. /**
  38. * Whether the debugger has been paused.
  39. */
  40. public var paused:Boolean;
  41. /**
  42. * Whether a "1 frame step forward" was requested.
  43. */
  44. public var stepRequested:Boolean;
  45. protected var _open:Bitmap;
  46. protected var _recordOff:Bitmap;
  47. protected var _recordOn:Bitmap;
  48. protected var _stop:Bitmap;
  49. protected var _flixel:Bitmap;
  50. protected var _restart:Bitmap;
  51. protected var _pause:Bitmap;
  52. protected var _play:Bitmap;
  53. protected var _step:Bitmap;
  54. protected var _overOpen:Boolean;
  55. protected var _overRecord:Boolean;
  56. protected var _overRestart:Boolean;
  57. protected var _overPause:Boolean;
  58. protected var _overStep:Boolean;
  59. protected var _pressingOpen:Boolean;
  60. protected var _pressingRecord:Boolean;
  61. protected var _pressingRestart:Boolean;
  62. protected var _pressingPause:Boolean;
  63. protected var _pressingStep:Boolean;
  64. protected var _file:FileReference;
  65. protected var _runtimeDisplay:TextField;
  66. protected var _runtime:uint;
  67. /**
  68. * Creates the "VCR" control panel for debugger pausing, stepping, and recording.
  69. */
  70. public function VCR()
  71. {
  72. super();
  73. var spacing:uint = 7;
  74. _open = new ImgOpen();
  75. addChild(_open);
  76. _recordOff = new ImgRecordOff();
  77. _recordOff.x = _open.x + _open.width + spacing;
  78. addChild(_recordOff);
  79. _recordOn = new ImgRecordOn();
  80. _recordOn.x = _recordOff.x;
  81. _recordOn.visible = false;
  82. addChild(_recordOn);
  83. _stop = new ImgStop();
  84. _stop.x = _recordOff.x;
  85. _stop.visible = false;
  86. addChild(_stop);
  87. _flixel = new ImgFlixel();
  88. _flixel.x = _recordOff.x + _recordOff.width + spacing;
  89. addChild(_flixel);
  90. _restart = new ImgRestart();
  91. _restart.x = _flixel.x + _flixel.width + spacing;
  92. addChild(_restart);
  93. _pause = new ImgPause();
  94. _pause.x = _restart.x + _restart.width + spacing;
  95. addChild(_pause);
  96. _play = new ImgPlay();
  97. _play.x = _pause.x;
  98. _play.visible = false;
  99. addChild(_play);
  100. _step = new ImgStep();
  101. _step.x = _pause.x + _pause.width + spacing;
  102. addChild(_step);
  103. _runtimeDisplay = new TextField();
  104. _runtimeDisplay.width = width;
  105. _runtimeDisplay.x = width;
  106. _runtimeDisplay.y = -2;
  107. _runtimeDisplay.multiline = false;
  108. _runtimeDisplay.wordWrap = false;
  109. _runtimeDisplay.selectable = false;
  110. _runtimeDisplay.defaultTextFormat = new TextFormat("Courier",12,0xffffff,null,null,null,null,null,"center");
  111. _runtimeDisplay.visible = false;
  112. addChild(_runtimeDisplay);
  113. _runtime = 0;
  114. stepRequested = false;
  115. _file = null;
  116. unpress();
  117. checkOver();
  118. updateGUI();
  119. addEventListener(Event.ENTER_FRAME,init);
  120. }
  121. /**
  122. * Clean up memory.
  123. */
  124. public function destroy():void
  125. {
  126. _file = null;
  127. removeChild(_open);
  128. _open = null;
  129. removeChild(_recordOff);
  130. _recordOff = null;
  131. removeChild(_recordOn);
  132. _recordOn = null;
  133. removeChild(_stop);
  134. _stop = null;
  135. removeChild(_flixel);
  136. _flixel = null;
  137. removeChild(_restart);
  138. _restart = null;
  139. removeChild(_pause);
  140. _pause = null;
  141. removeChild(_play);
  142. _play = null;
  143. removeChild(_step);
  144. _step = null;
  145. parent.removeEventListener(MouseEvent.MOUSE_MOVE,onMouseMove);
  146. parent.removeEventListener(MouseEvent.MOUSE_DOWN,onMouseDown);
  147. parent.removeEventListener(MouseEvent.MOUSE_UP,onMouseUp);
  148. }
  149. /**
  150. * Usually called by FlxGame when a requested recording has begun.
  151. * Just updates the VCR GUI so the buttons are in the right state.
  152. */
  153. public function recording():void
  154. {
  155. _stop.visible = false;
  156. _recordOff.visible = false;
  157. _recordOn.visible = true;
  158. }
  159. /**
  160. * Usually called by FlxGame when a replay has been stopped.
  161. * Just updates the VCR GUI so the buttons are in the right state.
  162. */
  163. public function stopped():void
  164. {
  165. _stop.visible = false;
  166. _recordOn.visible = false;
  167. _recordOff.visible = true;
  168. }
  169. /**
  170. * Usually called by FlxGame when a requested replay has begun.
  171. * Just updates the VCR GUI so the buttons are in the right state.
  172. */
  173. public function playing():void
  174. {
  175. _recordOff.visible = false;
  176. _recordOn.visible = false;
  177. _stop.visible = true;
  178. }
  179. /**
  180. * Just updates the VCR GUI so the runtime displays roughly the right thing.
  181. */
  182. public function updateRuntime(Time:uint):void
  183. {
  184. _runtime += Time;
  185. _runtimeDisplay.text = FlxU.formatTime(_runtime/1000,true);
  186. if(!_runtimeDisplay.visible)
  187. _runtimeDisplay.visible = true;
  188. }
  189. //*** ACTUAL BUTTON BEHAVIORS ***//
  190. /**
  191. * Called when the "open file" button is pressed.
  192. * Opens the file dialog and registers event handlers for the file dialog.
  193. */
  194. public function onOpen():void
  195. {
  196. _file = new FileReference();
  197. _file.addEventListener(Event.SELECT, onOpenSelect);
  198. _file.addEventListener(Event.CANCEL, onOpenCancel);
  199. _file.browse(FILE_TYPES);
  200. }
  201. /**
  202. * Called when a file is picked from the file dialog.
  203. * Attempts to load the file and registers file loading event handlers.
  204. *
  205. * @param E Flash event.
  206. */
  207. protected function onOpenSelect(E:Event=null):void
  208. {
  209. _file.removeEventListener(Event.SELECT, onOpenSelect);
  210. _file.removeEventListener(Event.CANCEL, onOpenCancel);
  211. _file.addEventListener(Event.COMPLETE, onOpenComplete);
  212. _file.addEventListener(IOErrorEvent.IO_ERROR, onOpenError);
  213. _file.load();
  214. }
  215. /**
  216. * Called when a file is opened successfully.
  217. * If there's stuff inside, then the contents are loaded into a new replay.
  218. *
  219. * @param E Flash Event.
  220. */
  221. protected function onOpenComplete(E:Event=null):void
  222. {
  223. _file.removeEventListener(Event.COMPLETE, onOpenComplete);
  224. _file.removeEventListener(IOErrorEvent.IO_ERROR, onOpenError);
  225. //Turn the file into a giant string
  226. var fileContents:String = null;
  227. var data:ByteArray = _file.data;
  228. if(data != null)
  229. fileContents = data.readUTFBytes(data.bytesAvailable);
  230. _file = null;
  231. if((fileContents == null) || (fileContents.length <= 0))
  232. {
  233. FlxG.log("ERROR: Empty flixel gameplay record.");
  234. return;
  235. }
  236. FlxG.loadReplay(fileContents);
  237. }
  238. /**
  239. * Called if the open file dialog is canceled.
  240. *
  241. * @param E Flash Event.
  242. */
  243. protected function onOpenCancel(E:Event=null):void
  244. {
  245. _file.removeEventListener(Event.SELECT, onOpenSelect);
  246. _file.removeEventListener(Event.CANCEL, onOpenCancel);
  247. _file = null;
  248. }
  249. /**
  250. * Called if there is a file open error.
  251. *
  252. * @param E Flash Event.
  253. */
  254. protected function onOpenError(E:Event=null):void
  255. {
  256. _file.removeEventListener(Event.COMPLETE, onOpenComplete);
  257. _file.removeEventListener(IOErrorEvent.IO_ERROR, onOpenError);
  258. _file = null;
  259. FlxG.log("ERROR: Unable to open flixel gameplay record.");
  260. }
  261. /**
  262. * Called when the user presses the white record button.
  263. * If Alt is pressed, the current state is reset, and a new recording is requested.
  264. * If Alt is NOT pressed, the game is reset, and a new recording is requested.
  265. *
  266. * @param StandardMode Whether to reset the whole game, or just this <code>FlxState</code>. StandardMode == false is useful for recording demos or attract modes.
  267. */
  268. public function onRecord(StandardMode:Boolean=false):void
  269. {
  270. if(_play.visible)
  271. onPlay();
  272. FlxG.recordReplay(StandardMode);
  273. }
  274. /**
  275. * Called when the user presses the red record button.
  276. * Stops the current recording, opens the save file dialog, and registers event handlers.
  277. */
  278. public function stopRecording():void
  279. {
  280. var data:String = FlxG.stopRecording();
  281. if((data != null) && (data.length > 0))
  282. {
  283. _file = new FileReference();
  284. _file.addEventListener(Event.COMPLETE, onSaveComplete);
  285. _file.addEventListener(Event.CANCEL,onSaveCancel);
  286. _file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError);
  287. _file.save(data, DEFAULT_FILE_NAME);
  288. }
  289. }
  290. /**
  291. * Called when the file is saved successfully.
  292. *
  293. * @param E Flash Event.
  294. */
  295. protected function onSaveComplete(E:Event=null):void
  296. {
  297. _file.removeEventListener(Event.COMPLETE, onSaveComplete);
  298. _file.removeEventListener(Event.CANCEL,onSaveCancel);
  299. _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
  300. _file = null;
  301. FlxG.log("FLIXEL: successfully saved flixel gameplay record.");
  302. }
  303. /**
  304. * Called when the save file dialog is cancelled.
  305. *
  306. * @param E Flash Event.
  307. */
  308. protected function onSaveCancel(E:Event=null):void
  309. {
  310. _file.removeEventListener(Event.COMPLETE, onSaveComplete);
  311. _file.removeEventListener(Event.CANCEL,onSaveCancel);
  312. _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
  313. _file = null;
  314. }
  315. /**
  316. * Called if there is an error while saving the gameplay recording.
  317. *
  318. * @param E Flash Event.
  319. */
  320. protected function onSaveError(E:Event=null):void
  321. {
  322. _file.removeEventListener(Event.COMPLETE, onSaveComplete);
  323. _file.removeEventListener(Event.CANCEL,onSaveCancel);
  324. _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError);
  325. _file = null;
  326. FlxG.log("ERROR: problem saving flixel gameplay record.");
  327. }
  328. /**
  329. * Called when the user presses the stop button.
  330. * Stops the current replay.
  331. */
  332. public function onStop():void
  333. {
  334. FlxG.stopReplay();
  335. }
  336. /**
  337. * Called when the user presses the Rewind-looking button.
  338. * If Alt is pressed, the entire game is reset.
  339. * If Alt is NOT pressed, only the current state is reset.
  340. * The GUI is updated accordingly.
  341. *
  342. * @param StandardMode Whether to reset the current game (== true), or just the current state. Just resetting the current state can be very handy for debugging.
  343. */
  344. public function onRestart(StandardMode:Boolean=false):void
  345. {
  346. if(FlxG.reloadReplay(StandardMode))
  347. {
  348. _recordOff.visible = false;
  349. _recordOn.visible = false;
  350. _stop.visible = true;
  351. }
  352. }
  353. /**
  354. * Called when the user presses the Pause button.
  355. * This is different from user-defined pause behavior, or focus lost behavior.
  356. * Does NOT pause music playback!!
  357. */
  358. public function onPause():void
  359. {
  360. paused = true;
  361. _pause.visible = false;
  362. _play.visible = true;
  363. }
  364. /**
  365. * Called when the user presses the Play button.
  366. * This is different from user-defined unpause behavior, or focus gained behavior.
  367. */
  368. public function onPlay():void
  369. {
  370. paused = false;
  371. _play.visible = false;
  372. _pause.visible = true;
  373. }
  374. /**
  375. * Called when the user presses the fast-forward-looking button.
  376. * Requests a 1-frame step forward in the game loop.
  377. */
  378. public function onStep():void
  379. {
  380. if(!paused)
  381. onPause();
  382. stepRequested = true;
  383. }
  384. //***EVENT HANDLERS***//
  385. /**
  386. * Just sets up basic mouse listeners, a la FlxWindow.
  387. *
  388. * @param E Flash event.
  389. */
  390. protected function init(E:Event=null):void
  391. {
  392. if(root == null)
  393. return;
  394. removeEventListener(Event.ENTER_FRAME,init);
  395. parent.addEventListener(MouseEvent.MOUSE_MOVE,onMouseMove);
  396. parent.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown);
  397. parent.addEventListener(MouseEvent.MOUSE_UP,onMouseUp);
  398. }
  399. /**
  400. * If the mouse moves, check to see if any buttons should be highlighted.
  401. *
  402. * @param E Flash mouse event.
  403. */
  404. protected function onMouseMove(E:MouseEvent=null):void
  405. {
  406. if(!checkOver())
  407. unpress();
  408. updateGUI();
  409. }
  410. /**
  411. * If the mouse is pressed down, check to see if the user started pressing down a specific button.
  412. *
  413. * @param E Flash mouse event.
  414. */
  415. protected function onMouseDown(E:MouseEvent=null):void
  416. {
  417. unpress();
  418. if(_overOpen)
  419. _pressingOpen = true;
  420. if(_overRecord)
  421. _pressingRecord = true;
  422. if(_overRestart)
  423. _pressingRestart = true;
  424. if(_overPause)
  425. _pressingPause = true;
  426. if(_overStep)
  427. _pressingStep = true;
  428. }
  429. /**
  430. * If the mouse is released, check to see if it was released over a button that was pressed.
  431. * If it was, take the appropriate action based on button state and visibility.
  432. *
  433. * @param E Flash mouse event.
  434. */
  435. protected function onMouseUp(E:MouseEvent=null):void
  436. {
  437. if(_overOpen && _pressingOpen)
  438. onOpen();
  439. else if(_overRecord && _pressingRecord)
  440. {
  441. if(_stop.visible)
  442. onStop();
  443. else if(_recordOn.visible)
  444. stopRecording();
  445. else
  446. onRecord(!E.altKey);
  447. }
  448. else if(_overRestart && _pressingRestart)
  449. onRestart(!E.altKey);
  450. else if(_overPause && _pressingPause)
  451. {
  452. if(_play.visible)
  453. onPlay();
  454. else
  455. onPause();
  456. }
  457. else if(_overStep && _pressingStep)
  458. onStep();
  459. unpress();
  460. checkOver();
  461. updateGUI();
  462. }
  463. //***MISC GUI MGMT STUFF***//
  464. /**
  465. * This function checks to see what button the mouse is currently over.
  466. * Has some special behavior based on whether a recording is happening or not.
  467. *
  468. * @return Whether the mouse was over any buttons or not.
  469. */
  470. protected function checkOver():Boolean
  471. {
  472. _overOpen = _overRecord = _overRestart = _overPause = _overStep = false;
  473. if((mouseX < 0) || (mouseX > width) || (mouseY < 0) || (mouseY > 15))
  474. return false;
  475. if((mouseX >= _recordOff.x) && (mouseX <= _recordOff.x + _recordOff.width))
  476. _overRecord = true;
  477. if(!_recordOn.visible && !_overRecord)
  478. {
  479. if((mouseX >= _open.x) && (mouseX <= _open.x + _open.width))
  480. _overOpen = true;
  481. else if((mouseX >= _restart.x) && (mouseX <= _restart.x + _restart.width))
  482. _overRestart = true;
  483. else if((mouseX >= _pause.x) && (mouseX <= _pause.x + _pause.width))
  484. _overPause = true;
  485. else if((mouseX >= _step.x) && (mouseX <= _step.x + _step.width))
  486. _overStep = true;
  487. }
  488. return true;
  489. }
  490. /**
  491. * Sets all the pressed state variables for the buttons to false.
  492. */
  493. protected function unpress():void
  494. {
  495. _pressingOpen = false;
  496. _pressingRecord = false;
  497. _pressingRestart = false;
  498. _pressingPause = false;
  499. _pressingStep = false;
  500. }
  501. /**
  502. * Figures out what buttons to highlight based on the _overWhatever and _pressingWhatever variables.
  503. */
  504. protected function updateGUI():void
  505. {
  506. if(_recordOn.visible)
  507. {
  508. _open.alpha = _restart.alpha = _pause.alpha = _step.alpha = 0.35;
  509. _recordOn.alpha = 1.0;
  510. return;
  511. }
  512. if(_overOpen && (_open.alpha != 1.0))
  513. _open.alpha = 1.0;
  514. else if(!_overOpen && (_open.alpha != 0.8))
  515. _open.alpha = 0.8;
  516. if(_overRecord && (_recordOff.alpha != 1.0))
  517. _recordOff.alpha = _recordOn.alpha = _stop.alpha = 1.0;
  518. else if(!_overRecord && (_recordOff.alpha != 0.8))
  519. _recordOff.alpha = _recordOn.alpha = _stop.alpha = 0.8;
  520. if(_overRestart && (_restart.alpha != 1.0))
  521. _restart.alpha = 1.0;
  522. else if(!_overRestart && (_restart.alpha != 0.8))
  523. _restart.alpha = 0.8;
  524. if(_overPause && (_pause.alpha != 1.0))
  525. _pause.alpha = _play.alpha = 1.0;
  526. else if(!_overPause && (_pause.alpha != 0.8))
  527. _pause.alpha = _play.alpha = 0.8;
  528. if(_overStep && (_step.alpha != 1.0))
  529. _step.alpha = 1.0;
  530. else if(!_overStep && (_step.alpha != 0.8))
  531. _step.alpha = 0.8;
  532. }
  533. }
  534. }