diff --git a/src/CameraController.hx b/src/CameraController.hx index 646baf3f..b77c859d 100644 --- a/src/CameraController.hx +++ b/src/CameraController.hx @@ -305,7 +305,7 @@ class CameraController extends Object { CameraYaw = this.level.replay.currentPlaybackFrame.cameraYaw; } - var marblePosition = level.marble.collider.transform.getPosition(); + var marblePosition = this.finish ? level.marble.collider.transform.getPosition() : level.marble.getAbsPos().getPosition(); if (this.finish) { // Move the target to the centre of the finish @@ -341,6 +341,10 @@ class CameraController extends Object { var closeness = 0.1; var rayCastOrigin = marblePosition.add(level.currentUp.multiply(marble._radius)).add(cameraVerticalTranslation); + for (pi in level.pathedInteriors) { + pi.pushTickState(); + } + var processedShapes = []; for (i in 0...3) { var rayCastDirection = camera.pos.sub(rayCastOrigin); @@ -388,6 +392,10 @@ class CameraController extends Object { break; } + for (pi in level.pathedInteriors) { + pi.popTickState(); + } + if (oob) { camera.pos = lastCamPos; camera.target = marblePosition.add(lastVertTranslation); diff --git a/src/Marble.hx b/src/Marble.hx index 83adbb52..dbc47780 100644 --- a/src/Marble.hx +++ b/src/Marble.hx @@ -230,6 +230,11 @@ class Marble extends GameObject { public var _mass:Float = 1; + var physicsAccumulator:Float = 0; + var oldPos:Vector; + var newPos:Vector; + var prevRot:Quat; + public var contacts:Array = []; public var bestContact:CollisionInfo; public var contactEntities:Array = []; @@ -1593,6 +1598,9 @@ class Marble extends GameObject { var passedTime = timeState.currentAttemptTime; + oldPos = this.collider.transform.getPosition(); + prevRot = this.getRotationQuat().clone(); + if (this.controllable) { for (interior in pathedInteriors) { // interior.pushTickState(); @@ -1604,7 +1612,7 @@ class Marble extends GameObject { if (timeRemaining <= 0) break; - var timeStep = 0.004; + var timeStep = 0.008; if (timeRemaining < timeStep) timeStep = timeRemaining; @@ -1691,13 +1699,13 @@ class Marble extends GameObject { var quat = new Quat(); quat.initRotation(omega.x * timeStep, omega.y * timeStep, omega.z * timeStep); quat.multiply(quat, rot); - this.setRotationQuat(quat); + // this.setRotationQuat(quat); var totMatrix = quat.toMatrix(); newPos.w = 1; // Fix shit blowing up totMatrix.setPosition(newPos); - this.setPosition(newPos.x, newPos.y, newPos.z); + // this.setPosition(newPos.x, newPos.y, newPos.z); this.collider.setTransform(totMatrix); this.collider.velocity = this.velocity; @@ -1746,6 +1754,7 @@ class Marble extends GameObject { } this.queuedContacts = []; + newPos = this.collider.transform.getPosition(); this.updateRollSound(timeState, contactTime / timeState.dt, this._slipAmount); } @@ -1796,8 +1805,32 @@ class Marble extends GameObject { } } + physicsAccumulator += timeState.dt; + playedSounds = []; - advancePhysics(timeState, move, collisionWorld, pathedInteriors); + while (physicsAccumulator > 0.032) { + var adt = timeState.clone(); + adt.dt = 0.032; + advancePhysics(adt, move, collisionWorld, pathedInteriors); + physicsAccumulator -= 0.032; + } + if (oldPos != null && newPos != null) { + var deltaT = physicsAccumulator / 0.032; + var renderPos = Util.lerpThreeVectors(this.oldPos, this.newPos, deltaT); + this.setPosition(renderPos.x, renderPos.y, renderPos.z); + + var rot = this.prevRot; + var quat = new Quat(); + quat.initRotation(omega.x * physicsAccumulator, omega.y * physicsAccumulator, omega.z * physicsAccumulator); + quat.multiply(quat, rot); + this.setRotationQuat(quat); + + var adt = timeState.clone(); + adt.dt = physicsAccumulator; + for (pi in pathedInteriors) { + pi.update(adt); + } + } if (this.controllable) { if (!this.level.isWatching) { @@ -1983,13 +2016,14 @@ class Marble extends GameObject { public function useBlast() { if (this.level.blastAmount < 0.25 || this.level.game != "ultra") - return; + return false; var impulse = this.level.currentUp.multiply(this.level.blastAmount * 8); this.applyImpulse(impulse); AudioManager.playSound(ResourceLoader.getResource('data/sound/use_blast.wav', ResourceLoader.getAudio, this.soundResources)); this.blastWave.doSequenceOnceBeginTime = this.level.timeState.timeSinceLoad; this.blastUseTime = this.level.timeState.currentAttemptTime; this.level.blastAmount = 0; + return true; } public function applyImpulse(impulse:Vector, contactImpulse:Bool = false) { @@ -2071,6 +2105,10 @@ class Marble extends GameObject { this.teleportDisableTime = null; this.teleportEnableTime = null; this.finishAnimTime = 0; + this.physicsAccumulator = 0; + this.prevRot = this.getRotationQuat().clone(); + this.oldPos = this.getAbsPos().getPosition(); + this.newPos = this.getAbsPos().getPosition(); if (this._radius != this._prevRadius) { this._radius = this._prevRadius; this._marbleScale = this._renderScale = this._defaultScale; diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx index 9dca7d46..7edde18d 100644 --- a/src/MarbleWorld.hx +++ b/src/MarbleWorld.hx @@ -1017,14 +1017,15 @@ class MarbleWorld extends Scheduler { this.tickSchedule(timeState.currentAttemptTime); - if (Key.isPressed(Settings.controlsSettings.blast) + if (Key.isDown(Settings.controlsSettings.blast) || (MarbleGame.instance.touchInput.blastbutton.pressed) - || Gamepad.isPressed(Settings.gamepadSettings.blast) + || Gamepad.isDown(Settings.gamepadSettings.blast) && !this.isWatching && this.game == "ultra") { - this.marble.useBlast(); - if (this.isRecording) { - this.replay.recordMarbleStateFlags(false, false, false, true); + if (this.marble.useBlast()) { + if (this.isRecording) { + this.replay.recordMarbleStateFlags(false, false, false, true); + } } } diff --git a/src/PathedInterior.hx b/src/PathedInterior.hx index 020a8bad..d81a1f1b 100644 --- a/src/PathedInterior.hx +++ b/src/PathedInterior.hx @@ -44,9 +44,15 @@ class PathedInterior extends InteriorObject { var baseOrientation:Quat; var baseScale:Vector; + var prevPosition:Vector; + var position:Vector; + public var velocity:Vector; + var _storedColliderTransform:Matrix; + var stopped:Bool = false; + var stoppedPosition:Vector; var soundChannel:Channel; @@ -133,8 +139,25 @@ class PathedInterior extends InteriorObject { onFinish(); } + public function pushTickState() { + this._storedColliderTransform = this.collider.transform.clone(); + var tform = this.getAbsPos(); + if (this.isCollideable) { + collider.setTransform(tform); + collisionWorld.updateTransform(this.collider); + } + } + + public function popTickState() { + if (this.isCollideable) { + collider.setTransform(this._storedColliderTransform); + collisionWorld.updateTransform(this.collider); + } + } + public function computeNextPathStep(timeDelta:Float) { stopped = false; + prevPosition = this.position.clone(); if (currentTime == targetTime) { velocity.set(0, 0, 0); } else { @@ -158,10 +181,10 @@ class PathedInterior extends InteriorObject { currentTime += delta; } - var curTform = this.getAbsPos(); + var curTform = this.position; var tForm = getTransformAtTime(currentTime); - var displaceDelta = tForm.getPosition().sub(curTform.getPosition()); + var displaceDelta = tForm.getPosition().sub(curTform); velocity.set(displaceDelta.x / timeDelta, displaceDelta.y / timeDelta, displaceDelta.z / timeDelta); this.collider.velocity = velocity.clone(); } @@ -172,9 +195,17 @@ class PathedInterior extends InteriorObject { return; if (this.velocity.length() == 0) return; - var newp = this.getAbsPos().getPosition().add(velocity.multiply(timeDelta)); - this.setPosition(newp.x, newp.y, newp.z); - this.setTransform(this.getTransform()); + velocity.w = 0; + var newp = position.add(velocity.multiply(timeDelta)); + var tform = this.getAbsPos().clone(); + tform.setPosition(newp); + // this.setPosition(newp.x, newp.y, newp.z); + if (this.isCollideable) { + collider.setTransform(tform); + collisionWorld.updateTransform(this.collider); + } + // this.setTransform(this.getTransform()); + this.position = newp; if (this.soundChannel != null) { var spat = this.soundChannel.getEffect(Spatialization); @@ -182,12 +213,22 @@ class PathedInterior extends InteriorObject { } } - public function update(timeState:TimeState) {} + public function update(timeState:TimeState) { + if (!stopped) + this.setPosition(prevPosition.x + + velocity.x * timeState.dt, prevPosition.y + + velocity.y * timeState.dt, + prevPosition.z + + velocity.z * timeState.dt); + else + this.setPosition(stoppedPosition.x, stoppedPosition.y, stoppedPosition.z); + } public function setStopped(stopped:Bool = true) { // if (!this.stopped) // this.stopTime = currentTime; this.stopped = stopped; + this.stoppedPosition = this.position.clone(); } function computeDuration() { @@ -205,6 +246,8 @@ class PathedInterior extends InteriorObject { function updatePosition() { var newp = this.getAbsPos().getPosition(); + this.position = newp; + this.prevPosition = newp; this.setPosition(newp.x, newp.y, newp.z); this.collider.setTransform(this.getTransform()); this.collider.velocity = this.velocity; diff --git a/src/gui/GuiXboxListButton.hx b/src/gui/GuiXboxListButton.hx index b78970b9..a6ac058b 100644 --- a/src/gui/GuiXboxListButton.hx +++ b/src/gui/GuiXboxListButton.hx @@ -94,7 +94,7 @@ class GuiXboxListButton extends GuiControl { _prevMousePos = mouseState.position.clone(); } if (selected && !disabled) { - if (Key.isDown(Key.MOUSE_LEFT)) { + if (Key.isDown(Key.MOUSE_LEFT) && renderRect.inRect(mouseState.position)) { this.button.anim.currentFrame = 1; this.buttonIcon.anim.currentFrame = 1; buttonText.text.textColor = 0x101010; diff --git a/src/rewind/RewindFrame.hx b/src/rewind/RewindFrame.hx index 2d28f7c2..2fa86f4d 100644 --- a/src/rewind/RewindFrame.hx +++ b/src/rewind/RewindFrame.hx @@ -1,5 +1,6 @@ package rewind; +import h3d.Matrix; import mis.MissionElement.MissionElementBase; import triggers.CheckpointTrigger; import src.PathedInterior.PIState; @@ -13,7 +14,10 @@ import shapes.Gem; @:publicFields class RewindFrame { var timeState:TimeState; - var marblePosition:Vector; + var marbleColliderTransform:Matrix; + var marblePrevPosition:Vector; + var marbleNextPosition:Vector; + var marblePhysicsAccmulator:Float; var marbleOrientation:Quat; var marbleVelocity:Vector; var marbleAngularVelocity:Vector; @@ -22,7 +26,9 @@ class RewindFrame { var mpStates:Array<{ curState:PIState, stopped:Bool, - position:Vector + stoppedPosition:Vector, + prevPosition:Vector, + position:Vector, }>; var gemCount:Int; var gemStates:Array; @@ -52,7 +58,10 @@ class RewindFrame { public function clone() { var c = new RewindFrame(); c.timeState = timeState.clone(); - c.marblePosition = marblePosition.clone(); + c.marbleColliderTransform = marbleColliderTransform.clone(); + c.marblePrevPosition = marblePrevPosition.clone(); + c.marbleNextPosition = marbleNextPosition.clone(); + c.marblePhysicsAccmulator = marblePhysicsAccmulator; c.marbleOrientation = marbleOrientation.clone(); c.marbleVelocity = marbleVelocity.clone(); c.marbleAngularVelocity = marbleAngularVelocity.clone(); @@ -75,6 +84,8 @@ class RewindFrame { }, stopped: s.stopped, position: s.position.clone(), + prevPosition: s.prevPosition.clone(), + stoppedPosition: s.stoppedPosition != null ? s.stoppedPosition.clone() : null, }); } c.trapdoorStates = []; diff --git a/src/rewind/RewindManager.hx b/src/rewind/RewindManager.hx index a2f80bc5..719f7817 100644 --- a/src/rewind/RewindManager.hx +++ b/src/rewind/RewindManager.hx @@ -21,8 +21,11 @@ class RewindManager { public function recordFrame() { var rf = new RewindFrame(); rf.timeState = level.timeState.clone(); - rf.marblePosition = level.marble.getAbsPos().getPosition().clone(); - rf.marbleOrientation = level.marble.getRotationQuat().clone(); + rf.marbleColliderTransform = level.marble.collider.transform.clone(); + rf.marblePrevPosition = @:privateAccess level.marble.oldPos.clone(); + rf.marbleNextPosition = @:privateAccess level.marble.newPos.clone(); + rf.marbleOrientation = @:privateAccess level.marble.prevRot.clone(); + rf.marblePhysicsAccmulator = @:privateAccess level.marble.physicsAccumulator; rf.marbleVelocity = level.marble.velocity.clone(); rf.marbleAngularVelocity = level.marble.omega.clone(); rf.marblePowerup = level.marble.heldPowerup; @@ -40,7 +43,9 @@ class RewindManager { velocity: x.velocity.clone(), }, stopped: @:privateAccess x.stopped, - position: x.getAbsPos().getPosition().clone(), + position: @:privateAccess x.position.clone(), + prevPosition: @:privateAccess x.prevPosition.clone(), + stoppedPosition: @:privateAccess x.stoppedPosition != null ? @:privateAccess x.stoppedPosition.clone() : null, } }); rf.powerupStates = []; @@ -82,10 +87,15 @@ class RewindManager { 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); + @:privateAccess level.marble.oldPos.load(rf.marblePrevPosition); + @:privateAccess level.marble.newPos.load(rf.marbleNextPosition); + @:privateAccess level.marble.collider.transform.load(rf.marbleColliderTransform); + @:privateAccess level.marble.physicsAccumulator = rf.marblePhysicsAccmulator; + @:privateAccess level.marble.prevRot.load(rf.marbleOrientation); + // level.marble.setMarblePosition(rf.marblePosition.x, rf.marblePosition.y, rf.marblePosition.z); + // level.marble.setRotationQuat(rf.marbleOrientation.clone()); + level.marble.velocity.load(rf.marbleVelocity); + level.marble.omega.load(rf.marbleAngularVelocity); if (level.marble.heldPowerup == null) { if (rf.marblePowerup != null) { @@ -123,15 +133,17 @@ class RewindManager { @:privateAccess level.orientationChangeTime = -1e8; } - level.currentUp.set(rf.currentUp.x, rf.currentUp.y, rf.currentUp.z); - level.marble.lastContactNormal.set(rf.lastContactNormal.x, rf.lastContactNormal.y, rf.lastContactNormal.z); + level.currentUp.load(rf.currentUp); + level.marble.lastContactNormal.load(rf.lastContactNormal); for (i in 0...rf.mpStates.length) { level.pathedInteriors[i].currentTime = rf.mpStates[i].curState.currentTime; level.pathedInteriors[i].targetTime = rf.mpStates[i].curState.targetTime; - level.pathedInteriors[i].velocity.set(rf.mpStates[i].curState.velocity.x, rf.mpStates[i].curState.velocity.y, rf.mpStates[i].curState.velocity.z); + level.pathedInteriors[i].velocity.load(rf.mpStates[i].curState.velocity); @:privateAccess level.pathedInteriors[i].stopped = rf.mpStates[i].stopped; - level.pathedInteriors[i].setPosition(rf.mpStates[i].position.x, rf.mpStates[i].position.y, rf.mpStates[i].position.z); - level.pathedInteriors[i].setTransform(level.pathedInteriors[i].getTransform()); + @: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; + // level.pathedInteriors[i].setTransform(level.pathedInteriors[i].getTransform()); } var pstates = rf.powerupStates.copy(); var lmstates = rf.landMineStates.copy();