diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 422b29601a..b0656e270e 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Utils;
+using osu.Game.Beatmaps;
using osu.Game.Extensions;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
@@ -86,9 +87,22 @@ namespace osu.Game.Rulesets.Scoring
///
private double maxBaseScore;
+ ///
+ /// The maximum number of basic (non-tick and non-bonus) hitobjects.
+ ///
+ private int maxBasicHitObjects;
+
+ ///
+ /// Maximum for a normal hit (i.e. not tick/bonus) for this ruleset.
+ /// Only populated via .
+ ///
+ private HitResult? maxBasicHitResult;
+
private double rollingMaxBaseScore;
private double baseScore;
+ private int basicHitObjects;
+ private readonly Dictionary scoreResultCounts = new Dictionary();
private readonly List hitEvents = new List();
private HitObject lastHitObject;
@@ -122,8 +136,6 @@ namespace osu.Game.Rulesets.Scoring
};
}
- private readonly Dictionary scoreResultCounts = new Dictionary();
-
protected sealed override void ApplyResultInternal(JudgementResult result)
{
result.ComboAtJudgement = Combo.Value;
@@ -160,6 +172,9 @@ namespace osu.Game.Rulesets.Scoring
rollingMaxBaseScore += result.Judgement.MaxNumericResult;
}
+ if (result.Type.IsBasic())
+ basicHitObjects++;
+
hitEvents.Add(CreateHitEvent(result));
lastHitObject = result.HitObject;
@@ -195,6 +210,9 @@ namespace osu.Game.Rulesets.Scoring
rollingMaxBaseScore -= result.Judgement.MaxNumericResult;
}
+ if (result.Type.IsBasic())
+ basicHitObjects--;
+
Debug.Assert(hitEvents.Count > 0);
lastHitObject = hitEvents[^1].LastHitObject;
hitEvents.RemoveAt(hitEvents.Count - 1);
@@ -210,15 +228,30 @@ namespace osu.Game.Rulesets.Scoring
TotalScore.Value = GetScore(Mode.Value);
}
- public double GetScore(ScoringMode mode) => GetScore(mode, calculateAccuracyRatio(baseScore), calculateComboRatio(HighestCombo.Value), scoreResultCounts);
+ ///
+ /// Computes the total score from judgements that have been applied to this
+ /// through and .
+ ///
+ ///
+ /// Requires an to have been applied via before use.
+ ///
+ /// The to represent the score as.
+ /// The total score in the given .
+ public double GetScore(ScoringMode mode)
+ {
+ return GetScore(mode, calculateAccuracyRatio(baseScore), calculateComboRatio(HighestCombo.Value), scoreResultCounts);
+ }
///
- /// Given a minimal set of inputs, return the computed score for the tracked beatmap / mods combination, at the current point in time.
+ /// Computes the total score from judgements counts in a statistics dictionary.
///
- /// The to compute the total score in.
+ ///
+ /// Requires an to have been applied via before use.
+ ///
+ /// The to represent the score as.
/// The maximum combo achievable in the beatmap.
- /// Statistics to be used for calculating accuracy, bonus score, etc.
- /// The computed score for provided inputs.
+ /// The statistics to compute the score for.
+ /// The total score computed from judgements in the statistics dictionary.
public double GetScore(ScoringMode mode, int maxCombo, Dictionary statistics)
{
// calculate base score from statistics pairs
@@ -236,25 +269,34 @@ namespace osu.Game.Rulesets.Scoring
}
///
- /// Computes the total score.
+ /// Computes the total score from given scoring component ratios.
///
- /// The to compute the total score in.
+ ///
+ /// Requires an to have been applied via before use.
+ ///
+ /// The to represent the score as.
/// The accuracy percentage achieved by the player.
- /// The proportion of the max combo achieved by the player.
- /// Any statistics to be factored in.
- /// The total score.
+ /// The portion of the max combo achieved by the player.
+ /// Any additional statistics to be factored in.
+ /// The total score computed from the given scoring component ratios.
public double GetScore(ScoringMode mode, double accuracyRatio, double comboRatio, Dictionary statistics)
{
- int totalHitObjects = statistics.Where(k => k.Key >= HitResult.Miss && k.Key <= HitResult.Perfect).Sum(k => k.Value);
-
- // If there are no hitobjects then the beatmap can be composed of only ticks or spinners, so ensure we don't multiply by 0 at all times.
- if (totalHitObjects == 0)
- totalHitObjects = 1;
-
- return GetScore(mode, accuracyRatio, comboRatio, statistics, totalHitObjects);
+ return GetScore(mode, accuracyRatio, comboRatio, maxBasicHitObjects, statistics);
}
- public double GetScore(ScoringMode mode, double accuracyRatio, double comboRatio, Dictionary statistics, int totalHitObjects)
+ ///
+ /// Computes the total score from given scoring component ratios.
+ ///
+ ///
+ /// Does not require an to have been applied via before use.
+ ///
+ /// The to represent the score as.
+ /// The accuracy percentage achieved by the player.
+ /// The portion of the max combo achieved by the player.
+ /// Any additional statistics to be factored in.
+ /// The total number of basic (non-tick and non-bonus) hitobjects in the beatmap.
+ /// The total score computed from the given scoring component ratios.
+ public double GetScore(ScoringMode mode, double accuracyRatio, double comboRatio, int totalBasicHitObjects, Dictionary statistics)
{
switch (mode)
{
@@ -268,7 +310,7 @@ namespace osu.Game.Rulesets.Scoring
// This gives a similar feeling to osu!stable scoring (ScoreV1) while keeping classic scoring as only a constant multiple of standardised scoring.
// The invariant is important to ensure that scores don't get re-ordered on leaderboards between the two scoring modes.
double scaledStandardised = GetScore(ScoringMode.Standardised, accuracyRatio, comboRatio, statistics) / max_score;
- return Math.Pow(scaledStandardised * totalHitObjects, 2) * 36;
+ return Math.Pow(scaledStandardised * totalBasicHitObjects, 2) * 36;
}
}
@@ -326,10 +368,12 @@ namespace osu.Game.Rulesets.Scoring
{
maxAchievableCombo = HighestCombo.Value;
maxBaseScore = baseScore;
+ maxBasicHitObjects = basicHitObjects;
}
baseScore = 0;
rollingMaxBaseScore = 0;
+ basicHitObjects = 0;
TotalScore.Value = 0;
Accuracy.Value = 1;
@@ -355,11 +399,6 @@ namespace osu.Game.Rulesets.Scoring
score.HitEvents = hitEvents;
}
- ///
- /// Maximum for a normal hit (i.e. not tick/bonus) for this ruleset. Only populated via .
- ///
- private HitResult? maxNormalResult;
-
public override void ResetFromReplayFrame(Ruleset ruleset, ReplayFrame frame)
{
base.ResetFromReplayFrame(ruleset, frame);
@@ -394,7 +433,7 @@ namespace osu.Game.Rulesets.Scoring
break;
default:
- maxResult = maxNormalResult ??= ruleset.GetHitResults().OrderByDescending(kvp => Judgement.ToNumericResult(kvp.result)).First().result;
+ maxResult = maxBasicHitResult ??= ruleset.GetHitResults().OrderByDescending(kvp => Judgement.ToNumericResult(kvp.result)).First().result;
break;
}