Chart Variations Backend Implementation
This commit is contained in:
@@ -83,77 +83,72 @@ class Chart {
|
||||
return data;
|
||||
}
|
||||
|
||||
public static function loadChartMeta(songName:String, difficulty:String = '', fromMods:Bool = true, includeMetaDifficulties:Bool = true):ChartMetaData {
|
||||
var metaPath = Paths.file('songs/${songName}/meta.json');
|
||||
var metaDiffPath = Paths.file('songs/${songName}/meta-${difficulty}.json');
|
||||
|
||||
var data:ChartMetaData = null, fromDifficulty = false;
|
||||
var fromMods:Bool = fromMods;
|
||||
for (path in [metaDiffPath, metaPath]) if (Assets.exists(path)) {
|
||||
public static function loadChartMeta(songName:String, ?variant:String, fromMods:Bool = true, includeMetaVariations = true):ChartMetaData {
|
||||
var defaultPath = Paths.file('songs/$songName/meta.json'), isVariant = false;
|
||||
var data:ChartMetaData = null, paths = (variant == null || variant == '') ? [defaultPath] : [Paths.file('songs/$songName/meta-$variant.json'), defaultPath];
|
||||
for (path in paths) if (Assets.exists(path)) {
|
||||
fromMods = Paths.assetsTree.existsSpecific(path, "TEXT", MODS);
|
||||
try {
|
||||
var tempData = Json.parse(Assets.getText(path));
|
||||
tempData.color = CoolUtil.getColorFromDynamic(tempData.color).getDefault(Flags.DEFAULT_COLOR);
|
||||
data = tempData;
|
||||
} catch(e) Logs.trace('Failed to load song metadata for ${songName} ($path): ${Std.string(e)}', ERROR);
|
||||
} catch(e) Logs.trace('Failed to load song metadata for $songName ($path): ${Std.string(e)}', ERROR);
|
||||
|
||||
if (data != null) {
|
||||
fromDifficulty = path == metaDiffPath;
|
||||
isVariant = path != defaultPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
data = {
|
||||
name: songName,
|
||||
bpm: Flags.DEFAULT_BPM
|
||||
};
|
||||
}
|
||||
else {
|
||||
data.name = songName;
|
||||
data.setFieldDefault("bpm", Flags.DEFAULT_BPM);
|
||||
}
|
||||
if (data != null) data.name = songName;
|
||||
else data = {
|
||||
name: songName,
|
||||
color: Flags.DEFAULT_COLOR
|
||||
};
|
||||
|
||||
if (isVariant) data.variant = variant;
|
||||
else data.variant = null;
|
||||
|
||||
data.setFieldDefault("displayName", data.name);
|
||||
|
||||
data.setFieldDefault("bpm", Flags.DEFAULT_BPM);
|
||||
data.setFieldDefault("beatsPerMeasure", Flags.DEFAULT_BEATS_PER_MEASURE);
|
||||
data.setFieldDefault("stepsPerBeat", Flags.DEFAULT_STEPS_PER_BEAT);
|
||||
data.setFieldDefault("icon", Flags.DEFAULT_HEALTH_ICON);
|
||||
data.setFieldDefault("difficulties", []);
|
||||
data.setFieldDefault("coopAllowed", Flags.DEFAULT_COOP_ALLOWED);
|
||||
data.setFieldDefault("opponentModeAllowed", Flags.DEFAULT_OPPONENT_MODE_ALLOWED);
|
||||
data.setFieldDefault("instSuffix", "");
|
||||
data.setFieldDefault("vocalsSuffix", "");
|
||||
data.setFieldDefault("needsVoices", true);
|
||||
data.setFieldDefault("difficulties", []);
|
||||
data.setFieldDefault("variants", []);
|
||||
|
||||
if (data.difficulties.length <= 0) {
|
||||
data.difficulties = [for(f in Paths.getFolderContent('songs/${songName}/charts/', false, fromMods ? MODS : SOURCE)) if (Path.extension(f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
|
||||
var tempDiffs = [];
|
||||
var path = 'songs/$songName/charts/';
|
||||
if (isVariant) path += '$variant/';
|
||||
|
||||
data.difficulties = [for(f in Paths.getFolderContent(path, false, fromMods ? MODS : SOURCE)) if (Path.extension(f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
|
||||
if (data.difficulties.length == 3) {
|
||||
for(d in data.difficulties) {
|
||||
switch(d.toLowerCase()) {
|
||||
case "easy": tempDiffs.insert(0, d);
|
||||
case "normal": tempDiffs.insert(1, d);
|
||||
case "hard": tempDiffs.insert(2, d);
|
||||
}
|
||||
var tempDiffs = [];
|
||||
for (d in data.difficulties) switch(d.toLowerCase()) {
|
||||
case "easy": tempDiffs.insert(0, d);
|
||||
case "normal": tempDiffs.insert(1, d);
|
||||
case "hard": tempDiffs.insert(2, d);
|
||||
}
|
||||
data.difficulties = tempDiffs;
|
||||
if (tempDiffs.length == 3) data.difficulties = tempDiffs;
|
||||
}
|
||||
}
|
||||
|
||||
if (includeMetaDifficulties && !fromDifficulty) {
|
||||
data.metas = [];
|
||||
for (difficulty in data.difficulties) {
|
||||
if (!data.metas.exists(difficulty) && Assets.exists(Paths.file('songs/${songName}/meta-${difficulty}.json')))
|
||||
data.metas.set(difficulty, loadChartMeta(songName, difficulty, fromMods, false));
|
||||
}
|
||||
if (includeMetaVariations & data.variants.length > 0) for (variant in data.variants) {
|
||||
if (!data.metas.exists(variant) && Assets.exists(Paths.file('songs/$songName/meta-$variant.json')))
|
||||
data.metas.set(variant, loadChartMeta(songName, variant, fromMods));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static function parse(songName:String, ?difficulty:String):ChartData {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
|
||||
var chartPath = Paths.chart(songName, difficulty);
|
||||
public static function parse(songName:String, ?variant:String):ChartData {
|
||||
var chartPath = Paths.chart(songName, variant);
|
||||
var base:ChartData = {
|
||||
strumLines: [],
|
||||
noteTypes: [],
|
||||
@@ -167,15 +162,15 @@ class Chart {
|
||||
fromMods: Paths.assetsTree.existsSpecific(chartPath, "TEXT", MODS)
|
||||
};
|
||||
|
||||
var valid:Bool = true;
|
||||
var valid:Bool = true, namePrint = (variant == null || variant == '') ? '$songName' : '$songName ($variant)';
|
||||
if (!Assets.exists(chartPath)) {
|
||||
Logs.error('Chart for song ${songName} ($difficulty) at "$chartPath" was not found.');
|
||||
Logs.error('Chart for song $namePrint at "$chartPath" was not found.');
|
||||
valid = false;
|
||||
}
|
||||
var data:Dynamic = null;
|
||||
if (valid) {
|
||||
try data = Json.parse(Assets.getText(chartPath))
|
||||
catch(e) Logs.trace('Could not parse chart for song ${songName} ($difficulty): ${Std.string(e)}', ERROR, RED);
|
||||
catch(e) Logs.trace('Could not parse chart for song $namePrint: ${Std.string(e)}', ERROR, RED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,13 +202,12 @@ class Chart {
|
||||
}
|
||||
#end
|
||||
|
||||
var loadedMeta = loadChartMeta(songName, difficulty, base.fromMods);
|
||||
var loadedMeta = loadChartMeta(songName, variant, base.fromMods, false);
|
||||
if (base.meta == null) base.meta = loadedMeta;
|
||||
else {
|
||||
for (field in Reflect.fields(base.meta)) {
|
||||
var f = Reflect.field(base.meta, field);
|
||||
if (f != null)
|
||||
Reflect.setField(loadedMeta, field, f);
|
||||
if (f != null) Reflect.setField(loadedMeta, field, f);
|
||||
}
|
||||
base.meta = loadedMeta;
|
||||
}
|
||||
@@ -252,61 +246,49 @@ class Chart {
|
||||
|
||||
/**
|
||||
* Saves the chart to the specific song folder path.
|
||||
* @param songFolderPath Path to the song folder (ex: `mods/your mod/songs/song/`)
|
||||
* @param chart Chart to save
|
||||
* @param difficulty Name of the difficulty
|
||||
* @param saveSettings
|
||||
* @param chart Chart to save.
|
||||
* @param difficulty Name of the difficulty (Optional).
|
||||
* @param variant Name of the Variant (Optional).
|
||||
* @param saveSettings (Optional).
|
||||
* @return Filtered chart used for saving.
|
||||
*/
|
||||
public static function save(songFolderPath:String, chart:ChartData, ?difficulty:String, ?saveSettings:ChartSaveSettings):ChartData {
|
||||
public static function save(chart:ChartData, ?difficulty:String, ?variant:String, ?saveSettings:ChartSaveSettings):ChartData {
|
||||
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
|
||||
if (saveSettings == null) saveSettings = {};
|
||||
|
||||
if (saveSettings.saveMetaInChart == null) saveSettings.saveMetaInChart = true;
|
||||
if (saveSettings.saveLocalEvents == null) saveSettings.saveLocalEvents = true;
|
||||
if (saveSettings.saveGlobalEvents == null) saveSettings.saveGlobalEvents = false;
|
||||
|
||||
var filteredChart = filterChartForSaving(chart, saveSettings.saveMetaInChart, saveSettings.saveLocalEvents, saveSettings.saveGlobalEvents);
|
||||
var meta = filteredChart.meta;
|
||||
|
||||
#if sys
|
||||
var saveFolder:String = saveSettings.folder == null ? "charts" : saveSettings.folder;
|
||||
var songPath = saveSettings.songFolder == null ? 'assets/songs/${chart.meta.name}' : saveSettings.songFolder;
|
||||
var metaPath = '$songPath/meta.json', temp:String;
|
||||
if ((temp = Paths.assetsTree.getPath(metaPath)) != null) metaPath = temp;
|
||||
|
||||
if (!FileSystem.exists('${songFolderPath}/$saveFolder/'))
|
||||
FileSystem.createDirectory('${songFolderPath}/$saveFolder/');
|
||||
var chartFolder = saveSettings.folder == null ? ((variant == null || variant == '') ? 'charts' : 'charts/$variant') : saveSettings.folder;
|
||||
var chartPath = '$songPath/$chartFolder/${difficulty.trim()}.json';
|
||||
if ((temp = Paths.assetsTree.getPath(chartPath)) != null) chartPath = temp;
|
||||
|
||||
var chartPath = '${songFolderPath}/$saveFolder/${difficulty.trim()}.json';
|
||||
var metaPath = '${songFolderPath}/meta.json';
|
||||
if (saveSettings.saveChart == null || saveSettings.saveChart == true)
|
||||
CoolUtil.safeSaveFile(chartPath, Json.stringify(filteredChart, null, saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null));
|
||||
|
||||
CoolUtil.safeSaveFile(chartPath, Json.stringify(filteredChart, null, saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null));
|
||||
|
||||
if (meta != null && (saveSettings.overrideExistingMeta || !FileSystem.exists(metaPath)))
|
||||
CoolUtil.safeSaveFile(metaPath, makeMetaSaveable(meta));
|
||||
if (filteredChart.meta != null && (saveSettings.overrideExistingMeta || !FileSystem.exists(metaPath)))
|
||||
CoolUtil.safeSaveFile(metaPath, makeMetaSaveable(filteredChart.meta));
|
||||
#end
|
||||
|
||||
return filteredChart;
|
||||
}
|
||||
|
||||
public static function filterChartForSaving(chart:ChartData, ?saveMetaInChart:Bool, ?saveLocalEvents:Bool, ?saveGlobalEvents:Bool):ChartData {
|
||||
public static function filterChartForSaving(chart:ChartData, saveMetaInChart = true, ?saveLocalEvents:Bool, ?saveGlobalEvents:Bool):ChartData {
|
||||
var meta = chart.meta, events = chart.events;
|
||||
chart.meta = null;
|
||||
chart.events = null;
|
||||
|
||||
var data = Reflect.copy(chart); // make a copy of the chart to leave the OG intact
|
||||
if (saveMetaInChart != true) {
|
||||
data.meta = null;
|
||||
} else {
|
||||
data.meta = Reflect.copy(chart.meta); // also make a copy of the metadata to leave the OG intact.
|
||||
if (data.meta != null && Reflect.hasField(data.meta, "parsedColor")) Reflect.deleteField(data.meta, "parsedColor");
|
||||
}
|
||||
|
||||
// in this part abt the events, i gotta account that these booleans can be null - Nex
|
||||
if (saveLocalEvents != true && saveGlobalEvents != true) data.events = null;
|
||||
else {
|
||||
data.events = [];
|
||||
for (event in chart.events) if ((saveLocalEvents == true && event.global != true) || (saveGlobalEvents == true && event.global == true)) {
|
||||
var copy = Reflect.copy(event);
|
||||
if (saveLocalEvents == true ? event.global != true : event.global == true) Reflect.deleteField(copy, "global"); // should NOT delete the field when saving with the local events and the event should have been global - Nex
|
||||
data.events.push(copy);
|
||||
}
|
||||
if (data.events.length == 0) data.events = null;
|
||||
}
|
||||
chart.meta = meta;
|
||||
chart.events = events;
|
||||
|
||||
data.meta = saveMetaInChart ? filterMetaForSaving(meta) : null;
|
||||
data.events = filterEventsForSaving(events, saveLocalEvents, saveGlobalEvents);
|
||||
data.fromMods = null;
|
||||
|
||||
var sortedData:Dynamic = {};
|
||||
@@ -318,19 +300,42 @@ class Chart {
|
||||
return sortedData;
|
||||
}
|
||||
|
||||
public static inline function makeMetaSaveable(meta:ChartMetaData, prettyPrint:Bool = true):String {
|
||||
public static function filterEventsForSaving(events:Array<ChartEvent>, saveLocalEvents = true, saveGlobalEvents = false):Array<ChartEvent> {
|
||||
var data = [];
|
||||
if (!saveLocalEvents && !saveGlobalEvents) return data;
|
||||
|
||||
for (event in chart.events) if ((saveLocalEvents && event.global != true) || (saveGlobalEvents && event.global == true)) {
|
||||
var copy = Reflect.copy(event);
|
||||
if (saveLocalEvents ? event.global != true : event.global == true) Reflect.deleteField(copy, "global"); // should NOT delete the field when saving with the local events and the event should have been global - Nex
|
||||
data.events.push(copy);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static inline function makeMetaSaveable(meta:ChartMetaData, prettyPrint:Bool = true):String
|
||||
return Json.stringify(filterMetaForSaving(meta), null, prettyPrint ? Flags.JSON_PRETTY_PRINT : null);
|
||||
|
||||
public static inline function filterMetaForSaving(meta:ChartMetaData):ChartMetaData {
|
||||
var data:Dynamic = Reflect.copy(meta);
|
||||
if (data.color != null) data.color = FlxColor.fromInt(data.color).toWebString(); // dont even ask me - Nex
|
||||
if (data.color != null) data.color = FlxColor.fromInt(data.color).toWebString(); // dont even ask me - Nex
|
||||
Reflect.deleteField(data, 'parsedColor');
|
||||
Reflect.deleteField(data, 'metas');
|
||||
return Json.stringify(data, null, prettyPrint ? Flags.JSON_PRETTY_PRINT : null);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
typedef ChartSaveSettings = {
|
||||
var ?overrideExistingMeta:Bool;
|
||||
var ?prettyPrint:Bool;
|
||||
|
||||
var ?saveMetaInChart:Bool;
|
||||
var ?saveLocalEvents:Bool;
|
||||
var ?saveGlobalEvents:Bool;
|
||||
var ?prettyPrint:Bool;
|
||||
|
||||
var ?saveChart:Bool;
|
||||
var ?overrideExistingMeta:Bool;
|
||||
var ?seperateGlobalEvents:Bool;
|
||||
|
||||
var ?folder:String;
|
||||
var ?songFolder:String;
|
||||
}
|
||||
|
||||
@@ -18,16 +18,23 @@ typedef ChartData = {
|
||||
|
||||
typedef ChartMetaData = {
|
||||
public var name:String;
|
||||
public var ?bpm:Float;
|
||||
public var ?variant:String;
|
||||
public var ?displayName:String;
|
||||
|
||||
public var ?bpm:Float;
|
||||
public var ?beatsPerMeasure:Float;
|
||||
public var ?stepsPerBeat:Int;
|
||||
|
||||
public var ?difficulties:Array<String>;
|
||||
public var ?variants:Array<String>;
|
||||
public var ?customValues:Dynamic;
|
||||
|
||||
public var ?icon:String;
|
||||
public var ?color:FlxColor;
|
||||
public var ?difficulties:Array<String>;
|
||||
|
||||
public var ?coopAllowed:Bool;
|
||||
public var ?opponentModeAllowed:Bool;
|
||||
public var ?customValues:Dynamic;
|
||||
|
||||
public var ?metas:Map<String, ChartMetaData>;
|
||||
public var ?instSuffix:String;
|
||||
public var ?vocalsSuffix:String;
|
||||
|
||||
Reference in New Issue
Block a user