Skip to content

Commit 09e31b7

Browse files
authored
Merge pull request #33875 from bdach/hitwindow-refactor
Refactor hit windows class structure to reduce rigidity
2 parents 12df6e0 + 55ff5a7 commit 09e31b7

File tree

18 files changed

+260
-169
lines changed

18 files changed

+260
-169
lines changed

osu.Game.Rulesets.Mania/Scoring/ManiaHitWindows.cs

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,30 @@
11
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
22
// See the LICENCE file in the repository root for full licence text.
33

4-
using System.Linq;
4+
using System;
5+
using osu.Game.Beatmaps;
56
using osu.Game.Rulesets.Scoring;
67

78
namespace osu.Game.Rulesets.Mania.Scoring
89
{
910
public class ManiaHitWindows : HitWindows
1011
{
12+
private static readonly DifficultyRange perfect_window_range = new DifficultyRange(22.4D, 19.4D, 13.9D);
13+
private static readonly DifficultyRange great_window_range = new DifficultyRange(64, 49, 34);
14+
private static readonly DifficultyRange good_window_range = new DifficultyRange(97, 82, 67);
15+
private static readonly DifficultyRange ok_window_range = new DifficultyRange(127, 112, 97);
16+
private static readonly DifficultyRange meh_window_range = new DifficultyRange(151, 136, 121);
17+
private static readonly DifficultyRange miss_window_range = new DifficultyRange(188, 173, 158);
18+
1119
private readonly double multiplier;
1220

21+
private double perfect;
22+
private double great;
23+
private double good;
24+
private double ok;
25+
private double meh;
26+
private double miss;
27+
1328
public ManiaHitWindows()
1429
: this(1)
1530
{
@@ -36,11 +51,41 @@ public override bool IsHitResultAllowed(HitResult result)
3651
return false;
3752
}
3853

39-
protected override DifficultyRange[] GetRanges() => base.GetRanges().Select(r =>
40-
new DifficultyRange(
41-
r.Result,
42-
r.Min * multiplier,
43-
r.Average * multiplier,
44-
r.Max * multiplier)).ToArray();
54+
public override void SetDifficulty(double difficulty)
55+
{
56+
perfect = IBeatmapDifficultyInfo.DifficultyRange(difficulty, perfect_window_range) * multiplier;
57+
great = IBeatmapDifficultyInfo.DifficultyRange(difficulty, great_window_range) * multiplier;
58+
good = IBeatmapDifficultyInfo.DifficultyRange(difficulty, good_window_range) * multiplier;
59+
ok = IBeatmapDifficultyInfo.DifficultyRange(difficulty, ok_window_range) * multiplier;
60+
meh = IBeatmapDifficultyInfo.DifficultyRange(difficulty, meh_window_range) * multiplier;
61+
miss = IBeatmapDifficultyInfo.DifficultyRange(difficulty, miss_window_range) * multiplier;
62+
}
63+
64+
public override double WindowFor(HitResult result)
65+
{
66+
switch (result)
67+
{
68+
case HitResult.Perfect:
69+
return perfect;
70+
71+
case HitResult.Great:
72+
return great;
73+
74+
case HitResult.Good:
75+
return good;
76+
77+
case HitResult.Ok:
78+
return ok;
79+
80+
case HitResult.Meh:
81+
return meh;
82+
83+
case HitResult.Miss:
84+
return miss;
85+
86+
default:
87+
throw new ArgumentOutOfRangeException(nameof(result), result, null);
88+
}
89+
}
4590
}
4691
}

osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -476,15 +476,24 @@ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, I
476476

477477
private class TestHitWindows : HitWindows
478478
{
479-
private static readonly DifficultyRange[] ranges =
479+
public override bool IsHitResultAllowed(HitResult result) => result == HitResult.Great || result == HitResult.Miss;
480+
481+
public override void SetDifficulty(double difficulty) { }
482+
483+
public override double WindowFor(HitResult result)
480484
{
481-
new DifficultyRange(HitResult.Great, 500, 500, 500),
482-
new DifficultyRange(HitResult.Miss, early_miss_window, early_miss_window, early_miss_window),
483-
};
485+
switch (result)
486+
{
487+
case HitResult.Great:
488+
return 500;
484489

485-
public override bool IsHitResultAllowed(HitResult result) => result == HitResult.Great || result == HitResult.Miss;
490+
case HitResult.Miss:
491+
return early_miss_window;
486492

487-
protected override DifficultyRange[] GetRanges() => ranges;
493+
default:
494+
throw new ArgumentOutOfRangeException(nameof(result), result, null);
495+
}
496+
}
488497
}
489498

490499
private partial class ScoreAccessibleReplayPlayer : ReplayPlayer

osu.Game.Rulesets.Osu/Objects/Spinner.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ public class Spinner : OsuHitObject, IHasDuration
2121
/// <summary>
2222
/// The RPM required to clear the spinner at ODs [ 0, 5, 10 ].
2323
/// </summary>
24-
private static readonly (int min, int mid, int max) clear_rpm_range = (90, 150, 225);
24+
private static readonly DifficultyRange clear_rpm_range = new DifficultyRange(90, 150, 225);
2525

2626
/// <summary>
2727
/// The RPM required to complete the spinner and receive full score at ODs [ 0, 5, 10 ].
2828
/// </summary>
29-
private static readonly (int min, int mid, int max) complete_rpm_range = (250, 380, 430);
29+
private static readonly DifficultyRange complete_rpm_range = new DifficultyRange(250, 380, 430);
3030

3131
public double EndTime
3232
{

osu.Game.Rulesets.Osu/OsuRuleset.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -373,10 +373,9 @@ public override BeatmapDifficulty GetRateAdjustedDisplayDifficulty(IBeatmapDiffi
373373
preempt /= rate;
374374
adjustedDifficulty.ApproachRate = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(preempt, OsuHitObject.PREEMPT_MAX, OsuHitObject.PREEMPT_MID, OsuHitObject.PREEMPT_MIN);
375375

376-
var greatHitWindowRange = OsuHitWindows.OSU_RANGES.Single(range => range.Result == HitResult.Great);
377-
double greatHitWindow = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.OverallDifficulty, greatHitWindowRange.Min, greatHitWindowRange.Average, greatHitWindowRange.Max);
376+
double greatHitWindow = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.OverallDifficulty, OsuHitWindows.GREAT_WINDOW_RANGE);
378377
greatHitWindow /= rate;
379-
adjustedDifficulty.OverallDifficulty = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(greatHitWindow, greatHitWindowRange.Min, greatHitWindowRange.Average, greatHitWindowRange.Max);
378+
adjustedDifficulty.OverallDifficulty = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(greatHitWindow, OsuHitWindows.GREAT_WINDOW_RANGE);
380379

381380
return adjustedDifficulty;
382381
}
Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
22
// See the LICENCE file in the repository root for full licence text.
33

4+
using System;
5+
using osu.Game.Beatmaps;
46
using osu.Game.Rulesets.Scoring;
57

68
namespace osu.Game.Rulesets.Osu.Scoring
79
{
810
public class OsuHitWindows : HitWindows
911
{
12+
public static readonly DifficultyRange GREAT_WINDOW_RANGE = new DifficultyRange(80, 50, 20);
13+
public static readonly DifficultyRange OK_WINDOW_RANGE = new DifficultyRange(140, 100, 60);
14+
public static readonly DifficultyRange MEH_WINDOW_RANGE = new DifficultyRange(200, 150, 100);
15+
1016
/// <summary>
1117
/// osu! ruleset has a fixed miss window regardless of difficulty settings.
1218
/// </summary>
1319
public const double MISS_WINDOW = 400;
1420

15-
internal static readonly DifficultyRange[] OSU_RANGES =
16-
{
17-
new DifficultyRange(HitResult.Great, 80, 50, 20),
18-
new DifficultyRange(HitResult.Ok, 140, 100, 60),
19-
new DifficultyRange(HitResult.Meh, 200, 150, 100),
20-
new DifficultyRange(HitResult.Miss, MISS_WINDOW, MISS_WINDOW, MISS_WINDOW),
21-
};
21+
private double great;
22+
private double ok;
23+
private double meh;
2224

2325
public override bool IsHitResultAllowed(HitResult result)
2426
{
@@ -34,6 +36,32 @@ public override bool IsHitResultAllowed(HitResult result)
3436
return false;
3537
}
3638

37-
protected override DifficultyRange[] GetRanges() => OSU_RANGES;
39+
public override void SetDifficulty(double difficulty)
40+
{
41+
great = IBeatmapDifficultyInfo.DifficultyRange(difficulty, GREAT_WINDOW_RANGE);
42+
ok = IBeatmapDifficultyInfo.DifficultyRange(difficulty, OK_WINDOW_RANGE);
43+
meh = IBeatmapDifficultyInfo.DifficultyRange(difficulty, MEH_WINDOW_RANGE);
44+
}
45+
46+
public override double WindowFor(HitResult result)
47+
{
48+
switch (result)
49+
{
50+
case HitResult.Great:
51+
return great;
52+
53+
case HitResult.Ok:
54+
return ok;
55+
56+
case HitResult.Meh:
57+
return meh;
58+
59+
case HitResult.Miss:
60+
return MISS_WINDOW;
61+
62+
default:
63+
throw new ArgumentOutOfRangeException(nameof(result), result, null);
64+
}
65+
}
3866
}
3967
}

osu.Game.Rulesets.Taiko.Tests/Judgements/TestSceneHitJudgements.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public void TestHighVelocityHit()
171171
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 6 });
172172
beatmap.ControlPointInfo.Add(0, new EffectControlPoint { ScrollSpeed = 10 });
173173

174-
var hitWindows = new HitWindows();
174+
var hitWindows = new DefaultHitWindows();
175175
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);
176176

177177
PerformTest(new List<ReplayFrame>
Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
22
// See the LICENCE file in the repository root for full licence text.
33

4+
using System;
5+
using osu.Game.Beatmaps;
46
using osu.Game.Rulesets.Scoring;
57

68
namespace osu.Game.Rulesets.Taiko.Scoring
79
{
810
public class TaikoHitWindows : HitWindows
911
{
10-
internal static readonly DifficultyRange[] TAIKO_RANGES =
11-
{
12-
new DifficultyRange(HitResult.Great, 50, 35, 20),
13-
new DifficultyRange(HitResult.Ok, 120, 80, 50),
14-
new DifficultyRange(HitResult.Miss, 135, 95, 70),
15-
};
12+
public static readonly DifficultyRange GREAT_WINDOW_RANGE = new DifficultyRange(50, 35, 20);
13+
public static readonly DifficultyRange OK_WINDOW_RANGE = new DifficultyRange(120, 80, 50);
14+
public static readonly DifficultyRange MISS_WINDOW_RANGE = new DifficultyRange(135, 95, 70);
15+
16+
private double great;
17+
private double ok;
18+
private double miss;
1619

1720
public override bool IsHitResultAllowed(HitResult result)
1821
{
@@ -27,6 +30,29 @@ public override bool IsHitResultAllowed(HitResult result)
2730
return false;
2831
}
2932

30-
protected override DifficultyRange[] GetRanges() => TAIKO_RANGES;
33+
public override void SetDifficulty(double difficulty)
34+
{
35+
great = IBeatmapDifficultyInfo.DifficultyRange(difficulty, GREAT_WINDOW_RANGE);
36+
ok = IBeatmapDifficultyInfo.DifficultyRange(difficulty, OK_WINDOW_RANGE);
37+
miss = IBeatmapDifficultyInfo.DifficultyRange(difficulty, MISS_WINDOW_RANGE);
38+
}
39+
40+
public override double WindowFor(HitResult result)
41+
{
42+
switch (result)
43+
{
44+
case HitResult.Great:
45+
return great;
46+
47+
case HitResult.Ok:
48+
return ok;
49+
50+
case HitResult.Miss:
51+
return miss;
52+
53+
default:
54+
throw new ArgumentOutOfRangeException(nameof(result), result, null);
55+
}
56+
}
3157
}
3258
}

osu.Game.Rulesets.Taiko/TaikoRuleset.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,9 @@ public override BeatmapDifficulty GetRateAdjustedDisplayDifficulty(IBeatmapDiffi
274274
{
275275
BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty);
276276

277-
var greatHitWindowRange = TaikoHitWindows.TAIKO_RANGES.Single(range => range.Result == HitResult.Great);
278-
double greatHitWindow = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.OverallDifficulty, greatHitWindowRange.Min, greatHitWindowRange.Average, greatHitWindowRange.Max);
277+
double greatHitWindow = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.OverallDifficulty, TaikoHitWindows.GREAT_WINDOW_RANGE);
279278
greatHitWindow /= rate;
280-
adjustedDifficulty.OverallDifficulty = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(greatHitWindow, greatHitWindowRange.Min, greatHitWindowRange.Average, greatHitWindowRange.Max);
279+
adjustedDifficulty.OverallDifficulty = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(greatHitWindow, TaikoHitWindows.GREAT_WINDOW_RANGE);
281280

282281
return adjustedDifficulty;
283282
}

osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ public JudgeableHitObject(HitResult maxResult = HitResult.Perfect)
359359
}
360360

361361
public override Judgement CreateJudgement() => new TestJudgement(maxResult);
362-
protected override HitWindows CreateHitWindows() => new HitWindows();
362+
protected override HitWindows CreateHitWindows() => new DefaultHitWindows();
363363

364364
private class TestJudgement : Judgement
365365
{

osu.Game.Tests/NonVisual/FirstAvailableHitWindowsTest.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public void Setup()
3333
public void TestResultIfOnlyParentHitWindowIsEmpty()
3434
{
3535
var testObject = new TestHitObject(HitWindows.Empty);
36-
HitObject nested = new TestHitObject(new HitWindows());
36+
HitObject nested = new TestHitObject(new DefaultHitWindows());
3737
testObject.AddNested(nested);
3838
testDrawableRuleset.HitObjects = new List<HitObject> { testObject };
3939

@@ -43,8 +43,8 @@ public void TestResultIfOnlyParentHitWindowIsEmpty()
4343
[Test]
4444
public void TestResultIfParentHitWindowsIsNotEmpty()
4545
{
46-
var testObject = new TestHitObject(new HitWindows());
47-
HitObject nested = new TestHitObject(new HitWindows());
46+
var testObject = new TestHitObject(new DefaultHitWindows());
47+
HitObject nested = new TestHitObject(new DefaultHitWindows());
4848
testObject.AddNested(nested);
4949
testDrawableRuleset.HitObjects = new List<HitObject> { testObject };
5050

@@ -58,7 +58,7 @@ public void TestResultIfParentAndChildHitWindowsAreEmpty()
5858
HitObject nested = new TestHitObject(HitWindows.Empty);
5959
firstObject.AddNested(nested);
6060

61-
var secondObject = new TestHitObject(new HitWindows());
61+
var secondObject = new TestHitObject(new DefaultHitWindows());
6262
testDrawableRuleset.HitObjects = new List<HitObject> { firstObject, secondObject };
6363

6464
Assert.AreSame(testDrawableRuleset.FirstAvailableHitWindows, secondObject.HitWindows);

0 commit comments

Comments
 (0)