diff --git a/src/Macros.hx b/src/Macros.hx index 3af34096..b958c58a 100644 --- a/src/Macros.hx +++ b/src/Macros.hx @@ -46,3 +46,127 @@ class MisParserMacros { } } } + +class MarbleWorldMacros { + public static macro function addStaticShapeOrItem() { + // Rip intellisense + return macro { + var shape:DtsObject = null; + + // Add the correct shape based on type + var dataBlockLowerCase = element.datablock.toLowerCase(); + if (dataBlockLowerCase == "") {} // Make sure we don't do anything if there's no data block + else if (dataBlockLowerCase == "startpad") + shape = new StartPad(); + else if (dataBlockLowerCase == "endpad") { + shape = new EndPad(); + if (element is MissionElementStaticShape && cast(element, MissionElementStaticShape) == endPadElement) + endPad = cast shape; + } else if (dataBlockLowerCase == "signfinish") + shape = new SignFinish(); + else if (StringTools.startsWith(dataBlockLowerCase, "gemitem")) { + shape = new Gem(cast element); + this.totalGems++; + this.gems.push(cast shape); + } else if (dataBlockLowerCase == "superjumpitem") + shape = new SuperJump(cast element); + else if (dataBlockLowerCase == "superbounceitem") + shape = new SuperBounce(cast element); + else if (dataBlockLowerCase == "roundbumper") + shape = new RoundBumper(); + else if (dataBlockLowerCase == "trianglebumper") + shape = new TriangleBumper(); + else if (dataBlockLowerCase == "helicopteritem") + shape = new Helicopter(cast element); + else if (dataBlockLowerCase == "easteregg") + shape = new EasterEgg(cast element); + else if (dataBlockLowerCase == "checkpoint") + shape = new Checkpoint(cast element); + else if (dataBlockLowerCase == "ductfan") + shape = new DuctFan(); + else if (dataBlockLowerCase == "smallductfan") + shape = new SmallDuctFan(); + else if (dataBlockLowerCase == "magnet") + shape = new Magnet(); + else if (dataBlockLowerCase == "antigravityitem") + shape = new AntiGravity(cast element); + else if (dataBlockLowerCase == "norespawnantigravityitem") + shape = new AntiGravity(cast element, true); + else if (dataBlockLowerCase == "landmine") + shape = new LandMine(); + else if (dataBlockLowerCase == "nuke") + shape = new Nuke(); + else if (dataBlockLowerCase == "shockabsorberitem") + shape = new ShockAbsorber(cast element); + else if (dataBlockLowerCase == "superspeeditem") + shape = new SuperSpeed(cast element); + else if (dataBlockLowerCase == "timetravelitem" || dataBlockLowerCase == "timepenaltyitem") + shape = new TimeTravel(cast element); + else if (dataBlockLowerCase == "randompowerupitem") + shape = new RandomPowerup(cast element); + else if (dataBlockLowerCase == "blastitem") + shape = new Blast(cast element); + else if (dataBlockLowerCase == "megamarbleitem") + shape = new MegaMarble(cast element); + else if (dataBlockLowerCase == "tornado") + shape = new Tornado(); + else if (dataBlockLowerCase == "trapdoor") + shape = new Trapdoor(); + else if (dataBlockLowerCase == "pushbutton") + shape = new PushButton(); + else if (dataBlockLowerCase == "oilslick") + shape = new Oilslick(); + else if (dataBlockLowerCase == "arrow" || StringTools.startsWith(dataBlockLowerCase, "sign")) + shape = new Sign(cast element); + else if ([ + "glass_3shape", + "glass_6shape", + "glass_9shape", + "glass_12shape", + "glass_15shape", + "glass_18shape" + ].contains(dataBlockLowerCase)) + shape = new Glass(cast element); + else if (["clear", "cloudy", "dusk", "wintry"].contains(dataBlockLowerCase)) + shape = new shapes.Sky(dataBlockLowerCase); + else { + Console.error("Unknown item: " + element.datablock); + onFinish(); + return; + } + + if (element._name != null && element._name != "") { + this.namedObjects.set(element._name, { + obj: shape, + elem: element + }); + } + + var shapePosition = MisParser.parseVector3(element.position); + shapePosition.x = -shapePosition.x; + var shapeRotation = MisParser.parseRotation(element.rotation); + shapeRotation.x = -shapeRotation.x; + shapeRotation.w = -shapeRotation.w; + var shapeScale = MisParser.parseVector3(element.scale); + + // Apparently we still do collide with zero-volume shapes + if (shapeScale.x == 0) + shapeScale.x = 0.0001; + if (shapeScale.y == 0) + shapeScale.y = 0.0001; + if (shapeScale.z == 0) + shapeScale.z = 0.0001; + + var mat = Matrix.S(shapeScale.x, shapeScale.y, shapeScale.z); + var tmp = new Matrix(); + shapeRotation.toMatrix(tmp); + mat.multiply3x4(mat, tmp); + mat.setPosition(shapePosition); + + this.addDtsObject(shape, () -> { + shape.setTransform(mat); + onFinish(); + }); + } + } +} diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx index b92327bb..02a009c1 100644 --- a/src/MarbleWorld.hx +++ b/src/MarbleWorld.hx @@ -1,5 +1,6 @@ package src; +import Macros.MarbleWorldMacros; import shapes.PushButton; #if js import gui.MainMenuGui; @@ -57,6 +58,7 @@ import shapes.Helicopter; import shapes.TriangleBumper; import shapes.RoundBumper; import shapes.SuperBounce; +import shapes.RandomPowerup; import shapes.SignCaution; import shapes.SuperJump; import shapes.Gem; @@ -712,242 +714,12 @@ class MarbleWorld extends Scheduler { public function addStaticShape(element:MissionElementStaticShape, onFinish:Void->Void) { var shape:DtsObject = null; - - // Add the correct shape based on type - var dataBlockLowerCase = element.datablock.toLowerCase(); - if (dataBlockLowerCase == "") {} // Make sure we don't do anything if there's no data block - else if (dataBlockLowerCase == "startpad") - shape = new StartPad(); - else if (dataBlockLowerCase == "endpad") { - shape = new EndPad(); - if (element == endPadElement) - endPad = cast shape; - } else if (dataBlockLowerCase == "signfinish") - shape = new SignFinish(); - else if (StringTools.startsWith(dataBlockLowerCase, "signplain")) - shape = new SignPlain(element); - else if (StringTools.startsWith(dataBlockLowerCase, "gemitem")) { - shape = new Gem(cast element); - this.totalGems++; - this.gems.push(cast shape); - } else if (dataBlockLowerCase == "superjumpitem") - shape = new SuperJump(cast element); - else if (StringTools.startsWith(dataBlockLowerCase, "signcaution")) - shape = new SignCaution(element); - else if (dataBlockLowerCase == "superbounceitem") - shape = new SuperBounce(cast element); - else if (dataBlockLowerCase == "roundbumper") - shape = new RoundBumper(); - else if (dataBlockLowerCase == "trianglebumper") - shape = new TriangleBumper(); - else if (dataBlockLowerCase == "helicopteritem") - shape = new Helicopter(cast element); - else if (dataBlockLowerCase == "easteregg") - shape = new EasterEgg(cast element); - else if (dataBlockLowerCase == "checkpoint") - shape = new Checkpoint(cast element); - else if (dataBlockLowerCase == "ductfan") - shape = new DuctFan(); - else if (dataBlockLowerCase == "smallductfan") - shape = new SmallDuctFan(); - else if (dataBlockLowerCase == "magnet") - shape = new Magnet(); - else if (dataBlockLowerCase == "antigravityitem") - shape = new AntiGravity(cast element); - else if (dataBlockLowerCase == "norespawnantigravityitem") - shape = new AntiGravity(cast element, true); - else if (dataBlockLowerCase == "landmine") - shape = new LandMine(); - else if (dataBlockLowerCase == "nuke") - shape = new Nuke(); - else if (dataBlockLowerCase == "shockabsorberitem") - shape = new ShockAbsorber(cast element); - else if (dataBlockLowerCase == "superspeeditem") - shape = new SuperSpeed(cast element); - else if (dataBlockLowerCase == "timetravelitem" || dataBlockLowerCase == "timepenaltyitem") - shape = new TimeTravel(cast element); - else if (dataBlockLowerCase == "blast") - shape = new Blast(cast element); - else if (dataBlockLowerCase == "megamarble") - shape = new MegaMarble(cast element); - else if (dataBlockLowerCase == "tornado") - shape = new Tornado(); - else if (dataBlockLowerCase == "trapdoor") - shape = new Trapdoor(); - else if (dataBlockLowerCase == "pushbutton") - shape = new PushButton(); - else if (dataBlockLowerCase == "oilslick") - shape = new Oilslick(); - else if (dataBlockLowerCase == "arrow" || StringTools.startsWith(dataBlockLowerCase, "sign")) - shape = new Sign(cast element); - else if ([ - "glass_3shape", - "glass_6shape", - "glass_9shape", - "glass_12shape", - "glass_15shape", - "glass_18shape" - ].contains(dataBlockLowerCase)) - shape = new Glass(cast element); - else if (["clear", "cloudy", "dusk", "wintry"].contains(dataBlockLowerCase)) - shape = new shapes.Sky(dataBlockLowerCase); - else { - Console.error("Unable to create static shape with data block '" + element.datablock + "'"); - onFinish(); - return; - } - - if (element._name != null && element._name != "") { - this.namedObjects.set(element._name, { - obj: shape, - elem: element - }); - } - - var shapePosition = MisParser.parseVector3(element.position); - shapePosition.x = -shapePosition.x; - var shapeRotation = MisParser.parseRotation(element.rotation); - shapeRotation.x = -shapeRotation.x; - shapeRotation.w = -shapeRotation.w; - var shapeScale = MisParser.parseVector3(element.scale); - - // Apparently we still do collide with zero-volume shapes - if (shapeScale.x == 0) - shapeScale.x = 0.0001; - if (shapeScale.y == 0) - shapeScale.y = 0.0001; - if (shapeScale.z == 0) - shapeScale.z = 0.0001; - - var mat = Matrix.S(shapeScale.x, shapeScale.y, shapeScale.z); - var tmp = new Matrix(); - shapeRotation.toMatrix(tmp); - mat.multiply3x4(mat, tmp); - var tmat = Matrix.T(shapePosition.x, shapePosition.y, shapePosition.z); - mat.multiply(mat, tmat); - - this.addDtsObject(shape, () -> { - shape.setTransform(mat); - onFinish(); - }); - - // else if (dataBlockLowerCase == "pushbutton") - // shape = new PushButton(); + MarbleWorldMacros.addStaticShapeOrItem(); } public function addItem(element:MissionElementItem, onFinish:Void->Void) { var shape:DtsObject = null; - - // Add the correct shape based on type - var dataBlockLowerCase = element.datablock.toLowerCase(); - if (dataBlockLowerCase == "") {} // Make sure we don't do anything if there's no data block - else if (dataBlockLowerCase == "startpad") - shape = new StartPad(); - else if (dataBlockLowerCase == "endpad") - shape = new EndPad(); - else if (dataBlockLowerCase == "signfinish") - shape = new SignFinish(); - else if (StringTools.startsWith(dataBlockLowerCase, "gemitem")) { - shape = new Gem(cast element); - this.totalGems++; - this.gems.push(cast shape); - } else if (dataBlockLowerCase == "superjumpitem") - shape = new SuperJump(cast element); - else if (dataBlockLowerCase == "superbounceitem") - shape = new SuperBounce(cast element); - else if (dataBlockLowerCase == "roundbumper") - shape = new RoundBumper(); - else if (dataBlockLowerCase == "trianglebumper") - shape = new TriangleBumper(); - else if (dataBlockLowerCase == "helicopteritem") - shape = new Helicopter(cast element); - else if (dataBlockLowerCase == "easteregg") - shape = new EasterEgg(cast element); - else if (dataBlockLowerCase == "checkpoint") - shape = new Checkpoint(cast element); - else if (dataBlockLowerCase == "ductfan") - shape = new DuctFan(); - else if (dataBlockLowerCase == "smallductfan") - shape = new SmallDuctFan(); - else if (dataBlockLowerCase == "magnet") - shape = new Magnet(); - else if (dataBlockLowerCase == "antigravityitem") - shape = new AntiGravity(cast element); - else if (dataBlockLowerCase == "norespawnantigravityitem") - shape = new AntiGravity(cast element, true); - else if (dataBlockLowerCase == "landmine") - shape = new LandMine(); - else if (dataBlockLowerCase == "nuke") - shape = new Nuke(); - else if (dataBlockLowerCase == "shockabsorberitem") - shape = new ShockAbsorber(cast element); - else if (dataBlockLowerCase == "superspeeditem") - shape = new SuperSpeed(cast element); - else if (dataBlockLowerCase == "timetravelitem" || dataBlockLowerCase == "timepenaltyitem") - shape = new TimeTravel(cast element); - else if (dataBlockLowerCase == "blastitem") - shape = new Blast(cast element); - else if (dataBlockLowerCase == "megamarbleitem") - shape = new MegaMarble(cast element); - else if (dataBlockLowerCase == "tornado") - shape = new Tornado(); - else if (dataBlockLowerCase == "trapdoor") - shape = new Trapdoor(); - else if (dataBlockLowerCase == "pushbutton") - shape = new PushButton(); - else if (dataBlockLowerCase == "oilslick") - shape = new Oilslick(); - else if (dataBlockLowerCase == "arrow" || StringTools.startsWith(dataBlockLowerCase, "sign")) - shape = new Sign(cast element); - else if ([ - "glass_3shape", - "glass_6shape", - "glass_9shape", - "glass_12shape", - "glass_15shape", - "glass_18shape" - ].contains(dataBlockLowerCase)) - shape = new Glass(cast element); - else if (["clear", "cloudy", "dusk", "wintry"].contains(dataBlockLowerCase)) - shape = new shapes.Sky(dataBlockLowerCase); - else { - Console.error("Unknown item: " + element.datablock); - onFinish(); - return; - } - - if (element._name != null && element._name != "") { - this.namedObjects.set(element._name, { - obj: shape, - elem: element - }); - } - - var shapePosition = MisParser.parseVector3(element.position); - shapePosition.x = -shapePosition.x; - var shapeRotation = MisParser.parseRotation(element.rotation); - shapeRotation.x = -shapeRotation.x; - shapeRotation.w = -shapeRotation.w; - var shapeScale = MisParser.parseVector3(element.scale); - - // Apparently we still do collide with zero-volume shapes - if (shapeScale.x == 0) - shapeScale.x = 0.0001; - if (shapeScale.y == 0) - shapeScale.y = 0.0001; - if (shapeScale.z == 0) - shapeScale.z = 0.0001; - - var mat = Matrix.S(shapeScale.x, shapeScale.y, shapeScale.z); - var tmp = new Matrix(); - shapeRotation.toMatrix(tmp); - mat.multiply3x4(mat, tmp); - mat.setPosition(shapePosition); - - this.addDtsObject(shape, () -> { - shape.setTransform(mat); - onFinish(); - }); + MarbleWorldMacros.addStaticShapeOrItem(); } public function addTrigger(element:MissionElementTrigger, onFinish:Void->Void) { diff --git a/src/Replay.hx b/src/Replay.hx index 3ce2ea2b..c0dfc2f7 100644 --- a/src/Replay.hx +++ b/src/Replay.hx @@ -202,6 +202,7 @@ class ReplayInitialState { var trapdoorLastCompletions:Array = []; var landMineDisappearTimes:Array = []; var pushButtonContactTimes:Array = []; + var randomGens:Array = []; public function new() {} @@ -224,6 +225,10 @@ class ReplayInitialState { for (time in this.pushButtonContactTimes) { bw.writeFloat(time); } + bw.writeInt16(this.randomGens.length); + for (ri in this.randomGens) { + bw.writeByte(ri); + } } public function read(br:BytesReader) { @@ -245,6 +250,10 @@ class ReplayInitialState { for (i in 0...pushButtonCount) { this.pushButtonContactTimes.push(br.readFloat()); } + var rcount = br.readInt16(); + for (i in 0...rcount) { + this.randomGens.push(br.readByte()); + } } } @@ -341,6 +350,14 @@ class Replay { initialState.pushButtonContactTimes.push(lastContactTime); } + public function recordRandomGenState(ri:Int) { + initialState.randomGens.push(ri); + } + + public function getRandomGenState() { + return initialState.randomGens.shift(); + } + public function getTrapdoorState(idx:Int) { return { lastContactTime: initialState.trapdoorLastContactTimes[idx], @@ -359,6 +376,7 @@ class Replay { public function clear() { this.frames = []; + this.initialState.randomGens = []; currentRecordFrame = null; } diff --git a/src/shapes/PowerUp.hx b/src/shapes/PowerUp.hx index cf9bf805..49fedb6e 100644 --- a/src/shapes/PowerUp.hx +++ b/src/shapes/PowerUp.hx @@ -8,29 +8,10 @@ import src.Util; import h3d.Vector; import src.DtsObject; -class PowerupParams { - public var duration:Float = 5; - public var airAccel = 1.0; - public var activateTime:Float = 0; - public var bounce = -1.0; - public var sizeScale = 1.0; - public var massScale = 1.0; - public var gravityMod = 1.0; - public var repulseDist = 0.0; - public var repulseMax = 0.0; - public var boostDir = new Vector(); - public var timeFreeze:Float = 0; - public var boostAmount = 0.0; - public var boostMassless = 0.0; - - public function new() {}; -} - abstract class PowerUp extends DtsObject { public var lastPickUpTime:Float = -1; public var cooldownDuration:Float = 7; public var autoUse:Bool = false; - public var powerupParams:PowerupParams = new PowerupParams(); public var pickUpName:String; public var element:MissionElementItem; public var pickupSound:Sound; diff --git a/src/shapes/RandomPowerup.hx b/src/shapes/RandomPowerup.hx new file mode 100644 index 00000000..d8d13223 --- /dev/null +++ b/src/shapes/RandomPowerup.hx @@ -0,0 +1,89 @@ +package shapes; + +import src.ResourceLoaderWorker; +import src.ResourceLoader; +import mis.MissionElement.MissionElementItem; +import src.TimeState; +import mis.MisParser; +import src.MarbleWorld; + +class RandomPowerup extends PowerUp { + var wasTimeTravel:Bool; + + public function new(element:MissionElementItem) { + super(element); + this.dtsPath = "data/shapes/items/random.dts"; + this.isCollideable = false; + this.isTSStatic = false; + this.identifier = "RandomPowerup"; + this.useInstancing = true; + this.autoUse = true; + this.wasTimeTravel = false; + } + + public override function init(level:MarbleWorld, onFinish:Void->Void) { + super.init(level, () -> { + var sounds = [ + "sound/pugyrocoptervoice.wav", + "sound/pushockabsorbervoice.wav", + "sound/pusuperbouncevoice.wav", + "sound/pusuperjumpvoice.wav", + "sound/pusuperspeedvoice.wav", + "sound/dosuperspeed.wav", + "sound/dosuperjump.wav", + "sound/putimetravelvoice.wav", + "sound/timetravelactive.wav" + ]; + var rlw = new ResourceLoaderWorker(onFinish); + for (sound in sounds) { + rlw.loadFile(sound); + } + rlw.run(); + }); + } + + public function pickUp():Bool { + while (true) { + var r = Std.random(6); + if (this.level.isWatching) + r = this.level.replay.getRandomGenState(); + var pow:PowerUp = null; + switch (r) { + case 0: + pow = new TimeTravel(this.element); + this.pickupSound = ResourceLoader.getResource("data/sound/putimetravelvoice.wav", ResourceLoader.getAudio, this.soundResources); + wasTimeTravel = true; + case 1: + pow = new SuperJump(this.element); + this.pickupSound = ResourceLoader.getResource("data/sound/pusuperjumpvoice.wav", ResourceLoader.getAudio, this.soundResources); + case 2: + pow = new SuperSpeed(this.element); + this.pickupSound = ResourceLoader.getResource("data/sound/pusuperspeedvoice.wav", ResourceLoader.getAudio, this.soundResources); + case 3: + pow = new ShockAbsorber(this.element); + this.pickupSound = ResourceLoader.getResource("data/sound/pushockabsorbervoice.wav", ResourceLoader.getAudio, this.soundResources); + case 4: + pow = new SuperBounce(this.element); + this.pickupSound = ResourceLoader.getResource("data/sound/pusuperbouncevoice.wav", ResourceLoader.getAudio, this.soundResources); + case 5: + pow = new Helicopter(this.element); + this.pickupSound = ResourceLoader.getResource("data/sound/pugyrocoptervoice.wav", ResourceLoader.getAudio, this.soundResources); + } + pow.level = this.level; + + if (pow.pickUp()) { + this.cooldownDuration = pow.cooldownDuration; + this.pickUpName = pow.pickUpName; + if (this.level.isRecording) + this.level.replay.recordRandomGenState(r); + return true; + } + } + return true; + } + + public function use(time:TimeState) { + if (this.wasTimeTravel) + this.level.addBonusTime(5); + } +}