From 542f59ef72f76563f3cea8a5b42c6448c8ecb59f Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 21 Jul 2025 03:41:39 -0400 Subject: [PATCH] tallyCompletion() function for cleanup + constency --- source/funkin/play/ResultState.hx | 3 +- source/funkin/play/scoring/Scoring.hx | 25 +++++++++++--- source/funkin/save/Save.hx | 6 ++-- source/funkin/ui/freeplay/FreeplayState.hx | 40 +++++++++------------- 4 files changed, 39 insertions(+), 35 deletions(-) diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index 74088ff17..e104f5eee 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -527,8 +527,7 @@ class ResultState extends MusicBeatSubState bgFlash.visible = true; FlxTween.tween(bgFlash, {alpha: 0}, 5 / 24); // NOTE: Only divide if totalNotes > 0 to prevent divide-by-zero errors. - var clearPercentFloat = params.scoreData.tallies.totalNotes == 0 ? 0.0 : (params.scoreData.tallies.sick + params.scoreData.tallies.good - - params.scoreData.tallies.missed) / params.scoreData.tallies.totalNotes * 100; + var clearPercentFloat = params.scoreData.tallies.totalNotes == 0 ? 0.0 : Scoring.tallyCompletion(params.scoreData.tallies) * 100; clearPercentTarget = Math.floor(clearPercentFloat); // Prevent off-by-one errors. diff --git a/source/funkin/play/scoring/Scoring.hx b/source/funkin/play/scoring/Scoring.hx index e2dab3b2f..b7691dbb8 100644 --- a/source/funkin/play/scoring/Scoring.hx +++ b/source/funkin/play/scoring/Scoring.hx @@ -384,21 +384,21 @@ class Scoring // Final Grade = (Sick + Good - Miss) / (Total Notes) - var grade = (scoreData.tallies.sick + scoreData.tallies.good - scoreData.tallies.missed) / scoreData.tallies.totalNotes; + var completionAmount:Float = Scoring.tallyCompletion(scoreData.tallies); - if (grade == Constants.RANK_PERFECT_THRESHOLD) + if (completionAmount == Constants.RANK_PERFECT_THRESHOLD) { return ScoringRank.PERFECT; } - else if (grade >= Constants.RANK_EXCELLENT_THRESHOLD) + else if (completionAmount >= Constants.RANK_EXCELLENT_THRESHOLD) { return ScoringRank.EXCELLENT; } - else if (grade >= Constants.RANK_GREAT_THRESHOLD) + else if (completionAmount >= Constants.RANK_GREAT_THRESHOLD) { return ScoringRank.GREAT; } - else if (grade >= Constants.RANK_GOOD_THRESHOLD) + else if (completionAmount >= Constants.RANK_GOOD_THRESHOLD) { return ScoringRank.GOOD; } @@ -407,6 +407,21 @@ class Scoring return ScoringRank.SHIT; } } + + /** + * Calculates the "completion" of a song, based on how many GOOD and SICK notes were hit, minus how many were missed + * Top secret funkin crew patented algorithm + * TODO: Could possibly move more of the "tallying" related handling here. + * In FreeplayState we make sure it's clamped between 0 and 1, and we probably always want to assume that? + * + * @param tallies + * @return Float Completion, as a float value between 0 and 1. If `tallies` is `null`, we return 0; + */ + public static function tallyCompletion(?tallies:SaveScoreTallyData):Float + { + if (tallies == null) return 0.0; + return (tallies.sick + tallies.good - tallies.missed) / tallies.totalNotes; + } } enum abstract ScoringRank(String) diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx index 0ca56c18e..c19c11af7 100644 --- a/source/funkin/save/Save.hx +++ b/source/funkin/save/Save.hx @@ -882,14 +882,12 @@ class Save return; } - var newCompletion = (newScoreData.tallies.sick + newScoreData.tallies.good) / newScoreData.tallies.totalNotes; - var previousCompletion = (previousScoreData.tallies.sick + previousScoreData.tallies.good) / previousScoreData.tallies.totalNotes; - // Set the high score and the high rank separately. var newScore:SaveScoreData = { score: (previousScoreData.score > newScoreData.score) ? previousScoreData.score : newScoreData.score, - tallies: (previousRank > newRank || previousCompletion > newCompletion) ? previousScoreData.tallies : newScoreData.tallies + tallies: (previousRank > newRank + || Scoring.tallyCompletion(previousScoreData.tallies) > Scoring.tallyCompletion(newScoreData.tallies)) ? previousScoreData.tallies : newScoreData.tallies }; song.set(difficultyId, newScore); diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index d1b6a261f..cbe114b16 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -31,6 +31,7 @@ import funkin.input.Controls; import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEventDispatcher; import funkin.play.PlayStatePlaylist; +import funkin.play.scoring.Scoring; import funkin.play.scoring.Scoring.ScoringRank; import funkin.play.song.Song; import funkin.save.Save; @@ -2300,11 +2301,10 @@ class FreeplayState extends MusicBeatSubState var songScore:Null = Save.instance.getSongScore(daSong.data.id, currentDifficulty, currentVariation); intendedScore = songScore?.score ?? 0; - intendedCompletion = songScore == null ? 0.0 : Math.max(0, - ((songScore.tallies.sick + songScore.tallies.good - songScore.tallies.missed) / songScore.tallies.totalNotes)); + intendedCompletion = Math.max(0, Scoring.tallyCompletion(songScore?.tallies)); rememberedDifficulty = currentDifficulty; if (!capsuleAnim) generateSongList(currentFilter, false, true, true); - currentCapsule.refreshDisplay((prepForNewRank == true) ? false : true); + currentCapsule.refreshDisplay(!prepForNewRank); } else { @@ -2719,26 +2719,18 @@ class FreeplayState extends MusicBeatSubState if (!prepForNewRank && curSelected != prevSelected) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4); - var daSongCapsule:SongMenuItem = currentCapsule; - if (daSongCapsule.freeplayData != null) - { - var songScore:Null = Save.instance.getSongScore(daSongCapsule.freeplayData.data.id, currentDifficulty, currentVariation); - intendedScore = songScore?.score ?? 0; - intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + - songScore.tallies.good - songScore.tallies.missed) / songScore.tallies.totalNotes); - rememberedSongId = daSongCapsule.freeplayData.data.id; - changeDiff(); - daSongCapsule.refreshDisplay((prepForNewRank == true) ? false : true); - } + var songScore:Null = Save.instance.getSongScore(currentCapsule.freeplayData?.data.id ?? "", currentDifficulty, currentVariation); + intendedScore = songScore?.score ?? 0; + + intendedCompletion = Scoring.tallyCompletion(songScore?.tallies); + rememberedSongId = currentCapsule.freeplayData?.data.id; + + if (currentCapsule.freeplayData == null) albumRoll.albumId = null; + + changeDiff(); + if (currentCapsule.freeplayData == null) currentCapsule.refreshDisplay(); else - { - intendedScore = 0; - intendedCompletion = 0.0; - rememberedSongId = null; - albumRoll.albumId = null; - changeDiff(); - daSongCapsule.refreshDisplay(); - } + currentCapsule.refreshDisplay(!prepForNewRank); for (index => capsule in grpCapsules.members) { @@ -2756,10 +2748,10 @@ class FreeplayState extends MusicBeatSubState if (grpCapsules.countLiving() > 0 && !prepForNewRank && controls.active) { - playCurSongPreview(daSongCapsule); + playCurSongPreview(currentCapsule); currentCapsule.selected = true; - // switchBackingImage(daSongCapsule.freeplayData); + // switchBackingImage(currentCapsule.freeplayData); } // Small vibrations every selection change.