diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs
index d883b91abc..567bf6968f 100644
--- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs
+++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs
@@ -84,6 +84,7 @@ public void TestSingleOsuHit(ScoringMode scoringMode, HitResult hitResult, int e
[TestCase(ScoringMode.Standardised, HitResult.SmallTickHit, HitResult.SmallTickHit, 493_652)]
[TestCase(ScoringMode.Standardised, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)]
[TestCase(ScoringMode.Standardised, HitResult.LargeTickHit, HitResult.LargeTickHit, 326_963)]
+ [TestCase(ScoringMode.Standardised, HitResult.SliderTailHit, HitResult.SliderTailHit, 326_963)]
[TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 1_000_030)]
[TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 1_000_150)]
[TestCase(ScoringMode.Classic, HitResult.Miss, HitResult.Great, 0)]
@@ -96,6 +97,7 @@ public void TestSingleOsuHit(ScoringMode scoringMode, HitResult hitResult, int e
[TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 49_365)]
[TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)]
[TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 32_696)]
+ [TestCase(ScoringMode.Classic, HitResult.SliderTailHit, HitResult.SliderTailHit, 32_696)]
[TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 100_003)]
[TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 100_015)]
public void TestFourVariousResultsOneMiss(ScoringMode scoringMode, HitResult hitResult, HitResult maxResult, int expectedScore)
@@ -167,6 +169,7 @@ public void TestEmptyBeatmap(
[TestCase(HitResult.Perfect, HitResult.Miss)]
[TestCase(HitResult.SmallTickHit, HitResult.SmallTickMiss)]
[TestCase(HitResult.LargeTickHit, HitResult.LargeTickMiss)]
+ [TestCase(HitResult.SliderTailHit, HitResult.LargeTickMiss)]
[TestCase(HitResult.SmallBonus, HitResult.IgnoreMiss)]
[TestCase(HitResult.LargeBonus, HitResult.IgnoreMiss)]
public void TestMinResults(HitResult hitResult, HitResult expectedMinResult)
@@ -187,6 +190,7 @@ public void TestMinResults(HitResult hitResult, HitResult expectedMinResult)
[TestCase(HitResult.SmallTickHit, false)]
[TestCase(HitResult.LargeTickMiss, true)]
[TestCase(HitResult.LargeTickHit, true)]
+ [TestCase(HitResult.SliderTailHit, true)]
[TestCase(HitResult.SmallBonus, false)]
[TestCase(HitResult.LargeBonus, false)]
public void TestAffectsCombo(HitResult hitResult, bool expectedReturnValue)
@@ -207,6 +211,7 @@ public void TestAffectsCombo(HitResult hitResult, bool expectedReturnValue)
[TestCase(HitResult.SmallTickHit, true)]
[TestCase(HitResult.LargeTickMiss, true)]
[TestCase(HitResult.LargeTickHit, true)]
+ [TestCase(HitResult.SliderTailHit, true)]
[TestCase(HitResult.SmallBonus, false)]
[TestCase(HitResult.LargeBonus, false)]
public void TestAffectsAccuracy(HitResult hitResult, bool expectedReturnValue)
@@ -227,6 +232,7 @@ public void TestAffectsAccuracy(HitResult hitResult, bool expectedReturnValue)
[TestCase(HitResult.SmallTickHit, false)]
[TestCase(HitResult.LargeTickMiss, false)]
[TestCase(HitResult.LargeTickHit, false)]
+ [TestCase(HitResult.SliderTailHit, false)]
[TestCase(HitResult.SmallBonus, true)]
[TestCase(HitResult.LargeBonus, true)]
public void TestIsBonus(HitResult hitResult, bool expectedReturnValue)
@@ -247,6 +253,7 @@ public void TestIsBonus(HitResult hitResult, bool expectedReturnValue)
[TestCase(HitResult.SmallTickHit, true)]
[TestCase(HitResult.LargeTickMiss, false)]
[TestCase(HitResult.LargeTickHit, true)]
+ [TestCase(HitResult.SliderTailHit, true)]
[TestCase(HitResult.SmallBonus, true)]
[TestCase(HitResult.LargeBonus, true)]
public void TestIsHit(HitResult hitResult, bool expectedReturnValue)
@@ -267,6 +274,7 @@ public void TestIsHit(HitResult hitResult, bool expectedReturnValue)
[TestCase(HitResult.SmallTickHit, true)]
[TestCase(HitResult.LargeTickMiss, true)]
[TestCase(HitResult.LargeTickHit, true)]
+ [TestCase(HitResult.SliderTailHit, true)]
[TestCase(HitResult.SmallBonus, true)]
[TestCase(HitResult.LargeBonus, true)]
public void TestIsScorable(HitResult hitResult, bool expectedReturnValue)
diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs
index 27402d522c..93386de483 100644
--- a/osu.Game/Rulesets/Judgements/Judgement.cs
+++ b/osu.Game/Rulesets/Judgements/Judgement.cs
@@ -73,6 +73,7 @@ public virtual HitResult MinResult
return HitResult.SmallTickMiss;
case HitResult.LargeTickHit:
+ case HitResult.SliderTailHit:
return HitResult.LargeTickMiss;
default:
@@ -104,6 +105,7 @@ protected virtual double HealthIncreaseFor(HitResult result)
case HitResult.SmallTickMiss:
return -DEFAULT_MAX_HEALTH_INCREASE * 0.5;
+ case HitResult.SliderTailHit:
case HitResult.LargeTickHit:
return DEFAULT_MAX_HEALTH_INCREASE;
diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs
index 2d55f1a649..f307344347 100644
--- a/osu.Game/Rulesets/Scoring/HitResult.cs
+++ b/osu.Game/Rulesets/Scoring/HitResult.cs
@@ -139,6 +139,13 @@ public enum HitResult
[Order(15)]
ComboBreak,
+ ///
+ /// A special judgement similar to that's used to increase the valuation of the final tick of a slider.
+ ///
+ [EnumMember(Value = "slider_tail_hit")]
+ [Order(16)]
+ SliderTailHit,
+
///
/// A special result used as a padding value for legacy rulesets. It is a hit type and affects combo, but does not affect the base score (does not affect accuracy).
///
@@ -188,6 +195,7 @@ public static bool AffectsCombo(this HitResult result)
case HitResult.LargeTickMiss:
case HitResult.LegacyComboIncrease:
case HitResult.ComboBreak:
+ case HitResult.SliderTailHit:
return true;
default:
@@ -246,6 +254,7 @@ public static bool IsTick(this HitResult result)
case HitResult.LargeTickMiss:
case HitResult.SmallTickHit:
case HitResult.SmallTickMiss:
+ case HitResult.SliderTailHit:
return true;
default:
@@ -329,6 +338,9 @@ public static bool IsScorable(this HitResult result)
case HitResult.ComboBreak:
return true;
+ case HitResult.SliderTailHit:
+ return true;
+
default:
// Note that IgnoreHit and IgnoreMiss are excluded as they do not affect score.
return result >= HitResult.Miss && result < HitResult.IgnoreMiss;
@@ -383,6 +395,9 @@ public static void ValidateHitResultPair(HitResult maxResult, HitResult minResul
if (minResult == HitResult.IgnoreMiss)
return;
+ if (maxResult == HitResult.SliderTailHit && minResult != HitResult.LargeTickMiss)
+ throw new ArgumentOutOfRangeException(nameof(minResult), $"{HitResult.LargeTickMiss} is the only valid minimum result for a {maxResult} judgement.");
+
if (maxResult == HitResult.LargeTickHit && minResult != HitResult.LargeTickMiss)
throw new ArgumentOutOfRangeException(nameof(minResult), $"{HitResult.LargeTickMiss} is the only valid minimum result for a {maxResult} judgement.");
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 5123668e54..837bb4080e 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -322,6 +322,9 @@ public virtual int GetBaseScoreForResult(HitResult result)
case HitResult.LargeTickHit:
return 30;
+ case HitResult.SliderTailHit:
+ return 150;
+
case HitResult.Meh:
return 50;