mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2025-10-30 08:11:25 +00:00
refactor a lot to get powerups sorta working
This commit is contained in:
parent
1fef9a5bdc
commit
bd8527bae6
28 changed files with 286 additions and 151 deletions
|
|
@ -6,6 +6,7 @@ import h3d.scene.Object;
|
|||
import src.Resource;
|
||||
import h3d.mat.Texture;
|
||||
import hxd.res.Sound;
|
||||
import src.Marble;
|
||||
|
||||
class GameObject extends Object {
|
||||
public var identifier:String;
|
||||
|
|
@ -22,13 +23,13 @@ class GameObject extends Object {
|
|||
return currentOpacity;
|
||||
}
|
||||
|
||||
public function onMarbleContact(time:TimeState, ?contact:CollisionInfo) {}
|
||||
public function onMarbleContact(marble:Marble, time:TimeState, ?contact:CollisionInfo) {}
|
||||
|
||||
public function onMarbleInside(time:TimeState) {}
|
||||
public function onMarbleInside(marble:Marble, time:TimeState) {}
|
||||
|
||||
public function onMarbleEnter(time:TimeState) {}
|
||||
public function onMarbleEnter(marble:Marble, time:TimeState) {}
|
||||
|
||||
public function onMarbleLeave(time:TimeState) {}
|
||||
public function onMarbleLeave(marble:Marble, time:TimeState) {}
|
||||
|
||||
public function onLevelStart() {}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package src;
|
||||
|
||||
import net.NetPacket.MarbleUpdatePacket;
|
||||
import net.MoveManager;
|
||||
import net.MoveManager.NetMove;
|
||||
import shaders.marble.CrystalMarb;
|
||||
|
|
@ -1573,7 +1574,7 @@ class Marble extends GameObject {
|
|||
var pTime = timeState.clone();
|
||||
pTime.dt = timeStep;
|
||||
pTime.currentAttemptTime = passedTime;
|
||||
this.heldPowerup.use(pTime);
|
||||
this.heldPowerup.use(this, pTime);
|
||||
this.heldPowerup = null;
|
||||
if (this.level.isRecording) {
|
||||
this.level.replay.recordPowerupPickup(null);
|
||||
|
|
@ -1608,7 +1609,7 @@ class Marble extends GameObject {
|
|||
|
||||
newPos = this.collider.transform.getPosition();
|
||||
|
||||
if (this.controllable && this.prevPos != null) {
|
||||
if (this.prevPos != null) {
|
||||
var tempTimeState = timeState.clone();
|
||||
tempTimeState.currentAttemptTime = passedTime;
|
||||
this.level.callCollisionHandlers(cast this, tempTimeState, oldPos, newPos);
|
||||
|
|
@ -1622,55 +1623,24 @@ class Marble extends GameObject {
|
|||
public function packUpdate(move:NetMove) {
|
||||
var b = new haxe.io.BytesOutput();
|
||||
b.writeByte(NetPacketType.MarbleUpdate);
|
||||
b.writeUInt16(connection != null ? connection.id : 0);
|
||||
MoveManager.packMove(move, b);
|
||||
b.writeUInt16(this.level.ticks); // So we can get the clients to do stuff about it
|
||||
b.writeFloat(this.newPos.x);
|
||||
b.writeFloat(this.newPos.y);
|
||||
b.writeFloat(this.newPos.z);
|
||||
b.writeFloat(this.velocity.x);
|
||||
b.writeFloat(this.velocity.y);
|
||||
b.writeFloat(this.velocity.z);
|
||||
b.writeFloat(this.omega.x);
|
||||
b.writeFloat(this.omega.y);
|
||||
b.writeFloat(this.omega.z);
|
||||
var marbleUpdate = new MarbleUpdatePacket();
|
||||
marbleUpdate.clientId = connection != null ? connection.id : 0;
|
||||
marbleUpdate.serverTicks = move.timeState.ticks;
|
||||
marbleUpdate.position = this.newPos;
|
||||
marbleUpdate.velocity = this.velocity;
|
||||
marbleUpdate.omega = this.omega;
|
||||
marbleUpdate.move = move;
|
||||
marbleUpdate.serialize(b);
|
||||
return b.getBytes();
|
||||
}
|
||||
|
||||
public function unpackUpdate(b:haxe.io.BytesInput) {
|
||||
public function unpackUpdate(p:MarbleUpdatePacket) {
|
||||
// Assume packet header is already read
|
||||
var serverMove = MoveManager.unpackMove(b);
|
||||
if (Net.isClient)
|
||||
Net.clientConnection.moveManager.acknowledgeMove(serverMove.id);
|
||||
var serverTicks = b.readUInt16();
|
||||
this.oldPos = this.newPos;
|
||||
this.newPos = new Vector(b.readFloat(), b.readFloat(), b.readFloat());
|
||||
this.newPos = p.position;
|
||||
this.collider.transform.setPosition(this.newPos);
|
||||
this.velocity = new Vector(b.readFloat(), b.readFloat(), b.readFloat());
|
||||
this.omega = new Vector(b.readFloat(), b.readFloat(), b.readFloat());
|
||||
|
||||
// Apply the moves we have queued
|
||||
if (Net.isClient) {
|
||||
this.isNetUpdate = true;
|
||||
if (this.controllable) {
|
||||
for (move in @:privateAccess Net.clientConnection.moveManager.queuedMoves) {
|
||||
moveMotionDir = move.motionDir;
|
||||
advancePhysics(move.timeState, move.move, this.level.collisionWorld, this.level.pathedInteriors);
|
||||
}
|
||||
} else {
|
||||
var tickDiff = this.level.ticks - serverTicks;
|
||||
if (tickDiff > 0) {
|
||||
var timeState = this.level.timeState.clone();
|
||||
timeState.dt = 0.032;
|
||||
var m = serverMove.move;
|
||||
moveMotionDir = serverMove.motionDir;
|
||||
for (o in 0...tickDiff) {
|
||||
advancePhysics(timeState, m, this.level.collisionWorld, this.level.pathedInteriors);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.isNetUpdate = false;
|
||||
}
|
||||
this.velocity = p.velocity;
|
||||
this.omega = p.omega;
|
||||
}
|
||||
|
||||
public function updateServer(timeState:TimeState, collisionWorld:CollisionWorld, pathedInteriors:Array<PathedInterior>, packets:Array<haxe.io.Bytes>) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package src;
|
||||
|
||||
import net.NetPacket.MarbleUpdatePacket;
|
||||
import net.NetPacket.MarbleMovePacket;
|
||||
import net.MoveManager;
|
||||
import net.NetCommands;
|
||||
import net.Net;
|
||||
|
|
@ -196,12 +198,14 @@ class MarbleWorld extends Scheduler {
|
|||
|
||||
public var startRealTime:Float = 0;
|
||||
public var multiplayerStarted:Bool = false;
|
||||
public var ticks:Int = 0; // How many 32ms ticks have happened
|
||||
|
||||
var tickAccumulator:Float = 0.0;
|
||||
var maxPredictionTicks:Int = 16;
|
||||
|
||||
var clientMarbles:Map<ClientConnection, Marble> = [];
|
||||
|
||||
public var lastMoves:Map<Int, MarbleUpdatePacket> = [];
|
||||
|
||||
// Loading
|
||||
var resourceLoadFuncs:Array<(() -> Void)->Void> = [];
|
||||
|
||||
|
|
@ -588,7 +592,7 @@ class MarbleWorld extends Scheduler {
|
|||
interior.reset();
|
||||
|
||||
this.setUp(startquat.up, this.timeState, true);
|
||||
this.deselectPowerUp();
|
||||
this.deselectPowerUp(this.marble);
|
||||
playGui.setCenterText('');
|
||||
|
||||
AudioManager.playSound(ResourceLoader.getResource('data/sound/spawn_alternate.wav', ResourceLoader.getAudio, this.soundResources));
|
||||
|
|
@ -1020,6 +1024,53 @@ class MarbleWorld extends Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
public function applyReceivedMoves() {
|
||||
for (client => lastMove in lastMoves) {
|
||||
if (lastMove.applied)
|
||||
continue;
|
||||
if (lastMove.clientId == Net.clientId)
|
||||
marble.unpackUpdate(lastMove);
|
||||
else
|
||||
clientMarbles[Net.clientIdMap[client]].unpackUpdate(lastMove);
|
||||
}
|
||||
}
|
||||
|
||||
public function applyClientPrediction() {
|
||||
for (client => lastMove in lastMoves) {
|
||||
if (lastMove.applied)
|
||||
continue;
|
||||
var marbleToUpdate = lastMove.clientId == Net.clientId ? marble : clientMarbles[Net.clientIdMap[client]];
|
||||
|
||||
@:privateAccess marbleToUpdate.isNetUpdate = true;
|
||||
if (marbleToUpdate == marble) {
|
||||
var moveManager = @:privateAccess Net.clientConnection.moveManager;
|
||||
var catchUpTickCount = 0;
|
||||
Net.clientConnection.moveManager.acknowledgeMove(lastMove.move.id);
|
||||
var advanceTimeState = timeState.clone();
|
||||
advanceTimeState.dt = 0.032;
|
||||
for (move in @:privateAccess moveManager.queuedMoves) {
|
||||
@:privateAccess marbleToUpdate.moveMotionDir = move.motionDir;
|
||||
@:privateAccess marbleToUpdate.advancePhysics(advanceTimeState, move.move, this.collisionWorld, this.pathedInteriors);
|
||||
}
|
||||
} else {
|
||||
var tickDiff = timeState.ticks - lastMove.serverTicks;
|
||||
if (tickDiff > this.maxPredictionTicks)
|
||||
tickDiff = this.maxPredictionTicks;
|
||||
if (tickDiff > 0) {
|
||||
var advanceTimeState = timeState.clone();
|
||||
advanceTimeState.dt = 0.032;
|
||||
var m = lastMove.move.move;
|
||||
@:privateAccess marbleToUpdate.moveMotionDir = lastMove.move.motionDir;
|
||||
for (o in 0...(tickDiff + 1)) {
|
||||
@:privateAccess marbleToUpdate.advancePhysics(advanceTimeState, m, this.collisionWorld, this.pathedInteriors);
|
||||
}
|
||||
}
|
||||
}
|
||||
@:privateAccess marbleToUpdate.isNetUpdate = false;
|
||||
lastMove.applied = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function rollback(t:Float) {
|
||||
var newT = timeState.currentAttemptTime - t;
|
||||
var rewindFrame = rewindManager.getNextRewindFrame(timeState.currentAttemptTime - t);
|
||||
|
|
@ -1216,6 +1267,14 @@ class MarbleWorld extends Scheduler {
|
|||
if (this.isMultiplayer) {
|
||||
tickAccumulator += timeState.dt;
|
||||
while (tickAccumulator >= 0.032) {
|
||||
// Apply the server side ticks
|
||||
if (Net.isClient) {
|
||||
applyReceivedMoves();
|
||||
// Catch up
|
||||
applyClientPrediction();
|
||||
}
|
||||
|
||||
// Do the clientside prediction sim
|
||||
var fixedDt = timeState.clone();
|
||||
fixedDt.dt = 0.032;
|
||||
tickAccumulator -= 0.032;
|
||||
|
|
@ -1231,7 +1290,7 @@ class MarbleWorld extends Scheduler {
|
|||
}
|
||||
}
|
||||
}
|
||||
ticks++;
|
||||
timeState.ticks++;
|
||||
}
|
||||
marble.updateClient(timeState, this.pathedInteriors);
|
||||
for (client => marble in clientMarbles) {
|
||||
|
|
@ -1491,10 +1550,10 @@ class MarbleWorld extends Scheduler {
|
|||
var shape:DtsObject = cast contact.go;
|
||||
|
||||
if (contact.boundingBox.collide(box)) {
|
||||
shape.onMarbleInside(timeState);
|
||||
shape.onMarbleInside(marble, timeState);
|
||||
if (!this.shapeOrTriggerInside.contains(contact.go)) {
|
||||
this.shapeOrTriggerInside.push(contact.go);
|
||||
shape.onMarbleEnter(timeState);
|
||||
shape.onMarbleEnter(marble, timeState);
|
||||
}
|
||||
inside.push(contact.go);
|
||||
}
|
||||
|
|
@ -1504,10 +1563,10 @@ class MarbleWorld extends Scheduler {
|
|||
var triggeraabb = trigger.collider.boundingBox;
|
||||
|
||||
if (triggeraabb.collide(box)) {
|
||||
trigger.onMarbleInside(timeState);
|
||||
trigger.onMarbleInside(marble, timeState);
|
||||
if (!this.shapeOrTriggerInside.contains(contact.go)) {
|
||||
this.shapeOrTriggerInside.push(contact.go);
|
||||
trigger.onMarbleEnter(timeState);
|
||||
trigger.onMarbleEnter(marble, timeState);
|
||||
}
|
||||
inside.push(contact.go);
|
||||
}
|
||||
|
|
@ -1518,7 +1577,7 @@ class MarbleWorld extends Scheduler {
|
|||
for (object in shapeOrTriggerInside) {
|
||||
if (!inside.contains(object)) {
|
||||
this.shapeOrTriggerInside.remove(object);
|
||||
object.onMarbleLeave(timeState);
|
||||
object.onMarbleLeave(marble, timeState);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1726,26 +1785,30 @@ class MarbleWorld extends Scheduler {
|
|||
return true;
|
||||
}
|
||||
|
||||
public function pickUpPowerUp(powerUp:PowerUp) {
|
||||
public function pickUpPowerUp(marble:Marble, powerUp:PowerUp) {
|
||||
if (powerUp == null)
|
||||
return false;
|
||||
if (this.marble.heldPowerup != null)
|
||||
if (this.marble.heldPowerup.identifier == powerUp.identifier)
|
||||
if (marble.heldPowerup != null)
|
||||
if (marble.heldPowerup.identifier == powerUp.identifier)
|
||||
return false;
|
||||
Console.log("PowerUp pickup: " + powerUp.identifier);
|
||||
this.marble.heldPowerup = powerUp;
|
||||
this.playGui.setPowerupImage(powerUp.identifier);
|
||||
MarbleGame.instance.touchInput.powerupButton.setEnabled(true);
|
||||
marble.heldPowerup = powerUp;
|
||||
if (this.marble == marble) {
|
||||
this.playGui.setPowerupImage(powerUp.identifier);
|
||||
MarbleGame.instance.touchInput.powerupButton.setEnabled(true);
|
||||
}
|
||||
if (this.isRecording) {
|
||||
this.replay.recordPowerupPickup(powerUp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deselectPowerUp() {
|
||||
this.marble.heldPowerup = null;
|
||||
this.playGui.setPowerupImage("");
|
||||
MarbleGame.instance.touchInput.powerupButton.setEnabled(false);
|
||||
public function deselectPowerUp(marble:Marble) {
|
||||
marble.heldPowerup = null;
|
||||
if (this.marble == marble) {
|
||||
this.playGui.setPowerupImage("");
|
||||
MarbleGame.instance.touchInput.powerupButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
public function addBonusTime(t:Float) {
|
||||
|
|
@ -1929,11 +1992,11 @@ class MarbleWorld extends Scheduler {
|
|||
this.playGui.setCenterText('');
|
||||
this.clearSchedule();
|
||||
this.outOfBounds = false;
|
||||
this.deselectPowerUp(); // Always deselect first
|
||||
this.deselectPowerUp(this.marble); // Always deselect first
|
||||
// Wait a bit to select the powerup to prevent immediately using it incase the user skipped the OOB screen by clicking
|
||||
if (this.checkpointHeldPowerup != null) {
|
||||
var powerup = this.checkpointHeldPowerup;
|
||||
this.pickUpPowerUp(powerup);
|
||||
this.pickUpPowerUp(this.marble, powerup);
|
||||
}
|
||||
AudioManager.playSound(ResourceLoader.getResource('data/sound/spawn_alternate.wav', ResourceLoader.getAudio, this.soundResources));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ class TimeState {
|
|||
var currentAttemptTime:Float;
|
||||
var gameplayClock:Float;
|
||||
var dt:Float;
|
||||
var ticks:Int; // How many 32ms ticks have happened
|
||||
|
||||
public function new() {}
|
||||
|
||||
|
|
@ -15,6 +16,7 @@ class TimeState {
|
|||
n.currentAttemptTime = this.currentAttemptTime;
|
||||
n.gameplayClock = this.gameplayClock;
|
||||
n.dt = this.dt;
|
||||
n.ticks = ticks;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ class CollisionEntity implements IOctreeObject implements IBVHObject {
|
|||
cinfo.force = surface.force;
|
||||
cinfo.friction = surface.friction;
|
||||
contacts.push(cinfo);
|
||||
this.go.onMarbleContact(timeState, cinfo);
|
||||
this.go.onMarbleContact(collisionEntity.marble, timeState, cinfo);
|
||||
// surfaceBestContact = cinfo;
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class CollisionHull extends CollisionEntity {
|
|||
cinfo.otherObject = this.go;
|
||||
cinfo.friction = friction;
|
||||
cinfo.force = force;
|
||||
this.go.onMarbleContact(timeState, cinfo);
|
||||
this.go.onMarbleContact(collisionEntity.marble, timeState, cinfo);
|
||||
return [cinfo];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ class Polygon extends MeshPrimitive {
|
|||
public var uvs:Array<UV>;
|
||||
public var idx:hxd.IndexBuffer;
|
||||
|
||||
var bounds:h3d.col.Bounds;
|
||||
|
||||
var scaled = 1.;
|
||||
var translatedX = 0.;
|
||||
var translatedY = 0.;
|
||||
|
|
@ -23,10 +25,13 @@ class Polygon extends MeshPrimitive {
|
|||
}
|
||||
|
||||
override function getBounds() {
|
||||
var b = new h3d.col.Bounds();
|
||||
for (p in points)
|
||||
b.addPoint(p);
|
||||
return b;
|
||||
if (bounds == null) {
|
||||
var b = new h3d.col.Bounds();
|
||||
for (p in points)
|
||||
b.addPoint(p);
|
||||
bounds = b;
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
override function alloc(engine:h3d.Engine) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package net;
|
||||
|
||||
import net.NetPacket.MarbleMovePacket;
|
||||
import src.TimeState;
|
||||
import src.Console;
|
||||
import net.Net.ClientConnection;
|
||||
|
|
@ -35,7 +36,7 @@ class MoveManager {
|
|||
var lastMove:NetMove;
|
||||
var lastAckMoveId:Int = -1;
|
||||
|
||||
static var maxMoves = 45; // Taken from Torque
|
||||
static var maxMoves = 16;
|
||||
|
||||
public function new(connection:ClientConnection) {
|
||||
queuedMoves = [];
|
||||
|
|
@ -84,15 +85,19 @@ class MoveManager {
|
|||
nextMoveId = 0;
|
||||
|
||||
var b = new haxe.io.BytesOutput();
|
||||
var movePacket = new MarbleMovePacket();
|
||||
movePacket.clientId = Net.clientId;
|
||||
movePacket.move = netMove;
|
||||
movePacket.clientTicks = timeState.ticks;
|
||||
b.writeByte(NetPacketType.MarbleMove);
|
||||
b.writeUInt16(Net.clientId);
|
||||
movePacket.serialize(b);
|
||||
|
||||
Net.sendPacketToHost(packMove(netMove, b));
|
||||
Net.sendPacketToHost(b);
|
||||
|
||||
return netMove;
|
||||
}
|
||||
|
||||
public static function packMove(m:NetMove, b:haxe.io.BytesOutput) {
|
||||
public static inline function packMove(m:NetMove, b:haxe.io.BytesOutput) {
|
||||
b.writeUInt16(m.id);
|
||||
b.writeFloat(m.move.d.x);
|
||||
b.writeFloat(m.move.d.y);
|
||||
|
|
@ -108,7 +113,7 @@ class MoveManager {
|
|||
return b;
|
||||
}
|
||||
|
||||
public static function unpackMove(b:haxe.io.BytesInput) {
|
||||
public static inline function unpackMove(b:haxe.io.BytesInput) {
|
||||
var moveId = b.readUInt16();
|
||||
var move = new Move();
|
||||
move.d = new Vector();
|
||||
|
|
@ -147,7 +152,6 @@ class MoveManager {
|
|||
if (queuedMoves.length == 0)
|
||||
return;
|
||||
while (m != queuedMoves[0].id) {
|
||||
trace('Ignoring move ${queuedMoves[0].id}, need ${m}');
|
||||
queuedMoves.shift();
|
||||
}
|
||||
if (m == queuedMoves[0].id)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package net;
|
||||
|
||||
import net.NetPacket.MarbleUpdatePacket;
|
||||
import net.NetPacket.MarbleMovePacket;
|
||||
import haxe.Json;
|
||||
import datachannel.RTCPeerConnection;
|
||||
import datachannel.RTCDataChannel;
|
||||
|
|
@ -241,21 +243,24 @@ class Net {
|
|||
}
|
||||
|
||||
case MarbleUpdate:
|
||||
var marbleClientId = input.readUInt16();
|
||||
if (marbleClientId == clientId) {
|
||||
if (MarbleGame.instance.world != null)
|
||||
MarbleGame.instance.world.marble.unpackUpdate(input);
|
||||
} else {
|
||||
var cc = clientIdMap[marbleClientId];
|
||||
if (MarbleGame.instance.world != null)
|
||||
@:privateAccess MarbleGame.instance.world.clientMarbles[cc].unpackUpdate(input);
|
||||
var marbleUpdatePacket = new MarbleUpdatePacket();
|
||||
marbleUpdatePacket.deserialize(input);
|
||||
var cc = marbleUpdatePacket.clientId;
|
||||
if (MarbleGame.instance.world != null) {
|
||||
var m = MarbleGame.instance.world.lastMoves;
|
||||
if (m.exists(cc)) {
|
||||
if (m[cc].serverTicks < marbleUpdatePacket.serverTicks)
|
||||
m.set(cc, marbleUpdatePacket);
|
||||
} else {
|
||||
m.set(cc, marbleUpdatePacket);
|
||||
}
|
||||
}
|
||||
|
||||
case MarbleMove:
|
||||
var marbleClientId = input.readUInt16();
|
||||
var cc = clientIdMap[marbleClientId];
|
||||
var m = MoveManager.unpackMove(input);
|
||||
cc.moveManager.queueMove(m);
|
||||
var movePacket = new MarbleMovePacket();
|
||||
movePacket.deserialize(input);
|
||||
var cc = clientIdMap[movePacket.clientId];
|
||||
cc.moveManager.queueMove(movePacket.move);
|
||||
|
||||
case _:
|
||||
trace("unknown command: " + packetType);
|
||||
|
|
|
|||
67
src/net/NetPacket.hx
Normal file
67
src/net/NetPacket.hx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package net;
|
||||
|
||||
import h3d.Vector;
|
||||
import net.MoveManager.NetMove;
|
||||
|
||||
interface NetPacket {
|
||||
public function serialize(b:haxe.io.BytesOutput):Void;
|
||||
public function deserialize(b:haxe.io.BytesInput):Void;
|
||||
}
|
||||
|
||||
@:publicFields
|
||||
class MarbleMovePacket implements NetPacket {
|
||||
var clientId:Int;
|
||||
var clientTicks:Int;
|
||||
var move:NetMove;
|
||||
|
||||
public function new() {}
|
||||
|
||||
public inline function deserialize(b:haxe.io.BytesInput) {
|
||||
clientId = b.readUInt16();
|
||||
clientTicks = b.readUInt16();
|
||||
move = MoveManager.unpackMove(b);
|
||||
}
|
||||
|
||||
public inline function serialize(b:haxe.io.BytesOutput) {
|
||||
b.writeUInt16(clientId);
|
||||
b.writeUInt16(clientTicks);
|
||||
MoveManager.packMove(move, b);
|
||||
}
|
||||
}
|
||||
|
||||
@:publicFields
|
||||
class MarbleUpdatePacket implements NetPacket {
|
||||
var clientId:Int;
|
||||
var move:NetMove;
|
||||
var serverTicks:Int;
|
||||
var position:Vector;
|
||||
var velocity:Vector;
|
||||
var omega:Vector;
|
||||
var applied:Bool = false;
|
||||
|
||||
public function new() {}
|
||||
|
||||
public inline function serialize(b:haxe.io.BytesOutput) {
|
||||
b.writeUInt16(clientId);
|
||||
MoveManager.packMove(move, b);
|
||||
b.writeUInt16(serverTicks);
|
||||
b.writeFloat(position.x);
|
||||
b.writeFloat(position.y);
|
||||
b.writeFloat(position.z);
|
||||
b.writeFloat(velocity.x);
|
||||
b.writeFloat(velocity.y);
|
||||
b.writeFloat(velocity.z);
|
||||
b.writeFloat(omega.x);
|
||||
b.writeFloat(omega.y);
|
||||
b.writeFloat(omega.z);
|
||||
}
|
||||
|
||||
public inline function deserialize(b:haxe.io.BytesInput) {
|
||||
clientId = b.readUInt16();
|
||||
move = MoveManager.unpackMove(b);
|
||||
serverTicks = b.readUInt16();
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
@ -128,13 +128,13 @@ class RewindManager {
|
|||
|
||||
if (level.marble.heldPowerup == null) {
|
||||
if (rf.marblePowerup != null) {
|
||||
level.pickUpPowerUp(rf.marblePowerup);
|
||||
level.pickUpPowerUp(level.marble, rf.marblePowerup);
|
||||
}
|
||||
} else {
|
||||
if (rf.marblePowerup == null) {
|
||||
level.deselectPowerUp();
|
||||
level.deselectPowerUp(level.marble);
|
||||
} else {
|
||||
level.pickUpPowerUp(rf.marblePowerup);
|
||||
level.pickUpPowerUp(level.marble, rf.marblePowerup);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import collision.CollisionInfo;
|
|||
import src.DtsObject;
|
||||
import src.TimeState;
|
||||
import src.Util;
|
||||
import src.Marble;
|
||||
|
||||
class AbstractBumper extends DtsObject {
|
||||
var lastContactTime = Math.NEGATIVE_INFINITY;
|
||||
|
|
@ -27,8 +28,8 @@ class AbstractBumper extends DtsObject {
|
|||
return completion;
|
||||
}
|
||||
|
||||
override function onMarbleContact(time:TimeState, ?contact:CollisionInfo) {
|
||||
super.onMarbleContact(time, contact);
|
||||
override function onMarbleContact(marble:Marble, time:TimeState, ?contact:CollisionInfo) {
|
||||
super.onMarbleContact(marble, time, contact);
|
||||
if (time.timeSinceLoad - this.lastContactTime <= 0)
|
||||
return;
|
||||
var currentCompletion = this.getCurrentCompletion(time);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package shapes;
|
||||
|
||||
import src.Marble;
|
||||
import mis.MisParser;
|
||||
import dts.DtsFile;
|
||||
import src.ResourceLoader;
|
||||
|
|
@ -26,13 +27,13 @@ class AntiGravity extends PowerUp {
|
|||
this.cooldownDuration = Math.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
public function pickUp():Bool {
|
||||
public function pickUp(marble:Marble):Bool {
|
||||
var direction = new Vector(0, 0, -1);
|
||||
direction.transform(this.getRotationQuat().toMatrix());
|
||||
return !direction.equals(this.level.currentUp);
|
||||
}
|
||||
|
||||
public function use(timeState:TimeState) {
|
||||
public function use(marble:Marble, timeState:TimeState) {
|
||||
if (!this.level.rewinding) {
|
||||
var direction = new Vector(0, 0, -1);
|
||||
direction.transform(this.getRotationQuat().toMatrix());
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package shapes;
|
||||
|
||||
import src.Marble;
|
||||
import src.MarbleWorld;
|
||||
import src.ResourceLoader;
|
||||
import src.TimeState;
|
||||
|
|
@ -27,11 +28,11 @@ class Blast extends PowerUp {
|
|||
});
|
||||
}
|
||||
|
||||
public function pickUp():Bool {
|
||||
public function pickUp(marble:Marble):Bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function use(timeState:TimeState) {
|
||||
public function use(marble:Marble, timeState:TimeState) {
|
||||
this.level.blastAmount = 1.2;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package shapes;
|
||||
|
||||
import src.Marble;
|
||||
import gui.AchievementsGui;
|
||||
import src.Settings;
|
||||
import mis.MissionElement.MissionElementItem;
|
||||
|
|
@ -16,7 +17,7 @@ class EasterEgg extends PowerUp {
|
|||
this.autoUse = true;
|
||||
}
|
||||
|
||||
public function pickUp():Bool {
|
||||
public function pickUp(marble:Marble):Bool {
|
||||
var found:Bool = false;
|
||||
if (Settings.easterEggs.exists(this.level.mission.path)) {
|
||||
found = true;
|
||||
|
|
@ -42,7 +43,7 @@ class EasterEgg extends PowerUp {
|
|||
});
|
||||
}
|
||||
|
||||
public function use(timeState:src.TimeState) {}
|
||||
public function use(marble:Marble, timeState:src.TimeState) {}
|
||||
|
||||
override function getPreloadMaterials(dts:dts.DtsFile) {
|
||||
var mats = super.getPreloadMaterials(dts);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import src.TimeState;
|
|||
import src.DtsObject;
|
||||
import src.ResourceLoaderWorker;
|
||||
import src.ResourceLoader;
|
||||
import src.Marble;
|
||||
|
||||
class Gem extends DtsObject {
|
||||
public var pickedUp:Bool;
|
||||
|
|
@ -60,8 +61,8 @@ class Gem extends DtsObject {
|
|||
}
|
||||
}
|
||||
|
||||
override function onMarbleInside(timeState:TimeState) {
|
||||
super.onMarbleInside(timeState);
|
||||
override function onMarbleInside(marble:Marble, timeState:TimeState) {
|
||||
super.onMarbleInside(marble, timeState);
|
||||
if (this.pickedUp || this.level.rewinding)
|
||||
return;
|
||||
this.pickedUp = true;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package shapes;
|
||||
|
||||
import src.Marble;
|
||||
import h3d.mat.Material;
|
||||
import src.ResourceLoader;
|
||||
import mis.MissionElement.MissionElementItem;
|
||||
|
|
@ -28,14 +29,13 @@ class Helicopter extends PowerUp {
|
|||
});
|
||||
}
|
||||
|
||||
public function pickUp():Bool {
|
||||
return this.level.pickUpPowerUp(this);
|
||||
public function pickUp(marble:Marble):Bool {
|
||||
return this.level.pickUpPowerUp(marble, this);
|
||||
}
|
||||
|
||||
public function use(timeState:TimeState) {
|
||||
var marble = this.level.marble;
|
||||
public function use(marble:Marble, timeState:TimeState) {
|
||||
marble.enableHelicopter(timeState.currentAttemptTime);
|
||||
this.level.deselectPowerUp();
|
||||
this.level.deselectPowerUp(marble);
|
||||
}
|
||||
|
||||
override function postProcessMaterial(matName:String, material:Material) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import src.ResourceLoader;
|
|||
import src.TimeState;
|
||||
import mis.MissionElement.MissionElementItem;
|
||||
import src.AudioManager;
|
||||
import src.Marble;
|
||||
|
||||
class MegaMarble extends PowerUp {
|
||||
public function new(element:MissionElementItem) {
|
||||
|
|
@ -35,13 +36,13 @@ class MegaMarble extends PowerUp {
|
|||
});
|
||||
}
|
||||
|
||||
public function pickUp():Bool {
|
||||
return this.level.pickUpPowerUp(this);
|
||||
public function pickUp(marble:Marble):Bool {
|
||||
return this.level.pickUpPowerUp(marble, this);
|
||||
}
|
||||
|
||||
public function use(timeState:TimeState) {
|
||||
this.level.marble.enableMegaMarble(timeState.currentAttemptTime);
|
||||
this.level.deselectPowerUp();
|
||||
public function use(marble:Marble, timeState:TimeState) {
|
||||
marble.enableMegaMarble(timeState.currentAttemptTime);
|
||||
this.level.deselectPowerUp(marble);
|
||||
AudioManager.playSound(ResourceLoader.getResource('data/sound/use_mega.wav', ResourceLoader.getAudio, this.soundResources));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package shapes;
|
||||
|
||||
import src.Marble;
|
||||
import src.AudioManager;
|
||||
import hxd.res.Sound;
|
||||
import mis.MissionElement.MissionElementItem;
|
||||
|
|
@ -26,27 +27,29 @@ abstract class PowerUp extends DtsObject {
|
|||
this.element = element;
|
||||
}
|
||||
|
||||
public override function onMarbleInside(timeState:TimeState) {
|
||||
public override function onMarbleInside(marble:Marble, timeState:TimeState) {
|
||||
var pickupable = this.lastPickUpTime == -1 || (timeState.currentAttemptTime - this.lastPickUpTime) >= this.cooldownDuration;
|
||||
if (!pickupable)
|
||||
return;
|
||||
|
||||
if (this.pickUp()) {
|
||||
if (this.pickUp(marble)) {
|
||||
// this.level.replay.recordMarbleInside(this);
|
||||
|
||||
this.lastPickUpTime = timeState.currentAttemptTime;
|
||||
if (this.autoUse)
|
||||
this.use(timeState);
|
||||
this.use(marble, timeState);
|
||||
|
||||
if (customPickupMessage != null)
|
||||
this.level.displayAlert(customPickupMessage);
|
||||
else
|
||||
this.level.displayAlert('You picked up ${this.pickUpName}!');
|
||||
if (this.element.showhelponpickup == "1" && !this.autoUse)
|
||||
this.level.displayHelp('Press <func:bind mousefire> to use the ${this.pickUpName}!');
|
||||
if (level.marble == marble) {
|
||||
if (customPickupMessage != null)
|
||||
this.level.displayAlert(customPickupMessage);
|
||||
else
|
||||
this.level.displayAlert('You picked up ${this.pickUpName}!');
|
||||
if (this.element.showhelponpickup == "1" && !this.autoUse)
|
||||
this.level.displayHelp('Press <func:bind mousefire> to use the ${this.pickUpName}!');
|
||||
|
||||
if (pickupSound != null && !this.level.rewinding) {
|
||||
AudioManager.playSound(pickupSound);
|
||||
if (pickupSound != null && !this.level.rewinding) {
|
||||
AudioManager.playSound(pickupSound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -62,9 +65,9 @@ abstract class PowerUp extends DtsObject {
|
|||
this.setOpacity(opacity);
|
||||
}
|
||||
|
||||
public abstract function pickUp():Bool;
|
||||
public abstract function pickUp(marble:Marble):Bool;
|
||||
|
||||
public abstract function use(timeState:TimeState):Void;
|
||||
public abstract function use(marble:Marble, timeState:TimeState):Void;
|
||||
|
||||
public override function reset() {
|
||||
this.lastPickUpTime = Math.NEGATIVE_INFINITY;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package shapes;
|
||||
|
||||
import src.Marble;
|
||||
import src.ResourceLoader;
|
||||
import mis.MissionElement.MissionElementItem;
|
||||
import src.TimeState;
|
||||
|
|
@ -62,21 +63,21 @@ class SuperJump extends PowerUp {
|
|||
});
|
||||
}
|
||||
|
||||
public function pickUp():Bool {
|
||||
return this.level.pickUpPowerUp(this);
|
||||
public function pickUp(marble:Marble):Bool {
|
||||
return this.level.pickUpPowerUp(marble, this);
|
||||
}
|
||||
|
||||
public function use(timeState:TimeState) {
|
||||
var marble = this.level.marble;
|
||||
public function use(marble:Marble, timeState:TimeState) {
|
||||
var masslessFactor = marble.getMass() * 0.7 + 1 - 0.7;
|
||||
var boost = this.level.currentUp.multiply(20 * masslessFactor / marble.getMass());
|
||||
marble.velocity = marble.velocity.add(boost);
|
||||
this.level.particleManager.createEmitter(superJumpParticleOptions, this.sjEmitterParticleData, null, () -> marble.getAbsPos().getPosition());
|
||||
// marble.body.addLinearVelocity(this.level.currentUp.scale(20)); // Simply add to vertical velocity
|
||||
// if (!this.level.rewinding)
|
||||
AudioManager.playSound(ResourceLoader.getResource("data/sound/use_superjump.wav", ResourceLoader.getAudio, this.soundResources));
|
||||
if (level.marble == marble)
|
||||
AudioManager.playSound(ResourceLoader.getResource("data/sound/use_superjump.wav", ResourceLoader.getAudio, this.soundResources));
|
||||
// this.level.particles.createEmitter(superJumpParticleOptions, null, () => Util.vecOimoToThree(marble.body.getPosition()));
|
||||
this.level.deselectPowerUp();
|
||||
this.level.deselectPowerUp(marble);
|
||||
}
|
||||
|
||||
override function getPreloadMaterials(dts:dts.DtsFile) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package shapes;
|
||||
|
||||
import src.Marble;
|
||||
import mis.MissionElement.MissionElementItem;
|
||||
import src.TimeState;
|
||||
import src.ResourceLoader;
|
||||
|
|
@ -63,12 +64,11 @@ class SuperSpeed extends PowerUp {
|
|||
});
|
||||
}
|
||||
|
||||
public function pickUp():Bool {
|
||||
return this.level.pickUpPowerUp(this);
|
||||
public function pickUp(marble:Marble):Bool {
|
||||
return this.level.pickUpPowerUp(marble, this);
|
||||
}
|
||||
|
||||
public function use(timeState:TimeState) {
|
||||
var marble = this.level.marble;
|
||||
public function use(marble:Marble, timeState:TimeState) {
|
||||
var movementVector = marble.getMarbleAxis()[0];
|
||||
|
||||
// Okay, so super speed directionality is a bit strange. In general, the direction is based on the normal vector of the last surface you had contact with.
|
||||
|
|
@ -86,9 +86,10 @@ class SuperSpeed extends PowerUp {
|
|||
// marble.body.addLinearVelocity(Util.vecThreeToOimo(movementVector).scale(24.7)); // Whirligig's determined value
|
||||
// marble.body.addLinearVelocity(this.level.currentUp.scale(20)); // Simply add to vertical velocity
|
||||
// if (!this.level.rewinding)
|
||||
AudioManager.playSound(ResourceLoader.getResource("data/sound/use_speed.wav", ResourceLoader.getAudio, this.soundResources));
|
||||
if (level.marble == marble)
|
||||
AudioManager.playSound(ResourceLoader.getResource("data/sound/use_speed.wav", ResourceLoader.getAudio, this.soundResources));
|
||||
this.level.particleManager.createEmitter(superSpeedParticleOptions, this.ssEmitterParticleData, null, () -> marble.getAbsPos().getPosition());
|
||||
this.level.deselectPowerUp();
|
||||
this.level.deselectPowerUp(marble);
|
||||
}
|
||||
|
||||
override function postProcessMaterial(matName:String, material:h3d.mat.Material) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package shapes;
|
||||
|
||||
import src.Marble;
|
||||
import src.ResourceLoader;
|
||||
import mis.MissionElement.MissionElementItem;
|
||||
import src.TimeState;
|
||||
|
|
@ -39,11 +40,11 @@ class TimeTravel extends PowerUp {
|
|||
});
|
||||
}
|
||||
|
||||
public function pickUp():Bool {
|
||||
public function pickUp(marble:Marble):Bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function use(time:TimeState) {
|
||||
public function use(marble:Marble, time:TimeState) {
|
||||
if (!this.level.rewinding)
|
||||
level.addBonusTime(this.timeBonus);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import src.ForceObject;
|
|||
import src.ResourceLoader;
|
||||
import src.AudioManager;
|
||||
import src.MarbleWorld;
|
||||
import src.Marble;
|
||||
|
||||
class Trapdoor extends DtsObject {
|
||||
var lastContactTime = -1e8;
|
||||
|
|
@ -64,8 +65,8 @@ class Trapdoor extends DtsObject {
|
|||
return completion;
|
||||
}
|
||||
|
||||
override function onMarbleContact(time:TimeState, ?contact:CollisionInfo) {
|
||||
super.onMarbleContact(time, contact);
|
||||
override function onMarbleContact(marble:Marble, time:TimeState, ?contact:CollisionInfo) {
|
||||
super.onMarbleContact(marble, time, contact);
|
||||
if (time.timeSinceLoad - this.lastContactTime <= 0)
|
||||
return; // The trapdoor is queued to open, so don't do anything.
|
||||
var currentCompletion = this.getCurrentCompletion(time);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import src.MarbleWorld;
|
|||
import mis.MissionElement.MissionElementTrigger;
|
||||
import src.ResourceLoader;
|
||||
import mis.MisParser;
|
||||
import src.Marble;
|
||||
|
||||
class CheckpointTrigger extends Trigger {
|
||||
public var disableOOB = false;
|
||||
|
|
@ -28,8 +29,8 @@ class CheckpointTrigger extends Trigger {
|
|||
});
|
||||
}
|
||||
|
||||
public override function onMarbleEnter(time:src.TimeState) {
|
||||
super.onMarbleEnter(time);
|
||||
public override function onMarbleEnter(marble:Marble, time:src.TimeState) {
|
||||
super.onMarbleEnter(marble, time);
|
||||
if (simGroup == null)
|
||||
return;
|
||||
var shape = level.simGroups[simGroup].filter(x -> x.identifier == "Checkpoint");
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ package triggers;
|
|||
import src.TimeState;
|
||||
import src.ResourceLoader;
|
||||
import src.AudioManager;
|
||||
import src.Marble;
|
||||
|
||||
class HelpTrigger extends Trigger {
|
||||
override function onMarbleEnter(timeState:TimeState) {
|
||||
override function onMarbleEnter(marble:Marble, timeState:TimeState) {
|
||||
AudioManager.playSound(ResourceLoader.getResource('data/sound/infotutorial.wav', ResourceLoader.getAudio, this.soundResources));
|
||||
if (this.element.text != null && this.element.text != "")
|
||||
this.level.displayHelp(this.element.text);
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ package triggers;
|
|||
|
||||
import src.TimeState;
|
||||
import src.ResourceLoader;
|
||||
import src.Marble;
|
||||
|
||||
class InBoundsTrigger extends Trigger {
|
||||
override function onMarbleLeave(timeState:TimeState) {
|
||||
override function onMarbleLeave(marble:Marble, timeState:TimeState) {
|
||||
this.level.goOutOfBounds();
|
||||
// this.level.replay.recordMarbleLeave(this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import src.PathedInterior;
|
|||
import mis.MissionElement.MissionElementTrigger;
|
||||
import src.TimeState;
|
||||
import mis.MisParser;
|
||||
import src.Marble;
|
||||
|
||||
class MustChangeTrigger extends Trigger {
|
||||
var interior:PathedInterior;
|
||||
|
|
@ -13,7 +14,7 @@ class MustChangeTrigger extends Trigger {
|
|||
this.interior = interior;
|
||||
}
|
||||
|
||||
public override function onMarbleEnter(time:TimeState) {
|
||||
public override function onMarbleEnter(marble:Marble, time:TimeState) {
|
||||
var ttime = MisParser.parseNumber(this.element.targettime);
|
||||
if (ttime > 0)
|
||||
ttime /= 1000;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ package triggers;
|
|||
|
||||
import src.TimeState;
|
||||
import src.ResourceLoader;
|
||||
import src.Marble;
|
||||
|
||||
class OutOfBoundsTrigger extends Trigger {
|
||||
override function onMarbleInside(time:TimeState) {
|
||||
override function onMarbleInside(marble:Marble, time:TimeState) {
|
||||
this.level.goOutOfBounds();
|
||||
// this.level.replay.recordMarbleInside(this);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue