From d2a113907ac4cf8138d8ef44aeb2082966c2df76 Mon Sep 17 00:00:00 2001 From: Ralty <78720179+Raltyro@users.noreply.github.com> Date: Fri, 8 Aug 2025 00:07:49 +0700 Subject: [PATCH] instSuffix, meta.metas for freeplay preview --- source/funkin/backend/assets/Paths.hx | 12 ++-- source/funkin/backend/chart/Chart.hx | 37 ++++++++---- source/funkin/backend/chart/ChartData.hx | 2 + source/funkin/editors/charter/Charter.hx | 4 +- source/funkin/game/PlayState.hx | 2 +- source/funkin/menus/FreeplayState.hx | 72 +++++++++++++++++------- 6 files changed, 88 insertions(+), 41 deletions(-) diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx index 1891a78b..94c9f68f 100644 --- a/source/funkin/backend/assets/Paths.hx +++ b/source/funkin/backend/assets/Paths.hx @@ -88,18 +88,18 @@ class Paths inline static public function music(key:String, ?library:String, ?ext:String) return getPath('music/$key.${ext != null ? ext : Flags.SOUND_EXT}', library); - inline static public function voices(song:String, ?difficulty:String, ?prefix:String = "", ?ext:String) { + inline static public function voices(song:String, ?difficulty:String, ?suffix:String = "", ?ext:String) { if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; if (ext == null) ext = Flags.SOUND_EXT; - var diff = getPath('songs/$song/song/Voices$prefix-${difficulty}.${ext}', null); - return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Voices$prefix.${ext}', null); + var diff = getPath('songs/$song/song/Voices$suffix-${difficulty}.${ext}', null); + return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Voices$suffix.${ext}', null); } - inline static public function inst(song:String, ?difficulty:String, ?prefix:String = "", ?ext:String) { + inline static public function inst(song:String, ?difficulty:String, ?suffix:String = "", ?ext:String) { if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; if (ext == null) ext = Flags.SOUND_EXT; - var diff = getPath('songs/$song/song/Inst$prefix-${difficulty}.${ext}', null); - return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Inst$prefix.${ext}', null); + var diff = getPath('songs/$song/song/Inst$suffix-${difficulty}.${ext}', null); + return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Inst$suffix.${ext}', null); } static public function image(key:String, ?library:String, checkForAtlas:Bool = false, ?ext:String) { diff --git a/source/funkin/backend/chart/Chart.hx b/source/funkin/backend/chart/Chart.hx index 8fd9fa26..765a96f5 100644 --- a/source/funkin/backend/chart/Chart.hx +++ b/source/funkin/backend/chart/Chart.hx @@ -83,13 +83,13 @@ class Chart { return data; } - public static function loadChartMeta(songName:String, ?difficulty:String, fromMods:Bool = true):ChartMetaData { + public static function loadChartMeta(songName:String, ?difficulty:String, fromMods:Bool = true, includeMetaDifficulties:Bool = false):ChartMetaData { if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY; var metaPath = Paths.file('songs/${songName}/meta.json'); var metaDiffPath = Paths.file('songs/${songName}/meta-${difficulty}.json'); - var data:ChartMetaData = null; + var data:ChartMetaData = null, fromDifficulty = false; var fromMods:Bool = fromMods; for (path in [metaDiffPath, metaPath]) if (Assets.exists(path)) { fromMods = Paths.assetsTree.existsSpecific(path, "TEXT", MODS); @@ -98,23 +98,31 @@ class Chart { 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); - if (data != null) break; + if (data != null) { + fromDifficulty = path == metaDiffPath; + break; + } } - if (data == null) data = { - name: songName, - bpm: 100 - }; + if (data == null) { + data = { + name: songName, + bpm: Flags.DEFAULT_BPM + }; + } + else { + data.name = songName; + data.setFieldDefault("bpm", Flags.DEFAULT_BPM); + } - data.name = songName; - data.setFieldDefault("bpm", Flags.DEFAULT_BPM); + data.setFieldDefault("displayName", data.name); 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("displayName", data.name); + data.setFieldDefault("instSuffix", ""); 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)]; @@ -131,6 +139,12 @@ class Chart { } } + 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)); + } + return data; } @@ -304,7 +318,8 @@ class Chart { public static inline function makeMetaSaveable(meta:ChartMetaData, prettyPrint:Bool = true):String { var data:Dynamic = Reflect.copy(meta); - if (data.color != null) data.color = new FlxColor(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, 'metas'); return Json.stringify(data, null, prettyPrint ? Flags.JSON_PRETTY_PRINT : null); } } diff --git a/source/funkin/backend/chart/ChartData.hx b/source/funkin/backend/chart/ChartData.hx index a72e5ee4..16653fd9 100644 --- a/source/funkin/backend/chart/ChartData.hx +++ b/source/funkin/backend/chart/ChartData.hx @@ -28,6 +28,8 @@ typedef ChartMetaData = { public var ?coopAllowed:Bool; public var ?opponentModeAllowed:Bool; public var ?customValues:Dynamic; + public var ?metas:Map; + public var ?instSuffix:String; } typedef ChartStrumLine = { diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 127e9b88..9514ffc2 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -601,7 +601,7 @@ class Charter extends UIState { Conductor.setupSong(PlayState.SONG); noteTypes = PlayState.SONG.noteTypes; - FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff))); + FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff, PlayState.SONG.meta.instSuffix))); vocals = Assets.exists(Paths.voices(__song, __diff)) ? FlxG.sound.load(Paths.voices(__song, __diff)) : new FlxSound(); vocals.group = FlxG.sound.defaultMusicGroup; @@ -680,7 +680,7 @@ class Charter extends UIState { if (FlxG.sound.music.loaded) wavesToGenerate.push({name: "Inst.ogg", sound: FlxG.sound.music}); - if (PlayState.SONG.meta.needsVoices != false && vocals.loaded) + if (vocals.loaded) wavesToGenerate.push({name: "Voices.ogg", sound: vocals}); for (strumLine in strumLines) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index e633aced..b61b1f9e 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1113,7 +1113,7 @@ class PlayState extends MusicBeatState curSong = songData.meta.name.toLowerCase(); curSongID = curSong.replace(" ", "-"); - FlxG.sound.setMusic(inst = FlxG.sound.load(Assets.getMusic(Paths.inst(SONG.meta.name, difficulty)))); + FlxG.sound.setMusic(inst = FlxG.sound.load(Assets.getMusic(Paths.inst(SONG.meta.name, difficulty, SONG.meta.instSuffix)))); var vocalsPath = Paths.voices(SONG.meta.name, difficulty); vocals = Assets.exists(vocalsPath) ? FlxG.sound.load(Options.streamedVocals ? Assets.getMusic(vocalsPath) : vocalsPath) : new FlxSound(); diff --git a/source/funkin/menus/FreeplayState.hx b/source/funkin/menus/FreeplayState.hx index 42952043..4b4d2d2a 100644 --- a/source/funkin/menus/FreeplayState.hx +++ b/source/funkin/menus/FreeplayState.hx @@ -19,6 +19,16 @@ class FreeplayState extends MusicBeatState */ public var songs:Array = []; + /** + * Current song metadata + */ + public var curSong:Null; + + /** + * Current song metadata difficulty + */ + public var curSongDifficulty:Null; + /** * Currently selected song */ @@ -107,7 +117,9 @@ class FreeplayState extends MusicBeatState curSelected = k; } } - if (songs[curSelected] != null) { + updateCurSongMeta(); + + if (curSong != null) { for(k=>diff in songs[curSelected].difficulties) { if (diff == Options.freeplayLastDifficulty) { curDifficulty = k; @@ -238,14 +250,14 @@ class FreeplayState extends MusicBeatState scoreText.x = coopText.x = scoreBG.x + 4; diffText.x = Std.int(scoreBG.x + ((scoreBG.width - diffText.width) / 2)); - interpColor.fpsLerpTo(songs[curSelected].color, 0.0625); + interpColor.fpsLerpTo(curSong.color, 0.0625); bg.color = interpColor.color; #if PRELOAD_ALL var dontPlaySongThisFrame = false; autoplayElapsed += elapsed; if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay || FlxG.keys.justPressed.SPACE)) { - if (curPlayingInst != (curPlayingInst = Paths.inst(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty]))) { + if (curPlayingInst != (curPlayingInst = Paths.inst(curSong.name, curSongDifficulty, curSong.instSuffix))) { var streamed = false; if (Options.streamedMusic) { var sound = Assets.getMusic(curPlayingInst, true, false); @@ -253,7 +265,7 @@ class FreeplayState extends MusicBeatState if (streamed && autoplayShouldPlay) { FlxG.sound.playMusic(sound, 0); - Conductor.changeBPM(songs[curSelected].bpm, songs[curSelected].beatsPerMeasure, songs[curSelected].stepsPerBeat); + Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat); } } @@ -268,7 +280,7 @@ class FreeplayState extends MusicBeatState if (sound != null && autoplayShouldPlay) { FlxG.sound.playMusic(sound, 0); - Conductor.changeBPM(songs[curSelected].bpm, songs[curSelected].beatsPerMeasure, songs[curSelected].stepsPerBeat); + Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat); } } if (!disableAsyncLoading) Main.execAsync(huh); @@ -302,7 +314,6 @@ class FreeplayState extends MusicBeatState function updateCoopModes() { __opponentMode = false; __coopMode = false; - var curSong = songs[curSelected]; if (curSong.coopAllowed && curSong.opponentModeAllowed) { __opponentMode = curCoopMode % 2 == 1; __coopMode = curCoopMode >= 2; @@ -321,7 +332,7 @@ class FreeplayState extends MusicBeatState if (songs[curSelected].difficulties.length <= 0) return; - var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty], __opponentMode, __coopMode)); + var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(curSong.name, curSongDifficulty, __opponentMode, __coopMode)); if (event.cancelled) return; @@ -329,17 +340,17 @@ class FreeplayState extends MusicBeatState autoplayShouldPlay = false; #end - Options.freeplayLastSong = songs[curSelected].name; - Options.freeplayLastDifficulty = songs[curSelected].difficulties[curDifficulty]; + Options.freeplayLastSong = curSong.name; + Options.freeplayLastDifficulty = curSongDifficulty; PlayState.loadSong(event.song, event.difficulty, event.opponentMode, event.coopMode); FlxG.switchState(new PlayState()); } public function convertChart() { - trace('Converting ${songs[curSelected].name} (${songs[curSelected].difficulties[curDifficulty]}) to Codename format...'); - var chart = Chart.parse(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty]); - Chart.save('${Main.pathBack}assets/songs/${songs[curSelected].name}', chart, songs[curSelected].difficulties[curDifficulty]); + 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); } /** @@ -355,10 +366,13 @@ class FreeplayState extends MusicBeatState var validDifficulties = curSong.difficulties.length > 0; var event = event("onChangeDiff", EventManager.get(MenuChangeEvent).recycle(curDifficulty, validDifficulties ? FlxMath.wrap(curDifficulty + change, 0, curSong.difficulties.length-1) : 0, change)); - if (event.cancelled) return; + if (event.cancelled) { + updateCurSongMeta(); + return; + } curDifficulty = event.value; - + updateCurSongMeta(); updateScore(); if (curSong.difficulties.length > 1) @@ -376,7 +390,7 @@ class FreeplayState extends MusicBeatState var changes:Array = []; if (__coopMode) changes.push(CCoopMode); if (__opponentMode) changes.push(COpponentMode); - var saveData = FunkinSave.getSongHighscore(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty], changes); + var saveData = FunkinSave.getSongHighscore(curSong.name, curSongDifficulty, changes); intendedScore = saveData.score; } @@ -397,9 +411,9 @@ class FreeplayState extends MusicBeatState */ public function changeCoopMode(change:Int = 0, force:Bool = false) { if (change == 0 && !force) return; - if (!songs[curSelected].coopAllowed && !songs[curSelected].opponentModeAllowed) return; + if (!curSong.coopAllowed && !curSong.opponentModeAllowed) return; - var bothEnabled = songs[curSelected].coopAllowed && songs[curSelected].opponentModeAllowed; + var bothEnabled = curSong.coopAllowed && curSong.opponentModeAllowed; var event = event("onChangeCoopMode", EventManager.get(MenuChangeEvent).recycle(curCoopMode, FlxMath.wrap(curCoopMode + change, 0, bothEnabled ? 3 : 1), change)); if (event.cancelled) return; @@ -408,12 +422,19 @@ class FreeplayState extends MusicBeatState updateScore(); + #if PRELOAD_ALL + if (songs[curSelected] != curSong) { + autoplayElapsed = 0; + songInstPlaying = false; + } + #end + var key = "[TAB] "; // TODO: make this configurable if (bothEnabled) { coopText.text = key + coopLabels[curCoopMode]; } else { - coopText.text = key + coopLabels[curCoopMode * (songs[curSelected].coopAllowed ? 2 : 1)]; + coopText.text = key + coopLabels[curCoopMode * (curSong.coopAllowed ? 2 : 1)]; } } @@ -426,7 +447,6 @@ class FreeplayState extends MusicBeatState { if (change == 0 && !force) return; - var bothEnabled = songs[curSelected].coopAllowed && songs[curSelected].opponentModeAllowed; var event = event("onChangeSelection", EventManager.get(MenuChangeEvent).recycle(curSelected, FlxMath.wrap(curSelected + change, 0, songs.length-1), change)); if (event.cancelled) return; @@ -440,7 +460,7 @@ class FreeplayState extends MusicBeatState songInstPlaying = false; #end - coopText.visible = songs[curSelected].coopAllowed || songs[curSelected].opponentModeAllowed; + coopText.visible = curSong.coopAllowed || curSong.opponentModeAllowed; } function updateOptionsAlpha() { @@ -465,6 +485,16 @@ class FreeplayState extends MusicBeatState item.alpha = selectedAlpha; } } + + function updateCurSongMeta() { + var song = songs[curSelected]; + if (song == null) { + curSong = null; + curSongDifficulty = null; + } + else if ((curSong = song.metas.get(curSongDifficulty = curSong.difficulties[curDifficulty])) == null) + curSong = song; + } } class FreeplaySonglist { @@ -485,7 +515,7 @@ class FreeplaySonglist { } if (songsFound == null) songsFound = Paths.getFolderDirectories("songs", false, source); if (songsFound.length > 0) { - for (s in songsFound) songs.push(Chart.loadChartMeta(s, Flags.DEFAULT_DIFFICULTY, source == MODS)); + for (s in songsFound) songs.push(Chart.loadChartMeta(s, source == MODS)); return false; } return true;