mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2025-10-30 08:11:25 +00:00
do more work on replays
This commit is contained in:
parent
24c87da4fd
commit
841b059f6e
5 changed files with 211 additions and 17 deletions
|
|
@ -214,7 +214,9 @@ class CameraController extends Object {
|
|||
}
|
||||
|
||||
if (!this.level.isWatching) {
|
||||
this.level.replay.recordCameraState(CameraPitch, CameraYaw);
|
||||
if (this.level.isRecording) {
|
||||
this.level.replay.recordCameraState(CameraPitch, CameraYaw);
|
||||
}
|
||||
} else {
|
||||
CameraPitch = this.level.replay.currentPlaybackFrame.cameraPitch;
|
||||
CameraYaw = this.level.replay.currentPlaybackFrame.cameraYaw;
|
||||
|
|
|
|||
|
|
@ -1335,7 +1335,11 @@ 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) {
|
||||
if (this.controllable
|
||||
&& this.mode != Finish
|
||||
&& !MarbleGame.instance.paused
|
||||
&& !this.level.isWatching
|
||||
&& this.level.isRecording) {
|
||||
if (Key.isDown(Settings.controlsSettings.forward)) {
|
||||
move.d.x -= 1;
|
||||
}
|
||||
|
|
@ -1367,16 +1371,20 @@ class Marble extends GameObject {
|
|||
move.powerup = true;
|
||||
move.d = new Vector(this.level.replay.currentPlaybackFrame.marbleX, this.level.replay.currentPlaybackFrame.marbleY, 0);
|
||||
} else {
|
||||
this.level.replay.recordMarbleStateFlags(move.jump, move.powerup, false);
|
||||
this.level.replay.recordMarbleInput(move.d.x, move.d.y);
|
||||
if (this.level.isRecording) {
|
||||
this.level.replay.recordMarbleStateFlags(move.jump, move.powerup, false);
|
||||
this.level.replay.recordMarbleInput(move.d.x, move.d.y);
|
||||
}
|
||||
}
|
||||
|
||||
playedSounds = [];
|
||||
advancePhysics(timeState, move, collisionWorld, pathedInteriors);
|
||||
|
||||
if (!this.level.isWatching)
|
||||
this.level.replay.recordMarbleState(this.getAbsPos().getPosition(), this.velocity, this.getRotationQuat(), this.omega);
|
||||
else {
|
||||
if (!this.level.isWatching) {
|
||||
if (this.level.isRecording) {
|
||||
this.level.replay.recordMarbleState(this.getAbsPos().getPosition(), this.velocity, this.getRotationQuat(), this.omega);
|
||||
}
|
||||
} else {
|
||||
var expectedPos = this.level.replay.currentPlaybackFrame.marblePosition.clone();
|
||||
var expectedVel = this.level.replay.currentPlaybackFrame.marbleVelocity.clone();
|
||||
var expectedOmega = this.level.replay.currentPlaybackFrame.marbleAngularVelocity.clone();
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ class MarbleWorld extends Scheduler {
|
|||
// Replay
|
||||
public var replay:Replay;
|
||||
public var isWatching:Bool = false;
|
||||
public var isRecording:Bool = true;
|
||||
|
||||
// Loading
|
||||
var resourceLoadFuncs:Array<(() -> Void)->Void> = [];
|
||||
|
|
@ -320,9 +321,10 @@ class MarbleWorld extends Scheduler {
|
|||
}
|
||||
|
||||
public function restart() {
|
||||
if (!this.isWatching)
|
||||
if (!this.isWatching) {
|
||||
this.replay.clear();
|
||||
else
|
||||
this.isRecording = true;
|
||||
} else
|
||||
this.replay.rewind();
|
||||
this.timeState.currentAttemptTime = 0;
|
||||
this.timeState.gameplayClock = 0;
|
||||
|
|
@ -339,6 +341,33 @@ class MarbleWorld extends Scheduler {
|
|||
this.playGui.formatGemCounter(this.gemCount, this.totalGems);
|
||||
}
|
||||
|
||||
// Record/Playback trapdoor and landmine states
|
||||
var tidx = 0;
|
||||
var lidx = 0;
|
||||
for (dtss in this.dtsObjects) {
|
||||
if (dtss is Trapdoor) {
|
||||
var trapdoor:Trapdoor = cast dtss;
|
||||
if (!this.isWatching) {
|
||||
this.replay.recordTrapdoorState(trapdoor.lastContactTime - this.timeState.timeSinceLoad, trapdoor.lastDirection, trapdoor.lastCompletion);
|
||||
} else {
|
||||
var state = this.replay.getTrapdoorState(tidx);
|
||||
trapdoor.lastContactTime = state.lastContactTime + this.timeState.timeSinceLoad;
|
||||
trapdoor.lastDirection = state.lastDirection;
|
||||
trapdoor.lastCompletion = state.lastCompletion;
|
||||
}
|
||||
tidx++;
|
||||
}
|
||||
if (dtss is LandMine) {
|
||||
var landmine:LandMine = cast dtss;
|
||||
if (!this.isWatching) {
|
||||
this.replay.recordLandMineState(landmine.disappearTime - this.timeState.timeSinceLoad);
|
||||
} else {
|
||||
landmine.disappearTime = this.replay.getLandMineState(lidx) + this.timeState.timeSinceLoad;
|
||||
}
|
||||
lidx++;
|
||||
}
|
||||
}
|
||||
|
||||
var startquat = this.getStartPositionAndOrientation();
|
||||
|
||||
this.marble.setPosition(startquat.position.x, startquat.position.y, startquat.position.z + 3);
|
||||
|
|
@ -819,9 +848,11 @@ class MarbleWorld extends Scheduler {
|
|||
if (!_ready) {
|
||||
return;
|
||||
}
|
||||
if (!this.isWatching)
|
||||
this.replay.startFrame();
|
||||
else {
|
||||
if (!this.isWatching) {
|
||||
if (this.isRecording) {
|
||||
this.replay.startFrame();
|
||||
}
|
||||
} else {
|
||||
if (!this.replay.advance(dt)) {
|
||||
if (Util.isTouchDevice()) {
|
||||
MarbleGame.instance.touchInput.hideControls(@:privateAccess this.playGui.playGuiCtrl);
|
||||
|
|
@ -834,6 +865,7 @@ class MarbleWorld extends Scheduler {
|
|||
#if js
|
||||
pointercontainer.hidden = false;
|
||||
#end
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -859,8 +891,11 @@ class MarbleWorld extends Scheduler {
|
|||
ProfilerUI.measure("updateAudio");
|
||||
AudioManager.update(this.scene);
|
||||
|
||||
if (!this.isWatching)
|
||||
this.replay.endFrame();
|
||||
if (!this.isWatching) {
|
||||
if (this.isRecording) {
|
||||
this.replay.endFrame();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.outOfBounds && this.finishTime == null && Key.isDown(Settings.controlsSettings.powerup)) {
|
||||
this.clearSchedule();
|
||||
|
|
@ -958,7 +993,7 @@ class MarbleWorld extends Scheduler {
|
|||
this.timeState.gameplayClock = finishTime.gameplayClock;
|
||||
playGui.formatTimer(this.timeState.gameplayClock);
|
||||
|
||||
if (!this.isWatching)
|
||||
if (!this.isWatching && this.isRecording)
|
||||
this.replay.recordTimeState(timeState.currentAttemptTime, timeState.gameplayClock, this.bonusTime);
|
||||
}
|
||||
|
||||
|
|
@ -1160,6 +1195,7 @@ class MarbleWorld extends Scheduler {
|
|||
var pointercontainer = js.Browser.document.querySelector("#pointercontainer");
|
||||
pointercontainer.hidden = false;
|
||||
#end
|
||||
this.isRecording = false; // Stop recording here
|
||||
if (Util.isTouchDevice()) {
|
||||
MarbleGame.instance.touchInput.setControlsEnabled(false);
|
||||
}
|
||||
|
|
|
|||
150
src/Replay.hx
150
src/Replay.hx
|
|
@ -1,5 +1,9 @@
|
|||
package src;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import haxe.io.BytesBuffer;
|
||||
import dif.io.BytesReader;
|
||||
import dif.io.BytesWriter;
|
||||
import haxe.EnumFlags;
|
||||
import h3d.Quat;
|
||||
import h3d.Vector;
|
||||
|
|
@ -94,13 +98,96 @@ class ReplayFrame {
|
|||
|
||||
return interpFrame;
|
||||
}
|
||||
|
||||
public function write(bw:BytesWriter) {
|
||||
bw.writeFloat(this.time);
|
||||
bw.writeFloat(this.clockTime);
|
||||
bw.writeFloat(this.bonusTime);
|
||||
bw.writeFloat(this.marblePosition.x);
|
||||
bw.writeFloat(this.marblePosition.y);
|
||||
bw.writeFloat(this.marblePosition.z);
|
||||
bw.writeFloat(this.marbleVelocity.x);
|
||||
bw.writeFloat(this.marbleVelocity.y);
|
||||
bw.writeFloat(this.marbleVelocity.z);
|
||||
bw.writeFloat(this.marbleOrientation.x);
|
||||
bw.writeFloat(this.marbleOrientation.y);
|
||||
bw.writeFloat(this.marbleOrientation.z);
|
||||
bw.writeFloat(this.marbleOrientation.w);
|
||||
bw.writeFloat(this.marbleAngularVelocity.x);
|
||||
bw.writeFloat(this.marbleAngularVelocity.y);
|
||||
bw.writeFloat(this.marbleAngularVelocity.z);
|
||||
bw.writeByte(this.marbleStateFlags.toInt());
|
||||
bw.writeFloat(this.cameraPitch);
|
||||
bw.writeFloat(this.cameraYaw);
|
||||
bw.writeFloat(this.marbleX);
|
||||
bw.writeFloat(this.marbleY);
|
||||
}
|
||||
|
||||
public function read(br:BytesReader) {
|
||||
this.time = br.readFloat();
|
||||
this.clockTime = br.readFloat();
|
||||
this.bonusTime = br.readFloat();
|
||||
this.marblePosition = new Vector(br.readFloat(), br.readFloat(), br.readFloat());
|
||||
this.marbleVelocity = new Vector(br.readFloat(), br.readFloat(), br.readFloat());
|
||||
this.marbleOrientation = new Quat(br.readFloat(), br.readFloat(), br.readFloat(), br.readFloat());
|
||||
this.marbleAngularVelocity = new Vector(br.readFloat(), br.readFloat(), br.readFloat());
|
||||
this.marbleStateFlags = EnumFlags.ofInt(br.readByte());
|
||||
this.cameraPitch = br.readFloat();
|
||||
this.cameraYaw = br.readFloat();
|
||||
this.marbleX = br.readFloat();
|
||||
this.marbleY = br.readFloat();
|
||||
}
|
||||
}
|
||||
|
||||
@:publicFields
|
||||
class ReplayInitialState {
|
||||
var trapdoorLastContactTimes:Array<Float> = [];
|
||||
var trapdoorLastDirections:Array<Int> = [];
|
||||
var trapdoorLastCompletions:Array<Float> = [];
|
||||
var landMineDisappearTimes:Array<Float> = [];
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function write(bw:BytesWriter) {
|
||||
bw.writeInt16(this.trapdoorLastContactTimes.length);
|
||||
for (time in this.trapdoorLastContactTimes) {
|
||||
bw.writeFloat(time);
|
||||
}
|
||||
for (dir in this.trapdoorLastDirections) {
|
||||
bw.writeByte(dir);
|
||||
}
|
||||
for (completion in this.trapdoorLastCompletions) {
|
||||
bw.writeFloat(completion);
|
||||
}
|
||||
bw.writeInt16(this.landMineDisappearTimes.length);
|
||||
for (time in this.landMineDisappearTimes) {
|
||||
bw.writeFloat(time);
|
||||
}
|
||||
}
|
||||
|
||||
public function read(br:BytesReader) {
|
||||
var trapdoorCount = br.readInt16();
|
||||
for (i in 0...trapdoorCount) {
|
||||
this.trapdoorLastContactTimes.push(br.readFloat());
|
||||
}
|
||||
for (i in 0...trapdoorCount) {
|
||||
this.trapdoorLastDirections.push(br.readByte());
|
||||
}
|
||||
for (i in 0...trapdoorCount) {
|
||||
this.trapdoorLastCompletions.push(br.readFloat());
|
||||
}
|
||||
var landMineCount = br.readInt16();
|
||||
for (i in 0...landMineCount) {
|
||||
this.landMineDisappearTimes.push(br.readFloat());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Replay {
|
||||
public var mission:String;
|
||||
|
||||
var frames:Array<ReplayFrame>;
|
||||
|
||||
var initialState:ReplayInitialState;
|
||||
var currentRecordFrame:ReplayFrame;
|
||||
|
||||
public var currentPlaybackFrame:ReplayFrame;
|
||||
|
|
@ -110,6 +197,7 @@ class Replay {
|
|||
|
||||
public function new(mission:String) {
|
||||
this.mission = mission;
|
||||
this.initialState = new ReplayInitialState();
|
||||
}
|
||||
|
||||
public function startFrame() {
|
||||
|
|
@ -153,6 +241,28 @@ class Replay {
|
|||
currentRecordFrame.cameraYaw = yaw;
|
||||
}
|
||||
|
||||
public function recordTrapdoorState(lastContactTime:Float, lastDirection:Int, lastCompletion:Float) {
|
||||
initialState.trapdoorLastContactTimes.push(lastContactTime);
|
||||
initialState.trapdoorLastDirections.push(lastDirection);
|
||||
initialState.trapdoorLastCompletions.push(lastCompletion);
|
||||
}
|
||||
|
||||
public function recordLandMineState(disappearTime:Float) {
|
||||
initialState.landMineDisappearTimes.push(disappearTime);
|
||||
}
|
||||
|
||||
public function getTrapdoorState(idx:Int) {
|
||||
return {
|
||||
lastContactTime: initialState.trapdoorLastContactTimes[idx],
|
||||
lastDirection: initialState.trapdoorLastDirections[idx],
|
||||
lastCompletion: initialState.trapdoorLastCompletions[idx]
|
||||
};
|
||||
}
|
||||
|
||||
public function getLandMineState(idx:Int) {
|
||||
return initialState.landMineDisappearTimes[idx];
|
||||
}
|
||||
|
||||
public function clear() {
|
||||
this.frames = [];
|
||||
currentRecordFrame = null;
|
||||
|
|
@ -188,4 +298,42 @@ class Replay {
|
|||
this.currentPlaybackFrame = null;
|
||||
this.currentPlaybackFrameIdx = 0;
|
||||
}
|
||||
|
||||
public function write() {
|
||||
var bw = new BytesWriter();
|
||||
|
||||
bw.writeStr(this.mission);
|
||||
this.initialState.write(bw);
|
||||
bw.writeInt32(this.frames.length);
|
||||
for (frame in this.frames) {
|
||||
frame.write(bw);
|
||||
}
|
||||
|
||||
var buf = bw.getBuffer();
|
||||
var bufsize = buf.length;
|
||||
var compressed = haxe.zip.Compress.run(bw.getBuffer(), 7);
|
||||
|
||||
var finalB = new BytesBuffer();
|
||||
finalB.addInt32(bufsize);
|
||||
finalB.addBytes(compressed, 4, compressed.length);
|
||||
|
||||
return finalB.getBytes();
|
||||
}
|
||||
|
||||
public function read(data:Bytes) {
|
||||
var uncompressedLength = data.getInt32(0);
|
||||
var compressedData = data.sub(4, data.length - 4);
|
||||
|
||||
var uncompressed = haxe.zip.Uncompress.run(compressedData, uncompressedLength);
|
||||
var br = new BytesReader(uncompressed);
|
||||
this.mission = br.readStr();
|
||||
this.initialState.read(br);
|
||||
var frameCount = br.readInt32();
|
||||
this.frames = [];
|
||||
for (i in 0...frameCount) {
|
||||
var frame = new ReplayFrame();
|
||||
frame.read(br);
|
||||
this.frames.push(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import src.MarbleWorld;
|
|||
class Trapdoor extends DtsObject {
|
||||
var lastContactTime = -1e8;
|
||||
var timeout:Float = 0.2;
|
||||
var lastDirection:Float;
|
||||
var lastDirection:Int;
|
||||
var lastCompletion:Float = 0;
|
||||
|
||||
public function new() {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue