feat: Variation-specific song scripts
This commit is contained in:
+1
-1
Submodule assets updated: e77b27834e...8d24090af6
@@ -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<T:(IRegistryEntry<J> & Constructible<EntryConstructo
|
||||
* @param id The ID of the entry to fetch.
|
||||
* @return The entry, or `null` if it does not exist.
|
||||
*/
|
||||
public function fetchEntry(id:String):Null<T>
|
||||
public function fetchEntry(id:String, ?params:Null<Dynamic>):Null<T>
|
||||
{
|
||||
return entries.get(id);
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ import funkin.data.DefaultRegistryImpl;
|
||||
|
||||
using funkin.data.song.migrator.SongDataMigrator;
|
||||
|
||||
@:nullSafety
|
||||
class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleton implements DefaultRegistryImpl
|
||||
@:nullSafety class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleton implements DefaultRegistryImpl
|
||||
{
|
||||
/**
|
||||
* The current version string for the stage data format.
|
||||
@@ -37,6 +36,8 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata> implements ISingleto
|
||||
|
||||
public static var DEFAULT_GENERATEDBY(get, never):String;
|
||||
|
||||
public var scriptedSongVariations:Map<String, Song> = new Map<String, Song>();
|
||||
|
||||
static function get_DEFAULT_GENERATEDBY():String
|
||||
{
|
||||
return '${Constants.TITLE} - ${Constants.VERSION}';
|
||||
@@ -63,9 +64,17 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata> 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<Song, SongMetadata> implements ISingleto
|
||||
return parseEntryMetadataRaw(contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* We override `fetchEntry` to handle song variations!
|
||||
*/
|
||||
public override function fetchEntry(id:String, ?params:Null<Dynamic>):Null<Song>
|
||||
{
|
||||
if (params != null && params.variation != null)
|
||||
{
|
||||
if (scriptedSongVariations.exists('${id}:${params.variation}'))
|
||||
{
|
||||
var variationSongScript:Null<Song> = scriptedSongVariations.get('${id}:${params.variation}');
|
||||
if (variationSongScript != null)
|
||||
{
|
||||
return variationSongScript;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.fetchEntry(id, params);
|
||||
}
|
||||
|
||||
public function parseEntryMetadata(id:String, ?variation:String):Null<SongMetadata>
|
||||
{
|
||||
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -832,7 +832,10 @@ class ResultState extends MusicBeatSubState
|
||||
|
||||
var stickerPackId:Null<String> = null;
|
||||
|
||||
var song:Null<Song> = params.songId == null ? null : SongRegistry.instance.fetchEntry(params.songId);
|
||||
var song:Null<Song> = params.songId == null ? null : SongRegistry.instance.fetchEntry(params.songId,
|
||||
{
|
||||
variation: params?.variationId
|
||||
});
|
||||
|
||||
if (song != null)
|
||||
{
|
||||
|
||||
@@ -89,7 +89,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass impl
|
||||
|
||||
var currentDialogueBox:Null<DialogueBox>;
|
||||
|
||||
public function new(id:String)
|
||||
public function new(id:String, ?params:Dynamic)
|
||||
{
|
||||
super();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
|
||||
/**
|
||||
* @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);
|
||||
|
||||
@@ -131,13 +131,16 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
||||
return Constants.DEFAULT_CHARTER;
|
||||
}
|
||||
|
||||
public var variation:Null<String> = 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<String, Map<String, SongDifficulty>>();
|
||||
|
||||
@@ -968,3 +971,11 @@ class SongDifficulty
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
typedef SongParams =
|
||||
{
|
||||
/**
|
||||
* The variation to use for this song.
|
||||
*/
|
||||
variation:String
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import flixel.graphics.FlxGraphic;
|
||||
@:nullSafety
|
||||
class Album implements IRegistryEntry<AlbumData>
|
||||
{
|
||||
public function new(id:String)
|
||||
public function new(id:String, ?params:Dynamic)
|
||||
{
|
||||
this.id = id;
|
||||
this._data = _fetchData(id);
|
||||
|
||||
@@ -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<String> = currentVariation;
|
||||
var targetSongId:String = cap?.freeplayData?.data.id ?? 'unknown';
|
||||
var targetSongNullable:Null<Song> = SongRegistry.instance.fetchEntry(targetSongId);
|
||||
var targetSongNullable:Null<Song> = 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<String> = currentVariation;
|
||||
var targetDifficultyId:String = currentDifficulty;
|
||||
var targetLevelId:Null<String> = cap?.freeplayData?.levelId;
|
||||
|
||||
trace('target song: ${targetSongId} (${targetVariation})');
|
||||
|
||||
PlayStatePlaylist.campaignId = targetLevelId ?? null;
|
||||
|
||||
var targetDifficulty:Null<SongDifficulty> = 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<ScoringRank>;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,7 @@ class FreeplayStyle implements IRegistryEntry<FreeplayStyleData>
|
||||
* 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);
|
||||
|
||||
@@ -15,7 +15,7 @@ class PlayableCharacter implements IRegistryEntry<PlayerData>
|
||||
/**
|
||||
* @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);
|
||||
|
||||
@@ -18,7 +18,7 @@ class Level implements IRegistryEntry<LevelData>
|
||||
/**
|
||||
* @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);
|
||||
|
||||
@@ -20,7 +20,7 @@ class StickerPack implements IRegistryEntry<StickerData>
|
||||
*/
|
||||
public final _data:StickerData;
|
||||
|
||||
public function new(id:String)
|
||||
public function new(id:String, ?params:Dynamic)
|
||||
{
|
||||
this.id = id;
|
||||
this._data = _fetchData(id);
|
||||
|
||||
Reference in New Issue
Block a user