diff --git a/src/DifBuilder.hx b/src/DifBuilder.hx index 70fe8f0c..71192227 100644 --- a/src/DifBuilder.hx +++ b/src/DifBuilder.hx @@ -125,6 +125,10 @@ class DifBuilder { friction: 4.5, restitution: 0.5 }, + "friction_ultrahigh" => { + friction: 4.5, + restitution: 0.5 + }, "friction_ramp_yellow" => { friction: 2.0, restitution: 1.0 @@ -341,23 +345,38 @@ class DifBuilder { 'tile_beginner_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), 'tile_beginner_red' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_red', 40, new Vector(1, 1, 1, 1)), 'tile_beginner_red_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_red_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), + 'tile_beginner_light' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_red', 40, new Vector(1, 1, 1, 1)), // MBP material 'tile_beginner_blue' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_blue', 40, new Vector(1, 1, 1, 1)), 'tile_beginner_blue_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_blue_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), + 'tile_beginner_dark' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '_blue', 40, new Vector(1, 1, 1, 1)), // MBP material 'tile_intermediate' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '', 40, new Vector(1, 1, 1, 1)), 'tile_intermediate_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), 'tile_intermediate_red' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_red', 40, new Vector(1, 1, 1, 1)), 'tile_intermediate_red_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_red_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), + 'tile_intermediate_dark' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_red', 40, + new Vector(1, 1, 1, 1)), // MBP material 'tile_intermediate_green' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_green', 40, new Vector(1, 1, 1, 1)), 'tile_intermediate_green_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_green_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), + 'tile_intermediate_light' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_green', 40, + new Vector(1, 1, 1, 1)), // MBP material 'tile_advanced' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '', 40, new Vector(1, 1, 1, 1)), 'tile_advanced_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), 'tile_advanced_blue' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_blue', 40, new Vector(1, 1, 1, 1)), 'tile_advanced_blue_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_blue_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), + 'tile_advanced_dark' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_blue', 40, new Vector(1, 1, 1, 1)), // MBP material 'tile_advanced_green' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_green', 40, new Vector(1, 1, 1, 1)), 'tile_advanced_green_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_green_shadow', 40, new Vector(0.2, 0.2, 0.2, 0.2)), + 'tile_advanced_light' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_green', 40, new Vector(1, 1, 1, 1)), // MBP Material + 'tile_blue' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_red', 40, new Vector(1, 1, 1, 1)), // MBP material + 'tile_bonus_blue' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_red', 40, new Vector(1, 1, 1, 1)), // MBP material + 'tile_bonus_shadow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_intermediate.png', '_red', 40, new Vector(1, 1, 1, 1)), // MBP material + 'tile_bonus' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_green', 40, new Vector(1, 1, 1, 1)), // MBP Material + 'tile_bonus_yellow' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_green', 40, new Vector(1, 1, 1, 1)), // MBP Material + 'tile_bonus_red' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_blue', 40, new Vector(1, 1, 1, 1)), // MBP material + 'tile_expert' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_advanced.png', '_blue', 40, new Vector(1, 1, 1, 1)), // MBP material 'tile_underside' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_underside.png', '', 40, new Vector(1, 1, 1, 1)), // 4x4 Variant 'tile_beginner_4x4' => (onFinish) -> createNoiseTileMaterial(onFinish, 'tile_beginner.png', '', 40, new Vector(1, 1, 1, 1), 4), @@ -400,9 +419,13 @@ class DifBuilder { 'data/textures/friction_low.normal.png', 128, new Vector(0.3, 0.3, 0.35, 1)), 'friction_high' => (onFinish) -> createDefaultMaterial(onFinish, 'data/textures/friction_high.png', 'data/textures/friction_high.normal.png', 10, new Vector(0.3, 0.3, 0.35, 1)), + 'friction_ultrahigh' => (onFinish) -> createDefaultMaterial(onFinish, 'data/textures/friction_high.png', 'data/textures/friction_high.normal.png', 10, + new Vector(0.3, 0.3, 0.35, 1)), 'friction_high_shadow' => (onFinish) -> createDefaultMaterial(onFinish, 'data/textures/friction_high_shadow.png', 'data/textures/friction_high.normal.png', 10, new Vector(0.15, 0.15, 0.16, 1.0)), 'friction_bouncy' => (onFinish) -> createDefaultNormalMaterial(onFinish, 'data/textures/friction_bouncy.png', 8, new Vector(0.4, 0.4, 0.2, 1)), + 'tile_bouncy' => (onFinish) -> createDefaultNormalMaterial(onFinish, 'data/textures/friction_bouncy.png', 8, new Vector(0.4, 0.4, 0.2, 1)), + 'tile_bouncy2' => (onFinish) -> createDefaultNormalMaterial(onFinish, 'data/textures/friction_bouncy.png', 8, new Vector(0.4, 0.4, 0.2, 1)), 'stripe_caution' => (onFinish) -> createDefaultNormalMaterial(onFinish, 'data/textures/stripe_caution.png', 12, new Vector(0.8, 0.8, 0.6, 1)), ]; diff --git a/src/Leaderboards.hx b/src/Leaderboards.hx index a7103bee..8febfe17 100644 --- a/src/Leaderboards.hx +++ b/src/Leaderboards.hx @@ -32,7 +32,7 @@ class Leaderboards { static var game = "Ultra"; public static function submitScore(mission:String, score:Float, rewindUsed:Bool, needsReplayCb:(Bool, Int) -> Void) { - if (!StringTools.startsWith(mission, "data/")) + if (!StringTools.startsWith(mission, "data/") && !StringTools.startsWith(mission, "custom/")) mission = "data/" + mission; Http.post('${host}/api/submit', Json.stringify({ mission: mission, @@ -54,7 +54,7 @@ class Leaderboards { } public static function getScores(mission:String, kind:LeaderboardsKind, cb:Array->Void) { - if (!StringTools.startsWith(mission, "data/")) + if (!StringTools.startsWith(mission, "data/") && !StringTools.startsWith(mission, "custom/")) mission = "data/" + mission; return Http.get('${host}/api/scores?mission=${StringTools.urlEncode(mission)}&game=${game}&view=${kind}&count=10', (b) -> { var s = b.toString(); @@ -86,7 +86,7 @@ class Leaderboards { } public static function watchTopReplay(mission:String, kind:LeaderboardsKind, cb:haxe.io.Bytes->Void) { - if (!StringTools.startsWith(mission, "data/")) + if (!StringTools.startsWith(mission, "data/") && !StringTools.startsWith(mission, "custom/")) mission = "data/" + mission; return Http.get('${host}/api/replay?mission=${StringTools.urlEncode(mission)}&game=${game}&view=${kind}', (b) -> { cb(b); diff --git a/src/MPCustoms.hx b/src/MPCustoms.hx index 248f55f3..ba9167ce 100644 --- a/src/MPCustoms.hx +++ b/src/MPCustoms.hx @@ -1,3 +1,6 @@ +package src; + +import src.Mission; import src.MissionList; import gui.MessageBoxOkDlg; import haxe.zip.Reader; @@ -7,12 +10,15 @@ import src.Http; import src.Console; import src.MarbleGame; import src.ResourceLoader; +import src.Marbleland; +import Main; typedef MPCustomEntry = { artist:String, description:String, path:String, - title:String + title:String, + id:Int, }; class MPCustoms { @@ -31,6 +37,26 @@ class MPCustoms { var b1 = b.title.toLowerCase(); return a1 < b1 ? -1 : (a1 > b1 ? 1 : 0); }); + + // Add this to marbleland customs too! + for (mis in missionList) { + var mission = new Mission(); + + mission.id = mis.id; + mission.path = mis.path; + mission.title = mis.title; + mission.artist = mis.artist; + mission.description = mis.description; + mission.qualifyTime = Math.POSITIVE_INFINITY; + mission.goldTime = 0; + mission.game = 'ultra'; + mission.isClaMission = true; + mission.customSource = "MPCustoms"; + + Marbleland.ultraMissions.push(mission); + Marbleland.missions.set(mission.id, mission); + } + Console.log('Loaded ${misList.length} custom missions.'); _requestSent = false; }, (e) -> { diff --git a/src/Main.hx b/src/Main.hx index 3affcb2e..05b08d3e 100644 --- a/src/Main.hx +++ b/src/Main.hx @@ -27,6 +27,7 @@ import src.Gamepad; import src.Http; import src.Renderer; import src.MissionList; +import src.MPCustoms; class Main extends hxd.App { var marbleGame:MarbleGame; @@ -99,7 +100,7 @@ class Main extends hxd.App { ResourceLoader.init(s2d, () -> { AudioManager.init(); AudioManager.playShell(); - // Marbleland.init(); + Marbleland.init(); marbleGame = new MarbleGame(s2d, s3d); MarbleGame.canvas.setContent(new PresentsGui()); haxe.Timer.delay(() -> { diff --git a/src/MarbleGame.hx b/src/MarbleGame.hx index 6add0f4a..7dab5ea6 100644 --- a/src/MarbleGame.hx +++ b/src/MarbleGame.hx @@ -1,5 +1,6 @@ package src; +import gui.SPCustomsGui; import gui.GuiControl; import haxe.io.Path; import gui.MultiplayerGui; @@ -42,7 +43,7 @@ class MarbleGame { static var canvas:Canvas; static var instance:MarbleGame; - static var currentVersion = "1.2.4"; + static var currentVersion = "1.2.5"; var world:MarbleWorld; var previewWorld:PreviewWorld; @@ -348,12 +349,22 @@ class MarbleGame { canvas.setContent(lobby); } } else { - var pmg = new LevelSelectGui(LevelSelectGui.currentDifficultyStatic); if (_exitingToMenu) { _exitingToMenu = false; + if (!isNotCustom) { + MarbleGame.instance.setPreviewMission('urban', () -> {}); + } canvas.setContent(new MainMenuGui()); } else { - canvas.setContent(pmg); + if (isNotCustom) { + var pmg = new LevelSelectGui(LevelSelectGui.currentDifficultyStatic); + canvas.setContent(pmg); + } else { + // Load to the custom menu + MarbleGame.instance.setPreviewMission('urban', () -> {}); + var pmg = new SPCustomsGui(); + canvas.setContent(pmg); + } } } } diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx index 924a107c..2d50d6e2 100644 --- a/src/MarbleWorld.hx +++ b/src/MarbleWorld.hx @@ -1,5 +1,6 @@ package src; +import gui.SPCustomsGui; import net.NetPacket.ScoreboardPacket; import net.NetPacket.PowerupPickupPacket; import net.Move; @@ -256,7 +257,12 @@ class MarbleWorld extends Scheduler { this.scene2d = scene2d; this.mission = mission; this.game = mission.game.toLowerCase(); - this.gameMode = GameModeFactory.getGameMode(cast this, mission.missionInfo.gamemode); + + var misGameMode = mission.missionInfo != null ? mission.missionInfo.gamemode : null; + if (mission.customSource == "MPCustoms") + misGameMode = "scrum"; + + this.gameMode = GameModeFactory.getGameMode(cast this, misGameMode); this.replay = new Replay(mission.path, mission.isClaMission ? mission.id : 0); this.isRecording = record; this.rewindManager = new RewindManager(cast this); @@ -2222,23 +2228,26 @@ class MarbleWorld extends Scheduler { this.finishYaw = this.marble.camera.CameraYaw; this.finishPitch = this.marble.camera.CameraPitch; displayAlert("Congratulations! You've finished!"); - if (!Settings.levelStatistics.exists(mission.path)) { - Settings.levelStatistics.set(mission.path, { + + var misPath = mission.isClaMission ? 'custom/mbu/${mission.id}' : mission.path; + + if (!Settings.levelStatistics.exists(misPath)) { + Settings.levelStatistics.set(misPath, { oobs: 0, respawns: 0, totalTime: 0, totalMPScore: 0 }); } - Analytics.trackLevelScore(mission.title, mission.path, + Analytics.trackLevelScore(mission.title, misPath, gameMode.getScoreType() == Time ? Std.int(1000 * gameMode.getFinishScore()) : Std.int(gameMode.getFinishScore()), - Settings.levelStatistics[mission.path].oobs, Settings.levelStatistics[mission.path].respawns, Settings.optionsSettings.rewindEnabled); + Settings.levelStatistics[misPath].oobs, Settings.levelStatistics[misPath].respawns, Settings.optionsSettings.rewindEnabled); if (!this.isWatching) { var myScore = { name: "Player", time: this.gameMode.getFinishScore() }; - Settings.saveScore(mission.path, myScore, this.gameMode.getScoreType()); + Settings.saveScore(misPath, myScore, this.gameMode.getScoreType()); var notifies = AchievementsGui.check(); var delay = 5.0; var achDelay = 0.0; @@ -2310,9 +2319,15 @@ class MarbleWorld extends Scheduler { } } else { this.dispose(); - LevelSelectGui.currentSelectionStatic = mission.index + 1; - var pmg = new LevelSelectGui(["beginner", "intermediate", "advanced", "multiplayer"][mission.difficultyIndex]); - MarbleGame.canvas.setContent(pmg); + if (mission.isClaMission) { + MarbleGame.instance.setPreviewMission('urban', () -> {}); + var pmg = new SPCustomsGui(); + MarbleGame.canvas.setContent(pmg); + } else { + LevelSelectGui.currentSelectionStatic = mission.index + 1; + var pmg = new LevelSelectGui(["beginner", "intermediate", "advanced", "multiplayer"][mission.difficultyIndex]); + MarbleGame.canvas.setContent(pmg); + } } #if js pointercontainer.hidden = false; @@ -2347,18 +2362,6 @@ class MarbleWorld extends Scheduler { } else { restartGameCode(); } - }, (sender) -> { - var nextLevelCode = () -> { - var nextMission = mission.getNextMission(); - if (nextMission != null) { - MarbleGame.instance.playMission(nextMission); - } - } - if (MarbleGame.instance.toRecord) { - MarbleGame.canvas.pushDialog(new ReplayNameDlg(nextLevelCode)); - } else { - nextLevelCode(); - } }, mission, this.gameMode.getFinishScore(), this.gameMode.getScoreType(), this.replay.write()); MarbleGame.canvas.pushDialog(egg); diff --git a/src/Marbleland.hx b/src/Marbleland.hx index fd923d7e..8f46cd5c 100644 --- a/src/Marbleland.hx +++ b/src/Marbleland.hx @@ -8,42 +8,51 @@ import src.Mission; import src.Http; import src.ResourceLoader; import src.Console; +import src.MarbleGame; class Marbleland { - public static var goldMissions = []; - public static var ultraMissions = []; - public static var platinumMissions = []; + public static var ultraMissions:Array = []; public static var missions:Map = []; public static function init() { - Http.get('https://raw.githubusercontent.com/Vanilagy/MarbleBlast/master/src/assets/customs_gold.json', (b) -> { - parseMissionList(b.toString(), "gold"); - Console.log('Loaded gold customs: ${goldMissions.length}'); - }, (e) -> {}); - Http.get('https://raw.githubusercontent.com/Vanilagy/MarbleBlast/master/src/assets/customs_ultra.json', (b) -> { - parseMissionList(b.toString(), "ultra"); + Http.get('https://marbleland.vaniverse.io/api/level/list', (b) -> { + parseMissionList(b.toString()); Console.log('Loaded ultra customs: ${ultraMissions.length}'); - }, (e) -> {}); - Http.get('https://raw.githubusercontent.com/Vanilagy/MarbleBlast/master/src/assets/customs_platinum.json', (b) -> { - parseMissionList(b.toString(), "platinum"); - Console.log('Loaded platinum customs: ${platinumMissions.length}'); - }, (e) -> {}); + // Load the marbleland level from JS + #if js + var urlParams = new js.html.URLSearchParams(js.Browser.window.location.search); + var playParam = urlParams.get("play"); + if (playParam != null) { + var intParam = Std.parseInt(playParam); + if (intParam != null) { + var mission = missions.get(intParam); + if (mission != null) { + MarbleGame.instance.playMission(mission); + } + } + } + #end + }, (e) -> { + Console.log('Error getting custom list from marbleland.'); + }); } - static function parseMissionList(s:String, game:String) { + static function parseMissionList(s:String) { var claJson:Array = Json.parse(s); - if (game == 'gold') { - claJson = claJson.filter(x -> x.modification == 'gold'); - } - if (game == 'platinum') { - claJson = claJson.filter(x -> x.gameType == 'single' && (x.gameMode == null || x.gameMode == 'null' || x.gamemode == '')); - } - if (game == 'ultra') { - claJson = claJson.filter(x -> x.gameType == 'single'); - } - var platDupes = new Map(); for (missionData in claJson) { + // filter + if (missionData.datablockCompatibility != 'mbw' && missionData.datablockCompatibility != 'mbg') + continue; + // if (!['gold', 'platinum', 'ultra', 'platinumquest'].contains(missionData.modification)) + // continue; + if (missionData.gameMode != null && !(missionData.gameMode == 'null' || missionData.gameMode.toLowerCase() == 'hunt')) + continue; + + var isMultiplayer = missionData.gameType == 'multi'; + if (isMultiplayer && (missionData.gameMode == null || missionData.gameMode.toLowerCase() != 'hunt')) + continue; + var mission = new Mission(); mission.id = missionData.id; mission.path = 'missions/' + missionData.baseName; @@ -56,59 +65,37 @@ class Marbleland { mission.description = missionData.desc != null ? missionData.desc : ""; mission.qualifyTime = (missionData.qualifyingTime != null && missionData.qualifyingTime != 0) ? missionData.qualifyingTime / 1000 : Math.POSITIVE_INFINITY; mission.goldTime = missionData.goldTime != null ? missionData.goldTime / 1000 : 0; + if (missionData.modification == 'platinumquest') + missionData.modification = 'platinum'; // play PQ levels compatible with web pls mission.game = missionData.modification; if (missionData.modification == 'platinum') mission.goldTime = missionData.platinumTime != null ? missionData.platinumTime / 1000 : mission.goldTime; mission.ultimateTime = missionData.ultimateTime != null ? missionData.ultimateTime / 1000 : 0; mission.hasEgg = missionData.hasEgg; mission.isClaMission = true; + mission.customSource = "Marbleland"; - if (game == 'platinum') { - if (platDupes.exists(mission.title + mission.description)) - continue; - else - platDupes.set(mission.title + mission.description, true); + var game = missionData.modification; + if (isMultiplayer) { + game = 'multiplayer'; } switch (game) { - case 'gold': - goldMissions.push(mission); case 'ultra': ultraMissions.push(mission); - case 'platinum': - platinumMissions.push(mission); } missions.set(mission.id, mission); } // sort according to name - switch (game) { - case 'gold': - goldMissions.sort((x, y) -> x.title > y.title ? 1 : (x.title < y.title ? -1 : 0)); - for (i in 0...goldMissions.length - 1) { - @:privateAccess goldMissions[i].next = goldMissions[i + 1]; - goldMissions[i].index = i; - } - @:privateAccess goldMissions[goldMissions.length - 1].next = goldMissions[0]; - goldMissions[goldMissions.length - 1].index = goldMissions.length - 1; - case 'platinum': - platinumMissions.sort((x, y) -> x.title > y.title ? 1 : (x.title < y.title ? -1 : 0)); - for (i in 0...platinumMissions.length - 1) { - @:privateAccess platinumMissions[i].next = platinumMissions[i + 1]; - platinumMissions[i].index = i; - } - @:privateAccess platinumMissions[platinumMissions.length - 1].next = platinumMissions[0]; - platinumMissions[platinumMissions.length - 1].index = platinumMissions.length - 1; - case 'ultra': - ultraMissions.sort((x, y) -> x.title > y.title ? 1 : (x.title < y.title ? -1 : 0)); - for (i in 0...ultraMissions.length - 1) { - @:privateAccess ultraMissions[i].next = ultraMissions[i + 1]; - ultraMissions[i].index = i; - } - @:privateAccess ultraMissions[ultraMissions.length - 1].next = ultraMissions[0]; - ultraMissions[ultraMissions.length - 1].index = ultraMissions.length - 1; + ultraMissions.sort((x, y) -> x.title > y.title ? 1 : (x.title < y.title ? -1 : 0)); + for (i in 0...ultraMissions.length - 1) { + @:privateAccess ultraMissions[i].next = ultraMissions[i + 1]; + ultraMissions[i].index = i; } + @:privateAccess ultraMissions[ultraMissions.length - 1].next = ultraMissions[0]; + ultraMissions[ultraMissions.length - 1].index = ultraMissions.length - 1; } public static function getMissionImage(id:Int, cb:Image->Void) { diff --git a/src/Mission.hx b/src/Mission.hx index 9e899c47..96cd6786 100644 --- a/src/Mission.hx +++ b/src/Mission.hx @@ -1,5 +1,10 @@ package src; +import h3d.Vector; +import mis.MissionElement.MissionElementTrigger; +import shapes.Checkpoint; +import mis.MissionElement.MissionElementStaticShape; +import mis.MissionElement.MissionElementSky; import src.Http.HttpRequest; import gui.Canvas; import gui.MessageBoxOkDlg; @@ -21,6 +26,7 @@ import src.Console; import src.Marbleland; import src.MarbleGame; import src.Http; +import src.MPCustoms; class Mission { public var root:MissionElementSimGroup; @@ -41,6 +47,7 @@ class Mission { public var hasEgg:Bool; public var isCustom:Bool; public var marbleAttributes:Map; + public var customSource:String; // Marbleland or MPCustom var next:Mission; @@ -84,6 +91,18 @@ class Mission { }; scanMission(root); // Scan for egg + if (this.isClaMission) + postProcessFromMarbleland(); + + if (this.customSource == "MPCustoms") { + // Fill the few details from missionInfo + if (missionInfo.time != null && missionInfo.time != "0") + this.qualifyTime = MisParser.parseNumber(missionInfo.time) / 1000; + if (missionInfo.goldtime != null) { + this.goldTime = MisParser.parseNumber(missionInfo.goldtime) / 1000; + } + this.type = missionInfo.type.toLowerCase(); + } } public function dispose() { @@ -241,14 +260,151 @@ class Mission { public function download(onFinish:Void->Void) { if (this.isClaMission) { - Marbleland.download(this.id, (zipEntries) -> { - if (zipEntries != null) { - ResourceLoader.loadZip(zipEntries, game); + if (this.customSource == "Marbleland") { + Marbleland.download(this.id, (zipEntries) -> { + if (zipEntries != null) { + ResourceLoader.loadZip(zipEntries, ''); + onFinish(); + } else { + MarbleGame.canvas.pushDialog(new MessageBoxOkDlg("Failed to download mission")); + } + }); + } + if (this.customSource == "MPCustoms") { + MPCustoms.download({ + id: this.id, + title: this.title, + path: this.path, + description: this.description, + artist: this.artist + }, () -> { onFinish(); - } else { + }, () -> { MarbleGame.canvas.pushDialog(new MessageBoxOkDlg("Failed to download mission")); + }); + } + } + } + + function postProcessFromMarbleland() { + // Since the mission is from Marbleland, we must postprocess it to port it to MBU formats. + + var skyEl:MissionElementSky = null; + + var processFunctions = []; + var cloudType = "none"; + + function postprocessMission(simGroup:MissionElementSimGroup) { + for (element in simGroup.elements) { + if (element._type == MissionElementType.Sky) { + // Change the sky!! + skyEl = cast(element, MissionElementSky); + + var skyMaterial = skyEl.materiallist.toLowerCase(); + switch (skyMaterial) { + case "~/data/skies/cloudy/cloudy.dml" | "~/data/skies/mbu/sky_beginner.dml": + skyEl.materiallist = "~/data/skies/sky_beginner.dml"; + cloudType = "beginner"; + + case "~/data/skies/mbu/sky_intermediate.dml": + skyEl.materiallist = "~/data/skies/sky_intermediate.dml"; + cloudType = "intermediate"; + + case "~/data/skies/mbu/sky_advanced.dml": + skyEl.materiallist = "~/data/skies/sky_advanced.dml"; + cloudType = "advanced"; + } } - }); + if (element._type == MissionElementType.StaticShape) { + var ss = cast(element, MissionElementStaticShape); + + var db = ss.datablock.toLowerCase(); + switch (db) { + case "clear": + skyEl.materiallist = "~/data/skies/sky_beginner.dml"; + cloudType = "beginner"; + + case "dusk": + skyEl.materiallist = "~/data/skies/sky_intermediate.dml"; + cloudType = "intermediate"; + + case "wintry": + skyEl.materiallist = "~/data/skies/sky_advanced.dml"; + cloudType = "advanced"; + } + } + if (element._type == MissionElementType.TSStatic) { + var ts = cast(element, mis.MissionElement.MissionElementTSStatic); + var shapeName = ts.shapename.toLowerCase(); + switch (shapeName) { + case "~/data/shapes/buttons/checkpoint.dts": + // This one needs to be changed to a "checkpoint" + var sg = simGroup; + processFunctions.push(() -> { + // First remove this element + sg.elements.remove(ts); + // Then add add the actual checkpoint shape + var checkpointEl = new MissionElementStaticShape(); + checkpointEl._name = ts._name; + checkpointEl.position = ts.position; + checkpointEl.rotation = ts.rotation; + checkpointEl.scale = ts.scale; + checkpointEl.datablock = "checkPointShape"; + checkpointEl.fields = []; + + // create new simgroup + var checkpointSG = new MissionElementSimGroup(); + checkpointSG._name = null; + checkpointSG.elements = []; + checkpointSG.elements.push(checkpointEl); + checkpointSG.fields = []; + + // Find the checkpoint triggers affecting this checkpoint + var affectedTriggers = sg.elements.filter(x -> x._type == MissionElementType.Trigger) + .filter(y -> cast(y, MissionElementTrigger).respawnpoint == ts._name); + + for (triggerEl in affectedTriggers) { + var trigger = cast(triggerEl, MissionElementTrigger); + // remove trigger from its current simgroup + sg.elements.remove(trigger); + // add trigger to checkpoint simgroup + checkpointSG.elements.push(trigger); + } + + sg.elements.push(checkpointSG); + }); + } + } + if (element._type == MissionElementType.Item) {} else if (element._type == MissionElementType.SimGroup) { + postprocessMission(cast element); + } + } + }; + + postprocessMission(root); + + // Add astrolabe, because it does not exist + var astrolabeEl = new MissionElementStaticShape(); + astrolabeEl._name = "Astrolabe"; + astrolabeEl.position = "0 0 -600"; + astrolabeEl.rotation = "1 0 0 0"; + astrolabeEl.scale = "1 1 1"; + astrolabeEl.datablock = "astrolabeShape"; + astrolabeEl.fields = []; + root.elements.push(astrolabeEl); + + // Add the clouds + var cloudEl = new MissionElementStaticShape(); + cloudEl._name = "CloudLayer"; + cloudEl.position = "0 0 0"; + cloudEl.rotation = "1 0 0 0"; + cloudEl.scale = "1 1 1"; + cloudEl.datablock = 'astrolabeClouds${cloudType}Shape'; + cloudEl.fields = []; + root.elements.push(cloudEl); + + for (f in processFunctions) { + f(); } } } diff --git a/src/gui/DifficultySelectGui.hx b/src/gui/DifficultySelectGui.hx index a9a2668c..41e61968 100644 --- a/src/gui/DifficultySelectGui.hx +++ b/src/gui/DifficultySelectGui.hx @@ -71,6 +71,9 @@ class DifficultySelectGui extends GuiImage { btnList.addButton(0, 'Gem Hunt', (e) -> { MarbleGame.canvas.setContent(new LevelSelectGui("multiplayer")); }, 20); + btnList.addButton(0, 'Custom Levels', (e) -> { + MarbleGame.canvas.setContent(new SPCustomsGui()); + }); var bottomBar = new GuiControl(); bottomBar.position = new Vector(0, 590); diff --git a/src/gui/EndGameGui.hx b/src/gui/EndGameGui.hx index 332a56bb..d84ada82 100644 --- a/src/gui/EndGameGui.hx +++ b/src/gui/EndGameGui.hx @@ -22,13 +22,12 @@ class EndGameGui extends GuiImage { var innerCtrl:GuiControl; var endGameWnd:GuiImage; var retryFunc:GuiControl->Void; - var nextFunc:GuiControl->Void; var continueFunc:GuiControl->Void; var scoreSubmitted:Bool = false; - public function new(continueFunc:GuiControl->Void, restartFunc:GuiControl->Void, nextLevelFunc:GuiControl->Void, mission:Mission, score:Float, - scoreType:ScoreType, replayData:haxe.io.Bytes) { + public function new(continueFunc:GuiControl->Void, restartFunc:GuiControl->Void, mission:Mission, score:Float, scoreType:ScoreType, + replayData:haxe.io.Bytes) { var res = ResourceLoader.getImage("data/ui/xbox/BG_fadeOutSoftEdge.png").resource.toTile(); super(res); this.horizSizing = Width; @@ -37,7 +36,6 @@ class EndGameGui extends GuiImage { this.extent = new Vector(640, 480); this.mission = mission; this.retryFunc = restartFunc; - this.nextFunc = nextLevelFunc; this.continueFunc = continueFunc; function loadButtonImages(path:String) { @@ -101,8 +99,12 @@ class EndGameGui extends GuiImage { var egResultLeft = new GuiMLText(arial14, mlFontLoader); egResultLeft.position = new Vector(28, 26); egResultLeft.extent = new Vector(180, 100); - if (scoreType == Time) - egResultLeft.text.text = '

Time:
Par Time:
Rating:
My Best Time:

'; + if (scoreType == Time) { + if (mission.isClaMission) + egResultLeft.text.text = '

Time:
Par Time:
My Best Time:

'; + else + egResultLeft.text.text = '

Time:
Par Time:
Rating:
My Best Time:

'; + } if (scoreType == Score) egResultLeft.text.text = '

Score:
My Best Score:

'; endGameWnd.addChild(egResultLeft); @@ -146,8 +148,12 @@ class EndGameGui extends GuiImage { egResultRight.extent = new Vector(180, 100); if (scoreType == Score) egResultRight.text.text = '${Util.formatScore(score)}
${Util.formatScore(bestScore.time)}'; - if (scoreType == Time) - egResultRight.text.text = '${Util.formatTime(score)}
${Util.formatTime(mission.qualifyTime)}
${rating}
${Util.formatTime(bestScore.time)}'; + if (scoreType == Time) { + if (mission.isClaMission) + egResultRight.text.text = '${Util.formatTime(score)}
${Util.formatTime(mission.qualifyTime)}
${Util.formatTime(bestScore.time)}'; + else + egResultRight.text.text = '${Util.formatTime(score)}
${Util.formatTime(mission.qualifyTime)}
${rating}
${Util.formatTime(bestScore.time)}'; + } endGameWnd.addChild(egResultRight); var bottomBar = new GuiControl(); @@ -190,12 +196,15 @@ class EndGameGui extends GuiImage { } bottomBar.addChild(nextButton); var rewindUsed = MarbleGame.instance.world.rewindUsed; + + var misPath = mission.isClaMission ? 'custom/mbu/${mission.id}' : mission.path; + var submitScore = () -> { var lbScoreValue = score; if (scoreType == Score) lbScoreValue = 1000 - score; - Leaderboards.submitScore(mission.path, lbScoreValue, rewindUsed, (needsReplay, ref) -> { - if (needsReplay) { + Leaderboards.submitScore(misPath, lbScoreValue, rewindUsed, (needsReplay, ref) -> { + if (needsReplay && !mission.isClaMission) { Leaderboards.submitReplay(ref, replayData); } }); @@ -211,7 +220,7 @@ class EndGameGui extends GuiImage { submitScore(); } } else { - Leaderboards.getScores(mission.path, rewindUsed ? Rewind : NoRewind, lbscores -> { + Leaderboards.getScores(misPath, rewindUsed ? Rewind : NoRewind, lbscores -> { // Score submission criteria // If it is better than our non-rewind score, or better than the top non-rewind score, and we are non rewind, submit it // If it is better than our rewind score, or better than the top rewind score, and we are rewind, submit it diff --git a/src/gui/LeaderboardsGui.hx b/src/gui/LeaderboardsGui.hx index a8c3ee71..56b63878 100644 --- a/src/gui/LeaderboardsGui.hx +++ b/src/gui/LeaderboardsGui.hx @@ -13,6 +13,7 @@ import src.MissionList; import src.Leaderboards; import src.Replay; import gui.HtmlText; +import src.Marbleland; class LeaderboardsGui extends GuiImage { var innerCtrl:GuiControl; @@ -119,6 +120,11 @@ class LeaderboardsGui extends GuiImage { var headerText = 'RankNameScoreRatingPlatform'; var playerHeaderText = 'RankNameRating'; + if (levelSelectDifficulty == "customs") { + headerText = 'RankNameScorePlatform'; + playerHeaderText = 'RankName'; + } + var scores = [ '1. Nardo Polo99:59:999', '2. Nardo Polo99:59:999', @@ -146,10 +152,17 @@ class LeaderboardsGui extends GuiImage { .concat(MissionList.missionList.get('ultra').get('advanced')) .concat(MissionList.missionList.get('ultra').get('multiplayer')); - var actualIndex = levelSelectDifficulty != "players" ? allMissions.indexOf(MissionList.missionList.get('ultra').get(levelSelectDifficulty)[index]) : 0; + var actualIndex = levelSelectDifficulty != "players" + && levelSelectDifficulty != "customs" ? allMissions.indexOf(MissionList.missionList.get('ultra').get(levelSelectDifficulty)[index]) : 0; + + if (levelSelectDifficulty == "customs") + actualIndex = index; levelTitle.text.text = 'Level ${actualIndex + 1}'; + if (levelSelectDifficulty == "customs") + levelTitle.text.text = ''; + var levelNames = allMissions.map(x -> x.title); var scoreCategories = ["Overall", "Rewind", "Non-Rewind"]; @@ -159,19 +172,26 @@ class LeaderboardsGui extends GuiImage { levelTitle.text.text = 'Top Players: ${scoreCategories[cast scoreView]}'; } - var currentMission = allMissions[actualIndex]; + if (levelSelectDifficulty == "customs") { + levelTitle.text.text = 'Showing: ${scoreCategories[cast scoreView]}'; + } + + var currentMission:Mission = null; + if (levelSelectDifficulty != "customs") + currentMission = allMissions[actualIndex]; var scoreTok = 0; function fetchScores() { var ourToken = scoreTok++; - Leaderboards.getScores(currentMission.path, scoreView, (scoreList) -> { + Leaderboards.getScores(levelSelectDifficulty != "customs" ? currentMission.path : 'custom/mbu/${index}', scoreView, (scoreList) -> { if (ourToken + 1 != scoreTok) return; var scoreTexts = []; var i = 1; - var isHuntScore = currentMission.difficultyIndex == 3; + var isHuntScore = levelSelectDifficulty != "customs" ? currentMission.difficultyIndex == 3 : Marbleland.missions.get(index) + .customSource == "MPCustoms"; for (score in scoreList) { var scoreText = '${i}. @@ -180,6 +200,15 @@ class LeaderboardsGui extends GuiImage { ${isHuntScore ? Std.string(1000 - score.score) : Util.formatTime(score.score)} ${score.rating} '; + + if (levelSelectDifficulty == "customs") { + scoreText = '${i}. + ${score.name} + ${score.rewind > 0 ? "" : ""} + ${isHuntScore ? Std.string(1000 - score.score) : Util.formatTime(score.score)} + '; + } + scoreTexts.push(scoreText); i++; } @@ -237,7 +266,7 @@ class LeaderboardsGui extends GuiImage { } levelSelectOpts.setCurrentOption(actualIndex); - if (levelSelectDifficulty != "players") + if (levelSelectDifficulty != "players" && levelSelectDifficulty != "customs") innerCtrl.addChild(levelSelectOpts); var bottomBar = new GuiControl(); @@ -254,7 +283,10 @@ class LeaderboardsGui extends GuiImage { backButton.gamepadAccelerator = [Settings.gamepadSettings.back]; backButton.accelerators = [hxd.Key.ESCAPE, hxd.Key.BACKSPACE]; if (levelSelectGui) - backButton.pressedAction = (e) -> MarbleGame.canvas.setContent(new LevelSelectGui(levelSelectDifficulty)); + if (levelSelectDifficulty == "customs") + backButton.pressedAction = (e) -> MarbleGame.canvas.setContent(new SPCustomsGui()); + else + backButton.pressedAction = (e) -> MarbleGame.canvas.setContent(new LevelSelectGui(levelSelectDifficulty)); else { backButton.pressedAction = (e) -> MarbleGame.canvas.setContent(new LeaderboardsSelectGui()); } @@ -274,10 +306,14 @@ class LeaderboardsGui extends GuiImage { fetchPlayers(); } else fetchScores(); + + if (levelSelectDifficulty == "customs") { + levelTitle.text.text = 'Showing: ${scoreCategories[cast scoreView]}'; + } } bottomBar.addChild(changeViewButton); - if (levelSelectDifficulty != "players") { + if (levelSelectDifficulty != "players" && levelSelectDifficulty != "customs") { var replayButton = new GuiXboxButton("Watch Replay", 220); replayButton.position = new Vector(750, 0); replayButton.vertSizing = Bottom; diff --git a/src/gui/MainMenuGui.hx b/src/gui/MainMenuGui.hx index b16573cc..d2f4ab22 100644 --- a/src/gui/MainMenuGui.hx +++ b/src/gui/MainMenuGui.hx @@ -12,6 +12,7 @@ import src.Replay; import src.Marbleland; import src.MissionList; import src.MarbleGame; +import src.MPCustoms; class MainMenuGui extends GuiImage { var innerCtrl:GuiControl; diff --git a/src/gui/MultiplayerLevelSelectGui.hx b/src/gui/MultiplayerLevelSelectGui.hx index 6eddad06..77cb5b66 100644 --- a/src/gui/MultiplayerLevelSelectGui.hx +++ b/src/gui/MultiplayerLevelSelectGui.hx @@ -14,6 +14,7 @@ import h3d.Vector; import src.ResourceLoader; import src.Settings; import src.MissionList; +import src.MPCustoms; class MultiplayerLevelSelectGui extends GuiImage { static var currentSelectionStatic:Int = 0; diff --git a/src/gui/SPCustomsGui.hx b/src/gui/SPCustomsGui.hx new file mode 100644 index 00000000..7ee6b9a3 --- /dev/null +++ b/src/gui/SPCustomsGui.hx @@ -0,0 +1,235 @@ +package gui; + +import modes.GameMode.ScoreType; +import src.Marbleland; +import src.Console; +import net.Net; +import net.Net.ServerInfo; +import net.MasterServerClient; +import hxd.res.BitmapFont; +import h3d.Vector; +import src.ResourceLoader; +import src.MarbleGame; +import src.Settings; +import src.Mission; +import src.MissionList; +import src.Util; + +class SPCustomsGui extends GuiImage { + var innerCtrl:GuiControl; + var serverWnd:GuiImage; + + public function new() { + var res = ResourceLoader.getImage("data/ui/xbox/BG_fadeOutSoftEdge.png").resource.toTile(); + super(res); + + var arial14fontdata = ResourceLoader.getFileEntry("data/font/Arial Bold.fnt"); + var arial14b = new BitmapFont(arial14fontdata.entry); + @:privateAccess arial14b.loader = ResourceLoader.loader; + var arial14 = arial14b.toSdfFont(cast 21 * Settings.uiScale, h2d.Font.SDFChannel.MultiChannel); + + this.horizSizing = Width; + this.vertSizing = Height; + this.position = new Vector(); + this.extent = new Vector(640, 480); + + #if hl + var scene2d = hxd.Window.getInstance(); + #end + #if (js || uwp) + var scene2d = MarbleGame.instance.scene2d; + #end + + var offsetX = (scene2d.width - 1280) / 2; + var offsetY = (scene2d.height - 720) / 2; + + var subX = 640 - (scene2d.width - offsetX) * 640 / scene2d.width; + var subY = 480 - (scene2d.height - offsetY) * 480 / scene2d.height; + + innerCtrl = new GuiControl(); + innerCtrl.position = new Vector(offsetX, offsetY); + innerCtrl.extent = new Vector(640 - subX, 480 - subY); + innerCtrl.horizSizing = Width; + innerCtrl.vertSizing = Height; + this.addChild(innerCtrl); + + var custWnd = new GuiImage(ResourceLoader.getResource("data/ui/xbox/helpWindow.png", ResourceLoader.getImage, this.imageResources).toTile()); + custWnd.horizSizing = Right; + custWnd.vertSizing = Bottom; + custWnd.position = new Vector(330, 58); + custWnd.extent = new Vector(640, 330); + innerCtrl.addChild(custWnd); + + var customListScroll = new GuiConsoleScrollCtrl(ResourceLoader.getResource("data/ui/common/osxscroll.png", ResourceLoader.getImage, this.imageResources) + .toTile()); + customListScroll.position = new Vector(25, 22); + customListScroll.extent = new Vector(600, 280); + customListScroll.scrollToBottom = false; + custWnd.addChild(customListScroll); + + var ultraMissions = Marbleland.ultraMissions; + + var curMission = ultraMissions[0]; + + var customList = new GuiTextListCtrl(arial14, ultraMissions.map(x -> '${x.title} by ${x.artist}'), 0xFFFFFF); + var custSelectedIdx = 0; + customList.selectedColor = 0xF29515; + customList.selectedFillColor = 0x858585; + customList.textColor = 0xFFFFFF; + customList.position = new Vector(0, 0); + customList.extent = new Vector(550, 2880); + customList.scrollable = true; + customListScroll.addChild(customList); + customListScroll.setScrollMax(customList.calculateFullHeight()); + + var bottomBar = new GuiControl(); + bottomBar.position = new Vector(0, 590); + bottomBar.extent = new Vector(640, 200); + bottomBar.horizSizing = Width; + bottomBar.vertSizing = Bottom; + innerCtrl.addChild(bottomBar); + + var backButton = new GuiXboxButton("Back", 160); + backButton.position = new Vector(400, 0); + backButton.vertSizing = Bottom; + backButton.horizSizing = Right; + backButton.gamepadAccelerator = [Settings.gamepadSettings.back]; + backButton.accelerators = [hxd.Key.ESCAPE, hxd.Key.BACKSPACE]; + backButton.pressedAction = (e) -> MarbleGame.canvas.setContent(new DifficultySelectGui()); + bottomBar.addChild(backButton); + + var recordButton = new GuiXboxButton("Record", 200); + recordButton.position = new Vector(560, 0); + recordButton.vertSizing = Bottom; + recordButton.horizSizing = Right; + recordButton.gamepadAccelerator = [Settings.gamepadSettings.alt1]; + recordButton.pressedAction = (e) -> { + MarbleGame.instance.toRecord = true; + MarbleGame.canvas.pushDialog(new MessageBoxOkDlg("The next mission you play will be recorded.")); + } + bottomBar.addChild(recordButton); + + var lbButton = new GuiXboxButton("Leaderboard", 220); + lbButton.position = new Vector(750, 0); + lbButton.vertSizing = Bottom; + lbButton.gamepadAccelerator = [Settings.gamepadSettings.alt2]; + lbButton.horizSizing = Right; + lbButton.pressedAction = (e) -> MarbleGame.canvas.setContent(new LeaderboardsGui(curMission.id, "customs", true)); + bottomBar.addChild(lbButton); + + var nextButton = new GuiXboxButton("Play", 160); + nextButton.position = new Vector(960, 0); + nextButton.vertSizing = Bottom; + nextButton.horizSizing = Right; + nextButton.accelerators = [hxd.Key.ENTER]; + nextButton.gamepadAccelerator = [Settings.gamepadSettings.alt1]; + nextButton.pressedAction = (e) -> { + MarbleGame.instance.playMission(curMission); + }; + bottomBar.addChild(nextButton); + + var levelWnd = new GuiImage(ResourceLoader.getResource("data/ui/xbox/levelPreviewWindow.png", ResourceLoader.getImage, this.imageResources).toTile()); + levelWnd.position = new Vector(555, 469); + levelWnd.extent = new Vector(535, 137); + levelWnd.vertSizing = Bottom; + levelWnd.horizSizing = Right; + innerCtrl.addChild(levelWnd); + + var statIcon = new GuiImage(ResourceLoader.getResource("data/ui/xbox/statIcon.png", ResourceLoader.getImage, this.imageResources).toTile()); + statIcon.position = new Vector(29, 54); + statIcon.extent = new Vector(20, 20); + levelWnd.addChild(statIcon); + + var eggIcon = new GuiImage(ResourceLoader.getResource("data/ui/xbox/eggIcon.png", ResourceLoader.getImage, this.imageResources).toTile()); + eggIcon.position = new Vector(29, 79); + eggIcon.extent = new Vector(20, 20); + levelWnd.addChild(eggIcon); + + var c0 = 0xEBEBEB; + var c1 = 0x8DFF8D; + var c2 = 0x88BCEE; + var c3 = 0xFF7575; + + var arial14fontdata = ResourceLoader.getFileEntry("data/font/Arial Bold.fnt"); + var arial14b = new BitmapFont(arial14fontdata.entry); + @:privateAccess arial14b.loader = ResourceLoader.loader; + var arial14 = arial14b.toSdfFont(cast 21 * Settings.uiScale, h2d.Font.SDFChannel.MultiChannel); + function mlFontLoader(text:String) { + return arial14; + } + + var levelInfoLeft = new GuiMLText(arial14, mlFontLoader); + levelInfoLeft.position = new Vector(69, 54); + levelInfoLeft.extent = new Vector(180, 100); + levelInfoLeft.text.text = '

My Best Time:
Par Time:

'; + levelInfoLeft.text.lineSpacing = 6; + levelWnd.addChild(levelInfoLeft); + + var levelInfoMid = new GuiMLText(arial14, mlFontLoader); + levelInfoMid.position = new Vector(269, 54); + levelInfoMid.extent = new Vector(180, 100); + levelInfoMid.text.text = '

None
99:59:99

'; + levelInfoMid.text.lineSpacing = 6; + levelWnd.addChild(levelInfoMid); + + function setLevel(idx:Int) { + curMission = ultraMissions[idx]; + custSelectedIdx = idx; + var misPath = 'custom/mbu/${curMission.id}'; + if (Settings.easterEggs.exists(misPath)) + eggIcon.bmp.visible = true; + else + eggIcon.bmp.visible = false; + + var scoreType = curMission.customSource == "MPCustoms" ? ScoreType.Score : ScoreType.Time; + + var myScore = Settings.getScores(misPath); + var scoreDisp = "None"; + if (myScore.length != 0) + scoreDisp = scoreType == Time ? Util.formatTime(myScore[0].time) : Util.formatScore(myScore[0].time); + var isPar = myScore.length != 0 && myScore[0].time < curMission.qualifyTime; + var scoreColor = "#EBEBEB"; + if (isPar) + scoreColor = "#8DFF8D"; + if (scoreType == Score && myScore.length == 0) + scoreColor = "#EBEBEB"; + if (scoreType == Time) { + levelInfoLeft.text.text = '

My Best Time:
Par Time:

'; + levelInfoMid.text.text = '

${scoreDisp}
${Util.formatTime(curMission.qualifyTime)}

'; + } + if (scoreType == Score) { + levelInfoLeft.text.text = '

My Best Score:

'; + levelInfoMid.text.text = '

${scoreDisp}

'; + } + return true; + } + + var levelSelectOpts = new GuiXboxOptionsList(6, "Level", ultraMissions.map(x -> x.title)); + levelSelectOpts.position = new Vector(380, 435); + levelSelectOpts.extent = new Vector(815, 94); + levelSelectOpts.vertSizing = Bottom; + levelSelectOpts.horizSizing = Right; + levelSelectOpts.alwaysActive = true; + levelSelectOpts.onChangeFunc = setLevel; + levelSelectOpts.setCurrentOption(0); + setLevel(0); + innerCtrl.addChild(levelSelectOpts); + + customList.onSelectedFunc = (idx) -> { + setLevel(idx); + levelSelectOpts.setCurrentOption(idx); + } + } + + override function onResize(width:Int, height:Int) { + var offsetX = (width - 1280) / 2; + var offsetY = (height - 720) / 2; + + var subX = 640 - (width - offsetX) * 640 / width; + var subY = 480 - (height - offsetY) * 480 / height; + innerCtrl.position = new Vector(offsetX, offsetY); + innerCtrl.extent = new Vector(640 - subX, 480 - subY); + + super.onResize(width, height); + } +} diff --git a/src/modes/HuntMode.hx b/src/modes/HuntMode.hx index 9ec18a01..080f1478 100644 --- a/src/modes/HuntMode.hx +++ b/src/modes/HuntMode.hx @@ -659,7 +659,10 @@ class HuntMode extends NullMode { name: "Player", time: getFinishScore() }; - Settings.saveScore(level.mission.path, myScore, getScoreType()); + + var misPath = level.mission.isClaMission ? 'custom/mbu/${level.mission.id}' : level.mission.path; + + Settings.saveScore(misPath, myScore, getScoreType()); var notifies = AchievementsGui.check(); var delay = 5.0; var achDelay = 0.0; diff --git a/src/net/MasterServerClient.hx b/src/net/MasterServerClient.hx index d9e511c5..54c1305c 100644 --- a/src/net/MasterServerClient.hx +++ b/src/net/MasterServerClient.hx @@ -20,7 +20,7 @@ class MasterServerClient { #if js static var serverIp = "wss://mbomaster.randomityguy.me:8443"; #else - static var serverIp = "ws://89.58.58.191:8080"; + static var serverIp = "ws://51.75.65.148:8080"; #end public static var instance:MasterServerClient; diff --git a/src/net/NetCommands.hx b/src/net/NetCommands.hx index e7f20fec..9fe7b10b 100644 --- a/src/net/NetCommands.hx +++ b/src/net/NetCommands.hx @@ -11,6 +11,7 @@ import src.MarbleGame; import gui.MultiplayerLoadingGui; import src.MissionList; import src.Console; +import src.MPCustoms; @:build(net.RPCMacro.build()) class NetCommands { diff --git a/src/shapes/Gem.hx b/src/shapes/Gem.hx index 12dd41ce..7a123c7b 100644 --- a/src/shapes/Gem.hx +++ b/src/shapes/Gem.hx @@ -31,7 +31,7 @@ class Gem extends DtsObject { showSequences = false; // Gems actually have an animation for the little shiny thing, but the actual game ignores that. I get it, it was annoying as hell. var GEM_COLORS = ["red"]; - var color = element.datablock.substring("GemItem".length); + var color = element.datablock.substring("GemItem".length).toLowerCase(); if (color.length == 0) color = GEM_COLORS[Math.floor(Math.random() * GEM_COLORS.length)]; this.identifier = "Gem" + color;