diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx index 48cc8f4f..5b8d8395 100644 --- a/source/funkin/backend/assets/Paths.hx +++ b/source/funkin/backend/assets/Paths.hx @@ -128,11 +128,11 @@ class Paths return scriptPath; } - static public function chart(song:String, ?difficulty:String):String + static public function chart(song:String, ?difficulty:String, ?variant:String):String { difficulty = (difficulty != null ? difficulty : Flags.DEFAULT_DIFFICULTY); - return getPath('songs/$song/charts/$difficulty.json', null); + return getPath('songs/$song/charts/${variant != null ? variant + "/" : ""}$difficulty.json', null); } public static function character(character:String):String { diff --git a/source/funkin/backend/chart/Chart.hx b/source/funkin/backend/chart/Chart.hx index 263b16d3..82d775b3 100644 --- a/source/funkin/backend/chart/Chart.hx +++ b/source/funkin/backend/chart/Chart.hx @@ -139,7 +139,8 @@ class Chart { } } - if (includeMetaVariations & data.variants.length > 0) for (variant in data.variants) { + data.metas = []; + 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)); } @@ -147,8 +148,10 @@ class Chart { return data; } - public static function parse(songName:String, ?variant:String):ChartData { - var chartPath = Paths.chart(songName, variant); + public static function parse(songName:String, ?difficulty:String, ?variant:String):ChartData { + if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; + + var chartPath = Paths.chart(songName, difficulty, variant); var base:ChartData = { strumLines: [], noteTypes: [], @@ -162,7 +165,7 @@ class Chart { fromMods: Paths.assetsTree.existsSpecific(chartPath, "TEXT", MODS) }; - var valid:Bool = true, namePrint = (variant == null || variant == '') ? '$songName' : '$songName ($variant)'; + var valid:Bool = true, namePrint = '$songName $difficulty' + ((variant != null && variant != '') ? ' ($variant)' : ''); if (!Assets.exists(chartPath)) { Logs.error('Chart for song $namePrint at "$chartPath" was not found.'); valid = false; @@ -256,22 +259,33 @@ class Chart { if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; if (saveSettings == null) saveSettings = {}; - var filteredChart = filterChartForSaving(chart, saveSettings.saveMetaInChart, saveSettings.saveLocalEvents, saveSettings.saveGlobalEvents); + var filteredChart = filterChartForSaving(chart, saveSettings.saveMetaInChart, saveSettings.saveLocalEvents, saveSettings.saveGlobalEvents && saveSettings.seperateGlobalEvents != true); #if sys - var songPath = saveSettings.songFolder == null ? 'assets/songs/${chart.meta.name}' : saveSettings.songFolder; + var prefix = 'assets/songs/', assetsRoot = Paths.getAssetsRoot() + '/'; + var songPath = saveSettings.songFolder == null ? '${chart.meta.name}' : saveSettings.songFolder, prettyPrint = saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null; var metaPath = '$songPath/meta.json', temp:String; - if ((temp = Paths.assetsTree.getPath(metaPath)) != null) metaPath = temp; + if ((temp = Paths.assetsTree.getPath(prefix + metaPath)) != null) metaPath = temp; + else metaPath = assetsRoot + metaPath; 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; + if ((temp = Paths.assetsTree.getPath(prefix + chartPath)) != null) chartPath = temp; + else chartPath = assetsRoot + chartPath; 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, prettyPrint)); - if (filteredChart.meta != null && (saveSettings.overrideExistingMeta || !FileSystem.exists(metaPath))) - CoolUtil.safeSaveFile(metaPath, makeMetaSaveable(filteredChart.meta)); + if (saveSettings.overrideExistingMeta || !FileSystem.exists(metaPath)) + CoolUtil.safeSaveFile(metaPath, Json.stringify(filterMetaForSaving(chart.meta), null, prettyPrint)); + + if (saveSettings.seperateGlobalEvents == true) { + var eventsPath = '$songPath/events.json'; + if ((temp = Paths.assetsTree.getPath(prefix + eventsPath)) != null) eventsPath = temp; + else eventsPath = assetsRoot + eventsPath; + + CoolUtil.safeSaveFile(eventsPath, Json.stringify({events: filterEventsForSaving(chart.events, false, true)}, null, prettyPrint)); + } #end return filteredChart; @@ -304,10 +318,10 @@ class Chart { var data = []; if (!saveLocalEvents && !saveGlobalEvents) return data; - for (event in chart.events) if ((saveLocalEvents && event.global != true) || (saveGlobalEvents && event.global == true)) { + for (event in 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); + data.push(copy); } return data; diff --git a/source/funkin/backend/scripting/events/menu/freeplay/FreeplaySongSelectEvent.hx b/source/funkin/backend/scripting/events/menu/freeplay/FreeplaySongSelectEvent.hx index e2ee1fc7..1445c6a9 100644 --- a/source/funkin/backend/scripting/events/menu/freeplay/FreeplaySongSelectEvent.hx +++ b/source/funkin/backend/scripting/events/menu/freeplay/FreeplaySongSelectEvent.hx @@ -9,6 +9,10 @@ final class FreeplaySongSelectEvent extends CancellableEvent { * Difficulty name */ public var difficulty:String; + /** + * Variation Name + */ + public var variant:String; /** * Whenever opponent mode is enabled or not. */ diff --git a/source/funkin/backend/system/macros/FunkinSaveMacro.hx b/source/funkin/backend/system/macros/FunkinSaveMacro.hx index ff96262d..8fc0e431 100644 --- a/source/funkin/backend/system/macros/FunkinSaveMacro.hx +++ b/source/funkin/backend/system/macros/FunkinSaveMacro.hx @@ -43,8 +43,6 @@ class FunkinSaveMacro { * SAVE FUNCTION */ var saveFuncBlocks:Array = [for(f in fieldNames) macro $i{saveFieldName}.data.$f = $i{f}]; - - saveFuncBlocks.push(macro $i{"__flush"}()); saveFuncBlocks.push(macro $i{saveFieldName}.flush()); fields.push({ @@ -60,6 +58,8 @@ class FunkinSaveMacro { access: [APublic, AStatic] }); + var loadFuncBlocks:Array = [for(f in fieldNames) macro if ($i{saveFieldName}.data.$f != null) $i{f} = $i{saveFieldName}.data.$f]; + /** * LOAD FUNCTION */ @@ -70,10 +70,7 @@ class FunkinSaveMacro { args: [], expr: { pos: Context.currentPos(), - expr: EBlock([for(f in fieldNames) - macro if ($i{saveFieldName}.data.$f != null) - $i{f} = $i{saveFieldName}.data.$f - , macro $i{"__load"}()]) + expr: EBlock(loadFuncBlocks) } }), access: [APublic, AStatic] diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 48163b78..284cb8e6 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -28,6 +28,7 @@ import flixel.util.FlxColor; class Charter extends UIState { public static var __song:String; static var __diff:String; + static var __variant:String; static var __reload:Bool; var chart(get, never):ChartData; @@ -114,11 +115,12 @@ class Charter extends UIState { private var SONGPOSINFO_BPM = TU.getRaw("songPosInfo.bpm"); private var SONGPOSINFO_TIMESIGNATURE = TU.getRaw("songPosInfo.timeSignature"); - public function new(song:String, diff:String, reload:Bool = true) { + public function new(song:String, diff:String, variant:String, reload:Bool = true) { super(); if (song != null) { __song = song; __diff = diff; + __variant = variant; __reload = reload; } } @@ -578,7 +580,7 @@ class Charter extends UIState { dataDisplay.screenCenter(Y); dataDisplay.cameras = [charterCamera]; dataDisplay.x = -dataDisplay.width; add(dataDisplay);*/ - DiscordUtil.call("onEditorLoaded", ["Chart Editor", __song + " (" + __diff + ")"]); + DiscordUtil.call("onEditorLoaded", ["Chart Editor", __song + " (" + __diff + ")" + (__variant != null && __variant != "" ? " (" + __variant + ")" : "")]); } override function destroy() { @@ -595,7 +597,7 @@ class Charter extends UIState { public function loadSong() { if (__reload) { EventsData.reloadEvents(); - PlayState.loadSong(__song, __diff, false, false); + PlayState.loadSong(__song, __diff, __variant, false, false); __resetStatics(); } Conductor.setupSong(PlayState.SONG); @@ -788,12 +790,13 @@ class Charter extends UIState { autoSaveTimer = Options.charterAutoSaveTime; if (!autoSaveNotif.cancelled) { buildChart(); - var songPath:String = '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}'; if (Options.charterAutoSavesSeparateFolder) - Chart.save(songPath, PlayState.SONG, __autoSaveLocation, {saveMetaInChart: true, saveLocalEvents: true, saveGlobalEvents: true, folder: "autosaves", prettyPrint: Options.editorCharterPrettyPrint}); - else // These two chart saves are particular, to avoid any kind of loss, stuff like meta, global and local events will be save all together - Nex - Chart.save(songPath, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: true, saveLocalEvents: true, saveGlobalEvents: true, prettyPrint: Options.editorCharterPrettyPrint}); + Chart.save(PlayState.SONG, __diff.toLowerCase(), __autoSaveLocation, {saveMetaInChart: true, saveLocalEvents: true, seperateGlobalEvents: true, folder: 'autosaves', prettyPrint: Options.editorCharterPrettyPrint}); + else + Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveMetaInChart: true, saveLocalEvents: true, seperateGlobalEvents: true, prettyPrint: Options.editorCharterPrettyPrint}); + + FlxG.sound.play(Paths.sound('editors/save')); undos.save(); } autoSaveNotif.cancelled = false; @@ -1476,8 +1479,8 @@ class Charter extends UIState { public static function saveChart(shouldBuild:Bool = true, withEvents:Bool = true) { #if sys - FlxG.sound.play(Paths.sound('editors/save')); - saveTo('${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}', !withEvents, shouldBuild); + if (shouldBuild && instance != null) instance.buildChart(); + Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveMetaInChart: false, saveLocalEvents: withEvents, prettyPrint: Options.editorCharterPrettyPrint}); if (undos != null) undos.save(); #else saveChartAs(shouldBuild, withEvents); @@ -1493,13 +1496,9 @@ class Charter extends UIState { public static function saveEvents(shouldBuild:Bool = true) { #if sys - FlxG.sound.play(Paths.sound('editors/save')); if (shouldBuild && instance != null) instance.buildChart(); - var data = {events: Chart.filterChartForSaving(PlayState.SONG, false, false, true).events}; - - var path = '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/events.json'; - if (data.events != null && data.events.length > 0) CoolUtil.safeSaveFile(path, Json.stringify(data, null, Options.editorCharterPrettyPrint ? Flags.JSON_PRETTY_PRINT : null)); - else if (FileSystem.exists(path)) FileSystem.deleteFile(path); // Instead of replacing with a useless empty file, deletes the file directly - Nex + Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveChart: false, seperateGlobalEvents: true, prettyPrint: Options.editorCharterPrettyPrint}); + if (undos != null) undos.save(); #else saveEventsAs(shouldBuild); #end @@ -1507,7 +1506,7 @@ class Charter extends UIState { public static function saveEventsAs(shouldBuild:Bool = true) { if (shouldBuild && instance != null) instance.buildChart(); - var data = {events: Chart.filterChartForSaving(PlayState.SONG, false, false, true).events}; + var data = {events: Chart.filterEventsForSaving(PlayState.SONG.events, false, true)}; saveAs(data, null, Options.editorCharterPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, { defaultSaveFile: 'events.json' @@ -1516,12 +1515,8 @@ class Charter extends UIState { public static function saveMeta(shouldBuild:Bool = true) { #if sys - FlxG.sound.play(Paths.sound('editors/save')); if (shouldBuild && instance != null) instance.buildChart(); - CoolUtil.safeSaveFile( - '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/meta.json', - Json.stringify(PlayState.SONG.meta == null ? {} : Chart.filterChartForSaving(PlayState.SONG, true, false, false).meta, null, Flags.JSON_PRETTY_PRINT) - ); + Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveChart: false, overrideExistingMeta: true, prettyPrint: true}); #else saveMetaAs(shouldBuild); #end @@ -1545,9 +1540,7 @@ class Charter extends UIState { }, null, shouldBuild); } - public static function saveAs(data:Dynamic, ?replacer:(key:Dynamic, value:Dynamic) -> Dynamic, ?space:String, ?options:SaveSubstate.SaveSubstateData, ?saveOptions:Map, shouldBuild:Bool = true) { - FlxG.sound.play(Paths.sound('editors/save')); - if (shouldBuild && instance != null) instance.buildChart(); + public static function saveAs(data:Dynamic, ?replacer:(key:Dynamic, value:Dynamic) -> Dynamic, ?space:String, ?options:SaveSubstate.SaveSubstateData, ?saveOptions:Map, shouldBuild:Bool = true) { if (shouldBuild && instance != null) instance.buildChart(); var cur = FlxG.state; while(true) { if (instance != null || cur.subState == null) return cur.openSubState(new SaveSubstate(Json.stringify(data, replacer, space), options, saveOptions)); @@ -1558,7 +1551,7 @@ class Charter extends UIState { #if sys public static function saveTo(path:String, separateEvents:Bool = false, shouldBuild:Bool = true) { if (shouldBuild && instance != null) instance.buildChart(); - Chart.save(path, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: false, saveLocalEvents: !separateEvents, prettyPrint: Options.editorCharterPrettyPrint}); + Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveMetaInChart: false, saveLocalEvents: !separateEvents, songFolder: path, prettyPrint: Options.editorCharterPrettyPrint}); } #end #end @@ -1570,17 +1563,17 @@ class Charter extends UIState { else {undos = null; FlxG.switchState(new CharterSelection()); Charter.instance.__clearStatics();} } - function _file_save_all(_) saveEverything(); - function _file_save(_) saveChart(); - function _file_saveas(_) saveChartAs(); - function _file_events_save(_) saveEvents(); - function _file_events_saveas(_) saveEventsAs(); - function _file_save_no_events(_) saveChart(true, false); - function _file_saveas_no_events(_) saveChartAs(true, false); - function _file_meta_save(_) saveMeta(); - function _file_meta_saveas(_) saveMetaAs(); - function _file_saveas_fnflegacy(_) saveLegacyChartAs(); - function _file_saveas_psych(_) savePsychChartAs(); + function _file_save_all(_) {saveEverything(); FlxG.sound.play(Paths.sound('editors/save'));} + function _file_save(_) {saveChart(); FlxG.sound.play(Paths.sound('editors/save'));} + function _file_saveas(_) {saveChartAs(); FlxG.sound.play(Paths.sound('editors/save'));} + function _file_events_save(_) {saveEvents(); FlxG.sound.play(Paths.sound('editors/save'));} + function _file_events_saveas(_) {saveEventsAs(); FlxG.sound.play(Paths.sound('editors/save'));} + function _file_save_no_events(_) {saveChart(true, false); FlxG.sound.play(Paths.sound('editors/save'));} + function _file_saveas_no_events(_) {saveChartAs(true, false); FlxG.sound.play(Paths.sound('editors/save'));} + function _file_meta_save(_) {saveMeta(); FlxG.sound.play(Paths.sound('editors/save'));} + function _file_meta_saveas(_) {saveMetaAs(); FlxG.sound.play(Paths.sound('editors/save'));} + function _file_saveas_fnflegacy(_) {saveLegacyChartAs(); FlxG.sound.play(Paths.sound('editors/save'));} + function _file_saveas_psych(_) {savePsychChartAs(); FlxG.sound.play(Paths.sound('editors/save'));} function _edit_copy(_, playSFX=true) { if (playSFX) FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_COPY_SOUND)); diff --git a/source/funkin/editors/charter/CharterSelection.hx b/source/funkin/editors/charter/CharterSelection.hx index 85776f44..ce105f42 100644 --- a/source/funkin/editors/charter/CharterSelection.hx +++ b/source/funkin/editors/charter/CharterSelection.hx @@ -26,7 +26,7 @@ class CharterSelectionScreen extends EditorTreeMenuScreen { public var curSong:ChartMetaData; inline public function makeChartOption(d:String, name:String):TextOption { - return new TextOption(d, getID('acceptDifficulty'), () -> FlxG.switchState(new Charter(name, d))); + return new TextOption(d, getID('acceptDifficulty'), () -> FlxG.switchState(new Charter(name, d, null))); } public function makeSongOption(s:ChartMetaData):IconOption { diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 1457d4ce..378a5d9b 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -65,6 +65,10 @@ class PlayState extends MusicBeatState * The selected difficulty name. */ public static var difficulty:String = Flags.DEFAULT_DIFFICULTY; + /** + * The selected variation name. (It can be null) + */ + public static var variation:Null; /** * Whenever the week is coming from the mods folder or not. */ @@ -657,7 +661,7 @@ class PlayState extends MusicBeatState persistentDraw = true; if (SONG == null) - SONG = Chart.parse('tutorial', 'normal'); + SONG = Chart.parse('tutorial', difficulty = 'normal', variation = null); scrollSpeed = SONG.scrollSpeed; @@ -1352,7 +1356,7 @@ class PlayState extends MusicBeatState updateRatingStuff(); if (canAccessDebugMenus && chartingMode && controls.DEV_ACCESS) - FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, false)); + FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, variation, false)); if (Options.camZoomOnBeat && camZooming) { var beat = Conductor.getBeats(camZoomingEvery, camZoomingInterval, camZoomingOffset); @@ -1697,7 +1701,7 @@ class PlayState extends MusicBeatState if (validScore) { #if !switch - FunkinSave.setSongHighscore(SONG.meta.name, difficulty, { + FunkinSave.setSongHighscore(SONG.meta.name, difficulty, variation, { score: songScore, misses: misses, accuracy: accuracy, @@ -1748,16 +1752,16 @@ class PlayState extends MusicBeatState FlxG.save.flush(); } else { - Logs.infos('Loading next song (${storyPlaylist[0].toLowerCase()}/$difficulty)', "PlayState"); + Logs.infos('Loading next song (${storyPlaylist[0].toLowerCase()}/$difficulty/$variation)', "PlayState"); registerSmoothTransition(); - __loadSong(storyPlaylist[0], difficulty); + __loadSong(storyPlaylist[0], difficulty, variation); FlxG.switchState(new PlayState()); } } else if (chartingMode) - FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, false)); + FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, variation, false)); else FlxG.switchState(new FreeplayState()); } @@ -2128,7 +2132,7 @@ class PlayState extends MusicBeatState * @param weekData Week Data * @param difficulty Week Difficulty */ - public static function loadWeek(weekData:WeekData, ?difficulty:String) { + public static function loadWeek(weekData:WeekData, ?difficulty:String, ?variation:String) { if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; storyWeek = weekData; storyPlaylist = [for (e in weekData.songs) e.name]; @@ -2139,7 +2143,7 @@ class PlayState extends MusicBeatState campaignAccuracyCount = 0; chartingMode = false; opponentMode = coopMode = false; - __loadSong(storyPlaylist[0], difficulty); + __loadSong(storyPlaylist[0], difficulty, variation); } /** @@ -2149,13 +2153,13 @@ class PlayState extends MusicBeatState * @param opponentMode Whenever opponent mode is on * @param coopMode Whenever co-op mode is on. */ - public static function loadSong(_name:String, ?_difficulty:String, _opponentMode:Bool = false, _coopMode:Bool = false) { + public static function loadSong(_name:String, ?_difficulty:String, ?_variation:String, _opponentMode:Bool = false, _coopMode:Bool = false) { if (_difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; isStoryMode = false; opponentMode = _opponentMode; chartingMode = false; coopMode = _coopMode; - __loadSong(_name, _difficulty); + __loadSong(_name, _difficulty, _variation); } /** @@ -2163,12 +2167,13 @@ class PlayState extends MusicBeatState * @param name Song name * @param difficulty Song difficulty */ - public static function __loadSong(_name:String, _difficulty:String) { + public static function __loadSong(_name:String, _difficulty:String, _variation:String) { difficulty = _difficulty; + variation = _variation; seenCutscene = false; deathCounter = 0; - SONG = Chart.parse(_name, _difficulty); + SONG = Chart.parse(_name, _difficulty, _variation); fromMods = SONG.fromMods; } } diff --git a/source/funkin/menus/FreeplayState.hx b/source/funkin/menus/FreeplayState.hx index e02c34aa..1d7cadf6 100644 --- a/source/funkin/menus/FreeplayState.hx +++ b/source/funkin/menus/FreeplayState.hx @@ -25,9 +25,14 @@ class FreeplayState extends MusicBeatState public var curSong:Null; /** - * Current song metadata difficulty + * Current song difficulties */ - public var curSongDifficulty:Null; + public var curDifficulties:Array; + + /** + * songs[curSelected].metas.get(curDiffMetaIndices[curDifficulty]) + */ + public var curDiffMetaKeys:Array = []; /** * Currently selected song @@ -117,10 +122,12 @@ class FreeplayState extends MusicBeatState curSelected = k; } } - updateCurSongMeta(); + + updateCurDifficulties(); + updateCurSong(); if (curSong != null) { - for(k=>diff in songs[curSelected].difficulties) { + for(k=>diff in curDifficulties) { if (diff == Options.freeplayLastDifficulty) { curDifficulty = k; } @@ -257,7 +264,7 @@ class FreeplayState extends MusicBeatState var dontPlaySongThisFrame = false; autoplayElapsed += elapsed; if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay)) { - if (curPlayingInst != (curPlayingInst = Paths.inst(curSong.name, curSongDifficulty, curSong.instSuffix))) { + if (curPlayingInst != (curPlayingInst = Paths.inst(curSong.name, curDifficulties[curDifficulty], curSong.instSuffix))) { var streamed = false; if (Options.streamedMusic) { var sound = Assets.getMusic(curPlayingInst, true, false); @@ -330,9 +337,9 @@ class FreeplayState extends MusicBeatState public function select() { updateCoopModes(); - if (songs[curSelected].difficulties.length <= 0) return; + if (curDifficulties.length == 0) return; - var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(curSong.name, curSongDifficulty, __opponentMode, __coopMode)); + var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(curSong.name, curDifficulties[curDifficulty], curSong.variant, __opponentMode, __coopMode)); if (event.cancelled) return; @@ -341,16 +348,17 @@ class FreeplayState extends MusicBeatState #end Options.freeplayLastSong = curSong.name; - Options.freeplayLastDifficulty = curSongDifficulty; + Options.freeplayLastDifficulty = curDifficulties[curDifficulty]; + Options.freeplayLastVariation = curSong.variant; - PlayState.loadSong(event.song, event.difficulty, event.opponentMode, event.coopMode); + PlayState.loadSong(event.song, event.difficulty, event.variant, event.opponentMode, event.coopMode); FlxG.switchState(new PlayState()); } public function convertChart() { - trace('Converting ${curSong.name} (${curSongDifficulty}) to Codename format...'); - var chart = Chart.parse(curSong.name, curSongDifficulty); - Chart.save('${Main.pathBack}assets/songs/${curSong.name}', chart, curSongDifficulty); + trace('Converting ${curSong.name} ${curDifficulties[curDifficulty]} ${curSong.variant} to Codename format...'); + var chart = Chart.parse(curSong.name, curDifficulties[curDifficulty], curSong.variant); + Chart.save(chart, curDifficulties[curDifficulty], curSong.variant); } /** @@ -358,39 +366,35 @@ class FreeplayState extends MusicBeatState * @param change How much to change. * @param force Force the change if `change` is equal to 0 */ - public function changeDiff(change:Int = 0, force:Bool = false) - { + public function changeDiff(change:Int = 0, force:Bool = false) { if (change == 0 && !force) return; - var song = songs[curSelected]; - var validDifficulties = song.difficulties.length > 0; - var event = event("onChangeDiff", EventManager.get(MenuChangeEvent).recycle(curDifficulty, validDifficulties ? FlxMath.wrap(curDifficulty + change, 0, song.difficulties.length-1) : 0, change)); + var validDifficulties = curDifficulties.length > 0; + var event = event("onChangeDiff", EventManager.get(MenuChangeEvent).recycle(curDifficulty, validDifficulties ? FlxMath.wrap(curDifficulty + change, 0, curDifficulties.length-1) : 0, change)); if (event.cancelled) { - if (force) updateCurSongMeta(); + if (force) updateCurSong(); return; } - var prevInstSuffix = curSong.instSuffix; + var prevSong = curSong; curDifficulty = event.value; - updateCurSongMeta(); + updateCurSong(); updateScore(); #if PRELOAD_ALL - if (curSong.instSuffix != prevInstSuffix) { + if (curSong != prevSong) { autoplayElapsed = 0; songInstPlaying = false; } #end - if (song.difficulties.length > 1) - diffText.text = '< ${song.difficulties[curDifficulty].toUpperCase()} >'; - else - diffText.text = validDifficulties ? song.difficulties[curDifficulty].toUpperCase() : "-"; + var text = validDifficulties ? curDifficulties[curDifficulty].toUpperCase() + (curSong != songs[curSelected] ? ' (${curSong.variant.toUpperCase()})' : '') : '-'; + diffText.text = curDifficulties.length > 1 ? '< $text >' : text; } function updateScore() { - if (songs[curSelected].difficulties.length <= 0) { + if (curDifficulties.length == 0) { intendedScore = 0; return; } @@ -398,7 +402,7 @@ class FreeplayState extends MusicBeatState var changes:Array = []; if (__coopMode) changes.push(CCoopMode); if (__opponentMode) changes.push(COpponentMode); - var saveData = FunkinSave.getSongHighscore(curSong.name, curSongDifficulty, changes); + var saveData = FunkinSave.getSongHighscore(curSong.name, curDifficulties[curDifficulty], curSong.variant, changes); intendedScore = saveData.score; } @@ -448,8 +452,7 @@ class FreeplayState extends MusicBeatState * @param change How much to change * @param force Force the change, even if `change` is equal to 0. */ - public function changeSelection(change:Int = 0, force:Bool = false) - { + public function changeSelection(change:Int = 0, force:Bool = false) { if (change == 0 && !force) return; var event = event("onChangeSelection", EventManager.get(MenuChangeEvent).recycle(curSelected, FlxMath.wrap(curSelected + change, 0, songs.length-1), change)); @@ -458,6 +461,7 @@ class FreeplayState extends MusicBeatState curSelected = event.value; if (event.playMenuSFX) CoolUtil.playMenuSFX(SCROLL, 0.7); + updateCurDifficulties(); changeDiff(0, true); #if PRELOAD_ALL @@ -491,13 +495,20 @@ class FreeplayState extends MusicBeatState } } - function updateCurSongMeta() { - var song = songs[curSelected]; - if (song == null) { - curSong = null; - curSongDifficulty = null; + function updateCurDifficulties() { + curDiffMetaKeys.resize(0); + curDifficulties = songs[curSelected].difficulties.copy(); + for (i in 0...curDifficulties.length) curDiffMetaKeys.push(null); + for (variant => meta in songs[curSelected].metas) { + curDifficulties = curDifficulties.concat(meta.difficulties); + for (i in 0...meta.difficulties.length) curDiffMetaKeys.push(variant); } - else if ((curSong = song.metas.get(curSongDifficulty = song.difficulties[curDifficulty])) == null) + } + + function updateCurSong() { + var song = songs[curSelected]; + if (song == null) curSong = null; + else if ((curSong = song.metas.get(curDiffMetaKeys[curDifficulty])) == null) curSong = song; } } diff --git a/source/funkin/menus/PauseSubState.hx b/source/funkin/menus/PauseSubState.hx index f790c1eb..d06cf013 100644 --- a/source/funkin/menus/PauseSubState.hx +++ b/source/funkin/menus/PauseSubState.hx @@ -167,7 +167,7 @@ class PauseSubState extends MusicBeatSubstate case "Change Options": FlxG.switchState(new OptionsMenu((_) -> FlxG.switchState(new PlayState()))); case "Exit to charter": - FlxG.switchState(new Charter(PlayState.SONG.meta.name, PlayState.difficulty, false)); + FlxG.switchState(new Charter(PlayState.SONG.meta.name, PlayState.difficulty, PlayState.variation, false)); case "Exit to menu": if (PlayState.chartingMode && Charter.undos.unsaved) game.saveWarn(false); diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx index c6c61a41..ac5c87ac 100644 --- a/source/funkin/options/Options.hx +++ b/source/funkin/options/Options.hx @@ -73,6 +73,7 @@ class Options */ public static var freeplayLastSong:String = null; public static var freeplayLastDifficulty:String = "normal"; + public static var freeplayLastVariation:String = null; public static var contributors:Array = []; public static var mainDevs:Array = []; // IDs public static var lastUpdated:Null; diff --git a/source/funkin/savedata/FunkinSave.hx b/source/funkin/savedata/FunkinSave.hx index 062ccfde..851b5c1f 100644 --- a/source/funkin/savedata/FunkinSave.hx +++ b/source/funkin/savedata/FunkinSave.hx @@ -11,14 +11,14 @@ import haxe.Unserializer; * Just add your save variables the way you would do in the Options.hx file. * The macro will automatically generate the `flush` and `load` functions. */ -@:build(funkin.backend.system.macros.FunkinSaveMacro.build("save", "flush", "load")) +@:build(funkin.backend.system.macros.FunkinSaveMacro.build("save", "__flush", "__load")) class FunkinSave { + @:doNotSave public static var highscores:Map = []; + /** * ONLY OPEN IF YOU WANT TO EDIT FUNCTIONS RELATED TO SAVING, LOADING OR HIGHSCORES. */ #if REGION - @:doNotSave public static var highscores:Map = []; - @:dox(hide) @:doNotSave private static var __eventAdded = false; @:doNotSave public static var save:FlxSave; @@ -45,7 +45,8 @@ class FunkinSave { } } - private static function __load() { + public static function load() { + __load(); if (save.data.highscores != null) { var temp; for (entryData in Reflect.fields(save.data.highscores)) if ((temp = __getHighscoreEntry(entryData)) != null) @@ -53,7 +54,8 @@ class FunkinSave { } } - private static function __flush() { + public static function flush() { + __flush(); if (save.data.highscores == null) save.data.highscores = {}; for (entry => score in highscores) Reflect.setField(save.data.highscores, __formatHighscoreEntry(entry), score); } @@ -62,7 +64,7 @@ class FunkinSave { try { var d = Unserializer.run(data); if (d.song is String) - return HSongEntry(d.song, d.diff, d.changes); + return HSongEntry(d.song, d.diff, d.variation, d.changes); else if (d.week is String) return HWeekEntry(d.week, d.diff); } @@ -72,10 +74,10 @@ class FunkinSave { static function __formatHighscoreEntry(entry:HighscoreEntry):String { switch (entry) { - case HWeekEntry(weekName:String, difficulty:String): + case HWeekEntry(weekName, difficulty): return Serializer.run({week: weekName, diff: difficulty}); - case HSongEntry(songName:String, difficulty:String, variation:Null, changes:Array): - var d = { + case HSongEntry(songName, difficulty, variation, changes): + var d:Dynamic = { song: songName, diff: difficulty, changes: changes @@ -92,14 +94,14 @@ class FunkinSave { * @param diff Song difficulty * @param changes Changes made to that song in freeplay. */ - public static inline function getSongHighscore(name:String, diff:String, ?changes:Array) { + public static inline function getSongHighscore(name:String, diff:String, ?variation:String, ?changes:Array) { if (changes == null) changes = []; - return safeGetHighscore(HSongEntry(name.toLowerCase(), diff.toLowerCase(), changes)); + return safeGetHighscore(HSongEntry(name.toLowerCase(), diff.toLowerCase(), variation, changes)); } - public static inline function setSongHighscore(name:String, diff:String, highscore:SongScore, ?changes:Array) { + public static inline function setSongHighscore(name:String, diff:String, ?variation:String, highscore:SongScore, ?changes:Array) { if (changes == null) changes = []; - if (safeRegisterHighscore(HSongEntry(name.toLowerCase(), diff.toLowerCase(), changes), highscore)) { + if (safeRegisterHighscore(HSongEntry(name.toLowerCase(), diff.toLowerCase(), variation, changes), highscore)) { flush(); return true; }