From 1da8cc86900a3f3538f6c2b7d4edcaa10fcc4f13 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 19 Dec 2019 19:02:11 +0900 Subject: [PATCH 01/15] 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/15] 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/15] 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/15] 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/15] 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 3e3415511476aa6941b8b0b914a72f7b2026fdf4 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 25 Dec 2019 21:56:43 +0300 Subject: [PATCH 06/15] Update ScreenTitle with the new design --- .../Graphics/UserInterface/ScreenTitle.cs | 52 ++++++++++++------- .../UserInterface/ScreenTitleTextureIcon.cs | 39 +++----------- osu.Game/Overlays/OverlayHeader.cs | 2 +- osu.Game/Screens/Multi/Header.cs | 2 +- 4 files changed, 41 insertions(+), 54 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ScreenTitle.cs b/osu.Game/Graphics/UserInterface/ScreenTitle.cs index 10fc312d8b..bea37d4f34 100644 --- a/osu.Game/Graphics/UserInterface/ScreenTitle.cs +++ b/osu.Game/Graphics/UserInterface/ScreenTitle.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Sprites; using osuTK; @@ -13,15 +14,12 @@ namespace osu.Game.Graphics.UserInterface { public abstract class ScreenTitle : CompositeDrawable, IHasAccentColour { - public const float ICON_WIDTH = ICON_SIZE + icon_spacing; - - public const float ICON_SIZE = 25; + public const float ICON_SIZE = 30; + private const int text_offset = 2; private SpriteIcon iconSprite; private readonly OsuSpriteText titleText, pageText; - private const float icon_spacing = 10; - protected IconUsage Icon { set @@ -63,26 +61,40 @@ namespace osu.Game.Graphics.UserInterface new FillFlowContainer { AutoSizeAxes = Axes.Both, - Spacing = new Vector2(icon_spacing, 0), + Spacing = new Vector2(6, 0), + Direction = FillDirection.Horizontal, Children = new[] { - CreateIcon(), - new FillFlowContainer + CreateIcon().With(t => { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(6, 0), - Children = new[] + t.Anchor = Anchor.Centre; + t.Origin = Anchor.Centre; + }), + titleText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold), + Margin = new MarginPadding { Bottom = text_offset } + }, + new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Masking = true, + Size = new Vector2(4), + Child = new Box { - titleText = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 30, weight: FontWeight.Light), - }, - pageText = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 30, weight: FontWeight.Light), - } + RelativeSizeAxes = Axes.Both, + Colour = Color4.Gray, } + }, + pageText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 20), + Margin = new MarginPadding { Bottom = text_offset } } } }, diff --git a/osu.Game/Graphics/UserInterface/ScreenTitleTextureIcon.cs b/osu.Game/Graphics/UserInterface/ScreenTitleTextureIcon.cs index f590e7e357..e36000af4e 100644 --- a/osu.Game/Graphics/UserInterface/ScreenTitleTextureIcon.cs +++ b/osu.Game/Graphics/UserInterface/ScreenTitleTextureIcon.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osuTK; @@ -16,8 +15,6 @@ namespace osu.Game.Graphics.UserInterface /// public class ScreenTitleTextureIcon : CompositeDrawable { - private const float circle_allowance = 0.8f; - private readonly string textureName; public ScreenTitleTextureIcon(string textureName) @@ -26,38 +23,16 @@ namespace osu.Game.Graphics.UserInterface } [BackgroundDependencyLoader] - private void load(TextureStore textures, OsuColour colours) + private void load(TextureStore textures) { - Size = new Vector2(ScreenTitle.ICON_SIZE / circle_allowance); + Size = new Vector2(ScreenTitle.ICON_SIZE); - InternalChildren = new Drawable[] + InternalChild = new Sprite { - new CircularContainer - { - Masking = true, - BorderColour = colours.Violet, - BorderThickness = 3, - MaskingSmoothness = 1, - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Sprite - { - RelativeSizeAxes = Axes.Both, - Texture = textures.Get(textureName), - Size = new Vector2(circle_allowance), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Violet, - Alpha = 0, - AlwaysPresent = true, - }, - } - }, + RelativeSizeAxes = Axes.Both, + Texture = textures.Get(textureName), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, }; } } diff --git a/osu.Game/Overlays/OverlayHeader.cs b/osu.Game/Overlays/OverlayHeader.cs index 2e032db2ba..95de1eace1 100644 --- a/osu.Game/Overlays/OverlayHeader.cs +++ b/osu.Game/Overlays/OverlayHeader.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays Depth = -float.MaxValue, Children = new Drawable[] { - CreateTitle().With(t => t.X = -ScreenTitle.ICON_WIDTH), + CreateTitle().With(t => t.X = -ScreenTitle.ICON_SIZE), TabControl = new OverlayHeaderTabControl { Anchor = Anchor.BottomLeft, diff --git a/osu.Game/Screens/Multi/Header.cs b/osu.Game/Screens/Multi/Header.cs index 1cbf2a45e7..402c5ca5bb 100644 --- a/osu.Game/Screens/Multi/Header.cs +++ b/osu.Game/Screens/Multi/Header.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Multi { Anchor = Anchor.CentreLeft, Origin = Anchor.BottomLeft, - X = -ScreenTitle.ICON_WIDTH, + X = -ScreenTitle.ICON_SIZE, }, breadcrumbs = new HeaderBreadcrumbControl(stack) { From c77679e36c6e4d339544a1c304459cf1bcdad141 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 25 Dec 2019 21:57:22 +0300 Subject: [PATCH 07/15] Naming fixes for overlays using ScreenTitle --- .../Overlays/Changelog/ChangelogHeader.cs | 2 +- osu.Game/Overlays/News/NewsHeader.cs | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index b2e9be24b3..18dcdf721c 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -111,7 +111,7 @@ namespace osu.Game.Overlays.Changelog public ChangelogHeaderTitle() { - Title = "Changelog"; + Title = "changelog"; Version = null; } diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 27620ab523..0f73663605 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.News { public class NewsHeader : OverlayHeader { - private const string front_page_string = "Front Page"; + private const string front_page_string = "frontpage"; private NewsHeaderTitle title; @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.News ShowFrontPage?.Invoke(); }; - Current.ValueChanged += showArticle; + Current.ValueChanged += showPost; } [BackgroundDependencyLoader] @@ -42,7 +42,7 @@ namespace osu.Game.Overlays.News TabControl.AccentColour = colour.Violet; } - private void showArticle(ValueChangedEvent e) + private void showPost(ValueChangedEvent e) { if (e.OldValue != null) TabControl.RemoveItem(e.OldValue); @@ -52,12 +52,12 @@ namespace osu.Game.Overlays.News TabControl.AddItem(e.NewValue); TabControl.Current.Value = e.NewValue; - title.IsReadingArticle = true; + title.IsReadingPost = true; } else { TabControl.Current.Value = front_page_string; - title.IsReadingArticle = false; + title.IsReadingPost = false; } } @@ -84,17 +84,17 @@ namespace osu.Game.Overlays.News private class NewsHeaderTitle : ScreenTitle { - private const string article_string = "Article"; + private const string post_string = "post"; - public bool IsReadingArticle + public bool IsReadingPost { - set => Section = value ? article_string : front_page_string; + set => Section = value ? post_string : front_page_string; } public NewsHeaderTitle() { - Title = "News"; - IsReadingArticle = false; + Title = "news"; + IsReadingPost = false; } protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/news"); From bb4d02a1f8d353c752ee2ba6fa4210869273156a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 25 Dec 2019 22:09:14 +0300 Subject: [PATCH 08/15] Fix incorrect icon offset --- osu.Game/Graphics/UserInterface/ScreenTitle.cs | 5 ++++- osu.Game/Overlays/OverlayHeader.cs | 2 +- osu.Game/Screens/Multi/Header.cs | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ScreenTitle.cs b/osu.Game/Graphics/UserInterface/ScreenTitle.cs index bea37d4f34..a8c8005a41 100644 --- a/osu.Game/Graphics/UserInterface/ScreenTitle.cs +++ b/osu.Game/Graphics/UserInterface/ScreenTitle.cs @@ -14,7 +14,10 @@ namespace osu.Game.Graphics.UserInterface { public abstract class ScreenTitle : CompositeDrawable, IHasAccentColour { + public const float ICON_WIDTH = ICON_SIZE + spacing; + public const float ICON_SIZE = 30; + private const float spacing = 6; private const int text_offset = 2; private SpriteIcon iconSprite; @@ -61,7 +64,7 @@ namespace osu.Game.Graphics.UserInterface new FillFlowContainer { AutoSizeAxes = Axes.Both, - Spacing = new Vector2(6, 0), + Spacing = new Vector2(spacing, 0), Direction = FillDirection.Horizontal, Children = new[] { diff --git a/osu.Game/Overlays/OverlayHeader.cs b/osu.Game/Overlays/OverlayHeader.cs index 95de1eace1..2e032db2ba 100644 --- a/osu.Game/Overlays/OverlayHeader.cs +++ b/osu.Game/Overlays/OverlayHeader.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays Depth = -float.MaxValue, Children = new Drawable[] { - CreateTitle().With(t => t.X = -ScreenTitle.ICON_SIZE), + CreateTitle().With(t => t.X = -ScreenTitle.ICON_WIDTH), TabControl = new OverlayHeaderTabControl { Anchor = Anchor.BottomLeft, diff --git a/osu.Game/Screens/Multi/Header.cs b/osu.Game/Screens/Multi/Header.cs index 402c5ca5bb..1cbf2a45e7 100644 --- a/osu.Game/Screens/Multi/Header.cs +++ b/osu.Game/Screens/Multi/Header.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Multi { Anchor = Anchor.CentreLeft, Origin = Anchor.BottomLeft, - X = -ScreenTitle.ICON_SIZE, + X = -ScreenTitle.ICON_WIDTH, }, breadcrumbs = new HeaderBreadcrumbControl(stack) { From d45556eb6a07280a6be18c275fa5e3b9aa340e09 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 26 Dec 2019 17:57:16 +0900 Subject: [PATCH 09/15] 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/15] 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/15] 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/15] 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) From 3d7c7a1ced8d88bf034429b38a1858a734e85839 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 26 Dec 2019 22:39:13 +0300 Subject: [PATCH 13/15] Fix profile title wasn't in lowercase --- osu.Game/Overlays/Profile/ProfileHeader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 76613c156d..3970fb2b97 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -101,8 +101,8 @@ namespace osu.Game.Overlays.Profile { public ProfileHeaderTitle() { - Title = "Player"; - Section = "Info"; + Title = "player"; + Section = "info"; } [BackgroundDependencyLoader] From fd8c5d77061b743300a8d16acd6120d1e84f3220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 26 Dec 2019 22:23:25 +0100 Subject: [PATCH 14/15] Improve notification overlay test robustness Stress testing one of the notification overlay tests by running it 10000 times on repeat has shown that it is susceptible to intermittent failures due to races between delays and asserts checking the number of currently progressing notifications and the actual progress update, which contains a random generation factor. Replace step sequences checking for notification completion by waiting and asserting with explicit until steps that don't terminate unless there are zero progressing notifications. --- .../UserInterface/TestSceneNotificationOverlay.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index d8a4514df1..35e5f9719c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -67,9 +67,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddRepeatStep(@"add many simple", sendManyNotifications, 3); - AddWaitStep("wait some", 5); - - checkProgressingCount(0); + waitForCompletion(); AddStep(@"progress #3", sendUploadProgress); @@ -77,9 +75,7 @@ namespace osu.Game.Tests.Visual.UserInterface checkDisplayedCount(33); - AddWaitStep("wait some", 10); - - checkProgressingCount(0); + waitForCompletion(); } [Test] @@ -109,9 +105,9 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep(@"background progress #1", sendBackgroundUploadProgress); - AddWaitStep("wait some", 5); + checkProgressingCount(1); - checkProgressingCount(0); + waitForCompletion(); checkDisplayedCount(2); @@ -190,6 +186,8 @@ namespace osu.Game.Tests.Visual.UserInterface private void checkProgressingCount(int expected) => AddAssert($"progressing count is {expected}", () => progressingNotifications.Count == expected); + private void waitForCompletion() => AddUntilStep("wait for notification progress completion", () => progressingNotifications.Count == 0); + private void sendBarrage() { switch (RNG.Next(0, 4)) From 183307d636b8bcd1fca0571276e300459d47ccd5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 27 Dec 2019 10:53:20 +0900 Subject: [PATCH 15/15] Use circle instead of CircularContainer --- osu.Game/Graphics/UserInterface/ScreenTitle.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ScreenTitle.cs b/osu.Game/Graphics/UserInterface/ScreenTitle.cs index a8c8005a41..f5bb97d465 100644 --- a/osu.Game/Graphics/UserInterface/ScreenTitle.cs +++ b/osu.Game/Graphics/UserInterface/ScreenTitle.cs @@ -80,17 +80,12 @@ namespace osu.Game.Graphics.UserInterface Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold), Margin = new MarginPadding { Bottom = text_offset } }, - new CircularContainer + new Circle { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Masking = true, Size = new Vector2(4), - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Gray, - } + Colour = Color4.Gray, }, pageText = new OsuSpriteText {