mirror of
https://github.com/ppy/osu.git
synced 2025-01-14 02:22:56 +08:00
Calculate classic score using total basic hitobject count
This commit is contained in:
parent
5b6b8d1fa9
commit
a8e99f1a95
@ -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
|
||||
/// </summary>
|
||||
private double maxBaseScore;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of basic (non-tick and non-bonus) hitobjects.
|
||||
/// </summary>
|
||||
private int maxBasicHitObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum <see cref="HitResult"/> for a normal hit (i.e. not tick/bonus) for this ruleset.
|
||||
/// Only populated via <see cref="ResetFromReplayFrame"/>.
|
||||
/// </summary>
|
||||
private HitResult? maxBasicHitResult;
|
||||
|
||||
private double rollingMaxBaseScore;
|
||||
private double baseScore;
|
||||
private int basicHitObjects;
|
||||
|
||||
private readonly Dictionary<HitResult, int> scoreResultCounts = new Dictionary<HitResult, int>();
|
||||
private readonly List<HitEvent> hitEvents = new List<HitEvent>();
|
||||
private HitObject lastHitObject;
|
||||
|
||||
@ -122,8 +136,6 @@ namespace osu.Game.Rulesets.Scoring
|
||||
};
|
||||
}
|
||||
|
||||
private readonly Dictionary<HitResult, int> scoreResultCounts = new Dictionary<HitResult, int>();
|
||||
|
||||
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);
|
||||
/// <summary>
|
||||
/// Computes the total score from judgements that have been applied to this <see cref="ScoreProcessor"/>
|
||||
/// through <see cref="JudgementProcessor.ApplyResult"/> and <see cref="JudgementProcessor.RevertResult"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Requires an <see cref="IBeatmap"/> to have been applied via <see cref="JudgementProcessor.ApplyBeatmap"/> before use.
|
||||
/// </remarks>
|
||||
/// <param name="mode">The <see cref="ScoringMode"/> to represent the score as.</param>
|
||||
/// <returns>The total score in the given <see cref="ScoringMode"/>.</returns>
|
||||
public double GetScore(ScoringMode mode)
|
||||
{
|
||||
return GetScore(mode, calculateAccuracyRatio(baseScore), calculateComboRatio(HighestCombo.Value), scoreResultCounts);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="mode">The <see cref="ScoringMode"/> to compute the total score in.</param>
|
||||
/// <remarks>
|
||||
/// Requires an <see cref="IBeatmap"/> to have been applied via <see cref="JudgementProcessor.ApplyBeatmap"/> before use.
|
||||
/// </remarks>
|
||||
/// <param name="mode">The <see cref="ScoringMode"/> to represent the score as.</param>
|
||||
/// <param name="maxCombo">The maximum combo achievable in the beatmap.</param>
|
||||
/// <param name="statistics">Statistics to be used for calculating accuracy, bonus score, etc.</param>
|
||||
/// <returns>The computed score for provided inputs.</returns>
|
||||
/// <param name="statistics">The statistics to compute the score for.</param>
|
||||
/// <returns>The total score computed from judgements in the statistics dictionary.</returns>
|
||||
public double GetScore(ScoringMode mode, int maxCombo, Dictionary<HitResult, int> statistics)
|
||||
{
|
||||
// calculate base score from statistics pairs
|
||||
@ -236,25 +269,34 @@ namespace osu.Game.Rulesets.Scoring
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the total score.
|
||||
/// Computes the total score from given scoring component ratios.
|
||||
/// </summary>
|
||||
/// <param name="mode">The <see cref="ScoringMode"/> to compute the total score in.</param>
|
||||
/// <remarks>
|
||||
/// Requires an <see cref="IBeatmap"/> to have been applied via <see cref="JudgementProcessor.ApplyBeatmap"/> before use.
|
||||
/// </remarks>
|
||||
/// <param name="mode">The <see cref="ScoringMode"/> to represent the score as.</param>
|
||||
/// <param name="accuracyRatio">The accuracy percentage achieved by the player.</param>
|
||||
/// <param name="comboRatio">The proportion of the max combo achieved by the player.</param>
|
||||
/// <param name="statistics">Any statistics to be factored in.</param>
|
||||
/// <returns>The total score.</returns>
|
||||
/// <param name="comboRatio">The portion of the max combo achieved by the player.</param>
|
||||
/// <param name="statistics">Any additional statistics to be factored in.</param>
|
||||
/// <returns>The total score computed from the given scoring component ratios.</returns>
|
||||
public double GetScore(ScoringMode mode, double accuracyRatio, double comboRatio, Dictionary<HitResult, int> 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<HitResult, int> statistics, int totalHitObjects)
|
||||
/// <summary>
|
||||
/// Computes the total score from given scoring component ratios.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Does not require an <see cref="IBeatmap"/> to have been applied via <see cref="JudgementProcessor.ApplyBeatmap"/> before use.
|
||||
/// </remarks>
|
||||
/// <param name="mode">The <see cref="ScoringMode"/> to represent the score as.</param>
|
||||
/// <param name="accuracyRatio">The accuracy percentage achieved by the player.</param>
|
||||
/// <param name="comboRatio">The portion of the max combo achieved by the player.</param>
|
||||
/// <param name="statistics">Any additional statistics to be factored in.</param>
|
||||
/// <param name="totalBasicHitObjects">The total number of basic (non-tick and non-bonus) hitobjects in the beatmap.</param>
|
||||
/// <returns>The total score computed from the given scoring component ratios.</returns>
|
||||
public double GetScore(ScoringMode mode, double accuracyRatio, double comboRatio, int totalBasicHitObjects, Dictionary<HitResult, int> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum <see cref="HitResult"/> for a normal hit (i.e. not tick/bonus) for this ruleset. Only populated via <see cref="ResetFromReplayFrame"/>.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user