From a884f20c6541227ef42896b1d042ace7af3e0f6a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 26 Apr 2022 22:19:20 +0300 Subject: [PATCH 1/6] Add failing test case --- .../TestSceneDrainingHealthProcessor.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs index 296c5cef76..56fb68ea27 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs @@ -160,6 +160,43 @@ namespace osu.Game.Tests.Gameplay assertHealthNotEqualTo(1); } + [Test] + public void TestFailConditions() + { + var beatmap = createBeatmap(0, 1000); + createProcessor(beatmap); + + AddStep("setup fail conditions", () => processor.FailConditions += ((_, result) => result.Type == HitResult.Miss)); + + AddStep("apply perfect hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Perfect })); + AddAssert("not failed", () => !processor.HasFailed); + AddStep("apply miss hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Miss })); + AddAssert("failed", () => processor.HasFailed); + } + + [Test] + public void TestMultipleFailConditions([Values] bool applyFirstCondition) + { + var beatmap = createBeatmap(0, 1000); + createProcessor(beatmap); + + AddStep("setup multiple fail conditions", () => + { + processor.FailConditions += ((_, result) => result.Type == HitResult.Miss); + processor.FailConditions += ((_, result) => result.Type == HitResult.Meh); + }); + + AddStep("apply perfect hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Perfect })); + AddAssert("not failed", () => !processor.HasFailed); + + if (applyFirstCondition) + AddStep("apply miss hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Miss })); + else + AddStep("apply meh hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Meh })); + + AddAssert("failed", () => processor.HasFailed); + } + [Test] public void TestBonusObjectsExcludedFromDrain() { From 31f64b13819620d1eee947e84dae43220b371081 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 26 Apr 2022 22:49:14 +0300 Subject: [PATCH 2/6] Fix `HealthProcessor` fail conditions not handling multiple invocations --- osu.Game/Rulesets/Scoring/HealthProcessor.cs | 32 +++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/HealthProcessor.cs b/osu.Game/Rulesets/Scoring/HealthProcessor.cs index a92c30e593..5e4e40b3bb 100644 --- a/osu.Game/Rulesets/Scoring/HealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/HealthProcessor.cs @@ -43,11 +43,11 @@ namespace osu.Game.Rulesets.Scoring Health.Value += GetHealthIncreaseFor(result); - if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true) - return; - - if (Failed?.Invoke() != false) - HasFailed = true; + if (meetsFailConditions(result)) + { + if (Failed?.Invoke() != false) + HasFailed = true; + } } protected override void RevertResultInternal(JudgementResult result) @@ -69,6 +69,28 @@ namespace osu.Game.Rulesets.Scoring /// protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value); + /// + /// Whether the current state of or the provided meets the fail conditions. + /// + /// The judgement result. + private bool meetsFailConditions(JudgementResult result) + { + if (DefaultFailCondition) + return true; + + if (FailConditions != null) + { + foreach (var condition in FailConditions.GetInvocationList()) + { + bool conditionResult = (bool)condition.Method.Invoke(condition.Target, new object[] { this, result }); + if (conditionResult) + return true; + } + } + + return false; + } + protected override void Reset(bool storeResults) { base.Reset(storeResults); From fb6a112708e5b1759fd5625f96d04a2788f5ebde Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 26 Apr 2022 23:09:51 +0300 Subject: [PATCH 3/6] Mark `OsuModTarget` and `OsuModSuddenDeath` as mutually exclusive `OsuModTarget` already fails on miss, so "Sudden Death" doesn't make sense to be enabled and may only cause issues. --- osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs | 6 +++++- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs index 0403e81229..429fe30fc5 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs @@ -9,6 +9,10 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModSuddenDeath : ModSuddenDeath { - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] + { + typeof(OsuModAutopilot), + typeof(OsuModTarget), + }).ToArray(); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 5285380097..4fab9b6a5a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Description => @"Practice keeping up with the beat of the song."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles) }; + public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSuddenDeath) }; [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))] public Bindable Seed { get; } = new Bindable From cccc9d7d39dbde7a876c3b48a60273edb0a03a3a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 27 Apr 2022 00:03:48 +0300 Subject: [PATCH 4/6] Rename method to better reflect what it's doing --- osu.Game/Rulesets/Scoring/HealthProcessor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/HealthProcessor.cs b/osu.Game/Rulesets/Scoring/HealthProcessor.cs index 5e4e40b3bb..0f51560476 100644 --- a/osu.Game/Rulesets/Scoring/HealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/HealthProcessor.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Scoring Health.Value += GetHealthIncreaseFor(result); - if (meetsFailConditions(result)) + if (meetsAnyFailCondition(result)) { if (Failed?.Invoke() != false) HasFailed = true; @@ -70,10 +70,10 @@ namespace osu.Game.Rulesets.Scoring protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value); /// - /// Whether the current state of or the provided meets the fail conditions. + /// Whether the current state of or the provided meets any fail condition. /// /// The judgement result. - private bool meetsFailConditions(JudgementResult result) + private bool meetsAnyFailCondition(JudgementResult result) { if (DefaultFailCondition) return true; From 1676c2c3f69ad7836e2199986f34f44edab73960 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 27 Apr 2022 00:05:15 +0300 Subject: [PATCH 5/6] Change boolean argument to `HitResult` instead --- .../Gameplay/TestSceneDrainingHealthProcessor.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs index 56fb68ea27..a7cd567635 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs @@ -174,8 +174,9 @@ namespace osu.Game.Tests.Gameplay AddAssert("failed", () => processor.HasFailed); } - [Test] - public void TestMultipleFailConditions([Values] bool applyFirstCondition) + [TestCase(HitResult.Miss)] + [TestCase(HitResult.Meh)] + public void TestMultipleFailConditions(HitResult resultApplied) { var beatmap = createBeatmap(0, 1000); createProcessor(beatmap); @@ -189,11 +190,7 @@ namespace osu.Game.Tests.Gameplay AddStep("apply perfect hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Perfect })); AddAssert("not failed", () => !processor.HasFailed); - if (applyFirstCondition) - AddStep("apply miss hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Miss })); - else - AddStep("apply meh hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Meh })); - + AddStep($"apply {resultApplied.ToString().ToLower()} hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Miss })); AddAssert("failed", () => processor.HasFailed); } From 8842847b1850e574f95e537fba07684ff9523c23 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 27 Apr 2022 00:05:49 +0300 Subject: [PATCH 6/6] Fix wrong result applied --- osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs index a7cd567635..a354464a8e 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs @@ -190,7 +190,7 @@ namespace osu.Game.Tests.Gameplay AddStep("apply perfect hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Perfect })); AddAssert("not failed", () => !processor.HasFailed); - AddStep($"apply {resultApplied.ToString().ToLower()} hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Miss })); + AddStep($"apply {resultApplied.ToString().ToLower()} hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = resultApplied })); AddAssert("failed", () => processor.HasFailed); }