diff --git a/data/skies/gemCubemapUp2.png b/data/skies/gemCubemapUp2.png new file mode 100644 index 00000000..5fc78d0d Binary files /dev/null and b/data/skies/gemCubemapUp2.png differ diff --git a/data/skies/gemCubemapUp3.png b/data/skies/gemCubemapUp3.png new file mode 100644 index 00000000..021783c2 Binary files /dev/null and b/data/skies/gemCubemapUp3.png differ diff --git a/src/Marble.hx b/src/Marble.hx index add302ae..4111c25a 100644 --- a/src/Marble.hx +++ b/src/Marble.hx @@ -239,6 +239,7 @@ class Marble extends GameObject { public var heldPowerup:PowerUp; public var lastContactNormal:Vector; + public var lastContactPosition:Vector; var helicopter:HelicopterImage; var blastWave:BlastWave; @@ -269,8 +270,6 @@ class Marble extends GameObject { public var mode:Mode = Play; - public var startPad:StartPad; - public var prevPos:Vector; var cloak:Bool = false; @@ -866,6 +865,7 @@ class Marble extends GameObject { a.set(a.x + aFriction.x, a.y + aFriction.y, a.z + aFriction.z); lastContactNormal = bestContact.normal; + lastContactPosition = this.getAbsPos().getPosition(); } a.set(a.x + aControl.x, a.y + aControl.y, a.z + aControl.z); if (this.mode == Finish) { diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx index 9b1aea63..0e83056e 100644 --- a/src/MarbleWorld.hx +++ b/src/MarbleWorld.hx @@ -89,6 +89,9 @@ import src.ResourceLoaderWorker; import haxe.io.Path; import src.Console; import src.Gamepad; +import modes.GameMode; +import modes.NullMode; +import modes.GameMode.GameModeFactory; class MarbleWorld extends Scheduler { public var collisionWorld:CollisionWorld; @@ -120,6 +123,8 @@ class MarbleWorld extends Scheduler { var endPad:EndPad; var skyElement:MissionElementSky; + public var gameMode:GameMode; + // Lighting public var ambient:Vector; public var dirLight:Vector; @@ -203,6 +208,7 @@ class MarbleWorld extends Scheduler { this.scene2d = scene2d; this.mission = mission; this.game = mission.game.toLowerCase(); + this.gameMode = GameModeFactory.getGameMode(this, mission.missionInfo.gamemode); this.replay = new Replay(mission.path, mission.isClaMission ? mission.id : 0); this.isRecording = record; this.rewindManager = new RewindManager(this); @@ -254,6 +260,7 @@ class MarbleWorld extends Scheduler { }; this.mission.load(); scanMission(this.mission.root); + this.gameMode.missionScan(this.mission); this.resourceLoadFuncs.push(fwd -> this.initScene(fwd)); this.resourceLoadFuncs.push(fwd -> this.initMarble(fwd)); this.resourceLoadFuncs.push(fwd -> { @@ -428,6 +435,14 @@ class MarbleWorld extends Scheduler { return 0; // Load checkpoint } + if (!full) { + var respawnT = this.gameMode.getRespawnTransform(); + if (respawnT != null) { + respawn(respawnT.position, respawnT.orientation, respawnT.up); + return 0; + } + } + if (!this.isWatching) { this.replay.clear(); } else { @@ -437,7 +452,7 @@ class MarbleWorld extends Scheduler { this.rewindManager.clear(); this.timeState.currentAttemptTime = 0; - this.timeState.gameplayClock = 0; + this.timeState.gameplayClock = this.gameMode.getStartTime(); this.bonusTime = 0; this.outOfBounds = false; this.blastAmount = 0; @@ -486,12 +501,12 @@ class MarbleWorld extends Scheduler { this.cancel(this.oobSchedule); this.cancel(this.oobSchedule2); - var startquat = this.getStartPositionAndOrientation(); + var startquat = this.gameMode.getSpawnTransform(); - this.marble.setMarblePosition(startquat.position.x, startquat.position.y, startquat.position.z + 0.727843); + this.marble.setMarblePosition(startquat.position.x, startquat.position.y, startquat.position.z); this.marble.reset(); - var euler = startquat.quat.toEuler(); + var euler = startquat.orientation.toEuler(); this.marble.camera.init(cast this); this.marble.camera.CameraYaw = euler.z + Math.PI / 2; this.marble.camera.CameraPitch = 0.45; @@ -500,7 +515,6 @@ class MarbleWorld extends Scheduler { this.marble.camera.oob = false; this.marble.camera.finish = false; this.marble.setMode(Start); - this.marble.startPad = cast startquat.pad; sky.follow = marble.camera; var missionInfo:MissionElementScriptObject = cast this.mission.root.elements.filter((element) -> element._type == MissionElementType.ScriptObject @@ -513,18 +527,54 @@ class MarbleWorld extends Scheduler { for (interior in this.interiors) interior.reset(); - this.currentUp = new Vector(0, 0, 1); - this.orientationChangeTime = -1e8; - this.oldOrientationQuat = new Quat(); - this.newOrientationQuat = new Quat(); + this.setUp(startquat.up, this.timeState, true); this.deselectPowerUp(); playGui.setCenterText(''); AudioManager.playSound(ResourceLoader.getResource('data/sound/spawn_alternate.wav', ResourceLoader.getAudio, this.soundResources)); + this.gameMode.onRestart(); + return 0; } + public function respawn(respawnPos:Vector, respawnQuat:Quat, respawnUp:Vector) { + var marble = this.marble; + // Determine where to spawn the marble + this.marble.setMarblePosition(respawnPos.x, respawnPos.y, respawnPos.z); + marble.velocity.set(0, 0, 0); + marble.omega.set(0, 0, 0); + Console.log('Respawn:'); + Console.log('Marble Position: ${respawnPos.x} ${respawnPos.y} ${respawnPos.z}'); + Console.log('Marble Velocity: ${marble.velocity.x} ${marble.velocity.y} ${marble.velocity.z}'); + Console.log('Marble Angular: ${marble.omega.x} ${marble.omega.y} ${marble.omega.z}'); + // Set camera orientation + var euler = respawnQuat.toEuler(); + this.marble.camera.CameraYaw = euler.z + Math.PI / 2; + this.marble.camera.CameraPitch = 0.45; + this.marble.camera.nextCameraYaw = this.marble.camera.CameraYaw; + this.marble.camera.nextCameraPitch = this.marble.camera.CameraPitch; + this.marble.camera.oob = false; + @:privateAccess this.marble.helicopterEnableTime = -1e8; + @:privateAccess this.marble.megaMarbleEnableTime = -1e8; + if (this.isRecording) { + this.replay.recordCameraState(this.marble.camera.CameraYaw, this.marble.camera.CameraPitch); + this.replay.recordMarbleInput(0, 0); + this.replay.recordMarbleState(respawnPos, marble.velocity, marble.getRotationQuat(), marble.omega); + this.replay.recordMarbleStateFlags(false, false, true, false); + } + + // In this case, we set the gravity to the relative "up" vector of the checkpoint shape. + var up = new Vector(0, 0, 1); + up.transform(respawnQuat.toMatrix()); + this.setUp(up, this.timeState, true); + + this.playGui.setCenterText(''); + this.clearSchedule(); + this.outOfBounds = false; + AudioManager.playSound(ResourceLoader.getResource('data/sound/spawn_alternate.wav', ResourceLoader.getAudio, this.soundResources)); + } + public function updateGameState() { if (this.outOfBounds) return; // We will update state manually @@ -539,25 +589,6 @@ class MarbleWorld extends Scheduler { } } - function getStartPositionAndOrientation() { - // The player is spawned at the last start pad in the mission file. - var startPad = this.dtsObjects.filter(x -> x is StartPad).pop(); - var position:Vector; - var quat:Quat = new Quat(); - if (startPad != null) { - // If there's a start pad, start there - position = startPad.getAbsPos().getPosition(); - quat = startPad.getRotationQuat().clone(); - } else { - position = new Vector(0, 0, 300); - } - return { - position: position, - quat: quat, - pad: startPad - }; - } - function addToSimgroup(obj:GameObject, simGroup:MissionElementSimGroup) { if (simGroup == null) return; @@ -1098,11 +1129,13 @@ class MarbleWorld extends Scheduler { var prevGameplayClock = this.timeState.gameplayClock; + var timeMultiplier = this.gameMode.timeMultiplier(); + if (!this.isWatching) { if (this.bonusTime != 0 && this.timeState.currentAttemptTime >= 3.5) { this.bonusTime -= dt; if (this.bonusTime < 0) { - this.timeState.gameplayClock -= this.bonusTime; + this.timeState.gameplayClock -= this.bonusTime * timeMultiplier; this.bonusTime = 0; } if (timeTravelSound == null) { @@ -1115,9 +1148,9 @@ class MarbleWorld extends Scheduler { timeTravelSound = null; } if (this.timeState.currentAttemptTime >= 3.5) { - this.timeState.gameplayClock += dt; + this.timeState.gameplayClock += dt * timeMultiplier; } else if (this.timeState.currentAttemptTime + dt >= 3.5) { - this.timeState.gameplayClock += (this.timeState.currentAttemptTime + dt) - 3.5; + this.timeState.gameplayClock += ((this.timeState.currentAttemptTime + dt) - 3.5) * timeMultiplier; } } this.timeState.currentAttemptTime += dt; @@ -1220,37 +1253,7 @@ class MarbleWorld extends Scheduler { } public function pickUpGem(gem:Gem) { - this.gemCount++; - var string:String; - - // Show a notification (and play a sound) based on the gems remaining - if (this.gemCount == this.totalGems) { - string = "You have all the gems, head for the finish!"; - // if (!this.rewinding) - AudioManager.playSound(ResourceLoader.getResource('data/sound/gem_all.wav', ResourceLoader.getAudio, this.soundResources)); - - // Some levels with this package end immediately upon collection of all gems - // if (this.mission.misFile.activatedPackages.includes('endWithTheGems')) { - // let - // completionOfImpact = this.physics.computeCompletionOfImpactWithBody(gem.bodies[0], 2); // Get the exact point of impact - // this.touchFinish(completionOfImpact); - // } - } else { - string = "You picked up a gem. "; - - var remaining = this.totalGems - this.gemCount; - if (remaining == 1) { - string += "Only one gem to go!"; - } else { - string += '${remaining} gems to go!'; - } - - // if (!this.rewinding) - AudioManager.playSound(ResourceLoader.getResource('data/sound/gem_collect.wav', ResourceLoader.getAudio, this.soundResources)); - } - - displayAlert(string); - this.playGui.formatGemCounter(this.gemCount, this.totalGems); + this.gameMode.onGemPickup(gem); } public function callCollisionHandlers(marble:Marble, timeState:TimeState, start:Vector, end:Vector, startQuat:Quat, endQuat:Quat) { diff --git a/src/Radar.hx b/src/Radar.hx index 6396fc12..21340ed8 100644 --- a/src/Radar.hx +++ b/src/Radar.hx @@ -40,7 +40,7 @@ class Radar { var gemCount = 0; for (gem in level.gems) { if (!gem.pickedUp) { - renderArrow(gem.boundingCollider.boundingBox.getCenter().toVector(), 0xE60000); + renderArrow(gem.boundingCollider.boundingBox.getCenter().toVector(), gem.radarColor); gemCount++; } } @@ -68,7 +68,6 @@ class Radar { var fovY = (level.scene.camera.fovY * 0.5) * Math.PI / 180.0; var blink = time < 3 ? ((Std.int(Math.floor((time * 1000) / 500))) % 2 == 1) : false; - trace((Std.int(time * 1000) / 500)); var gravityMat = level.getOrientationQuat(level.timeState.currentAttemptTime).toMatrix(); diff --git a/src/mis/MissionElement.hx b/src/mis/MissionElement.hx index 37d38a14..6bc62d1c 100644 --- a/src/mis/MissionElement.hx +++ b/src/mis/MissionElement.hx @@ -60,6 +60,9 @@ class MissionElementScriptObject extends MissionElementBase { var alarmstarttime:String; var game:String; var difficulty:String; + var gamemode:String; + var gemgroupradius:String; + var maxgemspergroup:String; public function new() { _type = MissionElementType.ScriptObject; @@ -160,6 +163,7 @@ class MissionElementSpawnSphere extends MissionElementBase { var datablock:String; var resettime:Null; var timeout:Null; + var gemdatablock:String; public function new() { _type = MissionElementType.SpawnSphere; diff --git a/src/modes/GameMode.hx b/src/modes/GameMode.hx new file mode 100644 index 00000000..5c45f5a4 --- /dev/null +++ b/src/modes/GameMode.hx @@ -0,0 +1,29 @@ +package modes; + +import shapes.Gem; +import h3d.Quat; +import h3d.Vector; +import src.MarbleWorld; +import src.Mission; + +interface GameMode { + public function getSpawnTransform():{position:Vector, orientation:Quat, up:Vector}; + public function getRespawnTransform():{position:Vector, orientation:Quat, up:Vector}; + public function missionScan(mission:Mission):Void; + public function getStartTime():Float; + public function timeMultiplier():Float; + + public function onRestart():Void; + public function onGemPickup(gem:Gem):Void; +} + +class GameModeFactory { + public static function getGameMode(level:MarbleWorld, mode:String):GameMode { + if (mode != null) + switch (mode.toLowerCase()) { + case "scrum": + return new HuntMode(level); + } + return new NullMode(level); + } +} diff --git a/src/modes/HuntMode.hx b/src/modes/HuntMode.hx new file mode 100644 index 00000000..86270b36 --- /dev/null +++ b/src/modes/HuntMode.hx @@ -0,0 +1,279 @@ +package modes; + +import shapes.GemBeam; +import shapes.Gem; +import src.Console; +import h3d.col.Bounds; +import octree.IOctreeObject.RayIntersectionData; +import octree.Octree; +import octree.IOctreeObject.IOctreeObject; +import mis.MisParser; +import h3d.Vector; +import h3d.Quat; +import mis.MissionElement.MissionElementType; +import mis.MissionElement; +import src.Mission; +import mis.MissionElement.MissionElementSpawnSphere; +import src.AudioManager; +import src.ResourceLoader; + +@:publicFields +class GemSpawnSphere { + var position:Vector; + var rotation:Quat; + var element:MissionElementSpawnSphere; + var gem:Gem; + var gemBeam:GemBeam; + var gemColor:String; + + public function new(elem:MissionElementSpawnSphere) { + position = MisParser.parseVector3(elem.position); + position.x *= -1; + rotation = MisParser.parseRotation(elem.rotation); + rotation.x *= -1; + rotation.w *= -1; + element = elem; + gemColor = "red"; + if (elem.gemdatablock != null) { + switch (elem.gemdatablock.toLowerCase()) { + case "gemitem_2pts": + gemColor = "yellow"; + case "gemitem_5pts": + gemColor = "blue"; + default: + gemColor = "red"; + } + } + } +} + +class GemOctreeElem implements IOctreeObject { + public var boundingBox:Bounds; + public var spawn:GemSpawnSphere; + + var priority:Int; + + public function new(vec:Vector, spawn:GemSpawnSphere) { + boundingBox = new Bounds(); + boundingBox.addPoint(vec.add(new Vector(-0.5, -0.5, -0.5)).toPoint()); + boundingBox.addPoint(vec.add(new Vector(0.5, 0.5, 0.5)).toPoint()); + this.spawn = spawn; + } + + public function getElementType() { + return 2; + } + + public function setPriority(priority:Int) { + this.priority = priority; + } + + public function rayCast(rayOrigin:Vector, rayDirection:Vector):Array { + throw new haxe.exceptions.NotImplementedException(); // Not applicable + } +} + +class HuntMode extends NullMode { + var gemSpawnPoints:Array = []; + var playerSpawnPoints:Array = []; + + var gemOctree:Octree; + var gemGroupRadius:Float; + var maxGemsPerGroup:Int; + var activeGemSpawnGroup:Array; + var activeGems:Array = []; + var gemBeams:Array = []; + var gemToBeamMap:Map = []; + + override function missionScan(mission:Mission) { + function scanMission(simGroup:MissionElementSimGroup) { + for (element in simGroup.elements) { + if ([MissionElementType.SpawnSphere].contains(element._type)) { + var spawnSphere:MissionElementSpawnSphere = cast element; + var dbname = spawnSphere.datablock.toLowerCase(); + if (dbname == "spawnspheremarker") + playerSpawnPoints.push(spawnSphere); + if (dbname == "gemspawnspheremarker") + gemSpawnPoints.push(new GemSpawnSphere(spawnSphere)); + } else if (element._type == MissionElementType.SimGroup) { + scanMission(cast element); + } + } + } + scanMission(mission.root); + }; + + override function getSpawnTransform() { + var randomSpawn = playerSpawnPoints[Math.floor(Math.random() * playerSpawnPoints.length)]; + var spawnPos = MisParser.parseVector3(randomSpawn.position); + spawnPos.x *= -1; + var spawnRot = MisParser.parseRotation(randomSpawn.rotation); + spawnRot.x *= -1; + spawnRot.w *= -1; + var spawnMat = spawnRot.toMatrix(); + var up = spawnMat.up(); + spawnPos = spawnPos.add(up.multiply(0.727843 / 3)); // 1.5 -> 0.5 + return { + position: spawnPos, + orientation: spawnRot, + up: up + } + } + + override function getRespawnTransform() { + var lastContactPos = this.level.marble.lastContactPosition; + // Pick closest spawn point + var closestSpawn:MissionElementSpawnSphere = null; + var closestDistance = 1e10; + for (spawn in playerSpawnPoints) { + var pos = MisParser.parseVector3(spawn.position); + pos.x *= -1; + var dist = pos.distance(lastContactPos); + if (dist < closestDistance) { + closestDistance = dist; + closestSpawn = spawn; + } + } + if (closestSpawn != null) { + var spawnPos = MisParser.parseVector3(closestSpawn.position); + spawnPos.x *= -1; + var spawnRot = MisParser.parseRotation(closestSpawn.rotation); + spawnRot.x *= -1; + spawnRot.w *= -1; + var spawnMat = spawnRot.toMatrix(); + var up = spawnMat.up(); + spawnPos = spawnPos.add(up.multiply(0.727843 / 3)); // 1.5 -> 0.5 + return { + position: spawnPos, + orientation: spawnRot, + up: up + } + } + return null; + } + + override public function getStartTime() { + return level.mission.qualifyTime; + } + + override public function timeMultiplier() { + return -1; + } + + override function onRestart() { + setupGems(); + } + + override function onGemPickup(gem:Gem) { + AudioManager.playSound(ResourceLoader.getResource('data/sound/gem_collect.wav', ResourceLoader.getAudio, @:privateAccess this.level.soundResources)); + activeGems.remove(gem); + var beam = gemToBeamMap.get(gem); + beam.setHide(true); + refillGemGroups(); + } + + function setupGems() { + gemGroupRadius = 20.0; + maxGemsPerGroup = 4; + if (level.mission.missionInfo.gemgroupradius != null && level.mission.missionInfo.gemgroupradius != "") + gemGroupRadius = Std.parseFloat(level.mission.missionInfo.gemgroupradius); + if (level.mission.missionInfo.maxgemspergroup != null && level.mission.missionInfo.maxgemspergroup != "") + maxGemsPerGroup = Std.parseInt(level.mission.missionInfo.maxgemspergroup); + + gemOctree = new Octree(); + for (gemSpawn in gemSpawnPoints) { + var vec = gemSpawn.position; + gemOctree.insert(new GemOctreeElem(vec, gemSpawn)); + } + + activeGems = []; + refillGemGroups(); + } + + function refillGemGroups() { + if (activeGems.length == 0) { + var spawnGroup = pickGemSpawnGroup(); + activeGemSpawnGroup = spawnGroup; + fillGemGroup(spawnGroup); + } + } + + function fillGemGroup(group:Array) { + for (gemSpawn in group) { + if (gemSpawn.gem != null) { + gemSpawn.gem.pickedUp = false; + gemSpawn.gem.setHide(false); + gemSpawn.gemBeam.setHide(false); + this.activeGems.push(gemSpawn.gem); + } else { + var melem = new MissionElementItem(); + melem.datablock = "GemItem" + gemSpawn.gemColor; + var gem = new Gem(melem); + gemSpawn.gem = gem; + gem.setPosition(gemSpawn.position.x, gemSpawn.position.y, gemSpawn.position.z); + gem.setRotationQuat(gemSpawn.rotation); + this.activeGems.push(gem); + + var gemBeam = new GemBeam(); + gemBeam.setPosition(gemSpawn.position.x, gemSpawn.position.y, gemSpawn.position.z); + gemBeam.setRotationQuat(gemSpawn.rotation); + this.gemBeams.push(gemBeam); + + gemSpawn.gemBeam = gemBeam; + this.gemToBeamMap.set(gem, gemBeam); + + level.addDtsObject(gemBeam, () -> { + level.addDtsObject(gem, () -> { + level.gems.push(gem); + }); // Please be fast lol + }); + } + } + } + + function pickGemSpawnGroup() { + var searchRadius = gemGroupRadius * 2; + + for (i in 0...6) { + var groupMainPt = new Vector(); + var group = findGemSpawnGroup(groupMainPt); + if (group.length == 0) { + Console.log("Gem spawn group has no spawn points!"); + continue; + } + + var ok = true; + if (activeGemSpawnGroup != null) { + for (gemSpawn in activeGemSpawnGroup) { + if (gemSpawn.position.distance(groupMainPt) < searchRadius) { + ok = false; + break; + } + } + } + if (!ok) + continue; + return group; + } + Console.log("Unable to find spawn group that works with active gem groups, using random!"); + var groupMainPt = new Vector(); + return findGemSpawnGroup(groupMainPt); + } + + function findGemSpawnGroup(outSpawnPoint:Vector) { + // Pick random spawn point + var spawnPoint = gemSpawnPoints[Math.floor(Math.random() * gemSpawnPoints.length)]; + var pos = spawnPoint.position; + + var results = []; + var search = gemOctree.radiusSearch(pos, gemGroupRadius); + for (elem in search) { + var gemElem:GemOctreeElem = cast elem; + results.push(gemElem.spawn); + if (results.length >= maxGemsPerGroup) + break; + } + outSpawnPoint.load(pos); + return results; + } +} diff --git a/src/modes/NullMode.hx b/src/modes/NullMode.hx new file mode 100644 index 00000000..3cc40aa3 --- /dev/null +++ b/src/modes/NullMode.hx @@ -0,0 +1,82 @@ +package modes; + +import shapes.Gem; +import h3d.Quat; +import h3d.Vector; +import shapes.StartPad; +import src.MarbleWorld; +import src.Mission; +import src.AudioManager; +import src.ResourceLoader; + +class NullMode implements GameMode { + var level:MarbleWorld; + + public function new(level:MarbleWorld) { + this.level = level; + } + + public function getSpawnTransform() { + // The player is spawned at the last start pad in the mission file. + var startPad = this.level.dtsObjects.filter(x -> x is StartPad).pop(); + var position:Vector; + var quat:Quat = new Quat(); + if (startPad != null) { + // If there's a start pad, start there + position = startPad.getAbsPos().getPosition(); + quat = startPad.getRotationQuat().clone(); + } else { + position = new Vector(0, 0, 300); + } + position.z += 0.727843; + return { + position: position, + orientation: quat, + up: new Vector(0, 0, 1) + }; + } + + public function getRespawnTransform():{up:Vector, position:Vector, orientation:Quat} { + return null; + } + + public function missionScan(mission:Mission) { + // Do nothing + } + + public function getStartTime() { + return 0.0; + } + + public function timeMultiplier() { + return 1.0; + } + + public function onRestart() {} + + public function onGemPickup(gem:Gem) { + this.level.gemCount++; + var string:String; + + // Show a notification (and play a sound) based on the gems remaining + if (this.level.gemCount == this.level.totalGems) { + string = "You have all the gems, head for the finish!"; + AudioManager.playSound(ResourceLoader.getResource('data/sound/gem_all.wav', ResourceLoader.getAudio, @:privateAccess this.level.soundResources)); + } else { + string = "You picked up a gem. "; + + var remaining = this.level.totalGems - this.level.gemCount; + if (remaining == 1) { + string += "Only one gem to go!"; + } else { + string += '${remaining} gems to go!'; + } + + AudioManager.playSound(ResourceLoader.getResource('data/sound/gem_collect.wav', ResourceLoader.getAudio, + @:privateAccess this.level.soundResources)); + } + + this.level.displayAlert(string); + @:privateAccess this.level.playGui.formatGemCounter(this.level.gemCount, this.level.totalGems); + } +} diff --git a/src/octree/PriorityQueue.hx b/src/octree/PriorityQueue.hx index c8367b3b..2748c3e1 100644 --- a/src/octree/PriorityQueue.hx +++ b/src/octree/PriorityQueue.hx @@ -12,7 +12,7 @@ class PriorityQueue { public function enqueue(val:T, priority:Float) { var node = new PriorityQueueNode(val, priority); - if (this.queue == null) { + if (this.queue == null || this.queue.length == 0) { this.queue = [node]; } else { if (this.queue[0].priority >= priority) { diff --git a/src/shapes/Gem.hx b/src/shapes/Gem.hx index 5e9b77fd..1a9fc08e 100644 --- a/src/shapes/Gem.hx +++ b/src/shapes/Gem.hx @@ -13,6 +13,8 @@ class Gem extends DtsObject { var gemColor:String; + public var radarColor:Int; + public function new(element:MissionElementItem) { super(); dtsPath = "data/shapes/items/gem.dts"; @@ -30,6 +32,14 @@ class Gem extends DtsObject { this.identifier = "Gem" + color; this.matNameOverride.set('base.gem', color + ".gem"); gemColor = color + ".gem"; + radarColor = switch (color) { + case "blue": + 0x0000E6; + case "yellow": + 0xE6FF00; + default: + 0xE60000; + } } public override function init(level:MarbleWorld, onFinish:Void->Void) { @@ -68,7 +78,18 @@ class Gem extends DtsObject { override function getPreloadMaterials(dts:dts.DtsFile) { var mats = super.getPreloadMaterials(dts); - mats.push("data/skies/gemCubemapUp.png"); + switch (gemColor) { + case "yellow": + mats.push('data/shapes/items/yellow.gem.png'); + mats.push("data/skies/gemCubemapUp2.png"); + case "blue": + mats.push('data/shapes/items/blue.gem.png'); + mats.push("data/skies/gemCubemapUp4.png"); + default: + mats.push('data/shapes/items/red.gem.png'); + mats.push("data/skies/gemCubemapUp.png"); + } + return mats; } @@ -94,5 +115,47 @@ class Gem extends DtsObject { material.shadows = false; material.receiveShadows = true; } + if (matName == "yellow.gem") { + var diffuseTex = ResourceLoader.getTexture('data/shapes/items/yellow.gem.png').resource; + diffuseTex.wrap = Repeat; + diffuseTex.mipMap = Nearest; + + var cubemapTex = new h3d.mat.Texture(64, 64, [Cube]); + var cubemapFace = ResourceLoader.getImage('data/skies/gemCubemapUp2.png').resource; + for (i in 0...6) { + cubemapTex.uploadPixels(cubemapFace.getPixels(), 0, i); + } + var shader = new shaders.DefaultCubemapNormalNoSpecMaterial(diffuseTex, 1, cubemapTex); + var dtsTex = material.mainPass.getShader(shaders.DtsTexture); + dtsTex.passThrough = true; + material.mainPass.removeShader(material.textureShader); + material.mainPass.addShader(shader); + var thisprops:Dynamic = material.getDefaultProps(); + thisprops.light = false; // We will calculate our own lighting + material.props = thisprops; + material.shadows = false; + material.receiveShadows = true; + } + if (matName == "blue.gem") { + var diffuseTex = ResourceLoader.getTexture('data/shapes/items/blue.gem.png').resource; + diffuseTex.wrap = Repeat; + diffuseTex.mipMap = Nearest; + + var cubemapTex = new h3d.mat.Texture(64, 64, [Cube]); + var cubemapFace = ResourceLoader.getImage('data/skies/gemCubemapUp3.png').resource; + for (i in 0...6) { + cubemapTex.uploadPixels(cubemapFace.getPixels(), 0, i); + } + var shader = new shaders.DefaultCubemapNormalNoSpecMaterial(diffuseTex, 1, cubemapTex); + var dtsTex = material.mainPass.getShader(shaders.DtsTexture); + dtsTex.passThrough = true; + material.mainPass.removeShader(material.textureShader); + material.mainPass.addShader(shader); + var thisprops:Dynamic = material.getDefaultProps(); + thisprops.light = false; // We will calculate our own lighting + material.props = thisprops; + material.shadows = false; + material.receiveShadows = true; + } } } diff --git a/src/shapes/GemBeam.hx b/src/shapes/GemBeam.hx new file mode 100644 index 00000000..aa6e9bd2 --- /dev/null +++ b/src/shapes/GemBeam.hx @@ -0,0 +1,94 @@ +package shapes; + +import h3d.mat.Material; +import src.DtsObject; +import src.ResourceLoader; + +class GemBeam extends DtsObject { + public function new() { + super(); + this.dtsPath = "data/shapes/items/gembeam.dts"; + this.isCollideable = false; + this.isTSStatic = false; + this.identifier = "GemBeam"; + this.useInstancing = true; + this.animateSubObjectOpacities = true; + } + + public override function init(level:src.MarbleWorld, onFinish:() -> Void) { + super.init(level, onFinish); + } + + override function update(timeState:src.TimeState) { + super.update(timeState); + } + + override function getPreloadMaterials(dts:dts.DtsFile) { + var mats = super.getPreloadMaterials(dts); + mats.push("data/shapes/pads/mistyglow.png"); + return mats; + } + + override function postProcessMaterial(matName:String, material:Material) { + if (matName == "mistyglow") { + var diffuseTex = ResourceLoader.getTexture("data/shapes/pads/mistyglow.png").resource; + diffuseTex.wrap = Repeat; + diffuseTex.mipMap = Nearest; + // aa + var trivialShader = new shaders.TrivialMaterial(diffuseTex); + material.mainPass.removeShader(material.textureShader); + var glowpass = material.mainPass.clone(); + + glowpass.addShader(trivialShader); + var dtsshader = glowpass.getShader(shaders.DtsTexture); + dtsshader.passThrough = true; + glowpass.setPassName("glow"); + glowpass.depthTest = LessEqual; + glowpass.depthWrite = false; + glowpass.enableLights = false; + glowpass.setBlendMode(Alpha); + // glowpass.blendSrc = SrcAlpha; + // glowpass.blendDst = OneMinusSrcAlpha; + material.addPass(glowpass); + + material.mainPass.setPassName("glowPreNoRender"); + material.mainPass.removeShader(material.textureShader); + material.mainPass.addShader(trivialShader); + dtsshader = material.mainPass.getShader(shaders.DtsTexture); + dtsshader.passThrough = true; + material.mainPass.enableLights = false; + + // var thisprops:Dynamic = material.getDefaultProps(); + // thisprops.light = false; // We will calculate our own lighting + // material.props = thisprops; + material.shadows = false; + // material.blendMode = Alpha; + material.mainPass.depthWrite = false; + + // var diffuseTex = ResourceLoader.getTexture("data/shapes/pads/mistyglow.png").resource; + // diffuseTex.wrap = Repeat; + // diffuseTex.mipMap = Nearest; + + // var trivialShader = new shaders.TrivialMaterial(diffuseTex); + + // // var glowpass = material.mainPass.clone(); + // // glowpass.addShader(trivialShader); + // // var dtsshader = glowpass.getShader(shaders.DtsTexture); + // // dtsshader.passThrough = true; + // // glowpass.removeShader(dtsshader); + // // glowpass.setPassName("glow"); + // // glowpass.depthTest = LessEqual; + // // glowpass.depthWrite = false; + // // glowpass.enableLights = false; + // // glowpass.setBlendMode(AlphaAdd); + // // material.addPass(glowpass); + + // material.mainPass.setPassName("glowPre"); + // material.mainPass.addShader(trivialShader); + // var dtsshader = material.mainPass.getShader(shaders.DtsTexture); + // dtsshader.passThrough = true; + // material.mainPass.enableLights = false; + // material.mainPass.setBlendMode(Alpha); + } + } +}