From 1da8cc86900a3f3538f6c2b7d4edcaa10fcc4f13 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Dec 2019 19:02:11 +0900 Subject: [PATCH 01/12] Encapsulate common logic of ScoreProcessor --- .../Rulesets/Scoring/JudgementProcessor.cs | 126 +++++++++++++++++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 129 +++--------------- 2 files changed, 146 insertions(+), 109 deletions(-) create mode 100644 osu.Game/Rulesets/Scoring/JudgementProcessor.cs diff --git a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs new file mode 100644 index 0000000000..854b926846 --- /dev/null +++ b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs @@ -0,0 +1,126 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Extensions.TypeExtensions; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Scoring +{ + public abstract class JudgementProcessor + { + /// + /// The maximum number of hits that can be judged. + /// + protected int MaxHits { get; private set; } + + /// + /// The total number of judged s at the current point in time. + /// + public int JudgedHits { get; private set; } + + protected JudgementProcessor(IBeatmap beatmap) + { + ApplyBeatmap(beatmap); + + Reset(false); + SimulateAutoplay(beatmap); + Reset(true); + } + + /// + /// Applies any properties of the which affect scoring to this . + /// + /// The to read properties from. + protected virtual void ApplyBeatmap(IBeatmap beatmap) + { + } + + /// + /// Applies the score change of a to this . + /// + /// The to apply. + public void ApplyResult(JudgementResult result) + { + JudgedHits++; + + ApplyResultInternal(result); + } + + /// + /// Reverts the score change of a that was applied to this . + /// + /// The judgement scoring result. + public void RevertResult(JudgementResult result) + { + JudgedHits--; + + RevertResultInternal(result); + } + + /// + /// Applies the score change of a to this . + /// + /// + /// Any changes applied via this method can be reverted via . + /// + /// The to apply. + protected abstract void ApplyResultInternal(JudgementResult result); + + /// + /// Reverts the score change of a that was applied to this via . + /// + /// The judgement scoring result. + protected abstract void RevertResultInternal(JudgementResult result); + + /// + /// Resets this to a default state. + /// + /// Whether to store the current state of the for future use. + protected virtual void Reset(bool storeResults) + { + if (storeResults) + MaxHits = JudgedHits; + + JudgedHits = 0; + } + + /// + /// Creates the that represents the scoring result for a . + /// + /// The which was judged. + /// The that provides the scoring information. + protected virtual JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new JudgementResult(hitObject, judgement); + + /// + /// Simulates an autoplay of the to determine scoring values. + /// + /// This provided temporarily. DO NOT USE. + /// The to simulate. + protected virtual void SimulateAutoplay(IBeatmap beatmap) + { + foreach (var obj in beatmap.HitObjects) + simulate(obj); + + void simulate(HitObject obj) + { + foreach (var nested in obj.NestedHitObjects) + simulate(nested); + + var judgement = obj.CreateJudgement(); + if (judgement == null) + return; + + var result = CreateResult(obj, judgement); + if (result == null) + throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}."); + + result.Type = judgement.MaxResult; + + ApplyResult(result); + } + } + } +} diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index a8a2294498..f885a860ec 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Extensions; -using osu.Framework.Extensions.TypeExtensions; using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; @@ -17,7 +16,7 @@ using osu.Game.Scoring; namespace osu.Game.Rulesets.Scoring { - public class ScoreProcessor + public class ScoreProcessor : JudgementProcessor { private const double base_portion = 0.3; private const double combo_portion = 0.7; @@ -95,16 +94,6 @@ namespace osu.Game.Rulesets.Scoring /// public bool HasFailed { get; private set; } - /// - /// The maximum number of hits that can be judged. - /// - protected int MaxHits { get; private set; } - - /// - /// The total number of judged s at the current point in time. - /// - public int JudgedHits { get; private set; } - private double maxHighestCombo; private double maxBaseScore; @@ -115,8 +104,14 @@ namespace osu.Game.Rulesets.Scoring private double scoreMultiplier = 1; public ScoreProcessor(IBeatmap beatmap) + : base(beatmap) { Debug.Assert(base_portion + combo_portion == 1.0); + } + + protected override void ApplyBeatmap(IBeatmap beatmap) + { + base.ApplyBeatmap(beatmap); Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue); Accuracy.ValueChanged += accuracy => @@ -126,12 +121,6 @@ namespace osu.Game.Rulesets.Scoring Rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue); }; - ApplyBeatmap(beatmap); - - Reset(false); - SimulateAutoplay(beatmap); - Reset(true); - if (maxBaseScore == 0 || maxHighestCombo == 0) { Mode.Value = ScoringMode.Classic; @@ -150,80 +139,9 @@ namespace osu.Game.Rulesets.Scoring }; } - /// - /// Applies any properties of the which affect scoring to this . - /// - /// The to read properties from. - protected virtual void ApplyBeatmap(IBeatmap beatmap) - { - } - - /// - /// Simulates an autoplay of the to determine scoring values. - /// - /// This provided temporarily. DO NOT USE. - /// The to simulate. - protected virtual void SimulateAutoplay(IBeatmap beatmap) - { - foreach (var obj in beatmap.HitObjects) - simulate(obj); - - void simulate(HitObject obj) - { - foreach (var nested in obj.NestedHitObjects) - simulate(nested); - - var judgement = obj.CreateJudgement(); - if (judgement == null) - return; - - var result = CreateResult(obj, judgement); - if (result == null) - throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}."); - - result.Type = judgement.MaxResult; - - ApplyResult(result); - } - } - - /// - /// Applies the score change of a to this . - /// - /// The to apply. - public void ApplyResult(JudgementResult result) - { - ApplyResultInternal(result); - - updateScore(); - updateFailed(result); - - NewJudgement?.Invoke(result); - - if (HasCompleted) - AllJudged?.Invoke(); - } - - /// - /// Reverts the score change of a that was applied to this . - /// - /// The judgement scoring result. - public void RevertResult(JudgementResult result) - { - RevertResultInternal(result); - updateScore(); - } - private readonly Dictionary scoreResultCounts = new Dictionary(); - /// - /// Applies the score change of a to this . - /// - /// - /// Any changes applied via this method can be reverted via . - /// - /// The to apply. - protected virtual void ApplyResultInternal(JudgementResult result) + protected sealed override void ApplyResultInternal(JudgementResult result) { result.ComboAtJudgement = Combo.Value; result.HighestComboAtJudgement = HighestCombo.Value; @@ -233,8 +151,6 @@ namespace osu.Game.Rulesets.Scoring if (HasFailed) return; - JudgedHits++; - if (result.Judgement.AffectsCombo) { switch (result.Type) @@ -267,13 +183,17 @@ namespace osu.Game.Rulesets.Scoring } Health.Value += HealthAdjustmentFactorFor(result) * result.Judgement.HealthIncreaseFor(result); + + updateScore(); + updateFailed(result); + + NewJudgement?.Invoke(result); + + if (HasCompleted) + AllJudged?.Invoke(); } - /// - /// Reverts the score change of a that was applied to this via . - /// - /// The judgement scoring result. - protected virtual void RevertResultInternal(JudgementResult result) + protected sealed override void RevertResultInternal(JudgementResult result) { Combo.Value = result.ComboAtJudgement; HighestCombo.Value = result.HighestComboAtJudgement; @@ -284,8 +204,6 @@ namespace osu.Game.Rulesets.Scoring if (result.FailedAtJudgement) return; - JudgedHits--; - if (result.Judgement.IsBonus) { if (result.IsHit) @@ -372,18 +290,18 @@ namespace osu.Game.Rulesets.Scoring /// Resets this ScoreProcessor to a default state. /// /// Whether to store the current state of the for future use. - protected virtual void Reset(bool storeResults) + protected override void Reset(bool storeResults) { + base.Reset(storeResults); + scoreResultCounts.Clear(); if (storeResults) { - MaxHits = JudgedHits; maxHighestCombo = HighestCombo.Value; maxBaseScore = baseScore; } - JudgedHits = 0; baseScore = 0; rollingMaxBaseScore = 0; bonusScore = 0; @@ -425,13 +343,6 @@ namespace osu.Game.Rulesets.Scoring /// Create a for this processor. /// public virtual HitWindows CreateHitWindows() => new HitWindows(); - - /// - /// Creates the that represents the scoring result for a . - /// - /// The which was judged. - /// The that provides the scoring information. - protected virtual JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new JudgementResult(hitObject, judgement); } public enum ScoringMode From 76f2fb378fbbed997505b64fd2a6624270a32704 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Dec 2019 20:03:14 +0900 Subject: [PATCH 02/12] Separate score and health parts of ScoreProcessor --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 + .../Scoring/CatchHealthProcessor.cs | 38 +++++++ .../Scoring/CatchScoreProcessor.cs | 22 ---- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 + .../Scoring/ManiaHealthProcessor.cs | 69 ++++++++++++ .../Scoring/ManiaScoreProcessor.cs | 69 ------------ osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 6 +- .../Scoring/OsuHealthProcessor.cs | 54 ++++++++++ .../Scoring/OsuScoreProcessor.cs | 33 ------ .../Scoring/TaikoHealthProcessor.cs | 58 ++++++++++ .../Scoring/TaikoScoreProcessor.cs | 42 -------- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 + .../Visual/Gameplay/TestSceneFailAnimation.cs | 2 +- .../Visual/Gameplay/TestSceneFailJudgement.cs | 2 +- .../Visual/Gameplay/TestSceneHUDOverlay.cs | 2 +- .../Visual/Gameplay/TestScenePause.cs | 8 +- .../Mods/IApplicableToHealthProcessor.cs | 15 +++ osu.Game/Rulesets/Mods/ModEasy.cs | 9 +- osu.Game/Rulesets/Mods/ModPerfect.cs | 2 +- osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 11 +- osu.Game/Rulesets/Ruleset.cs | 6 ++ osu.Game/Rulesets/Scoring/HealthProcessor.cs | 102 ++++++++++++++++++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 66 +----------- osu.Game/Screens/Play/HUDOverlay.cs | 17 ++- osu.Game/Screens/Play/Player.cs | 30 ++++-- 25 files changed, 404 insertions(+), 265 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs create mode 100644 osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs create mode 100644 osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs create mode 100644 osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs create mode 100644 osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs create mode 100644 osu.Game/Rulesets/Scoring/HealthProcessor.cs diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index b8a844cb86..26e6112f83 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Catch public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new CatchScoreProcessor(beatmap); + public override HealthProcessor CreateHealthProcessor(IBeatmap beatmap) => new CatchHealthProcessor(beatmap); + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap); diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs new file mode 100644 index 0000000000..49ba0f6122 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs @@ -0,0 +1,38 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Catch.Scoring +{ + public class CatchHealthProcessor : HealthProcessor + { + public CatchHealthProcessor(IBeatmap beatmap) + : base(beatmap) + { + } + + private float hpDrainRate; + + protected override void ApplyBeatmap(IBeatmap beatmap) + { + base.ApplyBeatmap(beatmap); + + hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; + } + + protected override double HealthAdjustmentFactorFor(JudgementResult result) + { + switch (result.Type) + { + case HitResult.Miss: + return hpDrainRate; + + default: + return 10.2 - hpDrainRate; // Award less HP as drain rate is increased + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index f67ca1213e..ad7520d57d 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Catch.Scoring @@ -14,27 +13,6 @@ namespace osu.Game.Rulesets.Catch.Scoring { } - private float hpDrainRate; - - protected override void ApplyBeatmap(IBeatmap beatmap) - { - base.ApplyBeatmap(beatmap); - - hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; - } - - protected override double HealthAdjustmentFactorFor(JudgementResult result) - { - switch (result.Type) - { - case HitResult.Miss: - return hpDrainRate; - - default: - return 10.2 - hpDrainRate; // Award less HP as drain rate is increased - } - } - public override HitWindows CreateHitWindows() => new CatchHitWindows(); } } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index bf630cf892..0ec1264cce 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -37,6 +37,8 @@ namespace osu.Game.Rulesets.Mania public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new ManiaScoreProcessor(beatmap); + public override HealthProcessor CreateHealthProcessor(IBeatmap beatmap) => new ManiaHealthProcessor(beatmap); + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap); public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score); diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs new file mode 100644 index 0000000000..c362c906a4 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs @@ -0,0 +1,69 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mania.Scoring +{ + public class ManiaHealthProcessor : HealthProcessor + { + /// + /// The hit HP multiplier at OD = 0. + /// + private const double hp_multiplier_min = 0.75; + + /// + /// The hit HP multiplier at OD = 0. + /// + private const double hp_multiplier_mid = 0.85; + + /// + /// The hit HP multiplier at OD = 0. + /// + private const double hp_multiplier_max = 1; + + /// + /// The MISS HP multiplier at OD = 0. + /// + private const double hp_multiplier_miss_min = 0.5; + + /// + /// The MISS HP multiplier at OD = 5. + /// + private const double hp_multiplier_miss_mid = 0.75; + + /// + /// The MISS HP multiplier at OD = 10. + /// + private const double hp_multiplier_miss_max = 1; + + /// + /// The MISS HP multiplier. This is multiplied to the miss hp increase. + /// + private double hpMissMultiplier = 1; + + /// + /// The HIT HP multiplier. This is multiplied to hit hp increases. + /// + private double hpMultiplier = 1; + + public ManiaHealthProcessor(IBeatmap beatmap) + : base(beatmap) + { + } + + protected override void ApplyBeatmap(IBeatmap beatmap) + { + base.ApplyBeatmap(beatmap); + + BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty; + hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max); + hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max); + } + + protected override double HealthAdjustmentFactorFor(JudgementResult result) + => result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier; + } +} diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index a678ef60e7..97f1ea721c 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -2,86 +2,17 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Scoring { internal class ManiaScoreProcessor : ScoreProcessor { - /// - /// The hit HP multiplier at OD = 0. - /// - private const double hp_multiplier_min = 0.75; - - /// - /// The hit HP multiplier at OD = 0. - /// - private const double hp_multiplier_mid = 0.85; - - /// - /// The hit HP multiplier at OD = 0. - /// - private const double hp_multiplier_max = 1; - - /// - /// The MISS HP multiplier at OD = 0. - /// - private const double hp_multiplier_miss_min = 0.5; - - /// - /// The MISS HP multiplier at OD = 5. - /// - private const double hp_multiplier_miss_mid = 0.75; - - /// - /// The MISS HP multiplier at OD = 10. - /// - private const double hp_multiplier_miss_max = 1; - - /// - /// The MISS HP multiplier. This is multiplied to the miss hp increase. - /// - private double hpMissMultiplier = 1; - - /// - /// The HIT HP multiplier. This is multiplied to hit hp increases. - /// - private double hpMultiplier = 1; - public ManiaScoreProcessor(IBeatmap beatmap) : base(beatmap) { } - protected override void ApplyBeatmap(IBeatmap beatmap) - { - base.ApplyBeatmap(beatmap); - - BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty; - hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max); - hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max); - } - - protected override void SimulateAutoplay(IBeatmap beatmap) - { - while (true) - { - base.SimulateAutoplay(beatmap); - - if (!HasFailed) - break; - - hpMultiplier *= 1.01; - hpMissMultiplier *= 0.98; - - Reset(false); - } - } - - protected override double HealthAdjustmentFactorFor(JudgementResult result) - => result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier; - public override HitWindows CreateHitWindows() => new ManiaHitWindows(); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index 63110b2797..831e4a700f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -18,7 +18,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModBlinds : Mod, IApplicableToDrawableRuleset, IApplicableToScoreProcessor + public class OsuModBlinds : Mod, IApplicableToDrawableRuleset, IApplicableToHealthProcessor { public override string Name => "Blinds"; public override string Description => "Play with blinds on your screen."; @@ -37,9 +37,9 @@ namespace osu.Game.Rulesets.Osu.Mods drawableRuleset.Overlays.Add(blinds = new DrawableOsuBlinds(drawableRuleset.Playfield.HitObjectContainer, drawableRuleset.Beatmap)); } - public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) + public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { - scoreProcessor.Health.ValueChanged += health => { blinds.AnimateClosedness((float)health.NewValue); }; + healthProcessor.Health.ValueChanged += health => { blinds.AnimateClosedness((float)health.NewValue); }; } public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs new file mode 100644 index 0000000000..36ccc80af6 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -0,0 +1,54 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Scoring +{ + public class OsuHealthProcessor : HealthProcessor + { + public OsuHealthProcessor(IBeatmap beatmap) + : base(beatmap) + { + } + + private float hpDrainRate; + + protected override void ApplyBeatmap(IBeatmap beatmap) + { + base.ApplyBeatmap(beatmap); + + hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; + } + + protected override double HealthAdjustmentFactorFor(JudgementResult result) + { + switch (result.Type) + { + case HitResult.Great: + return 10.2 - hpDrainRate; + + case HitResult.Good: + return 8 - hpDrainRate; + + case HitResult.Meh: + return 4 - hpDrainRate; + + // case HitResult.SliderTick: + // return Math.Max(7 - hpDrainRate, 0) * 0.01; + + case HitResult.Miss: + return hpDrainRate; + + default: + return 0; + } + } + + protected override JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new OsuJudgementResult(hitObject, judgement); + } +} diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index 6779271cb3..4593364e42 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -16,39 +16,6 @@ namespace osu.Game.Rulesets.Osu.Scoring { } - private float hpDrainRate; - - protected override void ApplyBeatmap(IBeatmap beatmap) - { - base.ApplyBeatmap(beatmap); - - hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; - } - - protected override double HealthAdjustmentFactorFor(JudgementResult result) - { - switch (result.Type) - { - case HitResult.Great: - return 10.2 - hpDrainRate; - - case HitResult.Good: - return 8 - hpDrainRate; - - case HitResult.Meh: - return 4 - hpDrainRate; - - // case HitResult.SliderTick: - // return Math.Max(7 - hpDrainRate, 0) * 0.01; - - case HitResult.Miss: - return hpDrainRate; - - default: - return 0; - } - } - protected override JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new OsuJudgementResult(hitObject, judgement); public override HitWindows CreateHitWindows() => new OsuHitWindows(); diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs new file mode 100644 index 0000000000..c8aa32a678 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs @@ -0,0 +1,58 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Scoring +{ + public class TaikoHealthProcessor : HealthProcessor + { + /// + /// A value used for calculating . + /// + private const double object_count_factor = 3; + + /// + /// Taiko fails at the end of the map if the player has not half-filled their HP bar. + /// + protected override bool DefaultFailCondition => JudgedHits == MaxHits && Health.Value <= 0.5; + + /// + /// HP multiplier for a successful . + /// + private double hpMultiplier; + + /// + /// HP multiplier for a . + /// + private double hpMissMultiplier; + + public TaikoHealthProcessor(IBeatmap beatmap) + : base(beatmap) + { + } + + protected override void ApplyBeatmap(IBeatmap beatmap) + { + base.ApplyBeatmap(beatmap); + + hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.OfType().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); + } + + protected override double HealthAdjustmentFactorFor(JudgementResult result) + => result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier; + + protected override void Reset(bool storeResults) + { + base.Reset(storeResults); + + Health.Value = 0; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index ae593d2e3a..10011d2669 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -1,60 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Scoring { internal class TaikoScoreProcessor : ScoreProcessor { - /// - /// A value used for calculating . - /// - private const double object_count_factor = 3; - - /// - /// Taiko fails at the end of the map if the player has not half-filled their HP bar. - /// - protected override bool DefaultFailCondition => JudgedHits == MaxHits && Health.Value <= 0.5; - - /// - /// HP multiplier for a successful . - /// - private double hpMultiplier; - - /// - /// HP multiplier for a . - /// - private double hpMissMultiplier; - public TaikoScoreProcessor(IBeatmap beatmap) : base(beatmap) { } - protected override void ApplyBeatmap(IBeatmap beatmap) - { - base.ApplyBeatmap(beatmap); - - hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.OfType().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); - } - - protected override double HealthAdjustmentFactorFor(JudgementResult result) - => result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier; - - protected override void Reset(bool storeResults) - { - base.Reset(storeResults); - - Health.Value = 0; - } - public override HitWindows CreateHitWindows() => new TaikoHitWindows(); } } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index ca7ab30867..0644f7fea6 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Taiko public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new TaikoScoreProcessor(beatmap); + public override HealthProcessor CreateHealthProcessor(IBeatmap beatmap) => new TaikoHealthProcessor(beatmap); + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap); public const string SHORT_NAME = "taiko"; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs index 992c47f856..81050b1637 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected override void LoadComplete() { base.LoadComplete(); - ScoreProcessor.FailConditions += (_, __) => true; + HealthProcessor.FailConditions += (_, __) => true; } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs index 1580aac8c5..a0c25521a6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected override void LoadComplete() { base.LoadComplete(); - ScoreProcessor.FailConditions += (_, __) => true; + HealthProcessor.FailConditions += (_, __) => true; } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs index 39c42980ab..ee58219cd3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create overlay", () => { - Child = hudOverlay = new HUDOverlay(null, null, Array.Empty()); + Child = hudOverlay = new HUDOverlay(null, null, null, Array.Empty()); action?.Invoke(hudOverlay); }); diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index e04315894e..1a83e35e4f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestResumeWithResumeOverlay() { AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre)); - AddUntilStep("wait for hitobjects", () => Player.ScoreProcessor.Health.Value < 1); + AddUntilStep("wait for hitobjects", () => Player.HealthProcessor.Health.Value < 1); pauseAndConfirm(); resume(); @@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestPauseWithResumeOverlay() { AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre)); - AddUntilStep("wait for hitobjects", () => Player.ScoreProcessor.Health.Value < 1); + AddUntilStep("wait for hitobjects", () => Player.HealthProcessor.Health.Value < 1); pauseAndConfirm(); @@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("move cursor to button", () => InputManager.MoveMouseTo(Player.HUDOverlay.HoldToQuit.Children.OfType().First().ScreenSpaceDrawQuad.Centre)); - AddUntilStep("wait for hitobjects", () => Player.ScoreProcessor.Health.Value < 1); + AddUntilStep("wait for hitobjects", () => Player.HealthProcessor.Health.Value < 1); pauseAndConfirm(); resumeAndConfirm(); @@ -285,7 +285,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected class PausePlayer : TestPlayer { - public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; + public new HealthProcessor HealthProcessor => base.HealthProcessor; public new HUDOverlay HUDOverlay => base.HUDOverlay; diff --git a/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs new file mode 100644 index 0000000000..a181955653 --- /dev/null +++ b/osu.Game/Rulesets/Mods/IApplicableToHealthProcessor.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mods +{ + public interface IApplicableToHealthProcessor : IApplicableMod + { + /// + /// Provide a to a mod. Called once on initialisation of a play instance. + /// + void ApplyToHealthProcessor(HealthProcessor healthProcessor); + } +} diff --git a/osu.Game/Rulesets/Mods/ModEasy.cs b/osu.Game/Rulesets/Mods/ModEasy.cs index a55ebc51d6..494bebbf10 100644 --- a/osu.Game/Rulesets/Mods/ModEasy.cs +++ b/osu.Game/Rulesets/Mods/ModEasy.cs @@ -7,11 +7,10 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; namespace osu.Game.Rulesets.Mods { - public abstract class ModEasy : Mod, IApplicableToDifficulty, IApplicableFailOverride, IApplicableToScoreProcessor + public abstract class ModEasy : Mod, IApplicableToDifficulty, IApplicableFailOverride, IApplicableToHealthProcessor { public override string Name => "Easy"; public override string Acronym => "EZ"; @@ -49,11 +48,9 @@ namespace osu.Game.Rulesets.Mods public bool RestartOnFail => false; - public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) + public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { - health = scoreProcessor.Health.GetBoundCopy(); + health = healthProcessor.Health.GetBoundCopy(); } - - public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; } } diff --git a/osu.Game/Rulesets/Mods/ModPerfect.cs b/osu.Game/Rulesets/Mods/ModPerfect.cs index 0994d1f7d3..afa263f1c9 100644 --- a/osu.Game/Rulesets/Mods/ModPerfect.cs +++ b/osu.Game/Rulesets/Mods/ModPerfect.cs @@ -15,6 +15,6 @@ namespace osu.Game.Rulesets.Mods public override IconUsage Icon => OsuIcon.ModPerfect; public override string Description => "SS or quit."; - protected override bool FailCondition(ScoreProcessor scoreProcessor, JudgementResult result) => scoreProcessor.Accuracy.Value != 1; + protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) => result.Type != result.Judgement.MaxResult; } } diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index c4c4ab1f04..a4d0631d8c 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -6,11 +6,10 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; -using osu.Game.Scoring; namespace osu.Game.Rulesets.Mods { - public abstract class ModSuddenDeath : Mod, IApplicableToScoreProcessor, IApplicableFailOverride + public abstract class ModSuddenDeath : Mod, IApplicableToHealthProcessor, IApplicableFailOverride { public override string Name => "Sudden Death"; public override string Acronym => "SD"; @@ -24,13 +23,11 @@ namespace osu.Game.Rulesets.Mods public bool AllowFail => true; public bool RestartOnFail => true; - public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) + public void ApplyToHealthProcessor(HealthProcessor healthProcessor) { - scoreProcessor.FailConditions += FailCondition; + healthProcessor.FailConditions += FailCondition; } - public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; - - protected virtual bool FailCondition(ScoreProcessor scoreProcessor, JudgementResult result) => scoreProcessor.Combo.Value == 0 && result.Judgement.AffectsCombo; + protected virtual bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) => !result.IsHit && result.Judgement.AffectsCombo; } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 7ad93379f0..a98412b434 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -69,6 +69,12 @@ namespace osu.Game.Rulesets /// The score processor. public virtual ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new ScoreProcessor(beatmap); + /// + /// Creates a for a beatmap converted to this ruleset. + /// + /// The health processor. + public virtual HealthProcessor CreateHealthProcessor(IBeatmap beatmap) => new HealthProcessor(beatmap); + /// /// Creates a to convert a to one that is applicable for this . /// diff --git a/osu.Game/Rulesets/Scoring/HealthProcessor.cs b/osu.Game/Rulesets/Scoring/HealthProcessor.cs new file mode 100644 index 0000000000..8af8177083 --- /dev/null +++ b/osu.Game/Rulesets/Scoring/HealthProcessor.cs @@ -0,0 +1,102 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Bindables; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; + +namespace osu.Game.Rulesets.Scoring +{ + public class HealthProcessor : JudgementProcessor + { + /// + /// Invoked when the is in a failed state. + /// Return true if the fail was permitted. + /// + public event Func Failed; + + /// + /// Additional conditions on top of that cause a failing state. + /// + public event Func FailConditions; + + /// + /// The current health. + /// + public readonly BindableDouble Health = new BindableDouble(1) { MinValue = 0, MaxValue = 1 }; + + /// + /// Whether this ScoreProcessor has already triggered the failed state. + /// + public bool HasFailed { get; private set; } + + public HealthProcessor(IBeatmap beatmap) + : base(beatmap) + { + } + + protected override void ApplyResultInternal(JudgementResult result) + { + result.HealthAtJudgement = Health.Value; + result.FailedAtJudgement = HasFailed; + + if (HasFailed) + return; + + Health.Value += HealthAdjustmentFactorFor(result) * result.Judgement.HealthIncreaseFor(result); + + if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true) + return; + + if (Failed?.Invoke() != false) + HasFailed = true; + } + + protected override void RevertResultInternal(JudgementResult result) + { + Health.Value = result.HealthAtJudgement; + + // Todo: Revert HasFailed state with proper player support + } + + /// + /// An adjustment factor which is multiplied into the health increase provided by a . + /// + /// The for which the adjustment should apply. + /// The adjustment factor. + protected virtual double HealthAdjustmentFactorFor(JudgementResult result) => 1; + + /// + /// The default conditions for failing. + /// + protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value); + + protected override void Reset(bool storeResults) + { + base.Reset(storeResults); + + Health.Value = 1; + HasFailed = false; + } + + /// + /// Checks if the score is in a failed state and notifies subscribers. + /// + /// This can only ever notify subscribers once. + /// + /// + private void updateFailed(JudgementResult result) + { + if (HasFailed) + return; + + if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true) + return; + + if (Failed?.Invoke() != false) + HasFailed = true; + } + } +} diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index f885a860ec..945c21e196 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Extensions; -using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; @@ -22,13 +21,6 @@ namespace osu.Game.Rulesets.Scoring private const double combo_portion = 0.7; private const double max_score = 1000000; - /// - /// Invoked when the is in a failed state. - /// This may occur regardless of whether an event is invoked. - /// Return true if the fail was permitted. - /// - public event Func Failed; - /// /// Invoked when all s have been judged. /// @@ -39,11 +31,6 @@ namespace osu.Game.Rulesets.Scoring /// public event Action NewJudgement; - /// - /// Additional conditions on top of that cause a failing state. - /// - public event Func FailConditions; - /// /// The current total score. /// @@ -54,11 +41,6 @@ namespace osu.Game.Rulesets.Scoring /// public readonly BindableDouble Accuracy = new BindableDouble(1) { MinValue = 0, MaxValue = 1 }; - /// - /// The current health. - /// - public readonly BindableDouble Health = new BindableDouble(1) { MinValue = 0, MaxValue = 1 }; - /// /// The current combo. /// @@ -89,11 +71,6 @@ namespace osu.Game.Rulesets.Scoring /// public bool HasCompleted => JudgedHits == MaxHits; - /// - /// Whether this ScoreProcessor has already triggered the failed state. - /// - public bool HasFailed { get; private set; } - private double maxHighestCombo; private double maxBaseScore; @@ -145,10 +122,8 @@ namespace osu.Game.Rulesets.Scoring { result.ComboAtJudgement = Combo.Value; result.HighestComboAtJudgement = HighestCombo.Value; - result.HealthAtJudgement = Health.Value; - result.FailedAtJudgement = HasFailed; - if (HasFailed) + if (result.FailedAtJudgement) return; if (result.Judgement.AffectsCombo) @@ -182,10 +157,7 @@ namespace osu.Game.Rulesets.Scoring rollingMaxBaseScore += result.Judgement.MaxNumericResult; } - Health.Value += HealthAdjustmentFactorFor(result) * result.Judgement.HealthIncreaseFor(result); - updateScore(); - updateFailed(result); NewJudgement?.Invoke(result); @@ -197,9 +169,6 @@ namespace osu.Game.Rulesets.Scoring { Combo.Value = result.ComboAtJudgement; HighestCombo.Value = result.HighestComboAtJudgement; - Health.Value = result.HealthAtJudgement; - - // Todo: Revert HasFailed state with proper player support if (result.FailedAtJudgement) return; @@ -219,13 +188,6 @@ namespace osu.Game.Rulesets.Scoring } } - /// - /// An adjustment factor which is multiplied into the health increase provided by a . - /// - /// The for which the adjustment should apply. - /// The adjustment factor. - protected virtual double HealthAdjustmentFactorFor(JudgementResult result) => 1; - private void updateScore() { if (rollingMaxBaseScore != 0) @@ -248,24 +210,6 @@ namespace osu.Game.Rulesets.Scoring } } - /// - /// Checks if the score is in a failed state and notifies subscribers. - /// - /// This can only ever notify subscribers once. - /// - /// - private void updateFailed(JudgementResult result) - { - if (HasFailed) - return; - - if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true) - return; - - if (Failed?.Invoke() != false) - HasFailed = true; - } - private ScoreRank rankFrom(double acc) { if (acc == 1) @@ -308,12 +252,9 @@ namespace osu.Game.Rulesets.Scoring TotalScore.Value = 0; Accuracy.Value = 1; - Health.Value = 1; Combo.Value = 0; Rank.Value = ScoreRank.X; HighestCombo.Value = 0; - - HasFailed = false; } /// @@ -334,11 +275,6 @@ namespace osu.Game.Rulesets.Scoring score.Statistics[result] = GetStatistic(result); } - /// - /// The default conditions for failing. - /// - protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value); - /// /// Create a for this processor. /// diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index e2f362780d..236bdc8442 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -41,6 +41,7 @@ namespace osu.Game.Screens.Play public Bindable ShowHealthbar = new Bindable(true); private readonly ScoreProcessor scoreProcessor; + private readonly HealthProcessor healthProcessor; private readonly DrawableRuleset drawableRuleset; private readonly IReadOnlyList mods; @@ -63,9 +64,10 @@ namespace osu.Game.Screens.Play private IEnumerable hideTargets => new Drawable[] { visibilityContainer, KeyCounter }; - public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, IReadOnlyList mods) + public HUDOverlay(ScoreProcessor scoreProcessor, HealthProcessor healthProcessor, DrawableRuleset drawableRuleset, IReadOnlyList mods) { this.scoreProcessor = scoreProcessor; + this.healthProcessor = healthProcessor; this.drawableRuleset = drawableRuleset; this.mods = mods; @@ -119,7 +121,10 @@ namespace osu.Game.Screens.Play private void load(OsuConfigManager config, NotificationOverlay notificationOverlay) { if (scoreProcessor != null) - BindProcessor(scoreProcessor); + BindScoreProcessor(scoreProcessor); + + if (healthProcessor != null) + BindHealthProcessor(healthProcessor); if (drawableRuleset != null) { @@ -288,15 +293,19 @@ namespace osu.Game.Screens.Play protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); - protected virtual void BindProcessor(ScoreProcessor processor) + protected virtual void BindScoreProcessor(ScoreProcessor processor) { ScoreCounter?.Current.BindTo(processor.TotalScore); AccuracyCounter?.Current.BindTo(processor.Accuracy); ComboCounter?.Current.BindTo(processor.Combo); - HealthDisplay?.Current.BindTo(processor.Health); if (HealthDisplay is StandardHealthDisplay shd) processor.NewJudgement += shd.Flash; } + + protected virtual void BindHealthProcessor(HealthProcessor processor) + { + HealthDisplay?.Current.BindTo(processor.Health); + } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 5dfdeb5ebc..b64604728c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -72,6 +72,9 @@ namespace osu.Game.Screens.Play public BreakOverlay BreakOverlay; protected ScoreProcessor ScoreProcessor { get; private set; } + + protected HealthProcessor HealthProcessor { get; private set; } + protected DrawableRuleset DrawableRuleset { get; private set; } protected HUDOverlay HUDOverlay { get; private set; } @@ -131,6 +134,8 @@ namespace osu.Game.Screens.Play ScoreProcessor = ruleset.CreateScoreProcessor(playableBeatmap); ScoreProcessor.Mods.BindTo(Mods); + HealthProcessor = ruleset.CreateHealthProcessor(playableBeatmap); + if (!ScoreProcessor.Mode.Disabled) config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode); @@ -145,15 +150,28 @@ namespace osu.Game.Screens.Play // bind clock into components that require it DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); - DrawableRuleset.OnNewResult += ScoreProcessor.ApplyResult; - DrawableRuleset.OnRevertResult += ScoreProcessor.RevertResult; + DrawableRuleset.OnNewResult += r => + { + HealthProcessor.ApplyResult(r); + ScoreProcessor.ApplyResult(r); + }; - // Bind ScoreProcessor to ourselves + DrawableRuleset.OnRevertResult += r => + { + HealthProcessor.RevertResult(r); + ScoreProcessor.RevertResult(r); + }; + + // Bind the judgement processors to ourselves ScoreProcessor.AllJudged += onCompletion; - ScoreProcessor.Failed += onFail; + HealthProcessor.Failed += onFail; foreach (var mod in Mods.Value.OfType()) mod.ApplyToScoreProcessor(ScoreProcessor); + + foreach (var mod in Mods.Value.OfType()) + mod.ApplyToHealthProcessor(HealthProcessor); + BreakOverlay.IsBreakTime.ValueChanged += _ => updatePauseOnFocusLostState(); } @@ -197,7 +215,7 @@ namespace osu.Game.Screens.Play // display the cursor above some HUD elements. DrawableRuleset.Cursor?.CreateProxy() ?? new Container(), DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(), - HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, Mods.Value) + HUDOverlay = new HUDOverlay(ScoreProcessor, HealthProcessor, DrawableRuleset, Mods.Value) { HoldToQuit = { @@ -342,7 +360,7 @@ namespace osu.Game.Screens.Play private void onCompletion() { // Only show the completion screen if the player hasn't failed - if (ScoreProcessor.HasFailed || completionProgressDelegate != null) + if (HealthProcessor.HasFailed || completionProgressDelegate != null) return; ValidForResume = false; From 04c3a6f8a4c58672cca7b98085548e00197554ce Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Dec 2019 20:18:17 +0900 Subject: [PATCH 03/12] Move more properties to the base class --- .../Rulesets/Scoring/JudgementProcessor.cs | 20 ++++++++++++++++++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 21 ------------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs index 854b926846..c7ac466eb0 100644 --- a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs +++ b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs @@ -11,6 +11,16 @@ namespace osu.Game.Rulesets.Scoring { public abstract class JudgementProcessor { + /// + /// Invoked when all s have been judged by this . + /// + public event Action AllJudged; + + /// + /// Invoked when a new judgement has occurred. This occurs after the judgement has been processed by this . + /// + public event Action NewJudgement; + /// /// The maximum number of hits that can be judged. /// @@ -21,6 +31,11 @@ namespace osu.Game.Rulesets.Scoring /// public int JudgedHits { get; private set; } + /// + /// Whether all s have been processed. + /// + public bool HasCompleted => JudgedHits == MaxHits; + protected JudgementProcessor(IBeatmap beatmap) { ApplyBeatmap(beatmap); @@ -47,6 +62,11 @@ namespace osu.Game.Rulesets.Scoring JudgedHits++; ApplyResultInternal(result); + + NewJudgement?.Invoke(result); + + if (HasCompleted) + AllJudged?.Invoke(); } /// diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 945c21e196..093ba16e57 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -10,7 +10,6 @@ using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; using osu.Game.Scoring; namespace osu.Game.Rulesets.Scoring @@ -21,16 +20,6 @@ namespace osu.Game.Rulesets.Scoring private const double combo_portion = 0.7; private const double max_score = 1000000; - /// - /// Invoked when all s have been judged. - /// - public event Action AllJudged; - - /// - /// Invoked when a new judgement has occurred. This occurs after the judgement has been processed by the . - /// - public event Action NewJudgement; - /// /// The current total score. /// @@ -66,11 +55,6 @@ namespace osu.Game.Rulesets.Scoring /// public readonly Bindable Mode = new Bindable(); - /// - /// Whether all s have been processed. - /// - public bool HasCompleted => JudgedHits == MaxHits; - private double maxHighestCombo; private double maxBaseScore; @@ -158,11 +142,6 @@ namespace osu.Game.Rulesets.Scoring } updateScore(); - - NewJudgement?.Invoke(result); - - if (HasCompleted) - AllJudged?.Invoke(); } protected sealed override void RevertResultInternal(JudgementResult result) From 50fa6c5f77284fe61805f7bd04f473e5f93558d4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Dec 2019 20:18:23 +0900 Subject: [PATCH 04/12] Update score on reverts --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 093ba16e57..acd394d955 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -165,6 +165,8 @@ namespace osu.Game.Rulesets.Scoring baseScore -= result.Judgement.NumericResultFor(result); rollingMaxBaseScore -= result.Judgement.MaxNumericResult; } + + updateScore(); } private void updateScore() From 00546787c889489362283dc28d998c2f035c8d3a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Dec 2019 20:36:57 +0900 Subject: [PATCH 05/12] Remove unused method --- osu.Game/Rulesets/Scoring/HealthProcessor.cs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/HealthProcessor.cs b/osu.Game/Rulesets/Scoring/HealthProcessor.cs index 8af8177083..d05e2d7b6b 100644 --- a/osu.Game/Rulesets/Scoring/HealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/HealthProcessor.cs @@ -80,23 +80,5 @@ namespace osu.Game.Rulesets.Scoring Health.Value = 1; HasFailed = false; } - - /// - /// Checks if the score is in a failed state and notifies subscribers. - /// - /// This can only ever notify subscribers once. - /// - /// - private void updateFailed(JudgementResult result) - { - if (HasFailed) - return; - - if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true) - return; - - if (Failed?.Invoke() != false) - HasFailed = true; - } } } From a47e5aeead8382a9b889d30c3e16183bf6a7ae7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Dec 2019 00:51:44 +0900 Subject: [PATCH 06/12] Fix sample lookup not working correctly for custom rulesets --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index df1b8078a6..f318539bb7 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -159,7 +159,7 @@ namespace osu.Game.Rulesets.UI dependencies.Cache(textureStore); localSampleStore = dependencies.Get().GetSampleStore(new NamespacedResourceStore(resources, "Samples")); - dependencies.CacheAs(new FallbackSampleStore(localSampleStore, dependencies.Get())); + dependencies.CacheAs(new FallbackSampleStore(localSampleStore, dependencies.Get())); } onScreenDisplay = dependencies.Get(); From 96a4f9ae53f537c236a17ad4728bf92c0ad87831 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 26 Dec 2019 15:14:19 +0900 Subject: [PATCH 07/12] Fix nub position getting offset by size changes --- .../Graphics/UserInterface/OsuSliderBar.cs | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 563dc2dad9..958390d5d2 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; @@ -31,6 +32,7 @@ namespace osu.Game.Graphics.UserInterface protected readonly Nub Nub; private readonly Box leftBox; private readonly Box rightBox; + private readonly Container nubContainer; public virtual string TooltipText { get; private set; } @@ -72,10 +74,15 @@ namespace osu.Game.Graphics.UserInterface Origin = Anchor.CentreRight, Alpha = 0.5f, }, - Nub = new Nub + nubContainer = new Container { - Origin = Anchor.TopCentre, - Expanded = true, + RelativeSizeAxes = Axes.Both, + Child = Nub = new Nub + { + Origin = Anchor.TopCentre, + RelativePositionAxes = Axes.X, + Expanded = true, + }, }, new HoverClickSounds() }; @@ -90,6 +97,13 @@ namespace osu.Game.Graphics.UserInterface AccentColour = colours.Pink; } + protected override void Update() + { + base.Update(); + + nubContainer.Padding = new MarginPadding { Horizontal = RangePadding }; + } + protected override void LoadComplete() { base.LoadComplete(); @@ -176,14 +190,14 @@ namespace osu.Game.Graphics.UserInterface { base.UpdateAfterChildren(); leftBox.Scale = new Vector2(Math.Clamp( - Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, DrawWidth), 1); + RangePadding + Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, DrawWidth), 1); rightBox.Scale = new Vector2(Math.Clamp( - DrawWidth - Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, DrawWidth), 1); + DrawWidth - Nub.DrawPosition.X - RangePadding - Nub.DrawWidth / 2, 0, DrawWidth), 1); } protected override void UpdateValue(float value) { - Nub.MoveToX(RangePadding + UsableWidth * value, 250, Easing.OutQuint); + Nub.MoveToX(value, 250, Easing.OutQuint); } /// From 9a013acb26b84e162e0a9a53d748728d93de7646 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Thu, 26 Dec 2019 14:25:41 +0800 Subject: [PATCH 08/12] Fix name typo of OsuModObjectScaleTween --- osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs | 2 +- .../{OsuModeObjectScaleTween.cs => OsuModObjectScaleTween.cs} | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename osu.Game.Rulesets.Osu/Mods/{OsuModeObjectScaleTween.cs => OsuModObjectScaleTween.cs} (96%) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs index adca95cf8a..9bf7525d33 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs @@ -5,7 +5,7 @@ using osu.Framework.Graphics.Sprites; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModDeflate : OsuModeObjectScaleTween + public class OsuModDeflate : OsuModObjectScaleTween { public override string Name => "Deflate"; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs index 3c81203ad7..76676ce888 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -5,7 +5,7 @@ using osu.Framework.Graphics.Sprites; namespace osu.Game.Rulesets.Osu.Mods { - internal class OsuModGrow : OsuModeObjectScaleTween + internal class OsuModGrow : OsuModObjectScaleTween { public override string Name => "Grow"; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs b/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs similarity index 96% rename from osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs rename to osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs index 923278f484..42ddddc4dd 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Adjusts the size of hit objects during their fade in animation. /// - public abstract class OsuModeObjectScaleTween : Mod, IReadFromConfig, IApplicableToDrawableHitObjects + public abstract class OsuModObjectScaleTween : Mod, IReadFromConfig, IApplicableToDrawableHitObjects { public override ModType Type => ModType.Fun; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs index e786ec86f9..eae218509e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; // todo: this mod should be able to be compatible with hidden with a bit of further implementation. - public override Type[] IncompatibleMods => new[] { typeof(OsuModeObjectScaleTween), typeof(OsuModHidden), typeof(OsuModTraceable) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModObjectScaleTween), typeof(OsuModHidden), typeof(OsuModTraceable) }; private const int rotate_offset = 360; private const float rotate_starting_width = 2; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index cf1ce517e8..dff9a77807 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => "Put your faith in the approach circles..."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModeObjectScaleTween) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModObjectScaleTween) }; private Bindable increaseFirstObjectVisibility = new Bindable(); public void ReadFromConfig(OsuConfigManager config) From d45556eb6a07280a6be18c275fa5e3b9aa340e09 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 26 Dec 2019 17:57:16 +0900 Subject: [PATCH 09/12] Remove whitespace --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 1 - osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 5bca8588b6..a579a7d07e 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -41,7 +41,6 @@ namespace osu.Game.Rulesets.Mania public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap, this); - public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score); public const string SHORT_NAME = "mania"; diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index c5c2027af1..d713a4145d 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -34,7 +34,6 @@ namespace osu.Game.Rulesets.Taiko public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap, this); - public const string SHORT_NAME = "taiko"; public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] From 8903f286efa4e92c6276fc18ea29af29d0b5f8d2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 26 Dec 2019 18:27:48 +0900 Subject: [PATCH 10/12] Fix escape not continuing to results screen --- osu.Game/Screens/Play/Player.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 8970f9ac88..ab32c55229 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -541,8 +541,14 @@ namespace osu.Game.Screens.Play { if (completionProgressDelegate != null && !completionProgressDelegate.Cancelled && !completionProgressDelegate.Completed) { - // proceed to result screen if beatmap already finished playing - completionProgressDelegate.RunTask(); + // Proceed to result screen if beatmap already finished playing. + // This is scheduled since the player needs to become the current screen before the delegate runs. This happens after the return true. + Scheduler.Add(() => + { + if (!completionProgressDelegate.Completed && !completionProgressDelegate.Cancelled) + completionProgressDelegate.RunTask(); + }); + return true; } From a041f32072ac44dd1d0ae9c6877ce7e753fc161e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Dec 2019 19:05:32 +0900 Subject: [PATCH 11/12] Use cleaner solution via cancellation of older schedule --- osu.Game/Screens/Play/Player.cs | 36 +++++++++++++++------------------ 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ab32c55229..6368c30def 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -350,18 +350,7 @@ namespace osu.Game.Screens.Play if (!showResults) return; using (BeginDelayedSequence(1000)) - { - completionProgressDelegate = Schedule(delegate - { - if (!this.IsCurrentScreen()) return; - - var score = CreateScore(); - if (DrawableRuleset.ReplayScore == null) - scoreManager.Import(score).Wait(); - - this.Push(CreateResults(score)); - }); - } + scheduleGotoRanking(); } protected virtual ScoreInfo CreateScore() @@ -541,14 +530,8 @@ namespace osu.Game.Screens.Play { if (completionProgressDelegate != null && !completionProgressDelegate.Cancelled && !completionProgressDelegate.Completed) { - // Proceed to result screen if beatmap already finished playing. - // This is scheduled since the player needs to become the current screen before the delegate runs. This happens after the return true. - Scheduler.Add(() => - { - if (!completionProgressDelegate.Completed && !completionProgressDelegate.Cancelled) - completionProgressDelegate.RunTask(); - }); - + // proceed to result screen if beatmap already finished playing + scheduleGotoRanking(); return true; } @@ -583,6 +566,19 @@ namespace osu.Game.Screens.Play storyboardReplacesBackground.Value = false; } + private void scheduleGotoRanking() + { + completionProgressDelegate?.Cancel(); + completionProgressDelegate = Schedule(delegate + { + var score = CreateScore(); + if (DrawableRuleset.ReplayScore == null) + scoreManager.Import(score).Wait(); + + this.Push(CreateResults(score)); + }); + } + #endregion } } From 00a36c388c76db43e9373369c1c54c9cf8a0c905 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 26 Dec 2019 19:18:39 +0900 Subject: [PATCH 12/12] Fix tests --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 ++ osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index c8a156dc57..c9ea5e6cf1 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -38,6 +38,8 @@ namespace osu.Game.Rulesets.Osu public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new OsuScoreProcessor(beatmap); + public override HealthProcessor CreateHealthProcessor(IBeatmap beatmap) => new OsuHealthProcessor(beatmap); + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap, this); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs index a0c25521a6..2045072c79 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs @@ -22,12 +22,12 @@ namespace osu.Game.Tests.Visual.Gameplay { AddUntilStep("wait for fail", () => Player.HasFailed); AddUntilStep("wait for multiple judged objects", () => ((FailPlayer)Player).DrawableRuleset.Playfield.AllHitObjects.Count(h => h.AllJudged) > 1); - AddAssert("total judgements == 1", () => ((FailPlayer)Player).ScoreProcessor.JudgedHits == 1); + AddAssert("total judgements == 1", () => ((FailPlayer)Player).HealthProcessor.JudgedHits >= 1); } private class FailPlayer : TestPlayer { - public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; + public new HealthProcessor HealthProcessor => base.HealthProcessor; public FailPlayer() : base(false, false)