1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-15 14:42:56 +08:00

Add overloads to + document + expose ExtractScoringValues

This commit is contained in:
Dan Balasescu 2022-05-31 18:39:29 +09:00
parent d6d56ee22d
commit 28d8799e11

View File

@ -11,6 +11,7 @@ using osu.Framework.Bindables;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Extensions; using osu.Game.Extensions;
using osu.Game.Online.Spectator;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
@ -89,13 +90,16 @@ namespace osu.Game.Rulesets.Scoring
private readonly double comboPortion; private readonly double comboPortion;
/// <summary> /// <summary>
/// Maximum achievable scoring values. /// Scoring values for a perfect play.
/// </summary> /// </summary>
private ScoringValues maximumScoringValues; private ScoringValues maximumScoringValues;
/// <summary> /// <summary>
/// Maximum achievable scoring values up to the current point in time. /// Maximum achievable scoring values up to the current point in time.
/// </summary> /// </summary>
/// <remarks>
/// This is only used to determine the accuracy with respect to the current point in time for an ongoing play session.
/// </remarks>
private ScoringValues rollingMaximumScoringValues; private ScoringValues rollingMaximumScoringValues;
/// <summary> /// <summary>
@ -284,7 +288,7 @@ namespace osu.Game.Rulesets.Scoring
if (!ruleset.RulesetInfo.Equals(scoreInfo.Ruleset)) if (!ruleset.RulesetInfo.Equals(scoreInfo.Ruleset))
throw new ArgumentException($"Unexpected score ruleset. Expected \"{ruleset.RulesetInfo.ShortName}\" but was \"{scoreInfo.Ruleset.ShortName}\"."); throw new ArgumentException($"Unexpected score ruleset. Expected \"{ruleset.RulesetInfo.ShortName}\" but was \"{scoreInfo.Ruleset.ShortName}\".");
extractFromStatistics(scoreInfo.Statistics, out var current, out var maximum); extractScoringValues(scoreInfo.Statistics, out var current, out var maximum);
current.MaxCombo = scoreInfo.MaxCombo; current.MaxCombo = scoreInfo.MaxCombo;
return ComputeScore(mode, current, maximum); return ComputeScore(mode, current, maximum);
@ -307,7 +311,7 @@ namespace osu.Game.Rulesets.Scoring
if (!beatmapApplied) if (!beatmapApplied)
throw new InvalidOperationException($"Cannot compute partial score without calling {nameof(ApplyBeatmap)}."); throw new InvalidOperationException($"Cannot compute partial score without calling {nameof(ApplyBeatmap)}.");
extractFromStatistics(scoreInfo.Statistics, out var current, out _); extractScoringValues(scoreInfo.Statistics, out var current, out _);
current.MaxCombo = scoreInfo.MaxCombo; current.MaxCombo = scoreInfo.MaxCombo;
return ComputeScore(mode, current, maximumScoringValues); return ComputeScore(mode, current, maximumScoringValues);
@ -332,7 +336,7 @@ namespace osu.Game.Rulesets.Scoring
double accuracyRatio = scoreInfo.Accuracy; double accuracyRatio = scoreInfo.Accuracy;
double comboRatio = maxAchievableCombo > 0 ? (double)scoreInfo.MaxCombo / maxAchievableCombo : 1; double comboRatio = maxAchievableCombo > 0 ? (double)scoreInfo.MaxCombo / maxAchievableCombo : 1;
extractFromStatistics(scoreInfo.Statistics, out var current, out var maximum); extractScoringValues(scoreInfo.Statistics, out var current, out var maximum);
// For legacy osu!mania scores, a full-GREAT score has 100% accuracy. If combined with a full-combo, the score becomes indistinguishable from a full-PERFECT score. // For legacy osu!mania scores, a full-GREAT score has 100% accuracy. If combined with a full-combo, the score becomes indistinguishable from a full-PERFECT score.
// To get around this, the accuracy ratio is always recalculated based on the hit statistics rather than trusting the score. // To get around this, the accuracy ratio is always recalculated based on the hit statistics rather than trusting the score.
@ -452,7 +456,7 @@ namespace osu.Game.Rulesets.Scoring
if (frame.Header == null) if (frame.Header == null)
return; return;
extractFromStatistics(frame.Header.Statistics, out var current, out var maximum); extractScoringValues(frame.Header.Statistics, out var current, out var maximum);
currentScoringValues.BaseScore = current.BaseScore; currentScoringValues.BaseScore = current.BaseScore;
currentScoringValues.MaxCombo = frame.Header.MaxCombo; currentScoringValues.MaxCombo = frame.Header.MaxCombo;
rollingMaximumScoringValues.BaseScore = maximum.BaseScore; rollingMaximumScoringValues.BaseScore = maximum.BaseScore;
@ -468,7 +472,72 @@ namespace osu.Game.Rulesets.Scoring
OnResetFromReplayFrame?.Invoke(); OnResetFromReplayFrame?.Invoke();
} }
private void extractFromStatistics(IReadOnlyDictionary<HitResult, int> statistics, out ScoringValues current, out ScoringValues maximum) #region ScoringValue extraction
/// <summary>
/// Applies a best-effort extraction of hit statistics into <see cref="ScoringValues"/>.
/// </summary>
/// <remarks>
/// This method is useful in a variety of situations, with a few drawbacks that need to be considered:
/// <list type="bullet">
/// <item>The maximum <see cref="ScoringValues.BonusScore"/> will always be 0.</item>
/// <item>The current and maximum <see cref="ScoringValues.HitObjects"/> will always be the same value.</item>
/// </list>
/// Consumers are expected to more accurately fill in the above values through external means.
/// <para>
/// <b>Ensure</b> to fill in the maximum <see cref="ScoringValues.HitObjects"/> for use in
/// <see cref="ComputeScore(osu.Game.Rulesets.Scoring.ScoringMode,osu.Game.Scoring.ScoringValues,osu.Game.Scoring.ScoringValues)"/>.
/// </para>
/// </remarks>
/// <param name="scoreInfo">The score to extract scoring values from.</param>
/// <param name="current">The "current" scoring values, representing the hit statistics as they appear.</param>
/// <param name="maximum">The "maximum" scoring values, representing the hit statistics as if the maximum hit result was attained each time.</param>
public void ExtractScoringValues(ScoreInfo scoreInfo, out ScoringValues current, out ScoringValues maximum)
{
extractScoringValues(scoreInfo.Statistics, out current, out maximum);
current.MaxCombo = scoreInfo.MaxCombo;
}
/// <summary>
/// Applies a best-effort extraction of hit statistics into <see cref="ScoringValues"/>.
/// </summary>
/// <remarks>
/// This method is useful in a variety of situations, with a few drawbacks that need to be considered:
/// <list type="bullet">
/// <item>The maximum <see cref="ScoringValues.BonusScore"/> will always be 0.</item>
/// <item>The current and maximum <see cref="ScoringValues.HitObjects"/> will always be the same value.</item>
/// </list>
/// Consumers are expected to more accurately fill in the above values through external means.
/// <para>
/// <b>Ensure</b> to fill in the maximum <see cref="ScoringValues.HitObjects"/> for use in
/// <see cref="ComputeScore(osu.Game.Rulesets.Scoring.ScoringMode,osu.Game.Scoring.ScoringValues,osu.Game.Scoring.ScoringValues)"/>.
/// </para>
/// </remarks>
/// <param name="header">The replay frame header to extract scoring values from.</param>
/// <param name="current">The "current" scoring values, representing the hit statistics as they appear.</param>
/// <param name="maximum">The "maximum" scoring values, representing the hit statistics as if the maximum hit result was attained each time.</param>
public void ExtractScoringValues(FrameHeader header, out ScoringValues current, out ScoringValues maximum)
{
extractScoringValues(header.Statistics, out current, out maximum);
current.MaxCombo = header.MaxCombo;
}
/// <summary>
/// Applies a best-effort extraction of hit statistics into <see cref="ScoringValues"/>.
/// </summary>
/// <remarks>
/// This method is useful in a variety of situations, with a few drawbacks that need to be considered:
/// <list type="bullet">
/// <item>The current <see cref="ScoringValues.MaxCombo"/> will always be 0.</item>
/// <item>The maximum <see cref="ScoringValues.BonusScore"/> will always be 0.</item>
/// <item>The current and maximum <see cref="ScoringValues.HitObjects"/> will always be the same value.</item>
/// </list>
/// Consumers are expected to more accurately fill in the above values (especially the current <see cref="ScoringValues.MaxCombo"/>) via external means (e.g. <see cref="ScoreInfo"/>).
/// </remarks>
/// <param name="statistics">The hit statistics to extract scoring values from.</param>
/// <param name="current">The "current" scoring values, representing the hit statistics as they appear.</param>
/// <param name="maximum">The "maximum" scoring values, representing the hit statistics as if the maximum hit result was attained each time.</param>
private void extractScoringValues(IReadOnlyDictionary<HitResult, int> statistics, out ScoringValues current, out ScoringValues maximum)
{ {
current = default; current = default;
maximum = default; maximum = default;
@ -479,10 +548,7 @@ namespace osu.Game.Rulesets.Scoring
continue; continue;
if (result.IsBonus()) if (result.IsBonus())
{
current.BonusScore += count * Judgement.ToNumericResult(result); current.BonusScore += count * Judgement.ToNumericResult(result);
maximum.BonusScore += count * Judgement.ToNumericResult(result);
}
else else
{ {
// The maximum result of this judgement if it wasn't a miss. // The maximum result of this judgement if it wasn't a miss.
@ -511,10 +577,7 @@ namespace osu.Game.Rulesets.Scoring
} }
if (result.AffectsCombo()) if (result.AffectsCombo())
{
current.MaxCombo += count;
maximum.MaxCombo += count; maximum.MaxCombo += count;
}
if (result.IsBasic()) if (result.IsBasic())
{ {
@ -524,6 +587,8 @@ namespace osu.Game.Rulesets.Scoring
} }
} }
#endregion
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);