From d651d32b4cf2a353dea7abd8071aeada802ea1c8 Mon Sep 17 00:00:00 2001 From: RandomityGuy <31925790+RandomityGuy@users.noreply.github.com> Date: Sat, 3 Feb 2024 01:06:40 +0530 Subject: [PATCH] get blasting and server side powerup images working --- src/Debug.hx | 4 +- src/Marble.hx | 119 ++++++++++++++++++++++++------- src/MarbleWorld.hx | 34 ++++++--- src/TimeState.hx | 2 + src/net/MarblePredictionStore.hx | 6 ++ src/net/Move.hx | 12 ++++ src/net/MoveManager.hx | 2 +- src/net/NetPacket.hx | 6 ++ src/rewind/InputRecorder.hx | 2 +- src/shapes/Blast.hx | 1 + 10 files changed, 147 insertions(+), 41 deletions(-) create mode 100644 src/net/Move.hx diff --git a/src/Debug.hx b/src/Debug.hx index ef21d474..a9f53327 100644 --- a/src/Debug.hx +++ b/src/Debug.hx @@ -17,7 +17,7 @@ class Debug { }> = []; static var debugTriangles:h3d.scene.Mesh; - static var debugSphere:h3d.scene.MeshBatch; + static var debugSphere:src.MeshBatch; public static function init() {} @@ -43,7 +43,7 @@ class Debug { var sphprim = new h3d.prim.Sphere(); sphprim.addUVs(); sphprim.addNormals(); - debugSphere = new h3d.scene.MeshBatch(sphprim, h3d.mat.Material.create()); + debugSphere = new src.MeshBatch(sphprim, h3d.mat.Material.create()); debugSphere.material.castShadows = false; debugSphere.material.receiveShadows = false; MarbleGame.instance.scene.addChild(debugSphere); diff --git a/src/Marble.hx b/src/Marble.hx index e57cc06e..182278a7 100644 --- a/src/Marble.hx +++ b/src/Marble.hx @@ -72,15 +72,8 @@ import src.InteriorObject; import src.Console; import src.Gamepad; import net.Net; - -class Move { - public var d:Vector; - public var jump:Bool; - public var powerup:Bool; - public var blast:Bool; - - public function new() {} -} +import net.Move; +import src.Debug; enum Mode { Start; @@ -224,6 +217,8 @@ class Marble extends GameObject { var minVelocityBounceSoft = 2.5; var minVelocityBounceHard = 12.0; var bounceMinGain = 0.2; + var maxBlastRepulse = 60.0; + var blastRepulseDist = 10.0; public var _bounceRestitution = 0.5; @@ -261,6 +256,10 @@ class Marble extends GameObject { var blastUseTime:Float = -1e8; public var blastAmount:Float = 0; + public var blastTicks:Int = 0; + public var blastUseTick:Int = 0; // blast is 12 ticks long + + var blastPerc:Float = 0.0; var teleportEnableTime:Null = null; var teleportDisableTime:Null = null; @@ -510,7 +509,7 @@ class Marble extends GameObject { this.helicopter.z = 1e8; this.helicopter.scale(0.3 / 0.2); - if (this.controllable) { + if (this.controllable || this.connection != null) { var worker = new ResourceLoaderWorker(onFinish); worker.addTask(fwd -> level.addDtsObject(this.helicopter, fwd)); worker.addTask(fwd -> level.addDtsObject(this.blastWave, fwd)); @@ -559,7 +558,7 @@ class Marble extends GameObject { } } - function getExternalForces(currentTime:Float, m:Move, dt:Float) { + function getExternalForces(currentTime:Float, m:Move, dt:Float, tick:Int) { if (this.mode == Finish) return this.velocity.multiply(-16); var gWorkGravityDir = this.currentUp.multiply(-1); @@ -573,6 +572,12 @@ class Marble extends GameObject { var force = cast(obj, ForceObject).getForce(this.collider.transform.getPosition()); A = A.add(force.multiply(1 / _mass)); } + for (marble in level.marbles) { + if (marble != this) { + var force = marble.getForce(this.collider.transform.getPosition(), tick); + A = A.add(force.multiply(1 / _mass)); + } + } } if (contacts.length != 0 && this.mode != Start) { var contactForce = 0.0; @@ -1483,8 +1488,8 @@ class Marble extends GameObject { } // Blast - if (m.blast) { - this.useBlast(); + if (m != null && m.blast) { + this.useBlast(timeState); if (level.isRecording) { level.replay.recordMarbleStateFlags(false, false, false, true); } @@ -1519,7 +1524,7 @@ class Marble extends GameObject { var isCentered = this.computeMoveForces(m, aControl, desiredOmega); stoppedPaths = this.velocityCancel(timeState.currentAttemptTime, timeStep, isCentered, false, stoppedPaths, pathedInteriors); - var A = this.getExternalForces(timeState.currentAttemptTime, m, timeStep); + var A = this.getExternalForces(timeState.currentAttemptTime, m, timeStep, timeState.ticks); var a = this.applyContactForces(timeStep, m, isCentered, aControl, desiredOmega, A); this.velocity.set(this.velocity.x + A.x * timeStep, this.velocity.y + A.y * timeStep, this.velocity.z + A.z * timeStep); this.omega.set(this.omega.x + a.x * timeStep, this.omega.y + a.y * timeStep, this.omega.z + a.z * timeStep); @@ -1654,6 +1659,8 @@ class Marble extends GameObject { marbleUpdate.omega = this.omega; marbleUpdate.move = move; marbleUpdate.moveQueueSize = this.connection != null ? this.connection.moveManager.getQueueSize() : 255; + marbleUpdate.blastAmount = this.blastTicks; + marbleUpdate.blastTick = this.blastUseTick; marbleUpdate.serialize(b); return b.getBytes(); } @@ -1672,6 +1679,8 @@ class Marble extends GameObject { this.collider.transform.setPosition(p.position); this.velocity = p.velocity; this.omega = p.omega; + this.blastTicks = p.blastAmount; + this.blastUseTick = p.blastTick; if (this.controllable && Net.isClient) { // We are client, need to do something about the queue var mm = Net.clientConnection.moveManager; @@ -1721,6 +1730,10 @@ class Marble extends GameObject { } move = new NetMove(innerMove, axis, timeState, 65535); } + + if (this.blastTicks < (30000 >> 5)) + this.blastTicks += 1; + playedSounds = []; advancePhysics(timeState, move.move, collisionWorld, pathedInteriors); physicsAccumulator = 0; @@ -1828,6 +1841,12 @@ class Marble extends GameObject { || Gamepad.isDown(Settings.gamepadSettings.powerup)) { move.powerup = true; } + + if (Key.isDown(Settings.controlsSettings.blast) + || (MarbleGame.instance.touchInput.blastbutton.pressed) + || Gamepad.isDown(Settings.gamepadSettings.blast)) + move.blast = true; + if (MarbleGame.instance.touchInput.movementInput.pressed) { move.d.y = -MarbleGame.instance.touchInput.movementInput.value.x; move.d.x = MarbleGame.instance.touchInput.movementInput.value.y; @@ -1838,7 +1857,7 @@ class Marble extends GameObject { // SP only function public function update(timeState:TimeState, collisionWorld:CollisionWorld, pathedInteriors:Array) { var move:Move = null; - if (this.controllable && this.mode != Finish && !MarbleGame.instance.paused && !this.level.isWatching && !this.level.isReplayingMovement) { + if (this.controllable && !this.level.isWatching && !this.level.isReplayingMovement) { move = recordMove(); } @@ -2042,7 +2061,7 @@ class Marble extends GameObject { } public function updatePowerupStates(currentTime:Float, dt:Float) { - if (!this.controllable) + if (!this.controllable && this.connection == null) return; if (currentTime - this.helicopterEnableTime < 5) { this.helicopter.setPosition(x, y, z); @@ -2053,6 +2072,7 @@ class Marble extends GameObject { this.helicopter.setPosition(1e8, 1e8, 1e8); this.helicopterSound.pause = true; } + if (this.blastUseTime > currentTime) { this.blastUseTime = Math.POSITIVE_INFINITY; this.blastWave.doSequenceOnceBeginTime = 0; @@ -2076,17 +2096,60 @@ class Marble extends GameObject { } } - public function useBlast() { - if (this.blastAmount < 0.25) - return false; - var impulse = this.currentUp.multiply(this.blastAmount * 8); - this.applyImpulse(impulse); - if (this.controllable) - 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.blastAmount = 0; - return true; + public function useBlast(timeState:TimeState) { + if (this.level.isMultiplayer) { + if (this.blastTicks < (7500 >> 5)) + return false; + this.blastUseTick = timeState.ticks; + var amount = this.blastTicks / (30000 >> 5); + this.blastPerc = amount; + var impulse = this.currentUp.multiply(amount * 8); + this.applyImpulse(impulse); + if (this.controllable) + 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.blastTicks = 0; + return true; + } else { + if (this.blastAmount < 0.25) + return false; + var impulse = this.currentUp.multiply(this.blastAmount * 8); + this.applyImpulse(impulse); + if (this.controllable) + 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.blastAmount = 0; + return true; + } + } + + public function getForce(position:Vector, tick:Int) { + var retForce = new Vector(); + if (tick - blastUseTick >= 12) + return retForce; + var delta = position.sub(newPos); + var deltaLen = delta.length(); + + var maxDist = Math.max(blastRepulseDist, blastRepulseDist * blastPerc); + var maxRepulse = maxBlastRepulse * blastPerc; + + if (deltaLen > maxDist) + return retForce; + + if (deltaLen >= 0.05) { + var dist = 0.0; + if (deltaLen >= 1.0) + dist = (1.0 / deltaLen - 1.0 / maxDist) * maxRepulse; + else + dist = maxRepulse / deltaLen; + + retForce.load(retForce.add(delta.multiply(dist))); + } else { + retForce.load(retForce.add(this.currentUp.multiply(maxRepulse))); + } + return retForce; } public function applyImpulse(impulse:Vector, contactImpulse:Bool = false) { @@ -2156,6 +2219,8 @@ class Marble extends GameObject { this.helicopterEnableTime = Math.NEGATIVE_INFINITY; this.megaMarbleEnableTime = Math.NEGATIVE_INFINITY; this.blastUseTime = Math.NEGATIVE_INFINITY; + this.blastUseTick = 0; + this.blastTicks = 0; this.lastContactNormal = new Vector(0, 0, 1); this.contactEntities = []; this.cloak = false; diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx index 7d27bc6a..443a9f8a 100644 --- a/src/MarbleWorld.hx +++ b/src/MarbleWorld.hx @@ -119,6 +119,7 @@ class MarbleWorld extends Scheduler { public var interiors:Array = []; public var pathedInteriors:Array = []; + public var marbles:Array = []; public var dtsObjects:Array = []; public var powerUps:Array = []; public var forceObjects:Array = []; @@ -982,6 +983,7 @@ class MarbleWorld extends Scheduler { public function addMarble(marble:Marble, client:GameConnection, onFinish:Void->Void) { marble.level = cast this; + this.marbles.push(marble); if (marble.controllable) { marble.init(cast this, client, () -> { this.scene.addChild(marble.camera); @@ -1234,7 +1236,7 @@ class MarbleWorld extends Scheduler { || Gamepad.isDown(Settings.gamepadSettings.blast) && !this.isWatching && this.game == "ultra") { - this.marble.useBlast(); + this.marble.useBlast(timeState); } this.updateGameState(); @@ -1369,7 +1371,7 @@ class MarbleWorld extends Scheduler { this.tickSchedule(timeState.currentAttemptTime); if (this.isWatching && this.replay.currentPlaybackFrame.marbleStateFlags.has(UsedBlast)) - this.marble.useBlast(); + this.marble.useBlast(timeState); // Replay gravity if (this.isWatching) { @@ -1441,6 +1443,7 @@ class MarbleWorld extends Scheduler { } timeState.ticks++; } + timeState.subframe = tickAccumulator / 0.032; marble.updateClient(timeState, this.pathedInteriors); for (client => marble in clientMarbles) { marble.updateClient(timeState, this.pathedInteriors); @@ -1600,16 +1603,27 @@ class MarbleWorld extends Scheduler { } public function updateBlast(marble:Marble, timestate:TimeState) { - if (marble.blastAmount < 1) { - marble.blastAmount = Util.clamp(marble.blastAmount + (timeState.dt / 30), 0, 1); - if (marble == this.marble) - this.renderBlastAmount = marble.blastAmount; + if (this.isMultiplayer) { + if (marble == this.marble) { + if (marble.blastTicks < (36000 >> 5)) { + this.renderBlastAmount = (marble.blastTicks + timestate.subframe) / (30000 >> 5); + } else { + this.renderBlastAmount = Math.min(marble.blastTicks / (30000 >> 5), timestate.dt * 0.75 + this.renderBlastAmount); + } + this.playGui.setBlastValue(this.renderBlastAmount); + } } else { + if (marble.blastAmount < 1) { + marble.blastAmount = Util.clamp(marble.blastAmount + (timeState.dt / 30), 0, 1); + if (marble == this.marble) + this.renderBlastAmount = marble.blastAmount; + } else { + if (marble == this.marble) + this.renderBlastAmount = Math.min(marble.blastAmount, timestate.dt * 0.75 + this.renderBlastAmount); + } if (marble == this.marble) - this.renderBlastAmount = Math.min(marble.blastAmount, timestate.dt * 0.75 + this.renderBlastAmount); + this.playGui.setBlastValue(this.renderBlastAmount); } - if (marble == this.marble) - this.playGui.setBlastValue(this.renderBlastAmount); } function updateTexts() { @@ -2234,7 +2248,7 @@ class MarbleWorld extends Scheduler { pathedInteriors.dispose(); } pathedInteriors = null; - for (client => marble in clientMarbles) { + for (marble in marbles) { marble.dispose(); } clientMarbles = null; diff --git a/src/TimeState.hx b/src/TimeState.hx index 8fbe3df8..bed80cf3 100644 --- a/src/TimeState.hx +++ b/src/TimeState.hx @@ -7,6 +7,7 @@ class TimeState { var gameplayClock:Float; var dt:Float; var ticks:Int; // How many 32ms ticks have happened + var subframe:Float; public function new() {} @@ -17,6 +18,7 @@ class TimeState { n.gameplayClock = this.gameplayClock; n.dt = this.dt; n.ticks = ticks; + n.subframe = subframe; return n; } } diff --git a/src/net/MarblePredictionStore.hx b/src/net/MarblePredictionStore.hx index bc7c2049..601e507b 100644 --- a/src/net/MarblePredictionStore.hx +++ b/src/net/MarblePredictionStore.hx @@ -12,16 +12,22 @@ class MarblePrediction { var position:Vector; var velocity:Vector; var omega:Vector; + var isControl:Bool; + var blastAmount:Int; public function new(marble:Marble, tick:Int) { this.tick = tick; position = @:privateAccess marble.newPos.clone(); velocity = @:privateAccess marble.velocity.clone(); omega = @:privateAccess marble.omega.clone(); + blastAmount = @:privateAccess marble.blastTicks; + isControl = @:privateAccess marble.controllable; } public inline function getError(p:MarbleUpdatePacket) { var subs = position.sub(p.position).lengthSq() + velocity.sub(p.velocity).lengthSq() + omega.sub(p.omega).lengthSq(); + if (isControl) + subs += Math.abs(blastAmount - p.blastAmount); return subs; } } diff --git a/src/net/Move.hx b/src/net/Move.hx new file mode 100644 index 00000000..22f38775 --- /dev/null +++ b/src/net/Move.hx @@ -0,0 +1,12 @@ +package net; + +import h3d.Vector; + +class Move { + public var d:Vector; + public var jump:Bool; + public var powerup:Bool; + public var blast:Bool; + + public function new() {} +} diff --git a/src/net/MoveManager.hx b/src/net/MoveManager.hx index 26355bf0..8f6d1558 100644 --- a/src/net/MoveManager.hx +++ b/src/net/MoveManager.hx @@ -8,7 +8,7 @@ import src.Console; import net.ClientConnection; import net.Net.NetPacketType; import src.MarbleWorld; -import src.Marble.Move; +import net.Move; import h3d.Vector; import src.Gamepad; import src.Settings; diff --git a/src/net/NetPacket.hx b/src/net/NetPacket.hx index 585a527c..f33e989a 100644 --- a/src/net/NetPacket.hx +++ b/src/net/NetPacket.hx @@ -38,6 +38,8 @@ class MarbleUpdatePacket implements NetPacket { var position:Vector; var velocity:Vector; var omega:Vector; + var blastAmount:Int; + var blastTick:Int; var moveQueueSize:Int; public function new() {} @@ -56,6 +58,8 @@ class MarbleUpdatePacket implements NetPacket { b.writeFloat(omega.x); b.writeFloat(omega.y); b.writeFloat(omega.z); + b.writeUInt16(blastAmount); + b.writeUInt16(blastTick); } public inline function deserialize(b:haxe.io.BytesInput) { @@ -66,5 +70,7 @@ class MarbleUpdatePacket implements NetPacket { position = new Vector(b.readFloat(), b.readFloat(), b.readFloat()); velocity = new Vector(b.readFloat(), b.readFloat(), b.readFloat()); omega = new Vector(b.readFloat(), b.readFloat(), b.readFloat()); + blastAmount = b.readUInt16(); + blastTick = b.readUInt16(); } } diff --git a/src/rewind/InputRecorder.hx b/src/rewind/InputRecorder.hx index 3078cc1c..7cf21750 100644 --- a/src/rewind/InputRecorder.hx +++ b/src/rewind/InputRecorder.hx @@ -2,7 +2,7 @@ package rewind; import src.MarbleWorld; import h3d.Vector; -import src.Marble.Move; +import net.Move; @:publicFields class InputRecorderFrame { diff --git a/src/shapes/Blast.hx b/src/shapes/Blast.hx index 864d3941..210431ad 100644 --- a/src/shapes/Blast.hx +++ b/src/shapes/Blast.hx @@ -34,6 +34,7 @@ class Blast extends PowerUp { public function use(marble:Marble, timeState:TimeState) { marble.blastAmount = 1.2; + marble.blastTicks = 36000 >> 5; } override function getPreloadMaterials(dts:dts.DtsFile) {