From c94d6bbb6640f7a9fbce8a202fbecf19d36da2f7 Mon Sep 17 00:00:00 2001 From: Abnormal <86753001+AbnormalPoof@users.noreply.github.com> Date: Tue, 27 May 2025 02:20:50 -0500 Subject: [PATCH] feat: Variation-specific song scripts --- assets | 2 +- source/funkin/data/BaseRegistry.hx | 6 +-- source/funkin/data/song/SongRegistry.hx | 39 ++++++++++++++++--- source/funkin/play/PauseSubState.hx | 3 +- source/funkin/play/PlayState.hx | 3 +- source/funkin/play/ResultState.hx | 5 ++- .../play/cutscene/dialogue/Conversation.hx | 2 +- .../play/cutscene/dialogue/DialogueBox.hx | 2 +- .../funkin/play/cutscene/dialogue/Speaker.hx | 2 +- .../funkin/play/notes/notestyle/NoteStyle.hx | 2 +- source/funkin/play/song/Song.hx | 15 ++++++- source/funkin/play/stage/Stage.hx | 2 +- source/funkin/ui/freeplay/Album.hx | 2 +- source/funkin/ui/freeplay/FreeplayState.hx | 27 ++++++++++--- source/funkin/ui/freeplay/FreeplayStyle.hx | 2 +- .../freeplay/charselect/PlayableCharacter.hx | 2 +- source/funkin/ui/story/Level.hx | 2 +- .../ui/transition/stickers/StickerPack.hx | 2 +- 18 files changed, 90 insertions(+), 30 deletions(-) diff --git a/assets b/assets index e77b27834..8d24090af 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit e77b27834e6cedb131ce5e8a0f4fae09db118c8c +Subproject commit 8d24090af6b2019d9c71b43b2fbccf80717e57ab diff --git a/source/funkin/data/BaseRegistry.hx b/source/funkin/data/BaseRegistry.hx index 38c4e03f9..6b193a065 100644 --- a/source/funkin/data/BaseRegistry.hx +++ b/source/funkin/data/BaseRegistry.hx @@ -5,9 +5,9 @@ import funkin.util.VersionUtil; import haxe.Constraints.Constructible; /** - * The entry's constructor function must take a single argument, the entry's ID. + * The entry's constructor function takes 2 arguments, the entry ID and optional parameters. */ -typedef EntryConstructorFunction = String->Void; +typedef EntryConstructorFunction = (String, ?Dynamic) -> Void; /** * A base type for a Registry, which is an object which handles loading scriptable objects. @@ -186,7 +186,7 @@ abstract class BaseRegistry & Constructible + public function fetchEntry(id:String, ?params:Null):Null { return entries.get(id); } diff --git a/source/funkin/data/song/SongRegistry.hx b/source/funkin/data/song/SongRegistry.hx index bc688f94c..4bea82306 100644 --- a/source/funkin/data/song/SongRegistry.hx +++ b/source/funkin/data/song/SongRegistry.hx @@ -15,8 +15,7 @@ import funkin.data.DefaultRegistryImpl; using funkin.data.song.migrator.SongDataMigrator; -@:nullSafety -class SongRegistry extends BaseRegistry implements ISingleton implements DefaultRegistryImpl +@:nullSafety class SongRegistry extends BaseRegistry implements ISingleton implements DefaultRegistryImpl { /** * The current version string for the stage data format. @@ -37,6 +36,8 @@ class SongRegistry extends BaseRegistry implements ISingleto public static var DEFAULT_GENERATEDBY(get, never):String; + public var scriptedSongVariations:Map = new Map(); + static function get_DEFAULT_GENERATEDBY():String { return '${Constants.TITLE} - ${Constants.VERSION}'; @@ -63,9 +64,17 @@ class SongRegistry extends BaseRegistry implements ISingleto if (entry != null) { - log('Successfully created scripted entry (${entryCls} = ${entry.id})'); - entries.set(entry.id, entry); - scriptedEntryIds.set(entry.id, entryCls); + if (entry.variation != null) + { + scriptedSongVariations.set('${entry.id}:${entry.variation}', entry); + log('Successfully created scripted entry (${entryCls} = ${entry.id}, ${entry.variation})'); + } + else + { + entries.set(entry.id, entry); + scriptedEntryIds.set(entry.id, entryCls); + log('Successfully created scripted entry (${entryCls} = ${entry.id})'); + } } else { @@ -120,6 +129,26 @@ class SongRegistry extends BaseRegistry implements ISingleto return parseEntryMetadataRaw(contents); } + /** + * We override `fetchEntry` to handle song variations! + */ + public override function fetchEntry(id:String, ?params:Null):Null + { + if (params != null && params.variation != null) + { + if (scriptedSongVariations.exists('${id}:${params.variation}')) + { + var variationSongScript:Null = scriptedSongVariations.get('${id}:${params.variation}'); + if (variationSongScript != null) + { + return variationSongScript; + } + } + } + + return super.fetchEntry(id, params); + } + public function parseEntryMetadata(id:String, ?variation:String):Null { variation = variation == null ? Constants.DEFAULT_VARIATION : variation; diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index 5f121106f..c095203a1 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -939,7 +939,8 @@ class PauseSubState extends MusicBeatSubState */ static function changeDifficulty(state:PauseSubState, difficulty:String):Void { - PlayState.instance.currentSong = SongRegistry.instance.fetchEntry(PlayState.instance.currentSong.id.toLowerCase()); + PlayState.instance.currentSong = SongRegistry.instance.fetchEntry(PlayState.instance.currentSong.id.toLowerCase(), + {variation: PlayState.instance.currentChart.variation}); // Reset campaign score when changing difficulty // So if you switch difficulty on the last song of a week you get a really low overall score. diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 2fc5e2d13..3beb2f12a 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1706,7 +1706,8 @@ class PlayState extends MusicBeatSubState { throw "No lastParams to refer to"; } - lastParams.targetSong = SongRegistry.instance.fetchEntry(currentSong.id) ?? throw "Could not load current song from ID. This shouldn't happen!"; + lastParams.targetSong = SongRegistry.instance.fetchEntry(currentSong.id, + {variation: currentVariation}) ?? throw "Could not load current song from ID. This shouldn't happen!"; LoadingState.loadPlayState(lastParams); } diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index e104f5eee..c83f6d893 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -832,7 +832,10 @@ class ResultState extends MusicBeatSubState var stickerPackId:Null = null; - var song:Null = params.songId == null ? null : SongRegistry.instance.fetchEntry(params.songId); + var song:Null = params.songId == null ? null : SongRegistry.instance.fetchEntry(params.songId, + { + variation: params?.variationId + }); if (song != null) { diff --git a/source/funkin/play/cutscene/dialogue/Conversation.hx b/source/funkin/play/cutscene/dialogue/Conversation.hx index a94de8f94..74632e5d9 100644 --- a/source/funkin/play/cutscene/dialogue/Conversation.hx +++ b/source/funkin/play/cutscene/dialogue/Conversation.hx @@ -89,7 +89,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass impl var currentDialogueBox:Null; - public function new(id:String) + public function new(id:String, ?params:Dynamic) { super(); diff --git a/source/funkin/play/cutscene/dialogue/DialogueBox.hx b/source/funkin/play/cutscene/dialogue/DialogueBox.hx index 3829722b4..694839109 100644 --- a/source/funkin/play/cutscene/dialogue/DialogueBox.hx +++ b/source/funkin/play/cutscene/dialogue/DialogueBox.hx @@ -106,7 +106,7 @@ class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass imple return FullScreenScaleMode.wideScale.x - 0.05; } - public function new(id:String) + public function new(id:String, ?params:Dynamic) { super(); this.id = id; diff --git a/source/funkin/play/cutscene/dialogue/Speaker.hx b/source/funkin/play/cutscene/dialogue/Speaker.hx index 21ea23f8f..af69b5d5a 100644 --- a/source/funkin/play/cutscene/dialogue/Speaker.hx +++ b/source/funkin/play/cutscene/dialogue/Speaker.hx @@ -86,7 +86,7 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass implements IRe return FullScreenScaleMode.wideScale.x - 0.05; } - public function new(id:String) + public function new(id:String, ?params:Dynamic) { super(); diff --git a/source/funkin/play/notes/notestyle/NoteStyle.hx b/source/funkin/play/notes/notestyle/NoteStyle.hx index 6ecc59fad..11d8677ee 100644 --- a/source/funkin/play/notes/notestyle/NoteStyle.hx +++ b/source/funkin/play/notes/notestyle/NoteStyle.hx @@ -39,7 +39,7 @@ class NoteStyle implements IRegistryEntry /** * @param id The ID of the JSON file to parse. */ - public function new(id:String) + public function new(id:String, ?params:Dynamic) { this.id = id; _data = _fetchData(id); diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index ed7295abc..249d5894a 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -131,13 +131,16 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry = null; + /** * @param id The ID of the song to load. - * @param ignoreErrors If false, an exception will be thrown if the song data could not be loaded. + * @param targetVariation The variation to load, optional. */ - public function new(id:String) + public function new(id:String, ?params:SongParams) { this.id = id; + this.variation = params?.variation; difficulties = new Map>(); @@ -968,3 +971,11 @@ class SongDifficulty return result; } } + +typedef SongParams = +{ + /** + * The variation to use for this song. + */ + variation:String +} diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index 22fa4580d..8a3243bb0 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -62,7 +62,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements * * @param id */ - public function new(id:String) + public function new(id:String, ?params:Dynamic) { super(); diff --git a/source/funkin/ui/freeplay/Album.hx b/source/funkin/ui/freeplay/Album.hx index c226e49ad..747cca636 100644 --- a/source/funkin/ui/freeplay/Album.hx +++ b/source/funkin/ui/freeplay/Album.hx @@ -12,7 +12,7 @@ import flixel.graphics.FlxGraphic; @:nullSafety class Album implements IRegistryEntry { - public function new(id:String) + public function new(id:String, ?params:Dynamic) { this.id = id; this._data = _fetchData(id); diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 32cec6e20..bd7a8e78f 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -380,7 +380,7 @@ class FreeplayState extends MusicBeatSubState continue; } - songs.push(new FreeplaySongData(song, level)); + songs.push(new FreeplaySongData(songId, level, this)); } } @@ -2548,8 +2548,9 @@ class FreeplayState extends MusicBeatSubState PlayStatePlaylist.isStoryMode = false; + var targetVariation:Null = currentVariation; var targetSongId:String = cap?.freeplayData?.data.id ?? 'unknown'; - var targetSongNullable:Null = SongRegistry.instance.fetchEntry(targetSongId); + var targetSongNullable:Null = SongRegistry.instance.fetchEntry(targetSongId, {variation: targetVariation}); if (targetSongNullable == null) { FlxG.log.warn('WARN: could not find song with id (${targetSongId})'); @@ -2558,8 +2559,11 @@ class FreeplayState extends MusicBeatSubState return; } var targetSong:Song = targetSongNullable; - var targetVariation:Null = currentVariation; + var targetDifficultyId:String = currentDifficulty; var targetLevelId:Null = cap?.freeplayData?.levelId; + + trace('target song: ${targetSongId} (${targetVariation})'); + PlayStatePlaylist.campaignId = targetLevelId ?? null; var targetDifficulty:Null = targetSong.getDifficulty(currentDifficulty, currentVariation); @@ -3038,7 +3042,13 @@ class FreeplaySongData * * @see Song */ - public var data:Song; + public var data(get, never):Song; + + function get_data():Song + { + @:privateAccess + return SongRegistry.instance.fetchEntry(songId, {variation: instance.currentVariation}); + } /** * The level id of the song, useful for sorting from week1 -> week 7 + weekend1 @@ -3053,6 +3063,8 @@ class FreeplaySongData var _levelId:String; + final songId:String; + /** * Whether or not the song has been favorited. */ @@ -3088,10 +3100,13 @@ class FreeplaySongData public var scoringRank(get, never):Null; - public function new(data:Song, levelData:Level) + public var instance:FreeplayState; + + public function new(songId:String, levelData:Level, instance:FreeplayState) { - this.data = data; + this.songId = songId; _levelId = levelData.id; + this.instance = instance; } /** diff --git a/source/funkin/ui/freeplay/FreeplayStyle.hx b/source/funkin/ui/freeplay/FreeplayStyle.hx index 037aaf056..13232b29f 100644 --- a/source/funkin/ui/freeplay/FreeplayStyle.hx +++ b/source/funkin/ui/freeplay/FreeplayStyle.hx @@ -21,7 +21,7 @@ class FreeplayStyle implements IRegistryEntry * The full data for a freeplay style. */ // public final _data:FreeplayStyleData; - public function new(id:String) + public function new(id:String, ?params:Dynamic) { this.id = id; this._data = _fetchData(id); diff --git a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx index fe5b8b9e1..808ce2625 100644 --- a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx +++ b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx @@ -15,7 +15,7 @@ class PlayableCharacter implements IRegistryEntry /** * @param id The ID of the JSON file to parse. */ - public function new(id:String) + public function new(id:String, ?params:Dynamic) { this.id = id; _data = _fetchData(id); diff --git a/source/funkin/ui/story/Level.hx b/source/funkin/ui/story/Level.hx index 2d7a697f1..c2121beb7 100644 --- a/source/funkin/ui/story/Level.hx +++ b/source/funkin/ui/story/Level.hx @@ -18,7 +18,7 @@ class Level implements IRegistryEntry /** * @param id The ID of the JSON file to parse. */ - public function new(id:String) + public function new(id:String, ?params:Dynamic) { this.id = id; _data = _fetchData(id); diff --git a/source/funkin/ui/transition/stickers/StickerPack.hx b/source/funkin/ui/transition/stickers/StickerPack.hx index ecd79bf37..06a2c6437 100644 --- a/source/funkin/ui/transition/stickers/StickerPack.hx +++ b/source/funkin/ui/transition/stickers/StickerPack.hx @@ -20,7 +20,7 @@ class StickerPack implements IRegistryEntry */ public final _data:StickerData; - public function new(id:String) + public function new(id:String, ?params:Dynamic) { this.id = id; this._data = _fetchData(id);