diff --git a/flixel/system/replay/FlxReplay.hx b/flixel/system/replay/FlxReplay.hx index 74d0f5ba5d..30299edc02 100644 --- a/flixel/system/replay/FlxReplay.hx +++ b/flixel/system/replay/FlxReplay.hx @@ -2,6 +2,7 @@ package flixel.system.replay; import flixel.FlxG; import flixel.util.FlxArrayUtil; +import flixel.util.FlxDestroyUtil; /** * The replay object both records and replays game recordings, @@ -10,167 +11,136 @@ import flixel.util.FlxArrayUtil; * but since Flixel is fairly deterministic, we can use these to play back * recordings of gameplay with a decent amount of fidelity. */ -class FlxReplay +@:nullSafety(Strict) +class FlxReplay implements IFlxDestroyable { /** * The random number generator seed value for this recording. */ - public var seed:Int; - + public var seed(default, null):Int = 0; + /** * The current frame for this recording. */ - public var frame:Int; - + public var frame(default, null):Int = 0; + /** * The number of frames in this recording. * **Note:** This doesn't include empty records, unlike `getDuration()` */ - public var frameCount:Int; - + public var frameCount(get, never):Int; + inline function get_frameCount() return _frames.length; + /** * Whether the replay has finished playing or not. */ - public var finished:Bool; - + public var finished(default, null):Bool = false; + /** * Internal container for all the frames in this replay. */ - var _frames:Array; - - /** - * Internal tracker for max number of frames we can fit before growing the _frames again. - */ - var _capacity:Int; - + final _frames:Array = []; + /** * Internal helper variable for keeping track of where we are in _frames during recording or replay. */ - var _marker:Int; - + var _marker:Int = 0; + /** * Instantiate a new replay object. Doesn't actually do much until you call create() or load(). */ - public function new() + public function new() {} + + /** + * Common initialization terms used by both create() and load() to set up the replay object. + */ + function init():Void { - seed = 0; - frame = 0; - frameCount = 0; - finished = false; - _frames = null; - _capacity = 0; - _marker = 0; + destroy(); } - + /** * Clean up memory. */ public function destroy():Void { - if (_frames == null) - { - return; - } - var i:Int = frameCount - 1; - while (i >= 0) - { - _frames[i--].destroy(); - } - _frames = null; + FlxDestroyUtil.destroyArray(_frames); } - + /** * Create a new gameplay recording. Requires the current random number generator seed. * - * @param Seed The current seed from the random number generator. + * @param seed The current seed from the random number generator. */ - public function create(Seed:Int):Void + public function create(seed:Int):Void { - destroy(); init(); - seed = Seed; + this.seed = seed; rewind(); } - + /** - * Load replay data from a String object. - * Strings can come from embedded assets or external + * Load replay data from a String object. Strings can come from embedded assets or external * files loaded through the debugger overlay. - * @param FileContents A String object containing a gameplay recording. + * + * @param fileContents A String object containing a gameplay recording. */ - public function load(FileContents:String):Void + public function load(fileContents:String):Void { init(); - - var lines:Array = FileContents.split("\n"); - - seed = Std.parseInt(lines[0]); - - var line:String; - var i:Int = 1; - var l:Int = lines.length; - while (i < l) + + final lines = fileContents.split("\n"); + final seedStr = lines.shift(); + final parsedSeed:Null = seedStr == null ? null : Std.parseInt(seedStr); + if (parsedSeed == null) + throw 'Invalid replay: $fileContents'; + + seed = parsedSeed; + for (line in lines) { - line = lines[i++]; if (line.length > 3) - { - _frames[frameCount++] = new FrameRecord().load(line); - if (frameCount >= _capacity) - { - _capacity *= 2; - _frames.resize(_capacity); - } - } + _frames.push(new FrameRecord().load(line)); } - + rewind(); } - + /** - * Save the current recording data off to a String object. - * Basically goes through and calls FrameRecord.save() on each frame in the replay. - * return The gameplay recording in simple ASCII format. + * Save the current recording data off to a String object. Basically goes through and + * calls FrameRecord.save() on each frame in the replay. + * + * @return The gameplay recording in simple ASCII format. */ - public function save():String + public function save():Null { - if (frameCount <= 0) - { - return null; - } - var output:String = seed + "\n"; - var i:Int = 0; - while (i < frameCount) - { - output += _frames[i++].save() + "\n"; - } - return output; + return Lambda.fold(_frames, (frame, result)->'${result}${frame.save()}\n', '$seed\n'); } - + /** * Get the current input data from the input managers and store it in a new frame record. */ public function recordFrame():Void { var continueFrame = true; - + #if FLX_KEYBOARD var keysRecord:Array = FlxG.keys.record(); if (keysRecord != null) continueFrame = false; #end - + #if FLX_MOUSE var mouseRecord:MouseRecord = FlxG.mouse.record(); if (mouseRecord != null) continueFrame = false; #end - + if (continueFrame) { frame++; return; } - + var frameRecorded = new FrameRecord().create(frame++); #if FLX_MOUSE frameRecorded.mouse = mouseRecord; @@ -178,24 +148,18 @@ class FlxReplay #if FLX_KEYBOARD frameRecorded.keys = keysRecord; #end - - _frames[frameCount++] = frameRecorded; - - if (frameCount >= _capacity) - { - _capacity *= 2; - _frames.resize(_capacity); - } + + _frames.push(frameRecorded); } - + /** * Get the current frame record data and load it into the input managers. */ public function playNextFrame():Void { FlxG.inputs.reset(); - - if (_marker >= frameCount) + + if (_marker >= _frames.length) { finished = true; return; @@ -204,16 +168,16 @@ class FlxReplay { return; } - + var fr:FrameRecord = _frames[_marker++]; - + #if FLX_KEYBOARD if (fr.keys != null) { FlxG.keys.playback(fr.keys); } #end - + #if FLX_MOUSE if (fr.mouse != null) { @@ -221,7 +185,7 @@ class FlxReplay } #end } - + /** * Reset the replay back to the first frame. */ @@ -231,16 +195,6 @@ class FlxReplay frame = 0; finished = false; } - - /** - * Common initialization terms used by both create() and load() to set up the replay object. - */ - function init():Void - { - _capacity = 100; - _frames = new Array(); - frameCount = 0; - } /** * The duration of this replay, in frames. **Note:** this is different from `frameCount`, which diff --git a/flixel/system/replay/FrameRecord.hx b/flixel/system/replay/FrameRecord.hx index 438e62ce01..ce3ccf17f2 100644 --- a/flixel/system/replay/FrameRecord.hx +++ b/flixel/system/replay/FrameRecord.hx @@ -1,9 +1,11 @@ package flixel.system.replay; +import flixel.util.FlxDestroyUtil; + /** * Helper class for the new replay system. Represents all the game inputs for one "frame" or "step" of the game loop. */ -class FrameRecord +class FrameRecord implements IFlxDestroyable { /** * Which frame of the game loop this record is from or for. diff --git a/flixel/util/FlxDestroyUtil.hx b/flixel/util/FlxDestroyUtil.hx index 039fd15117..1901c27eb2 100644 --- a/flixel/util/FlxDestroyUtil.hx +++ b/flixel/util/FlxDestroyUtil.hx @@ -34,8 +34,10 @@ class FlxDestroyUtil { for (e in array) destroy(e); - array.splice(0, array.length); + + array.resize(0); } + return null; }