From 2b4bf42ac4c55ec3524be7d0109e6814e241bf6a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 3 Apr 2024 01:40:08 -0400 Subject: [PATCH 1/2] Fix multiple music, and crashes in freeplay --- source/funkin/audio/FunkinSound.hx | 20 +++++++------------- source/funkin/ui/freeplay/FreeplayState.hx | 9 +++++++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 100cee262..8c1bf3b41 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -321,6 +321,13 @@ class FunkinSound extends FlxSound implements ICloneable } } + if (FlxG.sound.music != null) + { + FlxG.sound.music.fadeTween?.cancel(); + FlxG.sound.music.stop(); + FlxG.sound.music.kill(); + } + if (params?.mapTimeChanges ?? true) { var songMusicData:Null = SongRegistry.instance.parseMusicData(key); @@ -335,19 +342,6 @@ class FunkinSound extends FlxSound implements ICloneable } } - if (FlxG.sound.music != null) - { - FlxG.sound.music.fadeTween?.cancel(); - FlxG.sound.music.stop(); - FlxG.sound.music.kill(); - } - - // Apparently HaxeFlixel isn't null safe. - @:nullSafety(Off) - { - FlxG.sound.music = FunkinSound.load(Paths.music('$key/$key'), params?.startingVolume ?? 1.0, true, false, true); - } - var music = FunkinSound.load(Paths.music('$key/$key'), params?.startingVolume ?? 1.0, params.loop ?? true, false, true); if (music != null) { diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 531167a95..455805479 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -134,7 +134,7 @@ class FreeplayState extends MusicBeatSubState var stickerSubState:StickerSubState; public static var rememberedDifficulty:Null = Constants.DEFAULT_DIFFICULTY; - public static var rememberedSongId:Null = null; + public static var rememberedSongId:Null = 'tutorial'; public function new(?params:FreeplayStateParams, ?stickers:StickerSubState) { @@ -596,7 +596,7 @@ class FreeplayState extends MusicBeatSubState // Only now do we know that the filter is actually changing. - rememberedSongId = grpCapsules.members[curSelected]?.songData?.songId; + rememberedSongId = grpCapsules.members[curSelected]?.songData?.songId ?? rememberedSongId; for (cap in grpCapsules.members) { @@ -939,6 +939,11 @@ class FreeplayState extends MusicBeatSubState FlxTransitionableState.skipNextTransOut = true; if (Type.getClass(FlxG.state) == MainMenuState) { + FunkinSound.playMusic('freakyMenu', + { + overrideExisting: true, + restartTrack: false + }); close(); } else From f7141e7096f25a4a3a9986afe6c26357376ae49e Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 3 Apr 2024 01:01:58 -0400 Subject: [PATCH 2/2] Fixed an issue with save data not loading defaults properly. --- source/funkin/save/Save.hx | 8 +-- .../funkin/save/migrator/SaveDataMigrator.hx | 3 +- source/funkin/ui/freeplay/FreeplayState.hx | 2 +- source/funkin/util/StructureUtil.hx | 61 +++++++++++++++++++ 4 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 source/funkin/util/StructureUtil.hx diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx index 73ba8efa0..dc7c5f989 100644 --- a/source/funkin/save/Save.hx +++ b/source/funkin/save/Save.hx @@ -14,7 +14,7 @@ import thx.semver.Version; class Save { // Version 2.0.2 adds attributes to `optionsChartEditor`, that should return default values if they are null. - public static final SAVE_DATA_VERSION:thx.semver.Version = "2.0.2"; + public static final SAVE_DATA_VERSION:thx.semver.Version = "2.0.3"; public static final SAVE_DATA_VERSION_RULE:thx.semver.VersionRule = "2.0.x"; // We load this version's saves from a new save path, to maintain SOME level of backwards compatibility. @@ -650,9 +650,9 @@ class Save if (legacySaveData != null) { trace('[SAVE] Found legacy save data, converting...'); - var gameSave = SaveDataMigrator.migrate(legacySaveData); + var gameSave = SaveDataMigrator.migrateFromLegacy(legacySaveData); @:privateAccess - FlxG.save.mergeData(gameSave.data); + FlxG.save.mergeData(gameSave.data, true); } else { @@ -664,7 +664,7 @@ class Save trace('[SAVE] Loaded save data.'); @:privateAccess var gameSave = SaveDataMigrator.migrate(FlxG.save.data); - FlxG.save.mergeData(gameSave.data); + FlxG.save.mergeData(gameSave.data, true); } } diff --git a/source/funkin/save/migrator/SaveDataMigrator.hx b/source/funkin/save/migrator/SaveDataMigrator.hx index 00637d52a..3ed59e726 100644 --- a/source/funkin/save/migrator/SaveDataMigrator.hx +++ b/source/funkin/save/migrator/SaveDataMigrator.hx @@ -3,6 +3,7 @@ package funkin.save.migrator; import funkin.save.Save; import funkin.save.migrator.RawSaveData_v1_0_0; import thx.semver.Version; +import funkin.util.StructureUtil; import funkin.util.VersionUtil; @:nullSafety @@ -26,7 +27,7 @@ class SaveDataMigrator if (VersionUtil.validateVersion(version, Save.SAVE_DATA_VERSION_RULE)) { // Simply import the structured data. - var save:Save = new Save(inputData); + var save:Save = new Save(StructureUtil.deepMerge(Save.getDefault(), inputData)); return save; } else diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 455805479..66c829e11 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -962,7 +962,7 @@ class FreeplayState extends MusicBeatSubState public override function destroy():Void { super.destroy(); - var daSong:Null = grpCapsules.members[curSelected]?.songData; + var daSong:Null = currentFilteredSongs[curSelected]; if (daSong != null) { clearDaCache(daSong.songName); diff --git a/source/funkin/util/StructureUtil.hx b/source/funkin/util/StructureUtil.hx new file mode 100644 index 000000000..351d0e0a8 --- /dev/null +++ b/source/funkin/util/StructureUtil.hx @@ -0,0 +1,61 @@ +package funkin.util; + +import haxe.DynamicAccess; + +/** + * Utilities for working with anonymous structures. + */ +class StructureUtil +{ + /** + * Merge two structures, with the second overwriting the first. + * Performs a SHALLOW clone, where child structures are not merged. + * @param a The base structure. + * @param b The new structure. + * @return The merged structure. + */ + public static function merge(a:Dynamic, b:Dynamic):Dynamic + { + var result:DynamicAccess = Reflect.copy(a); + + for (field in Reflect.fields(b)) + { + result.set(field, Reflect.field(b, field)); + } + + return result; + } + + /** + * Merge two structures, with the second overwriting the first. + * Performs a DEEP clone, where child structures are also merged recursively. + * @param a The base structure. + * @param b The new structure. + * @return The merged structure. + */ + public static function deepMerge(a:Dynamic, b:Dynamic):Dynamic + { + if (a == null) return b; + if (b == null) return null; + if (!Reflect.isObject(a) || !Reflect.isObject(b)) return b; + + var result:DynamicAccess = Reflect.copy(a); + + for (field in Reflect.fields(b)) + { + if (Reflect.isObject(b)) + { + // Note that isObject also returns true for class instances, + // but we just assume that's not a problem here. + result.set(field, deepMerge(Reflect.field(result, field), Reflect.field(b, field))); + } + else + { + // If we're here, b[field] is a primitive. + result.set(field, Reflect.field(b, field)); + } + } + + return result; + } +}