diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
index 649b01c132..d971e777ec 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
@@ -19,33 +19,46 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray();
+ ///
+ /// How early before a hitobject's start time to trigger a hit.
+ ///
+ private const float relax_leniency = 3;
+
public void Update(Playfield playfield)
{
bool requiresHold = false;
bool requiresHit = false;
- const float relax_leniency = 3;
+ double time = playfield.Clock.CurrentTime;
- foreach (var drawable in playfield.HitObjectContainer.AliveObjects)
+ foreach (var h in playfield.HitObjectContainer.AliveObjects.OfType())
{
- if (!(drawable is DrawableOsuHitObject osuHit))
+ // we are not yet close enough to the object.
+ if (time < h.HitObject.StartTime - relax_leniency)
+ break;
+
+ // already hit or beyond the hittable end time.
+ if (h.IsHit || (h.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime))
continue;
- double time = osuHit.Clock.CurrentTime;
- double relativetime = time - osuHit.HitObject.StartTime;
-
- if (time < osuHit.HitObject.StartTime - relax_leniency) continue;
-
- if ((osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime) || osuHit.IsHit)
- continue;
-
- if (osuHit is DrawableHitCircle && osuHit.IsHovered)
+ switch (h)
{
- Debug.Assert(osuHit.HitObject.HitWindows != null);
- requiresHit |= osuHit.HitObject.HitWindows.CanBeHit(relativetime);
- }
+ case DrawableHitCircle circle:
+ handleHitCircle(circle);
+ break;
- requiresHold |= (osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered)) || osuHit is DrawableSpinner;
+ case DrawableSlider slider:
+ // Handles cases like "2B" beatmaps, where sliders may be overlapping and simply holding is not enough.
+ if (!slider.HeadCircle.IsHit)
+ handleHitCircle(slider.HeadCircle);
+
+ requiresHold |= slider.Ball.IsHovered || h.IsHovered;
+ break;
+
+ case DrawableSpinner _:
+ requiresHold = true;
+ break;
+ }
}
if (requiresHit)
@@ -55,6 +68,15 @@ namespace osu.Game.Rulesets.Osu.Mods
}
addAction(requiresHold);
+
+ void handleHitCircle(DrawableHitCircle circle)
+ {
+ if (!circle.IsHovered)
+ return;
+
+ Debug.Assert(circle.HitObject.HitWindows != null);
+ requiresHit |= circle.HitObject.HitWindows.CanBeHit(time - circle.HitObject.StartTime);
+ }
}
private bool wasHit;