mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2025-10-30 08:11:25 +00:00
some more work on replays, almost finish up it
This commit is contained in:
parent
841b059f6e
commit
bd748af198
11 changed files with 177 additions and 15 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
-cp src
|
-cp src
|
||||||
-lib heaps
|
-lib heaps
|
||||||
-lib stb_ogg_sound
|
-lib stb_ogg_sound
|
||||||
|
-lib zip
|
||||||
--js marblegame.js
|
--js marblegame.js
|
||||||
-D windowSize=1280x720
|
-D windowSize=1280x720
|
||||||
-D js-es=6
|
-D js-es=6
|
||||||
|
|
|
||||||
BIN
data/ui/play/playback.png
Normal file
BIN
data/ui/play/playback.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 308 B |
BIN
data/ui/play/record.png
Normal file
BIN
data/ui/play/record.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 141 B |
|
|
@ -1335,11 +1335,7 @@ class Marble extends GameObject {
|
||||||
public function update(timeState:TimeState, collisionWorld:CollisionWorld, pathedInteriors:Array<PathedInterior>) {
|
public function update(timeState:TimeState, collisionWorld:CollisionWorld, pathedInteriors:Array<PathedInterior>) {
|
||||||
var move = new Move();
|
var move = new Move();
|
||||||
move.d = new Vector();
|
move.d = new Vector();
|
||||||
if (this.controllable
|
if (this.controllable && this.mode != Finish && !MarbleGame.instance.paused && !this.level.isWatching) {
|
||||||
&& this.mode != Finish
|
|
||||||
&& !MarbleGame.instance.paused
|
|
||||||
&& !this.level.isWatching
|
|
||||||
&& this.level.isRecording) {
|
|
||||||
if (Key.isDown(Settings.controlsSettings.forward)) {
|
if (Key.isDown(Settings.controlsSettings.forward)) {
|
||||||
move.d.x -= 1;
|
move.d.x -= 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package src;
|
package src;
|
||||||
|
|
||||||
|
import src.Replay;
|
||||||
import touch.TouchInput;
|
import touch.TouchInput;
|
||||||
import src.ResourceLoader;
|
import src.ResourceLoader;
|
||||||
import src.AudioManager;
|
import src.AudioManager;
|
||||||
|
|
@ -28,6 +29,7 @@ class MarbleGame {
|
||||||
var scene:h3d.scene.Scene;
|
var scene:h3d.scene.Scene;
|
||||||
|
|
||||||
var paused:Bool;
|
var paused:Bool;
|
||||||
|
var toRecord:Bool = false;
|
||||||
|
|
||||||
var exitGameDlg:ExitGameDlg;
|
var exitGameDlg:ExitGameDlg;
|
||||||
|
|
||||||
|
|
@ -212,14 +214,26 @@ class MarbleGame {
|
||||||
paused = false;
|
paused = false;
|
||||||
var pmg = new PlayMissionGui();
|
var pmg = new PlayMissionGui();
|
||||||
PlayMissionGui.currentSelectionStatic = world.mission.index;
|
PlayMissionGui.currentSelectionStatic = world.mission.index;
|
||||||
|
if (world.isRecording) {
|
||||||
|
world.saveReplay();
|
||||||
|
}
|
||||||
world.dispose();
|
world.dispose();
|
||||||
world = null;
|
world = null;
|
||||||
canvas.setContent(pmg);
|
canvas.setContent(pmg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function playMission(mission:Mission) {
|
public function playMission(mission:Mission) {
|
||||||
|
canvas.clearContent();
|
||||||
|
world = new MarbleWorld(scene, scene2d, mission, toRecord);
|
||||||
|
toRecord = false;
|
||||||
|
world.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function watchMissionReplay(mission:Mission, replay:Replay) {
|
||||||
canvas.clearContent();
|
canvas.clearContent();
|
||||||
world = new MarbleWorld(scene, scene2d, mission);
|
world = new MarbleWorld(scene, scene2d, mission);
|
||||||
|
world.replay = replay;
|
||||||
|
world.isWatching = true;
|
||||||
world.init();
|
world.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ class MarbleWorld extends Scheduler {
|
||||||
// Replay
|
// Replay
|
||||||
public var replay:Replay;
|
public var replay:Replay;
|
||||||
public var isWatching:Bool = false;
|
public var isWatching:Bool = false;
|
||||||
public var isRecording:Bool = true;
|
public var isRecording:Bool = false;
|
||||||
|
|
||||||
// Loading
|
// Loading
|
||||||
var resourceLoadFuncs:Array<(() -> Void)->Void> = [];
|
var resourceLoadFuncs:Array<(() -> Void)->Void> = [];
|
||||||
|
|
@ -149,11 +149,12 @@ class MarbleWorld extends Scheduler {
|
||||||
|
|
||||||
var lock:Bool = false;
|
var lock:Bool = false;
|
||||||
|
|
||||||
public function new(scene:Scene, scene2d:h2d.Scene, mission:Mission) {
|
public function new(scene:Scene, scene2d:h2d.Scene, mission:Mission, record:Bool = false) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.scene2d = scene2d;
|
this.scene2d = scene2d;
|
||||||
this.mission = mission;
|
this.mission = mission;
|
||||||
this.replay = new Replay(mission.path);
|
this.replay = new Replay(mission.path);
|
||||||
|
this.isRecording = record;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function init() {
|
public function init() {
|
||||||
|
|
@ -323,7 +324,6 @@ class MarbleWorld extends Scheduler {
|
||||||
public function restart() {
|
public function restart() {
|
||||||
if (!this.isWatching) {
|
if (!this.isWatching) {
|
||||||
this.replay.clear();
|
this.replay.clear();
|
||||||
this.isRecording = true;
|
|
||||||
} else
|
} else
|
||||||
this.replay.rewind();
|
this.replay.rewind();
|
||||||
this.timeState.currentAttemptTime = 0;
|
this.timeState.currentAttemptTime = 0;
|
||||||
|
|
@ -863,6 +863,7 @@ class MarbleWorld extends Scheduler {
|
||||||
PlayMissionGui.currentSelectionStatic = mission.index + 1;
|
PlayMissionGui.currentSelectionStatic = mission.index + 1;
|
||||||
MarbleGame.canvas.setContent(pmg);
|
MarbleGame.canvas.setContent(pmg);
|
||||||
#if js
|
#if js
|
||||||
|
var pointercontainer = js.Browser.document.querySelector("#pointercontainer");
|
||||||
pointercontainer.hidden = false;
|
pointercontainer.hidden = false;
|
||||||
#end
|
#end
|
||||||
return;
|
return;
|
||||||
|
|
@ -1195,7 +1196,10 @@ class MarbleWorld extends Scheduler {
|
||||||
var pointercontainer = js.Browser.document.querySelector("#pointercontainer");
|
var pointercontainer = js.Browser.document.querySelector("#pointercontainer");
|
||||||
pointercontainer.hidden = false;
|
pointercontainer.hidden = false;
|
||||||
#end
|
#end
|
||||||
this.isRecording = false; // Stop recording here
|
if (this.isRecording) {
|
||||||
|
this.isRecording = false; // Stop recording here
|
||||||
|
this.saveReplay();
|
||||||
|
}
|
||||||
if (Util.isTouchDevice()) {
|
if (Util.isTouchDevice()) {
|
||||||
MarbleGame.instance.touchInput.setControlsEnabled(false);
|
MarbleGame.instance.touchInput.setControlsEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
@ -1323,6 +1327,20 @@ class MarbleWorld extends Scheduler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function saveReplay() {
|
||||||
|
var replayBytes = this.replay.write();
|
||||||
|
hxd.File.saveAs(replayBytes, {
|
||||||
|
title: 'Save Replay',
|
||||||
|
fileTypes: [
|
||||||
|
{
|
||||||
|
name: "Replay (*.mbr)",
|
||||||
|
extensions: ["mbr"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
defaultPath: '${this.mission.title}${this.timeState.gameplayClock}.mbr'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function dispose() {
|
public function dispose() {
|
||||||
this.playGui.dispose();
|
this.playGui.dispose();
|
||||||
scene.removeChildren();
|
scene.removeChildren();
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ class MissionList {
|
||||||
static var advancedMissions:Array<Mission>;
|
static var advancedMissions:Array<Mission>;
|
||||||
static var customMissions:Array<Mission>;
|
static var customMissions:Array<Mission>;
|
||||||
|
|
||||||
|
static var missions:Map<String, Mission>;
|
||||||
|
|
||||||
static var _build:Bool = false;
|
static var _build:Bool = false;
|
||||||
|
|
||||||
public function new() {}
|
public function new() {}
|
||||||
|
|
@ -17,6 +19,9 @@ class MissionList {
|
||||||
public static function buildMissionList() {
|
public static function buildMissionList() {
|
||||||
if (_build)
|
if (_build)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
missions = new Map<String, Mission>();
|
||||||
|
|
||||||
function parseDifficulty(difficulty:String) {
|
function parseDifficulty(difficulty:String) {
|
||||||
#if (hl && !android)
|
#if (hl && !android)
|
||||||
var difficultyFiles = ResourceLoader.fileSystem.dir("data/missions/" + difficulty);
|
var difficultyFiles = ResourceLoader.fileSystem.dir("data/missions/" + difficulty);
|
||||||
|
|
@ -30,6 +35,7 @@ class MissionList {
|
||||||
var misParser = new MisParser(file.getText());
|
var misParser = new MisParser(file.getText());
|
||||||
var mInfo = misParser.parseMissionInfo();
|
var mInfo = misParser.parseMissionInfo();
|
||||||
var mission = Mission.fromMissionInfo(file.path, mInfo);
|
var mission = Mission.fromMissionInfo(file.path, mInfo);
|
||||||
|
missions.set(file.path, mission);
|
||||||
difficultyMissions.push(mission);
|
difficultyMissions.push(mission);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package src;
|
package src;
|
||||||
|
|
||||||
|
import haxe.io.BytesInput;
|
||||||
|
import haxe.zip.Huffman;
|
||||||
import haxe.io.Bytes;
|
import haxe.io.Bytes;
|
||||||
import haxe.io.BytesBuffer;
|
import haxe.io.BytesBuffer;
|
||||||
import dif.io.BytesReader;
|
import dif.io.BytesReader;
|
||||||
|
|
@ -195,6 +197,8 @@ class Replay {
|
||||||
var currentPlaybackFrameIdx:Int;
|
var currentPlaybackFrameIdx:Int;
|
||||||
var currentPlaybackTime:Float;
|
var currentPlaybackTime:Float;
|
||||||
|
|
||||||
|
var version:Int = 1;
|
||||||
|
|
||||||
public function new(mission:String) {
|
public function new(mission:String) {
|
||||||
this.mission = mission;
|
this.mission = mission;
|
||||||
this.initialState = new ReplayInitialState();
|
this.initialState = new ReplayInitialState();
|
||||||
|
|
@ -311,20 +315,37 @@ class Replay {
|
||||||
|
|
||||||
var buf = bw.getBuffer();
|
var buf = bw.getBuffer();
|
||||||
var bufsize = buf.length;
|
var bufsize = buf.length;
|
||||||
var compressed = haxe.zip.Compress.run(bw.getBuffer(), 7);
|
#if hl
|
||||||
|
var compressed = haxe.zip.Compress.run(bw.getBuffer(), 9);
|
||||||
|
#end
|
||||||
|
#if js
|
||||||
|
var stream = zip.DeflateStream.create(zip.DeflateStream.CompressionLevel.GOOD, false);
|
||||||
|
stream.write(new BytesInput(bw.getBuffer()));
|
||||||
|
var compressed = stream.finalize();
|
||||||
|
#end
|
||||||
|
|
||||||
var finalB = new BytesBuffer();
|
var finalB = new BytesBuffer();
|
||||||
|
finalB.addByte(version);
|
||||||
finalB.addInt32(bufsize);
|
finalB.addInt32(bufsize);
|
||||||
finalB.addBytes(compressed, 4, compressed.length);
|
finalB.addBytes(compressed, 0, compressed.length);
|
||||||
|
|
||||||
return finalB.getBytes();
|
return finalB.getBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function read(data:Bytes) {
|
public function read(data:Bytes) {
|
||||||
var uncompressedLength = data.getInt32(0);
|
var replayVersion = data.get(0);
|
||||||
var compressedData = data.sub(4, data.length - 4);
|
if (replayVersion > version) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var uncompressedLength = data.getInt32(1);
|
||||||
|
var compressedData = data.sub(5, data.length - 5);
|
||||||
|
|
||||||
|
#if hl
|
||||||
var uncompressed = haxe.zip.Uncompress.run(compressedData, uncompressedLength);
|
var uncompressed = haxe.zip.Uncompress.run(compressedData, uncompressedLength);
|
||||||
|
#end
|
||||||
|
#if js
|
||||||
|
var uncompressed = haxe.zip.InflateImpl.run(new BytesInput(compressedData), uncompressedLength);
|
||||||
|
#end
|
||||||
var br = new BytesReader(uncompressed);
|
var br = new BytesReader(uncompressed);
|
||||||
this.mission = br.readStr();
|
this.mission = br.readStr();
|
||||||
this.initialState.read(br);
|
this.initialState.read(br);
|
||||||
|
|
@ -335,5 +356,6 @@ class Replay {
|
||||||
frame.read(br);
|
frame.read(br);
|
||||||
this.frames.push(frame);
|
this.frames.push(frame);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,15 @@ class BytesWriter {
|
||||||
public function writeUInt16(int:Int) {
|
public function writeUInt16(int:Int) {
|
||||||
var h = int >> 8;
|
var h = int >> 8;
|
||||||
var l = int & 0xFF;
|
var l = int & 0xFF;
|
||||||
this.bytes.addByte(h);
|
|
||||||
this.bytes.addByte(l);
|
this.bytes.addByte(l);
|
||||||
|
this.bytes.addByte(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function writeInt16(int:Int) {
|
public function writeInt16(int:Int) {
|
||||||
var h = int >> 8;
|
var h = int >> 8;
|
||||||
var l = int & 0xFF;
|
var l = int & 0xFF;
|
||||||
this.bytes.addByte(h);
|
|
||||||
this.bytes.addByte(l);
|
this.bytes.addByte(l);
|
||||||
|
this.bytes.addByte(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function writeByte(int:Int) {
|
public function writeByte(int:Int) {
|
||||||
|
|
|
||||||
60
src/gui/MessageBoxOkDlg.hx
Normal file
60
src/gui/MessageBoxOkDlg.hx
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
package gui;
|
||||||
|
|
||||||
|
import src.MarbleGame;
|
||||||
|
import hxd.res.BitmapFont;
|
||||||
|
import h3d.Vector;
|
||||||
|
import src.ResourceLoader;
|
||||||
|
import src.Settings;
|
||||||
|
|
||||||
|
class MessageBoxOkDlg extends GuiControl {
|
||||||
|
public function new(text:String) {
|
||||||
|
super();
|
||||||
|
this.horizSizing = Width;
|
||||||
|
this.vertSizing = Height;
|
||||||
|
this.position = new Vector();
|
||||||
|
this.extent = new Vector(640, 480);
|
||||||
|
|
||||||
|
var domcasual24fontdata = ResourceLoader.getFileEntry("data/font/DomCasualD.fnt");
|
||||||
|
var domcasual24b = new BitmapFont(domcasual24fontdata.entry);
|
||||||
|
@:privateAccess domcasual24b.loader = ResourceLoader.loader;
|
||||||
|
var domcasual24 = domcasual24b.toSdfFont(cast 20 * Settings.uiScale, MultiChannel);
|
||||||
|
|
||||||
|
function loadButtonImages(path:String) {
|
||||||
|
var normal = ResourceLoader.getResource('${path}_n.png', ResourceLoader.getImage, this.imageResources).toTile();
|
||||||
|
var hover = ResourceLoader.getResource('${path}_h.png', ResourceLoader.getImage, this.imageResources).toTile();
|
||||||
|
var pressed = ResourceLoader.getResource('${path}_d.png', ResourceLoader.getImage, this.imageResources).toTile();
|
||||||
|
return [normal, hover, pressed];
|
||||||
|
}
|
||||||
|
|
||||||
|
var yesNoFrame = new GuiImage(ResourceLoader.getResource("data/ui/common/dialog.png", ResourceLoader.getImage, this.imageResources).toTile());
|
||||||
|
yesNoFrame.horizSizing = Center;
|
||||||
|
yesNoFrame.vertSizing = Center;
|
||||||
|
yesNoFrame.position = new Vector(187, 156);
|
||||||
|
yesNoFrame.extent = new Vector(300, 161);
|
||||||
|
this.addChild(yesNoFrame);
|
||||||
|
|
||||||
|
var yesNoText = new GuiMLText(domcasual24, null);
|
||||||
|
yesNoText.position = new Vector(33, 46);
|
||||||
|
yesNoText.horizSizing = Center;
|
||||||
|
yesNoText.extent = new Vector(198, 23);
|
||||||
|
yesNoText.text.text = text;
|
||||||
|
yesNoText.text.textColor = 0;
|
||||||
|
yesNoText.text.maxWidth = 198;
|
||||||
|
yesNoFrame.addChild(yesNoText);
|
||||||
|
|
||||||
|
var okButton = new GuiButton(loadButtonImages("data/ui/common/ok"));
|
||||||
|
okButton.position = new Vector(117, 85);
|
||||||
|
okButton.extent = new Vector(78, 59);
|
||||||
|
okButton.vertSizing = Top;
|
||||||
|
okButton.pressedAction = (sender) -> {
|
||||||
|
MarbleGame.canvas.popDialog(this);
|
||||||
|
}
|
||||||
|
yesNoFrame.addChild(okButton);
|
||||||
|
|
||||||
|
if (yesNoText.text.getBounds().yMax > yesNoText.extent.y) {
|
||||||
|
var diff = yesNoText.text.getBounds().yMax - yesNoText.extent.y;
|
||||||
|
yesNoFrame.extent.y += diff;
|
||||||
|
okButton.position.y += diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package gui;
|
package gui;
|
||||||
|
|
||||||
|
import src.Replay;
|
||||||
import haxe.ds.Option;
|
import haxe.ds.Option;
|
||||||
import hxd.Key;
|
import hxd.Key;
|
||||||
import gui.GuiControl.MouseState;
|
import gui.GuiControl.MouseState;
|
||||||
|
|
@ -128,6 +129,50 @@ class PlayMissionGui extends GuiImage {
|
||||||
var filt = new ColorMatrix(Matrix.I());
|
var filt = new ColorMatrix(Matrix.I());
|
||||||
pmPreview.bmp.filter = filt;
|
pmPreview.bmp.filter = filt;
|
||||||
|
|
||||||
|
var replayPlayButton = new GuiImage(ResourceLoader.getResource("data/ui/play/playback.png", ResourceLoader.getImage, this.imageResources).toTile());
|
||||||
|
replayPlayButton.position = new Vector(38, 315);
|
||||||
|
replayPlayButton.extent = new Vector(18, 18);
|
||||||
|
replayPlayButton.pressedAction = (sender) -> {
|
||||||
|
hxd.File.browse((replayToLoad) -> {
|
||||||
|
replayToLoad.load((replayData) -> {
|
||||||
|
var replay = new Replay("");
|
||||||
|
if (!replay.read(replayData)) {
|
||||||
|
cast(this.parent, Canvas).pushDialog(new MessageBoxOkDlg("Cannot load replay."));
|
||||||
|
// Idk do something to notify the user here
|
||||||
|
} else {
|
||||||
|
var repmis = replay.mission;
|
||||||
|
#if js
|
||||||
|
repmis = StringTools.replace(repmis, "data/", "");
|
||||||
|
#end
|
||||||
|
var playMis = MissionList.missions.get(repmis);
|
||||||
|
if (playMis != null) {
|
||||||
|
cast(this.parent, Canvas).marbleGame.watchMissionReplay(playMis, replay);
|
||||||
|
} else {
|
||||||
|
cast(this.parent, Canvas).pushDialog(new MessageBoxOkDlg("Cannot load replay."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, {
|
||||||
|
title: "Select replay file",
|
||||||
|
fileTypes: [
|
||||||
|
{
|
||||||
|
name: "Replay (*.mbr)",
|
||||||
|
extensions: ["mbr"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
pmBox.addChild(replayPlayButton);
|
||||||
|
|
||||||
|
var replayRecordButton = new GuiImage(ResourceLoader.getResource("data/ui/play/record.png", ResourceLoader.getImage, this.imageResources).toTile());
|
||||||
|
replayRecordButton.position = new Vector(56, 315);
|
||||||
|
replayRecordButton.extent = new Vector(18, 18);
|
||||||
|
replayRecordButton.pressedAction = (sender) -> {
|
||||||
|
cast(this.parent, Canvas).marbleGame.toRecord = true;
|
||||||
|
cast(this.parent, Canvas).pushDialog(new MessageBoxOkDlg("The next mission you play will be recorded."));
|
||||||
|
};
|
||||||
|
pmBox.addChild(replayRecordButton);
|
||||||
|
|
||||||
var levelWnd = new GuiImage(ResourceLoader.getResource("data/ui/play/level_window.png", ResourceLoader.getImage, this.imageResources).toTile());
|
var levelWnd = new GuiImage(ResourceLoader.getResource("data/ui/play/level_window.png", ResourceLoader.getImage, this.imageResources).toTile());
|
||||||
levelWnd.position = new Vector();
|
levelWnd.position = new Vector();
|
||||||
levelWnd.extent = new Vector(258, 194);
|
levelWnd.extent = new Vector(258, 194);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue