mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2026-04-01 16:41:37 +00:00
328 lines
11 KiB
Haxe
328 lines
11 KiB
Haxe
package rewind;
|
|
|
|
import rewind.RewindFrame.RewindMPState;
|
|
import haxe.io.BytesInput;
|
|
import haxe.io.BytesBuffer;
|
|
import mis.MissionElement.MissionElementBase;
|
|
import src.GameObject;
|
|
import shapes.AbstractBumper;
|
|
import shapes.PowerUp;
|
|
import shapes.LandMine;
|
|
import src.MarbleWorld;
|
|
import shapes.Trapdoor;
|
|
import shapes.PushButton;
|
|
import src.Util;
|
|
import shapes.Nuke;
|
|
import src.Settings;
|
|
|
|
class RewindManager {
|
|
var frameData:BytesBuffer;
|
|
var frameElapsedTimes:Array<Float> = [];
|
|
var frameDataOffsets:Array<Int> = [];
|
|
var frameSizes:Array<Int> = [];
|
|
var allocObjMap:Map<GameObject, Int> = [];
|
|
var allocObjs:Array<GameObject> = [];
|
|
var allocMeMap:Map<MissionElementBase, Int> = [];
|
|
var allocMes:Array<MissionElementBase> = [];
|
|
var level:MarbleWorld;
|
|
var allocId = 0;
|
|
var allocMeId = 0;
|
|
|
|
public var timeScale:Float = 1;
|
|
|
|
public function new(level:MarbleWorld) {
|
|
this.level = level;
|
|
this.timeScale = Settings.optionsSettings.rewindTimescale;
|
|
this.frameData = new BytesBuffer();
|
|
}
|
|
|
|
public function recordFrame() {
|
|
var rf = new RewindFrame();
|
|
rf.timeState = level.timeState.clone();
|
|
rf.marblePosition = level.marble.collider.transform.getPosition().clone();
|
|
rf.marbleOrientation = level.marble.getRotationQuat().clone();
|
|
rf.marbleVelocity = level.marble.velocity.clone();
|
|
rf.marbleAngularVelocity = level.marble.omega.clone();
|
|
rf.marblePowerup = level.marble.heldPowerup;
|
|
rf.bonusTime = level.bonusTime;
|
|
rf.gemCount = level.gemCount;
|
|
rf.gemStates = level.gems.map(x -> x.pickedUp);
|
|
rf.activePowerupStates = [@:privateAccess
|
|
level.marble.superBounceEnableTime, @:privateAccess
|
|
level.marble.shockAbsorberEnableTime, @:privateAccess
|
|
level.marble.helicopterEnableTime, @:privateAccess
|
|
level.marble.megaMarbleEnableTime
|
|
];
|
|
rf.currentUp = level.marble.currentUp.clone();
|
|
rf.lastContactNormal = level.marble.lastContactNormal.clone();
|
|
rf.mpStates = level.pathedInteriors.map(x -> {
|
|
var mpstate = new RewindMPState();
|
|
mpstate.currentTime = x.currentTime;
|
|
mpstate.targetTime = x.targetTime;
|
|
mpstate.velocity = x.velocity.clone();
|
|
mpstate.stoppedPosition = @:privateAccess x.stopped ? @:privateAccess x.stoppedPosition.clone() : null;
|
|
mpstate.position = @:privateAccess x.position.clone();
|
|
mpstate.prevPosition = @:privateAccess x.prevPosition.clone();
|
|
return mpstate;
|
|
});
|
|
rf.powerupStates = [];
|
|
rf.landMineStates = [];
|
|
rf.trapdoorStates = [];
|
|
for (dts in level.dtsObjects) {
|
|
if (dts is PowerUp) {
|
|
var pow:PowerUp = cast dts;
|
|
rf.powerupStates.push(pow.lastPickUpTime);
|
|
}
|
|
if (dts is PushButton) {
|
|
var pow:PushButton = cast dts;
|
|
rf.powerupStates.push(pow.lastContactTime);
|
|
}
|
|
if (dts is LandMine) {
|
|
var lm:LandMine = cast dts;
|
|
rf.landMineStates.push(lm.disappearTime);
|
|
}
|
|
if (dts is Nuke) {
|
|
var lm:Nuke = cast dts;
|
|
rf.landMineStates.push(lm.disappearTime);
|
|
}
|
|
if (dts is Trapdoor) {
|
|
var td:Trapdoor = cast dts;
|
|
rf.trapdoorStates.push({
|
|
lastCompletion: td.lastCompletion,
|
|
lastDirection: td.lastDirection,
|
|
lastContactTime: td.lastContactTime
|
|
});
|
|
}
|
|
if (dts is AbstractBumper) {
|
|
var ab:AbstractBumper = cast dts;
|
|
rf.powerupStates.push(ab.lastContactTime);
|
|
}
|
|
}
|
|
rf.blastAmt = level.marble.blastAmount;
|
|
rf.oobState = {
|
|
oob: level.marble.outOfBounds,
|
|
timeState: level.marble.outOfBoundsTime != null ? level.marble.outOfBoundsTime.clone() : null
|
|
};
|
|
rf.checkpointState = {
|
|
currentCheckpoint: @:privateAccess level.currentCheckpoint,
|
|
currentCheckpointTrigger: @:privateAccess level.currentCheckpointTrigger,
|
|
checkpointBlast: @:privateAccess level.cheeckpointBlast,
|
|
checkpointCollectedGems: @:privateAccess level.checkpointCollectedGems.copy(),
|
|
checkpointHeldPowerup: @:privateAccess level.checkpointHeldPowerup,
|
|
checkpointUp: @:privateAccess level.checkpointUp != null ? @:privateAccess level.checkpointUp.clone() : null,
|
|
};
|
|
frameElapsedTimes.push(level.timeState.currentAttemptTime);
|
|
frameDataOffsets.push(frameData.length);
|
|
var frameDataSerialized = rf.serialize(this);
|
|
frameSizes.push(frameDataSerialized.length);
|
|
frameData.addBytes(frameDataSerialized, 0, frameDataSerialized.length);
|
|
// frames.push(rf);
|
|
}
|
|
|
|
public function applyFrame(rf:RewindFrame) {
|
|
level.timeState = rf.timeState.clone();
|
|
level.marble.setMarblePosition(rf.marblePosition.x, rf.marblePosition.y, rf.marblePosition.z);
|
|
level.marble.setRotationQuat(rf.marbleOrientation.clone());
|
|
level.marble.velocity.set(rf.marbleVelocity.x, rf.marbleVelocity.y, rf.marbleVelocity.z);
|
|
level.marble.omega.set(rf.marbleAngularVelocity.x, rf.marbleAngularVelocity.y, rf.marbleAngularVelocity.z);
|
|
|
|
if (level.marble.heldPowerup == null) {
|
|
if (rf.marblePowerup != null) {
|
|
level.pickUpPowerUp(level.marble, rf.marblePowerup);
|
|
}
|
|
} else {
|
|
if (rf.marblePowerup == null) {
|
|
level.deselectPowerUp(level.marble);
|
|
} else {
|
|
level.pickUpPowerUp(level.marble, rf.marblePowerup);
|
|
}
|
|
}
|
|
|
|
level.bonusTime = rf.bonusTime;
|
|
level.gemCount = rf.gemCount;
|
|
@:privateAccess level.playGui.formatGemCounter(level.gemCount, level.totalGems);
|
|
for (i in 0...rf.gemStates.length) {
|
|
level.gems[i].setHide(rf.gemStates[i]);
|
|
}
|
|
@:privateAccess level.marble.superBounceEnableTime = rf.activePowerupStates[0];
|
|
@:privateAccess level.marble.shockAbsorberEnableTime = rf.activePowerupStates[1];
|
|
@:privateAccess level.marble.helicopterEnableTime = rf.activePowerupStates[2];
|
|
@:privateAccess level.marble.megaMarbleEnableTime = rf.activePowerupStates[3];
|
|
|
|
if (level.marble.currentUp.x != rf.currentUp.x
|
|
|| level.marble.currentUp.y != rf.currentUp.y
|
|
|| level.marble.currentUp.z != rf.currentUp.z) {
|
|
level.setUp(level.marble, rf.currentUp, level.timeState);
|
|
// Hacky things
|
|
@:privateAccess level.orientationChangeTime = level.timeState.currentAttemptTime - 0.3;
|
|
var oldorient = level.newOrientationQuat;
|
|
level.newOrientationQuat = @:privateAccess level.oldOrientationQuat;
|
|
@:privateAccess level.oldOrientationQuat = oldorient;
|
|
}
|
|
|
|
var gravitycompletion = Util.clamp((level.timeState.currentAttemptTime - @:privateAccess level.orientationChangeTime) / 0.3, 0, 1);
|
|
if (gravitycompletion == 0) {
|
|
level.newOrientationQuat = @:privateAccess level.oldOrientationQuat;
|
|
@:privateAccess level.orientationChangeTime = -1e8;
|
|
}
|
|
|
|
level.marble.currentUp.set(rf.currentUp.x, rf.currentUp.y, rf.currentUp.z);
|
|
level.marble.lastContactNormal.set(rf.lastContactNormal.x, rf.lastContactNormal.y, rf.lastContactNormal.z);
|
|
for (i in 0...rf.mpStates.length) {
|
|
level.pathedInteriors[i].currentTime = rf.mpStates[i].currentTime;
|
|
level.pathedInteriors[i].targetTime = rf.mpStates[i].targetTime;
|
|
level.pathedInteriors[i].velocity.load(rf.mpStates[i].velocity);
|
|
@:privateAccess level.pathedInteriors[i].stopped = rf.mpStates[i].stoppedPosition != null;
|
|
@:privateAccess level.pathedInteriors[i].position.load(rf.mpStates[i].position);
|
|
@:privateAccess level.pathedInteriors[i].prevPosition.load(rf.mpStates[i].prevPosition);
|
|
@:privateAccess level.pathedInteriors[i].stoppedPosition = rf.mpStates[i].stoppedPosition;
|
|
if (level.pathedInteriors[i].isCollideable) {
|
|
var tform = level.pathedInteriors[i].getAbsPos().clone();
|
|
tform.setPosition(rf.mpStates[i].position);
|
|
@:privateAccess level.pathedInteriors[i].collider.setTransform(tform);
|
|
level.collisionWorld.updateTransform(@:privateAccess level.pathedInteriors[i].collider);
|
|
}
|
|
// level.pathedInteriors[i].setTransform(level.pathedInteriors[i].getTransform());
|
|
}
|
|
var pstates = rf.powerupStates.copy();
|
|
var lmstates = rf.landMineStates.copy();
|
|
var tstates = rf.trapdoorStates.copy();
|
|
for (dts in level.dtsObjects) {
|
|
if (dts is PowerUp) {
|
|
var pow:PowerUp = cast dts;
|
|
pow.lastPickUpTime = pstates.shift();
|
|
}
|
|
if (dts is PushButton) {
|
|
var pow:PushButton = cast dts;
|
|
pow.lastContactTime = pstates.shift();
|
|
}
|
|
if (dts is LandMine) {
|
|
var lm:LandMine = cast dts;
|
|
lm.disappearTime = lmstates.shift();
|
|
}
|
|
if (dts is Nuke) {
|
|
var lm:Nuke = cast dts;
|
|
lm.disappearTime = lmstates.shift();
|
|
}
|
|
if (dts is Trapdoor) {
|
|
var td:Trapdoor = cast dts;
|
|
var tdState = tstates.shift();
|
|
td.lastCompletion = tdState.lastCompletion;
|
|
td.lastDirection = tdState.lastDirection;
|
|
td.lastContactTime = tdState.lastContactTime;
|
|
}
|
|
if (dts is AbstractBumper) {
|
|
var ab:AbstractBumper = cast dts;
|
|
ab.lastContactTime = pstates.shift();
|
|
}
|
|
}
|
|
|
|
if (!rf.oobState.oob) {
|
|
@:privateAccess level.cancel(level.oobSchedule);
|
|
@:privateAccess level.cancel(level.marble.oobSchedule);
|
|
} else {
|
|
level.goOutOfBounds(level.marble);
|
|
}
|
|
|
|
level.marble.outOfBounds = rf.oobState.oob;
|
|
level.marble.camera.oob = rf.oobState.oob;
|
|
level.marble.outOfBoundsTime = rf.oobState.timeState != null ? rf.oobState.timeState.clone() : null;
|
|
level.marble.blastAmount = rf.blastAmt;
|
|
@:privateAccess level.checkpointUp = rf.checkpointState.checkpointUp;
|
|
@:privateAccess level.checkpointCollectedGems = rf.checkpointState.checkpointCollectedGems;
|
|
@:privateAccess level.cheeckpointBlast = rf.checkpointState.checkpointBlast;
|
|
@:privateAccess level.checkpointHeldPowerup = rf.checkpointState.checkpointHeldPowerup;
|
|
@:privateAccess level.currentCheckpoint = rf.checkpointState.currentCheckpoint;
|
|
@:privateAccess level.currentCheckpointTrigger = rf.checkpointState.currentCheckpointTrigger;
|
|
}
|
|
|
|
public function getNextRewindFrame(absTime:Float):RewindFrame {
|
|
if (frameElapsedTimes.length == 0)
|
|
return null;
|
|
|
|
var topFrame = frameElapsedTimes[frameElapsedTimes.length - 1];
|
|
while (topFrame > absTime) {
|
|
if (frameElapsedTimes.length == 1) {
|
|
return getFrameAtIndex(0);
|
|
}
|
|
popFrame();
|
|
if (frameElapsedTimes.length == 0)
|
|
return null;
|
|
topFrame = frameElapsedTimes[frameElapsedTimes.length - 1];
|
|
}
|
|
return getFrameAtIndex(frameElapsedTimes.length - 1);
|
|
// return topFrame;
|
|
}
|
|
|
|
function getFrameAtIndex(index:Int) {
|
|
var offset = frameDataOffsets[index];
|
|
var size = frameSizes[index];
|
|
#if sys
|
|
var frameBytes = @:privateAccess frameData.b.sub(offset, size);
|
|
var bi = new BytesInput(frameBytes.toBytes(size));
|
|
#end
|
|
#if js
|
|
var frameBytes = @:privateAccess frameData.buffer.slice(offset, offset + size);
|
|
var bi = new BytesInput(haxe.io.Bytes.ofData(frameBytes));
|
|
#end
|
|
var fr = new RewindFrame();
|
|
fr.deserialize(this, bi);
|
|
return fr;
|
|
return null;
|
|
}
|
|
|
|
function popFrame() {
|
|
frameElapsedTimes.pop();
|
|
var offset = frameDataOffsets[frameDataOffsets.length - 1];
|
|
@:privateAccess frameData.pos = offset;
|
|
frameDataOffsets.pop();
|
|
frameSizes.pop();
|
|
}
|
|
|
|
public function allocGO(go:GameObject) {
|
|
if (go == null)
|
|
return -1;
|
|
if (allocObjMap.exists(go))
|
|
return allocObjMap.get(go);
|
|
var newId = allocId++;
|
|
allocObjMap.set(go, newId);
|
|
allocObjs.push(go);
|
|
return newId;
|
|
}
|
|
|
|
public function getGO(id:Int):GameObject {
|
|
if (id == -1)
|
|
return null;
|
|
return allocObjs[id];
|
|
}
|
|
|
|
public function allocME(me:MissionElementBase) {
|
|
if (me == null)
|
|
return -1;
|
|
if (allocMeMap.exists(me))
|
|
return allocMeMap.get(me);
|
|
var newId = allocMeId++;
|
|
allocMeMap.set(me, newId);
|
|
allocMes.push(me);
|
|
return newId;
|
|
}
|
|
|
|
public function getME(id:Int):MissionElementBase {
|
|
if (id == -1)
|
|
return null;
|
|
return allocMes[id];
|
|
}
|
|
|
|
public function clear() {
|
|
frameData = new BytesBuffer(); // clear
|
|
frameDataOffsets = [];
|
|
frameElapsedTimes = [];
|
|
frameSizes = [];
|
|
allocObjs = [];
|
|
allocObjMap = [];
|
|
allocMes = [];
|
|
allocMeMap = [];
|
|
allocId = 0;
|
|
allocMeId = 0;
|
|
}
|
|
}
|