Skip to content

Commit 50e6f0a

Browse files
authored
Merge pull request #24233 from chayleaf/fix-taiko-maps-not-finishing
Fix taiko maps sporadically not completing with Hidden mod active
2 parents 2d51aa2 + 416ee0d commit 50e6f0a

File tree

7 files changed

+95
-17
lines changed

7 files changed

+95
-17
lines changed

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
22
// See the LICENCE file in the repository root for full licence text.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.Linq;
67
using NUnit.Framework;
@@ -10,6 +11,7 @@
1011
using osu.Game.Beatmaps.ControlPoints;
1112
using osu.Game.Replays;
1213
using osu.Game.Rulesets.Judgements;
14+
using osu.Game.Rulesets.Mods;
1315
using osu.Game.Rulesets.Replays;
1416
using osu.Game.Rulesets.Scoring;
1517
using osu.Game.Rulesets.Taiko.Objects;
@@ -36,11 +38,12 @@ protected void AssertResult<T>(int index, HitResult expectedResult)
3638
() => Is.EqualTo(expectedResult));
3739
}
3840

39-
protected void PerformTest(List<ReplayFrame> frames, Beatmap<TaikoHitObject>? beatmap = null)
41+
protected void PerformTest(List<ReplayFrame> frames, Beatmap<TaikoHitObject>? beatmap = null, Mod[]? mods = null)
4042
{
4143
AddStep("load player", () =>
4244
{
4345
Beatmap.Value = CreateWorkingBeatmap(beatmap);
46+
SelectedMods.Value = mods ?? Array.Empty<Mod>();
4447

4548
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
4649

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

+19
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,25 @@ public void TestHitNoneDrumRoll()
7575
AssertResult<DrumRoll>(0, HitResult.IgnoreHit);
7676
}
7777

78+
[Test]
79+
public void TestHitNoneStrongDrumRoll()
80+
{
81+
PerformTest(new List<ReplayFrame>
82+
{
83+
new TaikoReplayFrame(0),
84+
}, CreateBeatmap(createDrumRoll(true)));
85+
86+
AssertJudgementCount(12);
87+
88+
for (int i = 0; i < 5; ++i)
89+
{
90+
AssertResult<DrumRollTick>(i, HitResult.IgnoreMiss);
91+
AssertResult<DrumRollTick.StrongNestedHit>(i, HitResult.IgnoreMiss);
92+
}
93+
94+
AssertResult<DrumRoll>(0, HitResult.IgnoreHit);
95+
}
96+
7897
[Test]
7998
public void TestHitAllStrongDrumRollWithOneKey()
8099
{

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

+57
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44
using System.Collections.Generic;
55
using NUnit.Framework;
66
using osu.Game.Beatmaps.ControlPoints;
7+
using osu.Game.Rulesets.Mods;
78
using osu.Game.Rulesets.Replays;
89
using osu.Game.Rulesets.Scoring;
10+
using osu.Game.Rulesets.Taiko.Mods;
911
using osu.Game.Rulesets.Taiko.Objects;
12+
using osu.Game.Rulesets.Taiko.Objects.Drawables;
1013
using osu.Game.Rulesets.Taiko.Replays;
14+
using osu.Game.Rulesets.Taiko.Scoring;
1115

1216
namespace osu.Game.Rulesets.Taiko.Tests.Judgements
1317
{
@@ -157,5 +161,58 @@ public void TestHighVelocityHit()
157161
AssertJudgementCount(1);
158162
AssertResult<Hit>(0, HitResult.Ok);
159163
}
164+
165+
[Test]
166+
public void TestStrongHitOneKeyWithHidden()
167+
{
168+
const double hit_time = 1000;
169+
170+
var beatmap = CreateBeatmap(new Hit
171+
{
172+
Type = HitType.Centre,
173+
StartTime = hit_time,
174+
IsStrong = true
175+
});
176+
177+
var hitWindows = new TaikoHitWindows();
178+
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);
179+
180+
PerformTest(new List<ReplayFrame>
181+
{
182+
new TaikoReplayFrame(0),
183+
new TaikoReplayFrame(hit_time + hitWindows.WindowFor(HitResult.Ok) - 1, TaikoAction.LeftCentre),
184+
}, beatmap, new Mod[] { new TaikoModHidden() });
185+
186+
AssertJudgementCount(2);
187+
AssertResult<Hit>(0, HitResult.Ok);
188+
AssertResult<Hit.StrongNestedHit>(0, HitResult.IgnoreMiss);
189+
}
190+
191+
[Test]
192+
public void TestStrongHitTwoKeysWithHidden()
193+
{
194+
const double hit_time = 1000;
195+
196+
var beatmap = CreateBeatmap(new Hit
197+
{
198+
Type = HitType.Centre,
199+
StartTime = hit_time,
200+
IsStrong = true
201+
});
202+
203+
var hitWindows = new TaikoHitWindows();
204+
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);
205+
206+
PerformTest(new List<ReplayFrame>
207+
{
208+
new TaikoReplayFrame(0),
209+
new TaikoReplayFrame(hit_time + hitWindows.WindowFor(HitResult.Ok) - 1, TaikoAction.LeftCentre),
210+
new TaikoReplayFrame(hit_time + hitWindows.WindowFor(HitResult.Ok) + DrawableHit.StrongNestedHit.SECOND_HIT_WINDOW - 2, TaikoAction.LeftCentre, TaikoAction.RightCentre),
211+
}, beatmap, new Mod[] { new TaikoModHidden() });
212+
213+
AssertJudgementCount(2);
214+
AssertResult<Hit>(0, HitResult.Ok);
215+
AssertResult<Hit.StrongNestedHit>(0, HitResult.LargeBonus);
216+
}
160217
}
161218
}

osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs

+2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject,
6262
hitObject.LifetimeEnd = state == ArmedState.Idle || !hitObject.AllJudged
6363
? hitObject.HitObject.GetEndTime() + hitObject.HitObject.HitWindows.WindowFor(HitResult.Miss)
6464
: hitObject.HitStateUpdateTime;
65+
// extend the lifetime end of the object in order to allow its nested strong hit (if any) to be judged.
66+
hitObject.LifetimeEnd += DrawableHit.StrongNestedHit.SECOND_HIT_WINDOW;
6567
}
6668

6769
break;

osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs

-8
Original file line numberDiff line numberDiff line change
@@ -195,14 +195,6 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
195195
ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
196196
}
197197

198-
public override void OnKilled()
199-
{
200-
base.OnKilled();
201-
202-
if (Time.Current > ParentHitObject.HitObject.GetEndTime() && !Judged)
203-
ApplyResult(r => r.Type = r.Judgement.MinResult);
204-
}
205-
206198
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
207199
}
208200
}

osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs

-8
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,6 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
108108
ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
109109
}
110110

111-
public override void OnKilled()
112-
{
113-
base.OnKilled();
114-
115-
if (Time.Current > ParentHitObject.HitObject.GetEndTime() && !Judged)
116-
ApplyResult(r => r.Type = r.Judgement.MinResult);
117-
}
118-
119111
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
120112
}
121113
}

osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs

+13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
22
// See the LICENCE file in the repository root for full licence text.
33

4+
using osu.Game.Rulesets.Objects;
45
using osu.Game.Rulesets.Taiko.Judgements;
56

67
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
@@ -16,5 +17,17 @@ protected DrawableStrongNestedHit(StrongNestedHitObject? nestedHit)
1617
: base(nestedHit)
1718
{
1819
}
20+
21+
public override void OnKilled()
22+
{
23+
base.OnKilled();
24+
25+
// usually, the strong nested hit isn't judged itself, it is judged by its parent object.
26+
// however, in rare cases (see: drum rolls, hits with hidden active),
27+
// it can happen that the hit window of the nested strong hit extends past the lifetime of the parent object.
28+
// this is a safety to prevent such cases from causing the nested hit to never be judged and as such prevent gameplay from completing.
29+
if (!Judged && Time.Current > ParentHitObject?.HitObject.GetEndTime())
30+
ApplyResult(r => r.Type = r.Judgement.MinResult);
31+
}
1932
}
2033
}

0 commit comments

Comments
 (0)