mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2024-11-21 14:23:00 +00:00
fix(freeplay)!: Proper variation / difficulty loading for Freeplay Menu
Previously the game would load variations in a `variation-difficulty` string format, but now we map it out better and filter it based on that, rather than messing around with suffixes and whatnot. If you have a mod that depended on the functionality of the `variation-difficulty` format, you should accomodate that functionality in another way re-add freeplay song preview song names and icons implemented again implement the scoring rank, bpm, and difficulty crud albumId loading fix
This commit is contained in:
parent
f862fb2c3e
commit
c0314c85ec
|
@ -15,6 +15,7 @@ import funkin.data.song.SongRegistry;
|
|||
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.ui.freeplay.charselect.PlayableCharacter;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
import funkin.util.SortUtil;
|
||||
|
||||
/**
|
||||
|
@ -79,7 +80,12 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
|
||||
// key = variation id, value = metadata
|
||||
final _metadata:Map<String, SongMetadata>;
|
||||
final difficulties:Map<String, SongDifficulty>;
|
||||
|
||||
/**
|
||||
* holds the difficulties (as in SongDifficulty) for each variation
|
||||
* difficulties.get('default').get('easy') would return the easy difficulty for the default variation
|
||||
*/
|
||||
final difficulties:Map<String, Map<String, SongDifficulty>>;
|
||||
|
||||
/**
|
||||
* The list of variations a song has.
|
||||
|
@ -146,7 +152,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
{
|
||||
this.id = id;
|
||||
|
||||
difficulties = new Map<String, SongDifficulty>();
|
||||
difficulties = new Map<String, Map<String, SongDifficulty>>();
|
||||
|
||||
_data = _fetchData(id);
|
||||
|
||||
|
@ -156,7 +162,8 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
{
|
||||
for (vari in _data.playData.songVariations)
|
||||
{
|
||||
if (!validateVariationId(vari)) {
|
||||
if (!validateVariationId(vari))
|
||||
{
|
||||
trace(' [WARN] Variation id "$vari" is invalid, skipping...');
|
||||
continue;
|
||||
}
|
||||
|
@ -249,22 +256,39 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
* List the album IDs for each variation of the song.
|
||||
* @return A map of variation IDs to album IDs.
|
||||
*/
|
||||
public function listAlbums():Map<String, String>
|
||||
public function listAlbums(variation:String):Map<String, String>
|
||||
{
|
||||
var result:Map<String, String> = new Map<String, String>();
|
||||
|
||||
for (difficultyId in difficulties.keys())
|
||||
for (variationMap in difficulties)
|
||||
{
|
||||
var meta:Null<SongDifficulty> = difficulties.get(difficultyId);
|
||||
if (meta != null && meta.album != null)
|
||||
for (difficultyId in variationMap.keys())
|
||||
{
|
||||
result.set(difficultyId, meta.album);
|
||||
var meta:Null<SongDifficulty> = variationMap.get(difficultyId);
|
||||
if (meta != null && meta.album != null)
|
||||
{
|
||||
result.set(difficultyId, meta.album);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input a difficulty ID and a variation ID, and get the album ID.
|
||||
* @param diffId
|
||||
* @param variation
|
||||
* @return String
|
||||
*/
|
||||
public function getAlbumId(diffId:String, variation:String):String
|
||||
{
|
||||
var diff:Null<SongDifficulty> = getDifficulty(diffId, variation);
|
||||
if (diff == null) return '';
|
||||
|
||||
return diff.album ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the difficulty data from the provided metadata.
|
||||
* Does not load chart data (that is triggered later when we want to play the song).
|
||||
|
@ -285,6 +309,9 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
continue;
|
||||
}
|
||||
|
||||
// This resides within difficulties
|
||||
var difficultyMap:Map<String, SongDifficulty> = new Map<String, SongDifficulty>();
|
||||
|
||||
// There may be more difficulties in the chart file than in the metadata,
|
||||
// (i.e. non-playable charts like the one used for Pico on the speaker in Stress)
|
||||
// but all the difficulties in the metadata must be in the chart file.
|
||||
|
@ -309,10 +336,9 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
difficulty.noteStyle = metadata.playData.noteStyle;
|
||||
|
||||
difficulty.characters = metadata.playData.characters;
|
||||
|
||||
var variationSuffix = (metadata.variation != Constants.DEFAULT_VARIATION) ? '-${metadata.variation}' : '';
|
||||
difficulties.set('$diffId$variationSuffix', difficulty);
|
||||
difficultyMap.set(diffId, difficulty);
|
||||
}
|
||||
difficulties.set(metadata.variation, difficultyMap);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,15 +371,18 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
|
||||
for (diffId in chartNotes.keys())
|
||||
{
|
||||
// Retrieve the cached difficulty data.
|
||||
var variationSuffix = (variation != Constants.DEFAULT_VARIATION) ? '-$variation' : '';
|
||||
var difficulty:Null<SongDifficulty> = difficulties.get('$diffId$variationSuffix');
|
||||
if (difficulty == null)
|
||||
// Retrieve the cached difficulty data. This one could potentially be null.
|
||||
var nullDiff:Null<SongDifficulty> = getDifficulty(diffId, variation);
|
||||
|
||||
// if the difficulty doesn't exist, create a new one, and then proceed to fill it with data.
|
||||
// I mostly do this since I don't wanna throw around ? everywhere for null check lol?
|
||||
var difficulty:SongDifficulty = nullDiff ?? new SongDifficulty(this, diffId, variation);
|
||||
|
||||
if (nullDiff == null)
|
||||
{
|
||||
trace('Fabricated new difficulty for $diffId.');
|
||||
difficulty = new SongDifficulty(this, diffId, variation);
|
||||
var metadata = _metadata.get(variation);
|
||||
difficulties.set('$diffId$variationSuffix', difficulty);
|
||||
difficulties.get(variation)?.set(diffId, difficulty);
|
||||
|
||||
if (metadata != null)
|
||||
{
|
||||
|
@ -396,11 +425,9 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
|
||||
for (currentVariation in variations)
|
||||
{
|
||||
var variationSuffix = (currentVariation != Constants.DEFAULT_VARIATION) ? '-$currentVariation' : '';
|
||||
|
||||
if (difficulties.exists('$diffId$variationSuffix'))
|
||||
if (difficulties.get(currentVariation)?.exists(diffId) ?? false)
|
||||
{
|
||||
return difficulties.get('$diffId$variationSuffix');
|
||||
return difficulties.get(currentVariation)?.get(diffId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,8 +444,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
|
||||
for (variationId in possibleVariations)
|
||||
{
|
||||
var variationSuffix = (variationId != Constants.DEFAULT_VARIATION) ? '-$variationId' : '';
|
||||
if (difficulties.exists('$diffId$variationSuffix')) return variationId;
|
||||
if (difficulties.exists('$variationId')) return variationId;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -440,7 +466,6 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
}
|
||||
|
||||
var result = [];
|
||||
trace('Evaluating variations for ${this.id} ${char.id}: ${this.variations}');
|
||||
for (variation in variations)
|
||||
{
|
||||
var metadata = _metadata.get(variation);
|
||||
|
@ -459,6 +484,19 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nearly the same thing as getVariationsByCharacter, but takes a character ID instead.
|
||||
* @param charId
|
||||
* @return Array<String>
|
||||
* @see getVariationsByCharacter
|
||||
*/
|
||||
public function getVariationsByCharacterId(?charId:String):Array<String>
|
||||
{
|
||||
var charPlayer = PlayerRegistry.instance.fetchEntry(charId ?? '');
|
||||
|
||||
return getVariationsByCharacter(charPlayer);
|
||||
}
|
||||
|
||||
/**
|
||||
* List all the difficulties in this song.
|
||||
*
|
||||
|
@ -501,6 +539,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
/**
|
||||
* TODO: This line of code makes me sad, but you can't really fix it without a breaking migration.
|
||||
* @return `easy`, `erect`, `normal-pico`, etc.
|
||||
* @deprecated This function is deprecated, Funkin no longer uses suffixed difficulties.
|
||||
*/
|
||||
public function listSuffixedDifficulties(variationIds:Array<String>, ?showLocked:Bool, ?showHidden:Bool):Array<String>
|
||||
{
|
||||
|
@ -529,8 +568,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
|
||||
for (targetVariation in variationIds)
|
||||
{
|
||||
var variationSuffix = (targetVariation != Constants.DEFAULT_VARIATION) ? '-$targetVariation' : '';
|
||||
if (difficulties.exists('$diffId$variationSuffix')) return true;
|
||||
if (difficulties.get(targetVariation)?.exists(diffId) ?? false) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -565,13 +603,16 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
}
|
||||
|
||||
/**
|
||||
* Purge the cached chart data for each difficulty of this song.
|
||||
* Purge the cached chart data for each difficulty/variation of this song.
|
||||
*/
|
||||
public function clearCharts():Void
|
||||
{
|
||||
for (diff in difficulties)
|
||||
for (variationMap in difficulties)
|
||||
{
|
||||
diff.clearChart();
|
||||
for (diff in variationMap)
|
||||
{
|
||||
diff.clearChart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -647,7 +688,8 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
* Auto-accept if it's one of the base game default variations.
|
||||
* Reject if the ID starts with a number, or contains invalid characters.
|
||||
*/
|
||||
static function validateVariationId(variation:String):Bool {
|
||||
static function validateVariationId(variation:String):Bool
|
||||
{
|
||||
if (Constants.DEFAULT_VARIATION_LIST.contains(variation)) return true;
|
||||
|
||||
return VARIATION_REGEX.match(variation);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -36,7 +36,7 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
* Modify this by calling `init()`
|
||||
* If `null`, assume this SongMenuItem is for the "Random Song" option.
|
||||
*/
|
||||
public var songData(default, null):Null<FreeplaySongData> = null;
|
||||
public var freeplayData(default, null):Null<FreeplaySongData> = null;
|
||||
|
||||
public var selected(default, set):Bool;
|
||||
|
||||
|
@ -422,6 +422,34 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
return evilTrail.color;
|
||||
}
|
||||
|
||||
public function refreshDisplay():Void
|
||||
{
|
||||
if (freeplayData == null)
|
||||
{
|
||||
songText.text = 'Random';
|
||||
pixelIcon.visible = false;
|
||||
ranking.visible = false;
|
||||
blurredRanking.visible = false;
|
||||
favIcon.visible = false;
|
||||
favIconBlurred.visible = false;
|
||||
newText.visible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
songText.text = freeplayData.fullSongName;
|
||||
if (freeplayData.songCharacter != null) pixelIcon.setCharacter(freeplayData.songCharacter);
|
||||
pixelIcon.visible = true;
|
||||
updateBPM(Std.int(freeplayData.songStartingBpm) ?? 0);
|
||||
updateDifficultyRating(freeplayData.difficultyRating ?? 0);
|
||||
updateScoringRank(freeplayData.scoringRank);
|
||||
newText.visible = freeplayData.isNew;
|
||||
favIcon.visible = freeplayData.isFav;
|
||||
favIconBlurred.visible = freeplayData.isFav;
|
||||
checkClip();
|
||||
}
|
||||
updateSelected();
|
||||
}
|
||||
|
||||
function updateDifficultyRating(newRating:Int):Void
|
||||
{
|
||||
var ratingPadded:String = newRating < 10 ? '0$newRating' : '$newRating';
|
||||
|
@ -500,11 +528,11 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
updateSelected();
|
||||
}
|
||||
|
||||
public function init(?x:Float, ?y:Float, songData:Null<FreeplaySongData>, ?styleData:FreeplayStyle = null):Void
|
||||
public function init(?x:Float, ?y:Float, freeplayData:Null<FreeplaySongData>, ?styleData:FreeplayStyle = null):Void
|
||||
{
|
||||
if (x != null) this.x = x;
|
||||
if (y != null) this.y = y;
|
||||
this.songData = songData;
|
||||
this.freeplayData = freeplayData;
|
||||
|
||||
// im so mad i have to do this but im pretty sure with the capsules recycling i cant call the new function properly :/
|
||||
// if thats possible someone Please change the new function to be something like
|
||||
|
@ -517,21 +545,13 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
songText.applyStyle(styleData);
|
||||
}
|
||||
|
||||
// Update capsule text.
|
||||
songText.text = songData?.songName ?? 'Random';
|
||||
// Update capsule character.
|
||||
if (songData?.songCharacter != null) pixelIcon.setCharacter(songData.songCharacter);
|
||||
updateBPM(Std.int(songData?.songStartingBpm) ?? 0);
|
||||
updateDifficultyRating(songData?.difficultyRating ?? 0);
|
||||
updateScoringRank(songData?.scoringRank);
|
||||
newText.visible = songData?.isNew;
|
||||
updateScoringRank(freeplayData?.scoringRank);
|
||||
favIcon.animation.curAnim.curFrame = favIcon.animation.curAnim.numFrames - 1;
|
||||
favIconBlurred.animation.curAnim.curFrame = favIconBlurred.animation.curAnim.numFrames - 1;
|
||||
|
||||
// Update opacity, offsets, etc.
|
||||
updateSelected();
|
||||
refreshDisplay();
|
||||
|
||||
checkWeek(songData?.songId);
|
||||
checkWeek(freeplayData?.data.id);
|
||||
}
|
||||
|
||||
var frameInTicker:Float = 0;
|
||||
|
|
Loading…
Reference in a new issue