PageRenderTime 53ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/org/flixel/system/debug/VCR.hx

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