1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 12:33:01 +08:00

Remove abstract ScoreProcessor class (#7153)

Remove abstract ScoreProcessor class

Co-authored-by: Dean Herbert <pe@ppy.sh>
This commit is contained in:
Dean Herbert 2019-12-12 12:00:00 +09:00 committed by GitHub
commit 65c8855644
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 165 additions and 192 deletions

View File

@ -2,23 +2,21 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.Scoring namespace osu.Game.Rulesets.Catch.Scoring
{ {
public class CatchScoreProcessor : ScoreProcessor<CatchHitObject> public class CatchScoreProcessor : ScoreProcessor
{ {
public CatchScoreProcessor(DrawableRuleset<CatchHitObject> drawableRuleset) public CatchScoreProcessor(IBeatmap beatmap)
: base(drawableRuleset) : base(beatmap)
{ {
} }
private float hpDrainRate; private float hpDrainRate;
protected override void ApplyBeatmap(Beatmap<CatchHitObject> beatmap) protected override void ApplyBeatmap(IBeatmap beatmap)
{ {
base.ApplyBeatmap(beatmap); base.ApplyBeatmap(beatmap);

View File

@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.UI
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
} }
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(Beatmap);
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);

View File

@ -3,13 +3,11 @@
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Scoring namespace osu.Game.Rulesets.Mania.Scoring
{ {
internal class ManiaScoreProcessor : ScoreProcessor<ManiaHitObject> internal class ManiaScoreProcessor : ScoreProcessor
{ {
/// <summary> /// <summary>
/// The hit HP multiplier at OD = 0. /// The hit HP multiplier at OD = 0.
@ -51,12 +49,12 @@ namespace osu.Game.Rulesets.Mania.Scoring
/// </summary> /// </summary>
private double hpMultiplier = 1; private double hpMultiplier = 1;
public ManiaScoreProcessor(DrawableRuleset<ManiaHitObject> drawableRuleset) public ManiaScoreProcessor(IBeatmap beatmap)
: base(drawableRuleset) : base(beatmap)
{ {
} }
protected override void ApplyBeatmap(Beatmap<ManiaHitObject> beatmap) protected override void ApplyBeatmap(IBeatmap beatmap)
{ {
base.ApplyBeatmap(beatmap); base.ApplyBeatmap(beatmap);
@ -65,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max); hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
} }
protected override void SimulateAutoplay(Beatmap<ManiaHitObject> beatmap) protected override void SimulateAutoplay(IBeatmap beatmap)
{ {
while (true) while (true)
{ {

View File

@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages); protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages);
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(Beatmap);
public override int Variant => (int)(Beatmap.Stages.Count == 1 ? PlayfieldType.Single : PlayfieldType.Dual) + Beatmap.TotalColumns; public override int Variant => (int)(Beatmap.Stages.Count == 1 ? PlayfieldType.Single : PlayfieldType.Dual) + Beatmap.TotalColumns;

View File

@ -5,22 +5,20 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Osu.Scoring namespace osu.Game.Rulesets.Osu.Scoring
{ {
internal class OsuScoreProcessor : ScoreProcessor<OsuHitObject> internal class OsuScoreProcessor : ScoreProcessor
{ {
public OsuScoreProcessor(DrawableRuleset<OsuHitObject> drawableRuleset) public OsuScoreProcessor(IBeatmap beatmap)
: base(drawableRuleset) : base(beatmap)
{ {
} }
private float hpDrainRate; private float hpDrainRate;
protected override void ApplyBeatmap(Beatmap<OsuHitObject> beatmap) protected override void ApplyBeatmap(IBeatmap beatmap)
{ {
base.ApplyBeatmap(beatmap); base.ApplyBeatmap(beatmap);

View File

@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.UI
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor
public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(Beatmap);
protected override Playfield CreatePlayfield() => new OsuPlayfield(); protected override Playfield CreatePlayfield() => new OsuPlayfield();

View File

@ -1,15 +1,15 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Taiko.Scoring namespace osu.Game.Rulesets.Taiko.Scoring
{ {
internal class TaikoScoreProcessor : ScoreProcessor<TaikoHitObject> internal class TaikoScoreProcessor : ScoreProcessor
{ {
/// <summary> /// <summary>
/// A value used for calculating <see cref="hpMultiplier"/>. /// A value used for calculating <see cref="hpMultiplier"/>.
@ -31,16 +31,16 @@ namespace osu.Game.Rulesets.Taiko.Scoring
/// </summary> /// </summary>
private double hpMissMultiplier; private double hpMissMultiplier;
public TaikoScoreProcessor(DrawableRuleset<TaikoHitObject> drawableRuleset) public TaikoScoreProcessor(IBeatmap beatmap)
: base(drawableRuleset) : base(beatmap)
{ {
} }
protected override void ApplyBeatmap(Beatmap<TaikoHitObject> beatmap) protected override void ApplyBeatmap(IBeatmap beatmap)
{ {
base.ApplyBeatmap(beatmap); base.ApplyBeatmap(beatmap);
hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98)); hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.OfType<Hit>().Count() * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120); hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
} }

View File

@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.UI
new BarLineGenerator<BarLine>(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar))); new BarLineGenerator<BarLine>(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar)));
} }
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(Beatmap);
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer(); public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer();

View File

@ -13,13 +13,16 @@ using osu.Game.Beatmaps;
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;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring; using osu.Game.Scoring;
namespace osu.Game.Rulesets.Scoring namespace osu.Game.Rulesets.Scoring
{ {
public abstract class ScoreProcessor public class ScoreProcessor
{ {
private const double base_portion = 0.3;
private const double combo_portion = 0.7;
private const double max_score = 1000000;
/// <summary> /// <summary>
/// Invoked when the <see cref="ScoreProcessor"/> is in a failed state. /// Invoked when the <see cref="ScoreProcessor"/> is in a failed state.
/// This may occur regardless of whether an <see cref="AllJudged"/> event is invoked. /// This may occur regardless of whether an <see cref="AllJudged"/> event is invoked.
@ -67,11 +70,6 @@ namespace osu.Game.Rulesets.Scoring
/// </summary> /// </summary>
public readonly Bindable<IReadOnlyList<Mod>> Mods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>()); public readonly Bindable<IReadOnlyList<Mod>> Mods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
/// <summary>
/// Create a <see cref="HitWindows"/> for this processor.
/// </summary>
public virtual HitWindows CreateHitWindows() => new HitWindows();
/// <summary> /// <summary>
/// The current rank. /// The current rank.
/// </summary> /// </summary>
@ -90,132 +88,23 @@ namespace osu.Game.Rulesets.Scoring
/// <summary> /// <summary>
/// Whether all <see cref="Judgement"/>s have been processed. /// Whether all <see cref="Judgement"/>s have been processed.
/// </summary> /// </summary>
public virtual bool HasCompleted => false; public bool HasCompleted => JudgedHits == MaxHits;
/// <summary>
/// The total number of judged <see cref="HitObject"/>s at the current point in time.
/// </summary>
public int JudgedHits { get; protected set; }
/// <summary> /// <summary>
/// Whether this ScoreProcessor has already triggered the failed state. /// Whether this ScoreProcessor has already triggered the failed state.
/// </summary> /// </summary>
public virtual bool HasFailed { get; private set; } public bool HasFailed { get; private set; }
/// <summary> /// <summary>
/// The default conditions for failing. /// The maximum number of hits that can be judged.
/// </summary> /// </summary>
protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value);
protected ScoreProcessor()
{
Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); };
Accuracy.ValueChanged += delegate
{
Rank.Value = rankFrom(Accuracy.Value);
foreach (var mod in Mods.Value.OfType<IApplicableToScoreProcessor>())
Rank.Value = mod.AdjustRank(Rank.Value, Accuracy.Value);
};
}
private ScoreRank rankFrom(double acc)
{
if (acc == 1)
return ScoreRank.X;
if (acc > 0.95)
return ScoreRank.S;
if (acc > 0.9)
return ScoreRank.A;
if (acc > 0.8)
return ScoreRank.B;
if (acc > 0.7)
return ScoreRank.C;
return ScoreRank.D;
}
/// <summary>
/// Resets this ScoreProcessor to a default state.
/// </summary>
/// <param name="storeResults">Whether to store the current state of the <see cref="ScoreProcessor"/> for future use.</param>
protected virtual void Reset(bool storeResults)
{
TotalScore.Value = 0;
Accuracy.Value = 1;
Health.Value = 1;
Combo.Value = 0;
Rank.Value = ScoreRank.X;
HighestCombo.Value = 0;
JudgedHits = 0;
HasFailed = false;
}
/// <summary>
/// Checks if the score is in a failed state and notifies subscribers.
/// <para>
/// This can only ever notify subscribers once.
/// </para>
/// </summary>
protected void UpdateFailed(JudgementResult result)
{
if (HasFailed)
return;
if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true)
return;
if (Failed?.Invoke() != false)
HasFailed = true;
}
/// <summary>
/// Notifies subscribers of <see cref="NewJudgement"/> that a new judgement has occurred.
/// </summary>
/// <param name="result">The judgement scoring result to notify subscribers of.</param>
protected void NotifyNewJudgement(JudgementResult result)
{
NewJudgement?.Invoke(result);
if (HasCompleted)
AllJudged?.Invoke();
}
/// <summary>
/// Retrieve a score populated with data for the current play this processor is responsible for.
/// </summary>
public virtual void PopulateScore(ScoreInfo score)
{
score.TotalScore = (long)Math.Round(TotalScore.Value);
score.Combo = Combo.Value;
score.MaxCombo = HighestCombo.Value;
score.Accuracy = Math.Round(Accuracy.Value, 4);
score.Rank = Rank.Value;
score.Date = DateTimeOffset.Now;
var hitWindows = CreateHitWindows();
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
score.Statistics[result] = GetStatistic(result);
}
public abstract int GetStatistic(HitResult result);
public abstract double GetStandardisedScore();
}
public class ScoreProcessor<TObject> : ScoreProcessor
where TObject : HitObject
{
private const double base_portion = 0.3;
private const double combo_portion = 0.7;
private const double max_score = 1000000;
public sealed override bool HasCompleted => JudgedHits == MaxHits;
protected int MaxHits { get; private set; } protected int MaxHits { get; private set; }
/// <summary>
/// The total number of judged <see cref="HitObject"/>s at the current point in time.
/// </summary>
public int JudgedHits { get; private set; }
private double maxHighestCombo; private double maxHighestCombo;
private double maxBaseScore; private double maxBaseScore;
@ -225,17 +114,22 @@ namespace osu.Game.Rulesets.Scoring
private double scoreMultiplier = 1; private double scoreMultiplier = 1;
public ScoreProcessor(DrawableRuleset<TObject> drawableRuleset) public ScoreProcessor(IBeatmap beatmap)
{ {
Debug.Assert(base_portion + combo_portion == 1.0); Debug.Assert(base_portion + combo_portion == 1.0);
drawableRuleset.OnNewResult += applyResult; Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue);
drawableRuleset.OnRevertResult += revertResult; Accuracy.ValueChanged += accuracy =>
{
Rank.Value = rankFrom(accuracy.NewValue);
foreach (var mod in Mods.Value.OfType<IApplicableToScoreProcessor>())
Rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue);
};
ApplyBeatmap(drawableRuleset.Beatmap); ApplyBeatmap(beatmap);
Reset(false); Reset(false);
SimulateAutoplay(drawableRuleset.Beatmap); SimulateAutoplay(beatmap);
Reset(true); Reset(true);
if (maxBaseScore == 0 || maxHighestCombo == 0) if (maxBaseScore == 0 || maxHighestCombo == 0)
@ -257,19 +151,19 @@ namespace osu.Game.Rulesets.Scoring
} }
/// <summary> /// <summary>
/// Applies any properties of the <see cref="Beatmap{TObject}"/> which affect scoring to this <see cref="ScoreProcessor{TObject}"/>. /// Applies any properties of the <see cref="IBeatmap"/> which affect scoring to this <see cref="ScoreProcessor"/>.
/// </summary> /// </summary>
/// <param name="beatmap">The <see cref="Beatmap{TObject}"/> to read properties from.</param> /// <param name="beatmap">The <see cref="IBeatmap"/> to read properties from.</param>
protected virtual void ApplyBeatmap(Beatmap<TObject> beatmap) protected virtual void ApplyBeatmap(IBeatmap beatmap)
{ {
} }
/// <summary> /// <summary>
/// Simulates an autoplay of the <see cref="Beatmap{TObject}"/> to determine scoring values. /// Simulates an autoplay of the <see cref="IBeatmap"/> to determine scoring values.
/// </summary> /// </summary>
/// <remarks>This provided temporarily. DO NOT USE.</remarks> /// <remarks>This provided temporarily. DO NOT USE.</remarks>
/// <param name="beatmap">The <see cref="Beatmap{TObject}"/> to simulate.</param> /// <param name="beatmap">The <see cref="IBeatmap"/> to simulate.</param>
protected virtual void SimulateAutoplay(Beatmap<TObject> beatmap) protected virtual void SimulateAutoplay(IBeatmap beatmap)
{ {
foreach (var obj in beatmap.HitObjects) foreach (var obj in beatmap.HitObjects)
simulate(obj); simulate(obj);
@ -289,7 +183,7 @@ namespace osu.Game.Rulesets.Scoring
result.Type = judgement.MaxResult; result.Type = judgement.MaxResult;
applyResult(result); ApplyResult(result);
} }
} }
@ -297,22 +191,26 @@ namespace osu.Game.Rulesets.Scoring
/// Applies the score change of a <see cref="JudgementResult"/> to this <see cref="ScoreProcessor"/>. /// Applies the score change of a <see cref="JudgementResult"/> to this <see cref="ScoreProcessor"/>.
/// </summary> /// </summary>
/// <param name="result">The <see cref="JudgementResult"/> to apply.</param> /// <param name="result">The <see cref="JudgementResult"/> to apply.</param>
private void applyResult(JudgementResult result) public void ApplyResult(JudgementResult result)
{ {
ApplyResult(result); ApplyResultInternal(result);
updateScore();
UpdateFailed(result); updateScore();
NotifyNewJudgement(result); updateFailed(result);
NewJudgement?.Invoke(result);
if (HasCompleted)
AllJudged?.Invoke();
} }
/// <summary> /// <summary>
/// Reverts the score change of a <see cref="JudgementResult"/> that was applied to this <see cref="ScoreProcessor"/>. /// Reverts the score change of a <see cref="JudgementResult"/> that was applied to this <see cref="ScoreProcessor"/>.
/// </summary> /// </summary>
/// <param name="result">The judgement scoring result.</param> /// <param name="result">The judgement scoring result.</param>
private void revertResult(JudgementResult result) public void RevertResult(JudgementResult result)
{ {
RevertResult(result); RevertResultInternal(result);
updateScore(); updateScore();
} }
@ -322,10 +220,10 @@ namespace osu.Game.Rulesets.Scoring
/// Applies the score change of a <see cref="JudgementResult"/> to this <see cref="ScoreProcessor"/>. /// Applies the score change of a <see cref="JudgementResult"/> to this <see cref="ScoreProcessor"/>.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Any changes applied via this method can be reverted via <see cref="RevertResult"/>. /// Any changes applied via this method can be reverted via <see cref="RevertResultInternal"/>.
/// </remarks> /// </remarks>
/// <param name="result">The <see cref="JudgementResult"/> to apply.</param> /// <param name="result">The <see cref="JudgementResult"/> to apply.</param>
protected virtual void ApplyResult(JudgementResult result) protected virtual void ApplyResultInternal(JudgementResult result)
{ {
result.ComboAtJudgement = Combo.Value; result.ComboAtJudgement = Combo.Value;
result.HighestComboAtJudgement = HighestCombo.Value; result.HighestComboAtJudgement = HighestCombo.Value;
@ -372,10 +270,10 @@ namespace osu.Game.Rulesets.Scoring
} }
/// <summary> /// <summary>
/// Reverts the score change of a <see cref="JudgementResult"/> that was applied to this <see cref="ScoreProcessor"/> via <see cref="ApplyResult"/>. /// Reverts the score change of a <see cref="JudgementResult"/> that was applied to this <see cref="ScoreProcessor"/> via <see cref="ApplyResultInternal"/>.
/// </summary> /// </summary>
/// <param name="result">The judgement scoring result.</param> /// <param name="result">The judgement scoring result.</param>
protected virtual void RevertResult(JudgementResult result) protected virtual void RevertResultInternal(JudgementResult result)
{ {
Combo.Value = result.ComboAtJudgement; Combo.Value = result.ComboAtJudgement;
HighestCombo.Value = result.HighestComboAtJudgement; HighestCombo.Value = result.HighestComboAtJudgement;
@ -432,11 +330,49 @@ namespace osu.Game.Rulesets.Scoring
} }
} }
public override int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result); /// <summary>
/// Checks if the score is in a failed state and notifies subscribers.
/// <para>
/// This can only ever notify subscribers once.
/// </para>
/// </summary>
private void updateFailed(JudgementResult result)
{
if (HasFailed)
return;
public override double GetStandardisedScore() => getScore(ScoringMode.Standardised); if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true)
return;
protected override void Reset(bool storeResults) if (Failed?.Invoke() != false)
HasFailed = true;
}
private ScoreRank rankFrom(double acc)
{
if (acc == 1)
return ScoreRank.X;
if (acc > 0.95)
return ScoreRank.S;
if (acc > 0.9)
return ScoreRank.A;
if (acc > 0.8)
return ScoreRank.B;
if (acc > 0.7)
return ScoreRank.C;
return ScoreRank.D;
}
public int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result);
public double GetStandardisedScore() => getScore(ScoringMode.Standardised);
/// <summary>
/// Resets this ScoreProcessor to a default state.
/// </summary>
/// <param name="storeResults">Whether to store the current state of the <see cref="ScoreProcessor"/> for future use.</param>
protected virtual void Reset(bool storeResults)
{ {
scoreResultCounts.Clear(); scoreResultCounts.Clear();
@ -447,13 +383,49 @@ namespace osu.Game.Rulesets.Scoring
maxBaseScore = baseScore; maxBaseScore = baseScore;
} }
base.Reset(storeResults); JudgedHits = 0;
baseScore = 0; baseScore = 0;
rollingMaxBaseScore = 0; rollingMaxBaseScore = 0;
bonusScore = 0; bonusScore = 0;
TotalScore.Value = 0;
Accuracy.Value = 1;
Health.Value = 1;
Combo.Value = 0;
Rank.Value = ScoreRank.X;
HighestCombo.Value = 0;
HasFailed = false;
} }
/// <summary>
/// Retrieve a score populated with data for the current play this processor is responsible for.
/// </summary>
public virtual void PopulateScore(ScoreInfo score)
{
score.TotalScore = (long)Math.Round(TotalScore.Value);
score.Combo = Combo.Value;
score.MaxCombo = HighestCombo.Value;
score.Accuracy = Math.Round(Accuracy.Value, 4);
score.Rank = Rank.Value;
score.Date = DateTimeOffset.Now;
var hitWindows = CreateHitWindows();
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
score.Statistics[result] = GetStatistic(result);
}
/// <summary>
/// The default conditions for failing.
/// </summary>
protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value);
/// <summary>
/// Create a <see cref="HitWindows"/> for this processor.
/// </summary>
public virtual HitWindows CreateHitWindows() => new HitWindows();
/// <summary> /// <summary>
/// Creates the <see cref="JudgementResult"/> that represents the scoring result for a <see cref="HitObject"/>. /// Creates the <see cref="JudgementResult"/> that represents the scoring result for a <see cref="HitObject"/>.
/// </summary> /// </summary>

View File

@ -45,6 +45,10 @@ namespace osu.Game.Rulesets.UI
public abstract class DrawableRuleset<TObject> : DrawableRuleset, IProvideCursor, ICanAttachKeyCounter public abstract class DrawableRuleset<TObject> : DrawableRuleset, IProvideCursor, ICanAttachKeyCounter
where TObject : HitObject where TObject : HitObject
{ {
public override event Action<JudgementResult> OnNewResult;
public override event Action<JudgementResult> OnRevertResult;
/// <summary> /// <summary>
/// The selected variant. /// The selected variant.
/// </summary> /// </summary>
@ -91,16 +95,6 @@ namespace osu.Game.Rulesets.UI
} }
} }
/// <summary>
/// Invoked when a <see cref="JudgementResult"/> has been applied by a <see cref="DrawableHitObject"/>.
/// </summary>
public event Action<JudgementResult> OnNewResult;
/// <summary>
/// Invoked when a <see cref="JudgementResult"/> is being reverted by a <see cref="DrawableHitObject"/>.
/// </summary>
public event Action<JudgementResult> OnRevertResult;
/// <summary> /// <summary>
/// The beatmap. /// The beatmap.
/// </summary> /// </summary>
@ -309,7 +303,7 @@ namespace osu.Game.Rulesets.UI
/// <returns>The Playfield.</returns> /// <returns>The Playfield.</returns>
protected abstract Playfield CreatePlayfield(); protected abstract Playfield CreatePlayfield();
public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this); public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(Beatmap);
/// <summary> /// <summary>
/// Applies the active mods to this DrawableRuleset. /// Applies the active mods to this DrawableRuleset.
@ -366,6 +360,16 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
public abstract class DrawableRuleset : CompositeDrawable public abstract class DrawableRuleset : CompositeDrawable
{ {
/// <summary>
/// Invoked when a <see cref="JudgementResult"/> has been applied by a <see cref="DrawableHitObject"/>.
/// </summary>
public abstract event Action<JudgementResult> OnNewResult;
/// <summary>
/// Invoked when a <see cref="JudgementResult"/> is being reverted by a <see cref="DrawableHitObject"/>.
/// </summary>
public abstract event Action<JudgementResult> OnRevertResult;
/// <summary> /// <summary>
/// Whether a replay is currently loaded. /// Whether a replay is currently loaded.
/// </summary> /// </summary>

View File

@ -140,6 +140,9 @@ namespace osu.Game.Screens.Play
// bind clock into components that require it // bind clock into components that require it
DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused);
DrawableRuleset.OnNewResult += ScoreProcessor.ApplyResult;
DrawableRuleset.OnRevertResult += ScoreProcessor.RevertResult;
// Bind ScoreProcessor to ourselves // Bind ScoreProcessor to ourselves
ScoreProcessor.AllJudged += onCompletion; ScoreProcessor.AllJudged += onCompletion;
ScoreProcessor.Failed += onFail; ScoreProcessor.Failed += onFail;