the long awaited marbleland customs support, oh and you can now play MP customs in SP as well

This commit is contained in:
RandomityGuy 2025-11-06 13:30:32 +00:00
parent 83fbd0ea8a
commit df33307cdb
18 changed files with 610 additions and 114 deletions

View file

@ -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)),
];

View file

@ -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<LBScore>->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);

View file

@ -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) -> {

View file

@ -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(() -> {

View file

@ -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);
}
}
}
}

View file

@ -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);

View file

@ -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<Mission> = [];
public static var missions:Map<Int, Mission> = [];
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<Dynamic> = 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) {

View file

@ -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<String, String>;
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();
}
}
}

View file

@ -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);

View file

@ -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 = '<p align="right"><font color="${beatPar ? "#8DFF8D" : "#FF7575"}">Time:</font><br/><font color="#88BCEE">Par Time:</font><br/><font color="#EBEBEB">Rating:</font><br/><font color="#EBEBEB">My Best Time:</font></p>';
if (scoreType == Time) {
if (mission.isClaMission)
egResultLeft.text.text = '<p align="right"><font color="${beatPar ? "#8DFF8D" : "#FF7575"}">Time:</font><br/><font color="#88BCEE">Par Time:</font><br/><font color="#EBEBEB">My Best Time:</font></p>';
else
egResultLeft.text.text = '<p align="right"><font color="${beatPar ? "#8DFF8D" : "#FF7575"}">Time:</font><br/><font color="#88BCEE">Par Time:</font><br/><font color="#EBEBEB">Rating:</font><br/><font color="#EBEBEB">My Best Time:</font></p>';
}
if (scoreType == Score)
egResultLeft.text.text = '<p align="right"><font color="#8DFF8D">Score:</font><br/><font color="#EBEBEB">My Best Score:</font></p>';
endGameWnd.addChild(egResultLeft);
@ -146,8 +148,12 @@ class EndGameGui extends GuiImage {
egResultRight.extent = new Vector(180, 100);
if (scoreType == Score)
egResultRight.text.text = '<font color="#8DFF8D">${Util.formatScore(score)}</font><br/><font color="#EBEBEB">${Util.formatScore(bestScore.time)}</font>';
if (scoreType == Time)
egResultRight.text.text = '<font color="${beatPar ? "#8DFF8D" : "#FF7575"}">${Util.formatTime(score)}</font><br/><font color="#88BCEE">${Util.formatTime(mission.qualifyTime)}</font><br/><font color="#EBEBEB">${rating}</font><br/><font color="#EBEBEB">${Util.formatTime(bestScore.time)}</font>';
if (scoreType == Time) {
if (mission.isClaMission)
egResultRight.text.text = '<font color="${beatPar ? "#8DFF8D" : "#FF7575"}">${Util.formatTime(score)}</font><br/><font color="#88BCEE">${Util.formatTime(mission.qualifyTime)}</font><br/><font color="#EBEBEB">${Util.formatTime(bestScore.time)}</font>';
else
egResultRight.text.text = '<font color="${beatPar ? "#8DFF8D" : "#FF7575"}">${Util.formatTime(score)}</font><br/><font color="#88BCEE">${Util.formatTime(mission.qualifyTime)}</font><br/><font color="#EBEBEB">${rating}</font><br/><font color="#EBEBEB">${Util.formatTime(bestScore.time)}</font>';
}
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

View file

@ -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 = '<font face="arial12">Rank<offset value="50">Name</offset><offset value="400">Score</offset><offset value="500">Rating</offset><offset value="600">Platform</offset></font>';
var playerHeaderText = '<font face="arial12">Rank<offset value="50">Name</offset><offset value="575">Rating</offset></font>';
if (levelSelectDifficulty == "customs") {
headerText = '<font face="arial12">Rank<offset value="50">Name</offset><offset value="500">Score</offset><offset value="600">Platform</offset></font>';
playerHeaderText = '<font face="arial12">Rank<offset value="50">Name</offset></font>';
}
var scores = [
'<offset value="10">1. </offset><offset value="50">Nardo Polo</offset><offset value="500">99:59:999</offset><offset value="625"><img src="unknown"/></offset>',
'<offset value="10">2. </offset><offset value="50">Nardo Polo</offset><offset value="500">99:59:999</offset><offset value="625"><img src="pc"/></offset>',
@ -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 = '<offset value="10">${i}. </offset>
@ -180,6 +200,15 @@ class LeaderboardsGui extends GuiImage {
<offset value="400">${isHuntScore ? Std.string(1000 - score.score) : Util.formatTime(score.score)}</offset>
<offset value="500">${score.rating}</offset>
<offset value="625"><img src="${platformToString(score.platform)}"/></offset>';
if (levelSelectDifficulty == "customs") {
scoreText = '<offset value="10">${i}. </offset>
<offset value="50">${score.name}</offset>
<offset value="475">${score.rewind > 0 ? "<img src='rewind'/>" : ""}</offset>
<offset value="500">${isHuntScore ? Std.string(1000 - score.score) : Util.formatTime(score.score)}</offset>
<offset value="625"><img src="${platformToString(score.platform)}"/></offset>';
}
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;

View file

@ -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;

View file

@ -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;

235
src/gui/SPCustomsGui.hx Normal file
View file

@ -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 = '<p align="right"><font color="#EBEBEB">My Best Time:</font><br/><font color="#EBEBEB">Par Time:</font></p>';
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 = '<p align="left"><font color="#EBEBEB">None</font><br/><font color="#88BCEE">99:59:99</font></p>';
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 = '<p align="right"><font color="#EBEBEB">My Best Time:</font><br/><font color="#EBEBEB">Par Time:</font></p>';
levelInfoMid.text.text = '<p align="left"><font color="${scoreColor}">${scoreDisp}</font><br/><font color="#88BCEE">${Util.formatTime(curMission.qualifyTime)}</font></p>';
}
if (scoreType == Score) {
levelInfoLeft.text.text = '<p align="right"><font color="#EBEBEB">My Best Score:</font></p>';
levelInfoMid.text.text = '<p align="left"><font color="${scoreColor}">${scoreDisp}</font></p>';
}
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);
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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 {

View file

@ -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;