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
|
||||
-lib heaps
|
||||
-lib stb_ogg_sound
|
||||
-lib zip
|
||||
--js marblegame.js
|
||||
-D windowSize=1280x720
|
||||
-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>) {
|
||||
var move = new Move();
|
||||
move.d = new Vector();
|
||||
if (this.controllable
|
||||
&& this.mode != Finish
|
||||
&& !MarbleGame.instance.paused
|
||||
&& !this.level.isWatching
|
||||
&& this.level.isRecording) {
|
||||
if (this.controllable && this.mode != Finish && !MarbleGame.instance.paused && !this.level.isWatching) {
|
||||
if (Key.isDown(Settings.controlsSettings.forward)) {
|
||||
move.d.x -= 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package src;
|
||||
|
||||
import src.Replay;
|
||||
import touch.TouchInput;
|
||||
import src.ResourceLoader;
|
||||
import src.AudioManager;
|
||||
|
|
@ -28,6 +29,7 @@ class MarbleGame {
|
|||
var scene:h3d.scene.Scene;
|
||||
|
||||
var paused:Bool;
|
||||
var toRecord:Bool = false;
|
||||
|
||||
var exitGameDlg:ExitGameDlg;
|
||||
|
||||
|
|
@ -212,14 +214,26 @@ class MarbleGame {
|
|||
paused = false;
|
||||
var pmg = new PlayMissionGui();
|
||||
PlayMissionGui.currentSelectionStatic = world.mission.index;
|
||||
if (world.isRecording) {
|
||||
world.saveReplay();
|
||||
}
|
||||
world.dispose();
|
||||
world = null;
|
||||
canvas.setContent(pmg);
|
||||
}
|
||||
|
||||
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();
|
||||
world = new MarbleWorld(scene, scene2d, mission);
|
||||
world.replay = replay;
|
||||
world.isWatching = true;
|
||||
world.init();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ class MarbleWorld extends Scheduler {
|
|||
// Replay
|
||||
public var replay:Replay;
|
||||
public var isWatching:Bool = false;
|
||||
public var isRecording:Bool = true;
|
||||
public var isRecording:Bool = false;
|
||||
|
||||
// Loading
|
||||
var resourceLoadFuncs:Array<(() -> Void)->Void> = [];
|
||||
|
|
@ -149,11 +149,12 @@ class MarbleWorld extends Scheduler {
|
|||
|
||||
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.scene2d = scene2d;
|
||||
this.mission = mission;
|
||||
this.replay = new Replay(mission.path);
|
||||
this.isRecording = record;
|
||||
}
|
||||
|
||||
public function init() {
|
||||
|
|
@ -323,7 +324,6 @@ class MarbleWorld extends Scheduler {
|
|||
public function restart() {
|
||||
if (!this.isWatching) {
|
||||
this.replay.clear();
|
||||
this.isRecording = true;
|
||||
} else
|
||||
this.replay.rewind();
|
||||
this.timeState.currentAttemptTime = 0;
|
||||
|
|
@ -863,6 +863,7 @@ class MarbleWorld extends Scheduler {
|
|||
PlayMissionGui.currentSelectionStatic = mission.index + 1;
|
||||
MarbleGame.canvas.setContent(pmg);
|
||||
#if js
|
||||
var pointercontainer = js.Browser.document.querySelector("#pointercontainer");
|
||||
pointercontainer.hidden = false;
|
||||
#end
|
||||
return;
|
||||
|
|
@ -1195,7 +1196,10 @@ class MarbleWorld extends Scheduler {
|
|||
var pointercontainer = js.Browser.document.querySelector("#pointercontainer");
|
||||
pointercontainer.hidden = false;
|
||||
#end
|
||||
this.isRecording = false; // Stop recording here
|
||||
if (this.isRecording) {
|
||||
this.isRecording = false; // Stop recording here
|
||||
this.saveReplay();
|
||||
}
|
||||
if (Util.isTouchDevice()) {
|
||||
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() {
|
||||
this.playGui.dispose();
|
||||
scene.removeChildren();
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ class MissionList {
|
|||
static var advancedMissions:Array<Mission>;
|
||||
static var customMissions:Array<Mission>;
|
||||
|
||||
static var missions:Map<String, Mission>;
|
||||
|
||||
static var _build:Bool = false;
|
||||
|
||||
public function new() {}
|
||||
|
|
@ -17,6 +19,9 @@ class MissionList {
|
|||
public static function buildMissionList() {
|
||||
if (_build)
|
||||
return;
|
||||
|
||||
missions = new Map<String, Mission>();
|
||||
|
||||
function parseDifficulty(difficulty:String) {
|
||||
#if (hl && !android)
|
||||
var difficultyFiles = ResourceLoader.fileSystem.dir("data/missions/" + difficulty);
|
||||
|
|
@ -30,6 +35,7 @@ class MissionList {
|
|||
var misParser = new MisParser(file.getText());
|
||||
var mInfo = misParser.parseMissionInfo();
|
||||
var mission = Mission.fromMissionInfo(file.path, mInfo);
|
||||
missions.set(file.path, mission);
|
||||
difficultyMissions.push(mission);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package src;
|
||||
|
||||
import haxe.io.BytesInput;
|
||||
import haxe.zip.Huffman;
|
||||
import haxe.io.Bytes;
|
||||
import haxe.io.BytesBuffer;
|
||||
import dif.io.BytesReader;
|
||||
|
|
@ -195,6 +197,8 @@ class Replay {
|
|||
var currentPlaybackFrameIdx:Int;
|
||||
var currentPlaybackTime:Float;
|
||||
|
||||
var version:Int = 1;
|
||||
|
||||
public function new(mission:String) {
|
||||
this.mission = mission;
|
||||
this.initialState = new ReplayInitialState();
|
||||
|
|
@ -311,20 +315,37 @@ class Replay {
|
|||
|
||||
var buf = bw.getBuffer();
|
||||
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();
|
||||
finalB.addByte(version);
|
||||
finalB.addInt32(bufsize);
|
||||
finalB.addBytes(compressed, 4, compressed.length);
|
||||
finalB.addBytes(compressed, 0, compressed.length);
|
||||
|
||||
return finalB.getBytes();
|
||||
}
|
||||
|
||||
public function read(data:Bytes) {
|
||||
var uncompressedLength = data.getInt32(0);
|
||||
var compressedData = data.sub(4, data.length - 4);
|
||||
var replayVersion = data.get(0);
|
||||
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);
|
||||
#end
|
||||
#if js
|
||||
var uncompressed = haxe.zip.InflateImpl.run(new BytesInput(compressedData), uncompressedLength);
|
||||
#end
|
||||
var br = new BytesReader(uncompressed);
|
||||
this.mission = br.readStr();
|
||||
this.initialState.read(br);
|
||||
|
|
@ -335,5 +356,6 @@ class Replay {
|
|||
frame.read(br);
|
||||
this.frames.push(frame);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,15 +19,15 @@ class BytesWriter {
|
|||
public function writeUInt16(int:Int) {
|
||||
var h = int >> 8;
|
||||
var l = int & 0xFF;
|
||||
this.bytes.addByte(h);
|
||||
this.bytes.addByte(l);
|
||||
this.bytes.addByte(h);
|
||||
}
|
||||
|
||||
public function writeInt16(int:Int) {
|
||||
var h = int >> 8;
|
||||
var l = int & 0xFF;
|
||||
this.bytes.addByte(h);
|
||||
this.bytes.addByte(l);
|
||||
this.bytes.addByte(h);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import src.Replay;
|
||||
import haxe.ds.Option;
|
||||
import hxd.Key;
|
||||
import gui.GuiControl.MouseState;
|
||||
|
|
@ -128,6 +129,50 @@ class PlayMissionGui extends GuiImage {
|
|||
var filt = new ColorMatrix(Matrix.I());
|
||||
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());
|
||||
levelWnd.position = new Vector();
|
||||
levelWnd.extent = new Vector(258, 194);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue