Change to use TimeStates everywhere

This commit is contained in:
RandomityGuy 2021-06-11 15:57:07 +05:30
parent 1c3e17d1bd
commit c7b365cdcf
26 changed files with 448 additions and 113 deletions

View file

@ -1,5 +1,6 @@
package src;
import src.TimeState;
import shaders.Billboard;
import collision.BoxCollisionEntity;
import shaders.DtsTexture;
@ -559,7 +560,7 @@ class DtsObject extends GameObject {
this.level.collisionWorld.updateTransform(this.boundingCollider);
}
public function update(currentTime:Float, dt:Float) {
public function update(timeState:TimeState) {
for (sequence in this.dts.sequences) {
if (!this.showSequences)
break;
@ -569,7 +570,7 @@ class DtsObject extends GameObject {
var rot = sequence.rotationMatters.length > 0 ? sequence.rotationMatters[0] : 0;
var trans = sequence.translationMatters.length > 0 ? sequence.translationMatters[0] : 0;
var affectedCount = 0;
var completion = (currentTime + dt) / sequence.duration;
var completion = timeState.timeSinceLoad / sequence.duration;
var quaternions:Array<Quat> = null;
var translations:Array<Vector> = null;
@ -745,7 +746,7 @@ class DtsObject extends GameObject {
if (iflSequence.length == 0 || !this.showSequences)
continue;
var completion = (currentTime + dt) / (iflSequence[0].duration);
var completion = timeState.timeSinceLoad / (iflSequence[0].duration);
var keyframe = Math.floor(completion * info.length) % info.length;
var currentFile = info[keyframe];
var texture = ResourceLoader.getTexture(this.directoryPath + '/' + currentFile);
@ -760,7 +761,7 @@ class DtsObject extends GameObject {
if (this.ambientRotate) {
var spinAnimation = new Quat();
spinAnimation.initRotateAxis(0, 0, -1, (currentTime + dt) * this.ambientSpinFactor);
spinAnimation.initRotateAxis(0, 0, -1, timeState.timeSinceLoad * this.ambientSpinFactor);
var orientation = this.getRotationQuat();
// spinAnimation.multiply(orientation, spinAnimation);

View file

@ -1,5 +1,6 @@
package src;
import src.TimeState;
import collision.CollisionInfo;
import h3d.scene.Object;
@ -8,13 +9,13 @@ class GameObject extends Object {
public var currentOpacity:Float = 1;
public var isCollideable:Bool = false;
public function onMarbleContact(time:Float, ?contact:CollisionInfo) {}
public function onMarbleContact(time:TimeState, ?contact:CollisionInfo) {}
public function onMarbleInside(time:Float) {}
public function onMarbleInside(time:TimeState) {}
public function onMarbleEnter(time:Float) {}
public function onMarbleEnter(time:TimeState) {}
public function onMarbleLeave(time:Float) {}
public function onMarbleLeave(time:TimeState) {}
public function onLevelStart() {}

View file

@ -1,5 +1,6 @@
package;
import shapes.EndPad;
import shapes.LandMine;
import shapes.StartPad;
import shapes.TriangleBumper;
@ -143,14 +144,19 @@ class Main extends hxd.App {
world.addDtsObject(tb);
var spad = new StartPad();
tb.x = 5;
tb.y = 3;
spad.x = 5;
spad.y = 3;
world.addDtsObject(spad);
var lm = new LandMine();
lm.x = 7;
world.addDtsObject(lm);
var epad = new EndPad();
epad.x = 5;
epad.x = -3;
world.addDtsObject(epad);
// var le:ParticleEmitterOptions = {
// ejectionPeriod: 0.01,

View file

@ -1,5 +1,6 @@
package src;
import src.TimeState;
import src.ParticleSystem.ParticleEmitter;
import src.ParticleSystem.ParticleData;
import src.ParticleSystem.ParticleEmitterOptions;
@ -191,9 +192,9 @@ class Marble extends GameObject {
level.addDtsObject(this.helicopter);
}
function findContacts(collisiomWorld:CollisionWorld, dt:Float) {
function findContacts(collisiomWorld:CollisionWorld, timeState:TimeState) {
this.contacts = queuedContacts;
var c = collisiomWorld.sphereIntersection(this.collider, dt);
var c = collisiomWorld.sphereIntersection(this.collider, timeState);
contacts = contacts.concat(c);
}
@ -637,11 +638,11 @@ class Marble extends GameObject {
return intersectT;
}
function advancePhysics(currentTime:Float, dt:Float, m:Move, collisionWorld:CollisionWorld, pathedInteriors:Array<PathedInterior>) {
var timeRemaining = dt;
function advancePhysics(timeState:TimeState, m:Move, collisionWorld:CollisionWorld, pathedInteriors:Array<PathedInterior>) {
var timeRemaining = timeState.dt;
var it = 0;
var piTime = currentTime;
var piTime = timeState.currentAttemptTime;
// if (this.controllable) {
// for (interior in pathedInteriors) {
@ -667,20 +668,23 @@ class Marble extends GameObject {
if (timeRemaining < 0.00800000037997961)
timeStep = timeRemaining;
this.findContacts(collisionWorld, timeStep);
var tempState = timeState.clone();
tempState.dt = timeStep;
this.findContacts(collisionWorld, tempState);
var cmf = this.computeMoveForces(m);
var isCentered:Bool = cmf.result;
var aControl = cmf.aControl;
var desiredOmega = cmf.desiredOmega;
var stoppedPaths = false;
stoppedPaths = this.velocityCancel(currentTime, dt, isCentered, false, stoppedPaths, pathedInteriors);
var A = this.getExternalForces(currentTime, m, timeStep);
stoppedPaths = this.velocityCancel(timeState.currentAttemptTime, timeStep, isCentered, false, stoppedPaths, pathedInteriors);
var A = this.getExternalForces(timeState.currentAttemptTime, m, timeStep);
var retf = this.applyContactForces(timeStep, m, isCentered, aControl, desiredOmega, A);
A = retf[0];
var a = retf[1];
this.velocity = this.velocity.add(A.multiply(timeStep));
this.omega = this.omega.add(a.multiply(timeStep));
stoppedPaths = this.velocityCancel(currentTime, dt, isCentered, true, stoppedPaths, pathedInteriors);
stoppedPaths = this.velocityCancel(timeState.currentAttemptTime, timeStep, isCentered, true, stoppedPaths, pathedInteriors);
this._totalTime += timeStep;
if (contacts.length != 0) {
this._contactTime += timeStep;
@ -703,7 +707,10 @@ class Marble extends GameObject {
for (interior in pathedInteriors) {
// interior.popTickState();
// interior.setStopped(stoppedPaths);
interior.update(piTime, timeStep);
var piDT = timeState.clone();
piDT.currentAttemptTime = piTime;
piDT.dt = timeStep;
interior.update(piDT);
}
}
@ -724,7 +731,10 @@ class Marble extends GameObject {
this.collider.velocity = this.velocity;
if (this.heldPowerup != null && m.powerup) {
this.heldPowerup.use(currentTime);
var pTime = timeState.clone();
pTime.dt = timeStep;
pTime.currentAttemptTime = piTime;
this.heldPowerup.use(pTime);
this.heldPowerup = null;
}
@ -734,7 +744,7 @@ class Marble extends GameObject {
this.queuedContacts = [];
}
public function update(currentTime:Float, dt:Float, collisionWorld:CollisionWorld, pathedInteriors:Array<PathedInterior>) {
public function update(timeState:TimeState, collisionWorld:CollisionWorld, pathedInteriors:Array<PathedInterior>) {
var move = new Move();
move.d = new Vector();
if (this.controllable) {
@ -758,17 +768,17 @@ class Marble extends GameObject {
}
}
advancePhysics(currentTime, dt, move, collisionWorld, pathedInteriors);
advancePhysics(timeState, move, collisionWorld, pathedInteriors);
if (this.controllable) {
this.camera.update(currentTime, dt);
this.camera.update(timeState.currentAttemptTime, timeState.dt);
}
updatePowerupStates(currentTime, dt);
updatePowerupStates(timeState.currentAttemptTime, timeState.dt);
this.trailEmitter();
if (bounceEmitDelay > 0)
bounceEmitDelay -= dt;
bounceEmitDelay -= timeState.dt;
if (bounceEmitDelay < 0)
bounceEmitDelay = 0;

View file

@ -1,5 +1,6 @@
package src;
import src.TimeState;
import gui.PlayGui;
import src.ParticleSystem.ParticleManager;
import src.Util;
@ -35,8 +36,7 @@ class MarbleWorld extends Scheduler {
var shapeImmunity:Array<DtsObject> = [];
var shapeOrTriggerInside:Array<DtsObject> = [];
public var currentTime:Float = 0;
public var elapsedTime:Float = 0;
public var timeState:TimeState = new TimeState();
public var bonusTime:Float = 0;
public var sky:Sky;
@ -78,8 +78,8 @@ class MarbleWorld extends Scheduler {
}
public function restart() {
this.currentTime = 0;
this.elapsedTime = 0;
this.timeState.currentAttemptTime = 0;
this.timeState.gameplayClock = 0;
this.bonusTime = 0;
this.outOfBounds = false;
this.marble.camera.CameraPitch = 0.45;
@ -99,19 +99,19 @@ class MarbleWorld extends Scheduler {
}
public function updateGameState() {
if (this.currentTime < 0.5) {
if (this.timeState.currentAttemptTime < 0.5) {
this.playGui.setCenterText('none');
}
if (this.currentTime >= 0.5 && this.currentTime < 2) {
if ((this.timeState.currentAttemptTime >= 0.5) && (this.timeState.currentAttemptTime < 2)) {
this.playGui.setCenterText('ready');
}
if (this.currentTime >= 2 && this.currentTime < 3.5) {
if ((this.timeState.currentAttemptTime >= 2) && (this.timeState.currentAttemptTime < 3.5)) {
this.playGui.setCenterText('set');
}
if (this.currentTime >= 3.5 && this.currentTime < 5.5) {
if ((this.timeState.currentAttemptTime >= 3.5) && (this.timeState.currentAttemptTime < 5.5)) {
this.playGui.setCenterText('go');
}
if (this.currentTime >= 5.5) {
if (this.timeState.currentAttemptTime >= 5.5) {
this.playGui.setCenterText('none');
}
if (this.outOfBounds) {
@ -169,18 +169,18 @@ class MarbleWorld extends Scheduler {
}
public function update(dt:Float) {
this.tickSchedule(currentTime);
this.updateTimer(dt);
this.tickSchedule(timeState.currentAttemptTime);
this.updateGameState();
for (obj in dtsObjects) {
obj.update(currentTime, dt);
obj.update(timeState);
}
for (marble in marbles) {
marble.update(currentTime, dt, collisionWorld, this.pathedInteriors);
marble.update(timeState, collisionWorld, this.pathedInteriors);
}
this.instanceManager.update(dt);
this.particleManager.update(1000 * currentTime, dt);
this.updateTimer(dt);
this.playGui.update(currentTime, dt);
this.particleManager.update(1000 * timeState.timeSinceLoad, dt);
this.playGui.update(timeState);
if (this.marble != null) {
callCollisionHandlers(marble);
@ -193,31 +193,33 @@ class MarbleWorld extends Scheduler {
}
public function updateTimer(dt:Float) {
currentTime += dt;
this.timeState.dt = dt;
this.timeState.currentAttemptTime += dt;
this.timeState.timeSinceLoad += dt;
if (this.bonusTime != 0) {
this.bonusTime -= dt;
if (this.bonusTime < 0) {
this.elapsedTime -= this.bonusTime;
this.timeState.gameplayClock -= this.bonusTime;
this.bonusTime = 0;
}
} else {
this.elapsedTime += dt;
this.timeState.gameplayClock += dt;
}
playGui.formatTimer(this.elapsedTime);
playGui.formatTimer(this.timeState.gameplayClock);
}
function updateTexts() {
var helpTextTime = this.helpTextTimeState;
var alertTextTime = this.alertTextTimeState;
var helpTextCompletion = Math.pow(Util.clamp((this.currentTime - helpTextTime - 3), 0, 1), 2);
var alertTextCompletion = Math.pow(Util.clamp((this.currentTime - alertTextTime - 3), 0, 1), 2);
var helpTextCompletion = Math.pow(Util.clamp((this.timeState.currentAttemptTime - helpTextTime - 3), 0, 1), 2);
var alertTextCompletion = Math.pow(Util.clamp((this.timeState.currentAttemptTime - alertTextTime - 3), 0, 1), 2);
this.playGui.setHelpTextOpacity(1 - helpTextCompletion);
this.playGui.setAlertTextOpacity(1 - alertTextCompletion);
}
public function displayAlert(text:String) {
this.playGui.setAlertText(text);
this.alertTextTimeState = this.currentTime;
this.alertTextTimeState = this.timeState.currentAttemptTime;
}
function callCollisionHandlers(marble:Marble) {
@ -234,7 +236,7 @@ class MarbleWorld extends Scheduler {
if (contact.go is DtsObject) {
var shape:DtsObject = cast contact.go;
var contacttest = shape.colliders.filter(x -> x != null).map(x -> x.sphereIntersection(contactsphere, 0));
var contacttest = shape.colliders.filter(x -> x != null).map(x -> x.sphereIntersection(contactsphere, timeState));
var contactlist:Array<collision.CollisionInfo> = [];
for (l in contacttest) {
contactlist = contactlist.concat(l);
@ -243,13 +245,13 @@ class MarbleWorld extends Scheduler {
if (!calledShapes.contains(shape) && !this.shapeImmunity.contains(shape) && contactlist.length != 0) {
calledShapes.push(shape);
newImmunity.push(shape);
shape.onMarbleContact(currentTime);
shape.onMarbleContact(timeState);
}
shape.onMarbleInside(currentTime);
shape.onMarbleInside(timeState);
if (!this.shapeOrTriggerInside.contains(shape)) {
this.shapeOrTriggerInside.push(shape);
shape.onMarbleEnter(currentTime);
shape.onMarbleEnter(timeState);
}
inside.push(shape);
}
@ -259,7 +261,7 @@ class MarbleWorld extends Scheduler {
for (object in shapeOrTriggerInside) {
if (!inside.contains(object)) {
this.shapeOrTriggerInside.remove(object);
object.onMarbleLeave(currentTime);
object.onMarbleLeave(timeState);
}
}
@ -286,9 +288,9 @@ class MarbleWorld extends Scheduler {
return q;
}
public function setUp(vec:Vector, time:Float) {
public function setUp(vec:Vector, timeState:TimeState) {
this.currentUp = vec;
var currentQuat = this.getOrientationQuat(time);
var currentQuat = this.getOrientationQuat(timeState.currentAttemptTime);
var oldUp = new Vector(0, 0, 1);
oldUp.transform(currentQuat.toMatrix());
@ -329,7 +331,7 @@ class MarbleWorld extends Scheduler {
this.newOrientationQuat = quatChange;
this.oldOrientationQuat = currentQuat;
this.orientationChangeTime = time;
this.orientationChangeTime = timeState.currentAttemptTime;
}
}

View file

@ -1,5 +1,6 @@
package src;
import src.TimeState;
import h3d.prim.UV;
import h3d.parts.Data.BlendMode;
import src.MarbleWorld;
@ -230,7 +231,7 @@ class ParticleEmitter {
this.currentWaitPeriod = this.o.ejectionPeriod;
var pos = this.getPosAtTime(time).clone();
if (this.o.spawnOffset != null)
pos.add(this.o.spawnOffset()); // Call the spawnOffset function if it's there
pos = pos.add(this.o.spawnOffset()); // Call the spawnOffset function if it's there
// This isn't necessarily uniform but it's fine for the purpose.
var randomPointOnSphere = new Vector(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1).normalized();
// Compute the total velocity

View file

@ -1,5 +1,6 @@
package src;
import src.TimeState;
import src.MarbleWorld;
import h3d.Matrix;
import h3d.Vector;
@ -43,7 +44,7 @@ class PathedInterior extends InteriorObject {
this.reset();
}
public function update(currentTime:Float, dt:Float) {
public function update(timeState:TimeState) {
// this.previousState = {
// currentTime: currentTime,
// targetTime: targetTime,
@ -71,7 +72,7 @@ class PathedInterior extends InteriorObject {
// if (!stopped)
// this.currentTime = currentTime;
velocity = position.sub(this.prevPosition).multiply(1 / dt);
velocity = position.sub(this.prevPosition).multiply(1 / timeState.dt);
}
public function setStopped(stopped:Bool = true) {

20
src/TimeState.hx Normal file
View file

@ -0,0 +1,20 @@
package src;
@:publicFields
class TimeState {
var timeSinceLoad:Float;
var currentAttemptTime:Float;
var gameplayClock:Float;
var dt:Float;
public function new() {}
public function clone() {
var n = new TimeState();
n.timeSinceLoad = this.timeSinceLoad;
n.currentAttemptTime = this.currentAttemptTime;
n.gameplayClock = this.gameplayClock;
n.dt = this.dt;
return n;
}
}

View file

@ -1,5 +1,6 @@
package collision;
import src.TimeState;
import h3d.Matrix;
import src.GameObject;
import src.Marble;
@ -27,7 +28,7 @@ class BoxCollisionEntity extends CollisionEntity {
return boundingBox.rayIntersection(Ray.fromValues(rayOrigin.x, rayOrigin.y, rayOrigin.z, rayDirection.x, rayDirection.y, rayDirection.z), true) != -1;
}
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, dt:Float) {
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState) {
return [];
}
}

View file

@ -1,5 +1,6 @@
package collision;
import src.TimeState;
import src.GameObject;
import dif.math.Point3F;
import dif.math.PlaneF;
@ -79,7 +80,7 @@ class CollisionEntity implements IOctreeObject {
this.priority = priority;
}
public function sphereIntersection(collisionEntity:SphereCollisionEntity, dt:Float) {
public function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState) {
var position = collisionEntity.transform.getPosition();
var velocity = collisionEntity.velocity;
var radius = collisionEntity.radius;
@ -91,7 +92,7 @@ class CollisionEntity implements IOctreeObject {
var surfaces = octree.radiusSearch(localpos, radius * 1.1);
var tform = transform.clone();
tform.setPosition(tform.getPosition().add(this.velocity.multiply(dt)));
tform.setPosition(tform.getPosition().add(this.velocity.multiply(timeState.dt)));
function toDifPoint(pt:Vector) {
return new Point3F(pt.x, pt.y, pt.z);

View file

@ -1,5 +1,6 @@
package collision;
import src.TimeState;
import src.GameObject;
import h3d.col.Bounds;
import collision.gjk.GJK;
@ -17,7 +18,7 @@ class CollisionHull extends CollisionEntity {
super(go);
}
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, dt:Float):Array<CollisionInfo> {
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState):Array<CollisionInfo> {
var bbox = this.boundingBox;
var box = new Bounds();
var pos = collisionEntity.transform.getPosition();
@ -33,7 +34,7 @@ class CollisionHull extends CollisionEntity {
sph.position = pos;
sph.radius = collisionEntity.radius;
var newTform = this.transform.clone();
var newpos = this.transform.getPosition().add(this.velocity.multiply(dt));
var newpos = this.transform.getPosition().add(this.velocity.multiply(timeState.dt));
newTform.setPosition(newpos);
hull.setTransform(newTform);
@ -48,7 +49,7 @@ class CollisionHull extends CollisionEntity {
cinfo.otherObject = this.go;
cinfo.friction = friction;
cinfo.force = force;
this.go.onMarbleContact(dt, cinfo);
this.go.onMarbleContact(timeState, cinfo);
return [cinfo];
}
}

View file

@ -1,5 +1,6 @@
package collision;
import src.TimeState;
import h3d.col.Bounds;
import h3d.col.Sphere;
import h3d.Vector;
@ -14,11 +15,11 @@ class CollisionWorld {
this.octree = new Octree();
}
public function sphereIntersection(spherecollision:SphereCollisionEntity, dt:Float) {
public function sphereIntersection(spherecollision:SphereCollisionEntity, timeState:TimeState) {
var position = spherecollision.transform.getPosition();
var radius = spherecollision.radius;
var velocity = spherecollision.velocity;
var searchdist = (velocity.length() * dt) + radius;
var searchdist = (velocity.length() * timeState.dt) + radius;
var intersections = this.octree.radiusSearch(position, searchdist);
var box = new Bounds();
@ -35,14 +36,14 @@ class CollisionWorld {
var entity:CollisionEntity = cast obj;
if (entity.go.isCollideable) {
contacts = contacts.concat(entity.sphereIntersection(spherecollision, dt));
contacts = contacts.concat(entity.sphereIntersection(spherecollision, timeState));
}
}
for (obj in dynamicEntities) {
if (obj != spherecollision) {
if (obj.boundingBox.collide(box) && obj.go.isCollideable)
contacts = contacts.concat(obj.sphereIntersection(spherecollision, dt));
contacts = contacts.concat(obj.sphereIntersection(spherecollision, timeState));
}
}
return contacts;

View file

@ -1,5 +1,6 @@
package collision;
import src.TimeState;
import src.Marble;
import h3d.col.Ray;
import h3d.Vector;
@ -31,7 +32,7 @@ class SphereCollisionEntity extends CollisionEntity {
return boundingBox.rayIntersection(Ray.fromValues(rayOrigin.x, rayOrigin.y, rayOrigin.z, rayDirection.x, rayDirection.y, rayDirection.z), true) != -1;
}
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, dt:Float) {
public override function sphereIntersection(collisionEntity:SphereCollisionEntity, timeState:TimeState) {
var contacts = [];
var thispos = transform.getPosition();
var position = collisionEntity.transform.getPosition();

View file

@ -1,5 +1,6 @@
package gui;
import src.TimeState;
import format.gif.Data.Block;
import hxd.res.BitmapFont;
import h2d.Text;
@ -306,11 +307,11 @@ class PlayGui {
engine.popTarget();
}
public function update(currentTime:Float, dt:Float) {
this.gemImageObject.update(currentTime, dt);
this.gemImageScene.setElapsedTime(dt);
public function update(timeState:TimeState) {
this.gemImageObject.update(timeState);
this.gemImageScene.setElapsedTime(timeState.dt);
if (this.powerupImageObject != null)
this.powerupImageObject.update(currentTime, dt);
this.powerupImageScene.setElapsedTime(dt);
this.powerupImageObject.update(timeState);
this.powerupImageScene.setElapsedTime(timeState.dt);
}
}

View file

@ -1,5 +1,6 @@
package shapes;
import src.TimeState;
import h3d.Vector;
import src.DtsObject;
@ -20,10 +21,10 @@ class AntiGravity extends PowerUp {
return !direction.equals(this.level.currentUp);
}
public function use(time:Float) {
public function use(timeState:TimeState) {
var direction = new Vector(0, 0, -1);
direction.transform(this.getRotationQuat().toMatrix());
this.level.setUp(direction, time);
this.level.setUp(direction, timeState);
// marble.body.addLinearVelocity(this.level.currentUp.scale(20)); // Simply add to vertical velocity
// if (!this.level.rewinding)
// AudioManager.play(this.sounds[1]);

View file

@ -1,8 +1,21 @@
package shapes;
import src.TimeState;
import collision.CollisionInfo;
import src.Util;
import src.ResourceLoader;
import src.ParticleSystem.ParticleData;
import src.MarbleWorld;
import src.DtsObject;
import src.MarbleWorld.Scheduler;
import src.ParticleSystem.ParticleEmitter;
import src.ParticleSystem.ParticleEmitterOptions;
import h3d.Vector;
class EndPad extends DtsObject {
var fireworks:Array<Firework> = [];
var isEntered:Bool = false;
public function new() {
super();
this.dtsPath = "data/shapes/pads/endarea.dts";
@ -10,4 +23,266 @@ class EndPad extends DtsObject {
this.isCollideable = true;
this.identifier = "EndPad";
}
override function onMarbleContact(timeState:TimeState, ?contact:CollisionInfo) {
if (!isEntered) {
isEntered = true;
spawnFirework(timeState);
}
}
function spawnFirework(time:TimeState) {
var firework = new Firework(this.getAbsPos().getPosition(), time.timeSinceLoad, this.level);
this.fireworks.push(firework);
// AudioManager.play(this.sounds[0], 1, AudioManager.soundGain, this.worldPosition);
}
override function update(timeState:TimeState) {
super.update(timeState);
for (firework in this.fireworks) {
firework.tick(timeState.timeSinceLoad);
if (timeState.timeSinceLoad - firework.spawnTime >= 10)
this.fireworks.remove(firework);
// We can safely remove the firework
}
}
}
final fireworkSmoke:ParticleEmitterOptions = {
ejectionPeriod: 100,
ambientVelocity: new Vector(0, 0, 1),
ejectionVelocity: 0,
velocityVariance: 0,
emitterLifetime: 4000,
spawnOffset: () -> {
var r = Math.sqrt(Math.random());
var theta = Math.random() * Math.PI * 2;
var randomPointInCircle = new Vector(r * Math.cos(theta), r * Math.sin(theta));
return new Vector(randomPointInCircle.x * 1.6, randomPointInCircle.y * 1.6, Math.random() * 0.4 - 0.5);
},
inheritedVelFactor: 0,
particleOptions: {
texture: 'particles/saturn.png',
blending: Alpha,
spinSpeed: 0,
spinRandomMin: -90,
spinRandomMax: 90,
lifetime: 2000,
lifetimeVariance: 200,
dragCoefficient: 0.5,
acceleration: 0,
colors: [new Vector(1, 1, 0, 0), new Vector(1, 0, 0, 1), new Vector(1, 0, 0, 0)],
sizes: [0.1, 0.2, 0.3],
times: [0, 0.2, 1]
}
};
final redTrail:ParticleEmitterOptions = {
ejectionPeriod: 30,
ambientVelocity: new Vector(0, 0, 0),
ejectionVelocity: 0,
velocityVariance: 0,
emitterLifetime: 10000,
inheritedVelFactor: 0,
particleOptions: {
texture: 'particles/spark.png',
blending: Alpha,
spinSpeed: 0,
spinRandomMin: -90,
spinRandomMax: 90,
lifetime: 600,
lifetimeVariance: 100,
dragCoefficient: 0,
acceleration: 0,
colors: [new Vector(1, 1, 0, 1), new Vector(1, 0, 0, 1), new Vector(1, 0, 0, 0)],
sizes: [0.1, 0.05, 0.01],
times: [0, 0.5, 1]
}
};
final blueTrail:ParticleEmitterOptions = {
ejectionPeriod: 30,
ambientVelocity: new Vector(0, 0, 0),
ejectionVelocity: 0,
velocityVariance: 0,
emitterLifetime: 10000,
inheritedVelFactor: 0,
particleOptions: {
texture: 'particles/spark.png',
blending: Alpha,
spinSpeed: 0,
spinRandomMin: -90,
spinRandomMax: 90,
lifetime: 600,
lifetimeVariance: 100,
dragCoefficient: 0,
acceleration: 0,
colors: [new Vector(0, 0, 1, 1), new Vector(0.5, 0.5, 1, 1), new Vector(1, 1, 1, 0)],
sizes: [0.1, 0.05, 0.01],
times: [0, 0.5, 1]
}
};
final redSpark:ParticleEmitterOptions = {
ejectionPeriod: 1,
ambientVelocity: new Vector(0, 0, 0),
ejectionVelocity: 0.8,
velocityVariance: 0.25,
emitterLifetime: 10,
inheritedVelFactor: 0,
particleOptions: {
texture: 'particles/star.png',
blending: Alpha,
spinSpeed: 40,
spinRandomMin: -90,
spinRandomMax: 90,
lifetime: 500,
lifetimeVariance: 50,
dragCoefficient: 0.5,
acceleration: 0,
colors: [new Vector(1, 1, 0, 1), new Vector(1, 1, 0, 1), new Vector(1, 0, 0, 0)],
sizes: [0.2, 0.2, 0.2],
times: [0, 0.5, 1]
}
};
final blueSpark:ParticleEmitterOptions = {
ejectionPeriod: 1,
ambientVelocity: new Vector(0, 0, 0),
ejectionVelocity: 0.5,
velocityVariance: 0.25,
emitterLifetime: 10,
inheritedVelFactor: 0,
particleOptions: {
texture: 'particles/bubble.png',
blending: Alpha,
spinSpeed: 40,
spinRandomMin: -90,
spinRandomMax: 90,
lifetime: 2000,
lifetimeVariance: 200,
dragCoefficient: 0,
acceleration: 0,
colors: [new Vector(0, 0, 1, 1), new Vector(0.5, 0.5, 1, 1), new Vector(1, 1, 1, 0)],
sizes: [0.2, 0.2, 0.2],
times: [0, 0.5, 1]
}
};
typedef Trail = {
var type:String;
var smokeEmitter:ParticleEmitter;
var targetPos:Vector;
var spawnTime:Float;
var lifetime:Float;
}
@:publicFields
class Firework extends Scheduler {
var pos:Vector;
var spawnTime:Float;
var trails:Array<Trail> = [];
/** The fireworks are spawned in waves, this controls how many are left. */
var wavesLeft = 4;
var level:MarbleWorld;
var fireworkSmokeData:ParticleData;
var fireworkRedTrailData:ParticleData;
var fireworkBlueTrailData:ParticleData;
var fireworkRedSparkData:ParticleData;
var fireworkBlueSparkData:ParticleData;
public function new(pos:Vector, spawnTime:Float, level:MarbleWorld) {
this.pos = pos;
this.spawnTime = spawnTime;
this.level = level;
fireworkSmokeData = new ParticleData();
fireworkSmokeData.identifier = "fireworkSmoke";
fireworkSmokeData.texture = ResourceLoader.getTexture("data/particles/saturn.png");
fireworkRedTrailData = new ParticleData();
fireworkRedTrailData.identifier = "fireworkRedTrail";
fireworkRedTrailData.texture = ResourceLoader.getTexture("data/particles/spark.png");
fireworkBlueTrailData = new ParticleData();
fireworkBlueTrailData.identifier = "fireworkBlueTrail";
fireworkBlueTrailData.texture = ResourceLoader.getTexture("data/particles/spark.png");
fireworkRedSparkData = new ParticleData();
fireworkRedSparkData.identifier = "fireworkRedSpark";
fireworkRedSparkData.texture = ResourceLoader.getTexture("data/particles/star.png");
fireworkBlueSparkData = new ParticleData();
fireworkBlueSparkData.identifier = "fireworkBlueSpark";
fireworkBlueSparkData.texture = ResourceLoader.getTexture("data/particles/bubble.png");
level.particleManager.createEmitter(fireworkSmoke, fireworkSmokeData, this.pos); // Start the smoke
this.doWave(this.spawnTime); // Start the first wave
}
public function tick(time:Float) {
this.tickSchedule(time);
// Update the trails
for (trail in this.trails) {
var completion = Util.clamp((time - trail.spawnTime) / trail.lifetime, 0, 1);
completion = 1 - Math.pow((1 - completion), 2); // ease-out
// Make the trail travel along an arc (parabola, whatever)
var pos = this.pos.clone().multiply(1 - completion).add(trail.targetPos.clone().multiply(completion));
pos = pos.sub(new Vector(0, 0, 1).multiply(Math.pow(completion, 2)));
trail.smokeEmitter.setPos(pos, time);
if (completion == 1) {
// The trail has reached its end, remove the emitter and spawn the explosion.
level.particleManager.removeEmitter(trail.smokeEmitter);
this.trails.remove(trail);
if (trail.type == 'red') {
level.particleManager.createEmitter(redSpark, fireworkRedSparkData, pos);
} else {
level.particleManager.createEmitter(blueSpark, fireworkBlueSparkData, pos);
}
}
}
}
/** Spawns a bunch of trails going in random directions. */
function doWave(time:Float) {
var count = Math.floor(17 + Math.random() * 10);
for (i in 0...count)
this.spawnTrail(time);
this.wavesLeft--;
if (this.wavesLeft > 0) {
var nextWaveTime = time + 0.5 + 1 * Math.random();
this.schedule(nextWaveTime, () -> this.doWave(nextWaveTime));
}
return null;
}
function spawnTrail(time:Float) {
var type = (Math.random() < 0.5) ? 'red' : 'blue';
var lifetime = 0.25 + Math.random() * 2;
var distanceFac = 0.5 + lifetime / 5; // Make sure the firework doesn't travel a great distance way too quickly
var emitter = level.particleManager.createEmitter((type == 'red') ? redTrail : blueTrail,
(type == 'red') ? fireworkRedTrailData : fireworkBlueTrailData, this.pos);
var r = Math.sqrt(Math.random());
var theta = Math.random() * Math.PI * 2;
var randomPointInCircle = new Vector(r * Math.cos(theta), r * Math.sin(theta));
var targetPos = new Vector(randomPointInCircle.x * 3, randomPointInCircle.y * 3, 1 + Math.sqrt(Math.random()) * 3).multiply(distanceFac).add(this.pos);
var trail:Trail = {
type: type,
smokeEmitter: emitter,
targetPos: targetPos,
spawnTime: time,
lifetime: lifetime
};
this.trails.push(trail);
}
}

View file

@ -1,5 +1,6 @@
package shapes;
import src.TimeState;
import src.DtsObject;
class Gem extends DtsObject {
@ -19,7 +20,7 @@ class Gem extends DtsObject {
this.matNameOverride.set('base.gem', color + ".gem");
}
public function setHide(hide:Bool) {
public override function setHide(hide:Bool) {
if (hide) {
this.pickedUp = true;
this.setOpacity(0);
@ -29,8 +30,8 @@ class Gem extends DtsObject {
}
}
override function onMarbleInside(time:Float) {
super.onMarbleInside(time);
override function onMarbleInside(timeState:TimeState) {
super.onMarbleInside(timeState);
if (this.pickedUp)
return;
this.pickedUp = true;

View file

@ -1,5 +1,6 @@
package shapes;
import src.TimeState;
import src.DtsObject;
class Helicopter extends PowerUp {
@ -17,9 +18,9 @@ class Helicopter extends PowerUp {
return this.level.pickUpPowerUp(this);
}
public function use(time:Float) {
public function use(timeState:TimeState) {
var marble = this.level.marble;
marble.enableHelicopter(time);
marble.enableHelicopter(timeState.currentAttemptTime);
// marble.body.addLinearVelocity(this.level.currentUp.scale(20)); // Simply add to vertical velocity
// if (!this.level.rewinding)
// AudioManager.play(this.sounds[1]);

View file

@ -1,5 +1,6 @@
package shapes;
import src.TimeState;
import collision.CollisionHull;
import collision.CollisionInfo;
import src.DtsObject;
@ -112,10 +113,10 @@ class LandMine extends DtsObject {
landMineSparkParticleData.texture = ResourceLoader.getTexture("data/particles/spark.png");
}
override function onMarbleContact(time:Float, ?contact:CollisionInfo) {
override function onMarbleContact(timeState:TimeState, ?contact:CollisionInfo) {
if (this.isCollideable) {
// marble.velocity = marble.velocity.add(vec);
this.disappearTime = this.level.currentTime;
this.disappearTime = timeState.timeSinceLoad;
this.setCollisionEnabled(false);
// if (!this.level.rewinding)
@ -143,9 +144,9 @@ class LandMine extends DtsObject {
return v;
}
override function update(currentTime:Float, dt:Float) {
super.update(currentTime, dt);
if (currentTime >= this.disappearTime + 5 || currentTime < this.disappearTime) {
override function update(timeState:TimeState) {
super.update(timeState);
if (timeState.timeSinceLoad >= this.disappearTime + 5 || timeState.timeSinceLoad < this.disappearTime) {
this.setHide(false);
} else {
this.setHide(true);
@ -164,7 +165,7 @@ class LandMine extends DtsObject {
}
}
var opacity = Util.clamp((currentTime - (this.disappearTime + 5)), 0, 1);
var opacity = Util.clamp((timeState.timeSinceLoad - (this.disappearTime + 5)), 0, 1);
this.setOpacity(opacity);
}
}

View file

@ -1,5 +1,6 @@
package shapes;
import src.TimeState;
import src.Util;
import h3d.Vector;
import src.DtsObject;
@ -35,29 +36,29 @@ abstract class PowerUp extends DtsObject {
this.ambientRotate = true;
}
public override function onMarbleInside(time:Float) {
var pickupable = this.lastPickUpTime == -1 || (time - this.lastPickUpTime) >= this.cooldownDuration;
public override function onMarbleInside(timeState:TimeState) {
var pickupable = this.lastPickUpTime == -1 || (timeState.currentAttemptTime - this.lastPickUpTime) >= this.cooldownDuration;
if (!pickupable)
return;
if (this.pickUp()) {
// this.level.replay.recordMarbleInside(this);
this.lastPickUpTime = time;
this.lastPickUpTime = timeState.currentAttemptTime;
if (this.autoUse)
this.use(time);
this.use(timeState);
this.level.displayAlert('You picked up a ${this.pickUpName}!');
// if (this.element.showhelponpickup === "1" && !this.autoUse) displayHelp(`Press <func:bind mousefire> to use the ${this.pickUpName}!`);
}
}
public override function update(currentTime:Float, dt:Float) {
super.update(currentTime, dt);
public override function update(timeState:TimeState) {
super.update(timeState);
var opacity = 1.0;
if (this.lastPickUpTime > 0 && this.cooldownDuration > 0) {
var availableTime = this.lastPickUpTime + this.cooldownDuration;
opacity = Util.clamp((currentTime - availableTime), 0, 1);
opacity = Util.clamp((timeState.currentAttemptTime - availableTime), 0, 1);
}
this.setOpacity(opacity);
@ -65,5 +66,5 @@ abstract class PowerUp extends DtsObject {
public abstract function pickUp():Bool;
public abstract function use(time:Float):Void;
public abstract function use(timeState:TimeState):Void;
}

View file

@ -1,5 +1,6 @@
package shapes;
import src.TimeState;
import src.DtsObject;
class ShockAbsorber extends PowerUp {
@ -16,9 +17,9 @@ class ShockAbsorber extends PowerUp {
return this.level.pickUpPowerUp(this);
}
public function use(time:Float) {
public function use(timeState:TimeState) {
var marble = this.level.marble;
marble.enableShockAbsorber(time);
marble.enableShockAbsorber(timeState.currentAttemptTime);
// marble.body.addLinearVelocity(this.level.currentUp.scale(20)); // Simply add to vertical velocity
// if (!this.level.rewinding)
// AudioManager.play(this.sounds[1]);

View file

@ -1,5 +1,6 @@
package shapes;
import src.TimeState;
import src.DtsObject;
class SuperBounce extends PowerUp {
@ -16,9 +17,9 @@ class SuperBounce extends PowerUp {
return this.level.pickUpPowerUp(this);
}
public function use(time:Float) {
public function use(timeState:TimeState) {
var marble = this.level.marble;
marble.enableSuperBounce(time);
marble.enableSuperBounce(timeState.currentAttemptTime);
// marble.body.addLinearVelocity(this.level.currentUp.scale(20)); // Simply add to vertical velocity
// if (!this.level.rewinding)
// AudioManager.play(this.sounds[1]);

View file

@ -1,5 +1,6 @@
package shapes;
import src.TimeState;
import src.ResourceLoader;
import src.ParticleSystem.ParticleData;
import h3d.Vector;
@ -47,7 +48,7 @@ class SuperJump extends PowerUp {
return this.level.pickUpPowerUp(this);
}
public function use(time:Float) {
public function use(timeState:TimeState) {
var marble = this.level.marble;
marble.velocity = marble.velocity.add(this.level.currentUp.multiply(20));
this.level.particleManager.createEmitter(superJumpParticleOptions, this.sjEmitterParticleData, null, () -> marble.getAbsPos().getPosition());

View file

@ -1,5 +1,6 @@
package shapes;
import src.TimeState;
import src.ResourceLoader;
import src.ParticleSystem.ParticleData;
import src.ParticleSystem.ParticleEmitterOptions;
@ -54,7 +55,7 @@ class SuperSpeed extends PowerUp {
return this.level.pickUpPowerUp(this);
}
public function use(time:Float) {
public function use(timeState:TimeState) {
var marble = this.level.marble;
var movementVector = marble.getMarbleAxis()[0];

View file

@ -1,5 +1,7 @@
package shapes;
import src.TimeState;
class TimeTravel extends PowerUp {
var timeBonus:Float = 5;
@ -19,5 +21,5 @@ class TimeTravel extends PowerUp {
return true;
}
public function use(time:Float) {}
public function use(time:TimeState) {}
}

View file

@ -1,5 +1,6 @@
package shapes;
import src.TimeState;
import collision.CollisionInfo;
import src.Util;
import src.DtsObject;
@ -21,12 +22,12 @@ class Trapdoor extends DtsObject {
this.hasNonVisualSequences = true;
}
public override function update(currentTime:Float, dt:Float) {
var currentCompletion = this.getCurrentCompletion(currentTime);
public override function update(timeState:TimeState) {
var currentCompletion = this.getCurrentCompletion(timeState);
// Override the keyframe
this.sequenceKeyframeOverride.set(this.dts.sequences[0], currentCompletion * (this.dts.sequences[0].numKeyFrames - 1));
super.update(currentTime, dt);
super.update(timeState);
var diff = (currentCompletion - this.lastCompletion);
var direction = 0;
@ -43,22 +44,22 @@ class Trapdoor extends DtsObject {
this.lastDirection = direction;
}
function getCurrentCompletion(time:Float) {
var elapsed = time - this.lastContactTime;
function getCurrentCompletion(timeState:TimeState) {
var elapsed = timeState.timeSinceLoad - this.lastContactTime;
var completion = Util.clamp(elapsed / 1.6666676998138428, 0, 1);
if (elapsed > 5)
completion = Util.clamp(1 - (elapsed - 5) / 1.6666676998138428, 0, 1);
return completion;
}
override function onMarbleContact(time:Float, ?contact:CollisionInfo) {
super.onMarbleContact(this.level.currentTime, contact);
if (this.level.currentTime - this.lastContactTime <= 0)
override function onMarbleContact(time:TimeState, ?contact:CollisionInfo) {
super.onMarbleContact(time, contact);
if (time.timeSinceLoad - this.lastContactTime <= 0)
return; // The trapdoor is queued to open, so don't do anything.
var currentCompletion = this.getCurrentCompletion(this.level.currentTime);
var currentCompletion = this.getCurrentCompletion(time);
// Set the last contact time accordingly so that the trapdoor starts closing (again)
this.lastContactTime = this.level.currentTime - currentCompletion * 1.6666676998138428;
this.lastContactTime = time.timeSinceLoad - currentCompletion * 1.6666676998138428;
if (currentCompletion == 0)
this.lastContactTime += this.timeout;