diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index 3c0260f5f5..77094f928b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -123,7 +123,7 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
return;
}
- var result = HitObject.HitWindows.ResultFor(timeOffset);
+ var result = ResultFor(timeOffset);
if (result == HitResult.None || CheckHittable?.Invoke(this, Time.Current) == false)
{
@@ -146,6 +146,13 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
});
}
+ ///
+ /// Retrieves the for a time offset.
+ ///
+ /// The time offset.
+ /// The hit result, or if doesn't result in a judgement.
+ protected virtual HitResult ResultFor(double timeOffset) => HitObject.HitWindows.ResultFor(timeOffset);
+
protected override void UpdateInitialTransforms()
{
base.UpdateInitialTransforms();
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 511cbc2347..7061ce59d0 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -15,6 +15,7 @@
using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Rulesets.Osu.UI;
+using osu.Game.Rulesets.Scoring;
using osuTK.Graphics;
using osu.Game.Skinning;
@@ -249,7 +250,28 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
if (userTriggered || Time.Current < HitObject.EndTime)
return;
- ApplyResult(r => r.Type = NestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult);
+ if (HitObject.IgnoreJudgement)
+ {
+ ApplyResult(r => r.Type = NestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult);
+ return;
+ }
+
+ // If not ignoring judgement, score proportionally based on the number of ticks hit, counting the head circle as a tick.
+ ApplyResult(r =>
+ {
+ int totalTicks = NestedHitObjects.Count;
+ int hitTicks = NestedHitObjects.Count(h => h.IsHit);
+ double hitFraction = (double)totalTicks / hitTicks;
+
+ if (hitTicks == totalTicks)
+ r.Type = HitResult.Great;
+ else if (hitFraction >= 0.5)
+ r.Type = HitResult.Ok;
+ else if (hitFraction > 0)
+ r.Type = HitResult.Meh;
+ else
+ r.Type = HitResult.Miss;
+ });
}
public override void PlaySamples()
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
index c051a9918d..08e9c5eb14 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
@@ -7,6 +7,7 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -19,6 +20,8 @@ public class DrawableSliderHead : DrawableHitCircle
protected DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject;
+ public override bool DisplayResult => HitObject?.JudgeAsNormalHitCircle ?? base.DisplayResult;
+
private readonly IBindable pathVersion = new Bindable();
protected override OsuSkinComponents CirclePieceComponent => OsuSkinComponents.SliderHeadHitCircle;
@@ -73,6 +76,18 @@ protected override void Update()
}
}
+ protected override HitResult ResultFor(double timeOffset)
+ {
+ Debug.Assert(HitObject != null);
+
+ if (HitObject.JudgeAsNormalHitCircle)
+ return base.ResultFor(timeOffset);
+
+ // If not judged as a normal hitcircle, only track whether a hit has occurred (via IgnoreHit) rather than a scorable hit result.
+ var result = base.ResultFor(timeOffset);
+ return result.IsHit() ? HitResult.IgnoreHit : result;
+ }
+
public Action OnShake;
public override void Shake(double maximumLength) => OnShake?.Invoke(maximumLength);
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 1670df24a8..e3365a8ccf 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -114,6 +114,12 @@ public int RepeatCount
///
public double TickDistanceMultiplier = 1;
+ ///
+ /// Whether this 's judgement should be ignored.
+ /// If false, this will be judged proportionally to the number of ticks hit.
+ ///
+ public bool IgnoreJudgement = true;
+
[JsonIgnore]
public HitCircle HeadCircle { get; protected set; }
@@ -233,7 +239,7 @@ private void updateNestedSamples()
HeadCircle.Samples = this.GetNodeSamples(0);
}
- public override Judgement CreateJudgement() => new OsuIgnoreJudgement();
+ public override Judgement CreateJudgement() => IgnoreJudgement ? new OsuIgnoreJudgement() : new OsuJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderHeadCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderHeadCircle.cs
index 5fc480883a..13eac60300 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderHeadCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderHeadCircle.cs
@@ -1,13 +1,25 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Osu.Judgements;
+
namespace osu.Game.Rulesets.Osu.Objects
{
public class SliderHeadCircle : HitCircle
{
///
- /// Makes the head circle track the follow circle when the start time is reached.
+ /// Makes this track the follow circle when the start time is reached.
+ /// If false, this will be pinned to its initial position in the slider.
///
public bool TrackFollowCircle = true;
+
+ ///
+ /// Whether to treat this as a normal for judgement purposes.
+ /// If false, judgement will be ignored.
+ ///
+ public bool JudgeAsNormalHitCircle = true;
+
+ public override Judgement CreateJudgement() => JudgeAsNormalHitCircle ? base.CreateJudgement() : new OsuIgnoreJudgement();
}
}