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;