diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Aim/AgilityEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Aim/AgilityEvaluator.cs index 98a5c7680e15..da641cc4d80d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Aim/AgilityEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Aim/AgilityEvaluator.cs @@ -22,10 +22,8 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current) return 0; var osuCurrObj = (OsuDifficultyHitObject)current; - var osuPrevObj = current.Index > 0 ? (OsuDifficultyHitObject)current.Previous(0) : null; - double travelDistance = osuPrevObj?.LazyTravelDistance ?? 0; - double distance = travelDistance + osuCurrObj.LazyJumpDistance; + double distance = osuCurrObj.GetDistance(true); double distanceScaled = Math.Min(distance, distance_cap) / distance_cap; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Aim/FlowAimEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Aim/FlowAimEvaluator.cs index 9b1903e6137c..a3efa671e67b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Aim/FlowAimEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Aim/FlowAimEvaluator.cs @@ -26,18 +26,12 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with var osuLastObj = (OsuDifficultyHitObject)current.Previous(0); var osuLastLastObj = (OsuDifficultyHitObject)current.Previous(1); - double currDistance = withSliderTravelDistance ? osuCurrObj.LazyJumpDistance : osuCurrObj.JumpDistance; - double prevDistance = withSliderTravelDistance ? osuLastObj.LazyJumpDistance : osuLastObj.JumpDistance; + double currDistance = osuCurrObj.GetDistance(withSliderTravelDistance); + double prevDistance = withSliderTravelDistance ? osuLastObj.TailJumpDistance : osuLastObj.GetDistance(false); - double currVelocity = currDistance / osuCurrObj.AdjustedDeltaTime; - - if (osuLastObj.BaseObject is Slider && withSliderTravelDistance) - { - // If the last object is a slider, then we extend the travel velocity through the slider into the current object. - double sliderDistance = osuLastObj.LazyTravelDistance + osuCurrObj.LazyJumpDistance; - currVelocity = Math.Max(currVelocity, sliderDistance / osuCurrObj.AdjustedDeltaTime); - } + double currFakeDistance = withSliderTravelDistance ? osuCurrObj.TailJumpDistance : osuCurrObj.GetDistance(false); // Keeping to preserve values + double currVelocity = currDistance / osuCurrObj.AdjustedDeltaTime; double prevVelocity = prevDistance / osuLastObj.AdjustedDeltaTime; double flowDifficulty = currVelocity; @@ -85,7 +79,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with { if (withSliderTravelDistance) { - currVelocity = currDistance / osuCurrObj.AdjustedDeltaTime; + currVelocity = currFakeDistance / osuCurrObj.AdjustedDeltaTime; } // Scale with ratio of difference compared to 0.5 * max dist. @@ -104,7 +98,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with if (osuCurrObj.BaseObject is Slider && withSliderTravelDistance) { // Include slider velocity to make velocity more consistent with snap - flowDifficulty += osuCurrObj.TravelDistance / osuCurrObj.TravelTime; + flowDifficulty += osuCurrObj.SliderBonusDistance / osuCurrObj.SliderTravelTime; } // Final velocity is being raised to a power because flow difficulty scales harder with both high distance and time, and we want to account for that diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Aim/SnapAimEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Aim/SnapAimEvaluator.cs index 75ecfe89ecc1..89feb90e0ba8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Aim/SnapAimEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Aim/SnapAimEvaluator.cs @@ -42,17 +42,12 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with const int diameter = OsuDifficultyHitObject.NORMALISED_DIAMETER; // Calculate the velocity to the current hitobject, which starts with a base distance / time assuming the last object is a hitcircle. - double currDistance = withSliderTravelDistance ? osuCurrObj.LazyJumpDistance : osuCurrObj.JumpDistance; - double currVelocity = currDistance / osuCurrObj.AdjustedDeltaTime; + double currDistance = osuCurrObj.GetDistance(withSliderTravelDistance); + double prevDistance = withSliderTravelDistance ? osuLastObj.TailJumpDistance : osuLastObj.GetDistance(false); - // But if the last object is a slider, then we extend the travel velocity through the slider into the current object. - if (osuLastObj.BaseObject is Slider && withSliderTravelDistance) - { - double sliderDistance = osuLastObj.LazyTravelDistance + osuCurrObj.LazyJumpDistance; - currVelocity = Math.Max(currVelocity, sliderDistance / osuCurrObj.AdjustedDeltaTime); - } + double currFakeDistance = withSliderTravelDistance ? osuCurrObj.TailJumpDistance : osuCurrObj.GetDistance(false); // Keeping to preserve values - double prevDistance = withSliderTravelDistance ? osuLastObj.LazyJumpDistance : osuLastObj.JumpDistance; + double currVelocity = currDistance / osuCurrObj.AdjustedDeltaTime; double prevVelocity = prevDistance / osuLastObj.AdjustedDeltaTime; double wideAngleBonus = 0; @@ -94,8 +89,8 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with // Apply wiggle bonus for jumps that are [radius, 3*diameter] in distance, with < 110 angle // https://www.desmos.com/calculator/dp0v0nvowc wiggleBonus = angleBonus - * DifficultyCalculationUtils.Smootherstep(currDistance, radius, diameter) - * Math.Pow(DifficultyCalculationUtils.ReverseLerp(currDistance, diameter * 3, diameter), 1.8) + * DifficultyCalculationUtils.Smootherstep(currFakeDistance, radius, diameter) + * Math.Pow(DifficultyCalculationUtils.ReverseLerp(currFakeDistance, diameter * 3, diameter), 1.8) * DifficultyCalculationUtils.Smootherstep(currAngle, double.DegreesToRadians(110), double.DegreesToRadians(60)) * DifficultyCalculationUtils.Smootherstep(prevDistance, radius, diameter) * Math.Pow(DifficultyCalculationUtils.ReverseLerp(prevDistance, diameter * 3, diameter), 1.8) @@ -122,7 +117,8 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with if (withSliderTravelDistance) { // We want to use just the object jump without slider velocity when awarding differences - currVelocity = currDistance / osuCurrObj.AdjustedDeltaTime; + // This is objectively incorrect in some cases, but kept for now to preserve values + currVelocity = currFakeDistance / osuCurrObj.AdjustedDeltaTime; } // Scale with ratio of difference compared to 0.5 * max dist. @@ -140,7 +136,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with if (osuCurrObj.BaseObject is Slider) { // Reward sliders based on velocity. - sliderBonus = osuCurrObj.TravelDistance / osuCurrObj.TravelTime; + sliderBonus = osuCurrObj.SliderBonusDistance / osuCurrObj.SliderTravelTime; } // Penalize angle repetition. @@ -159,7 +155,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with // Apply high circle size bonus aimStrain *= osuCurrObj.SmallCircleBonus; - aimStrain *= highBpmBonus(osuCurrObj.AdjustedDeltaTime, osuCurrObj.LazyJumpDistance); + aimStrain *= highBpmBonus(osuCurrObj.AdjustedDeltaTime, osuCurrObj.TailJumpDistance); return aimStrain; } @@ -201,7 +197,7 @@ private static double vectorAngleRepetition(OsuDifficultyHitObject current, OsuD double vectorRepetition = Math.Pow(Math.Min(0.5 / constantAngleCount, 1), 2); - double stackFactor = DifficultyCalculationUtils.Smootherstep(current.LazyJumpDistance, 0, OsuDifficultyHitObject.NORMALISED_DIAMETER); + double stackFactor = DifficultyCalculationUtils.Smootherstep(current.TailJumpDistance, 0, OsuDifficultyHitObject.NORMALISED_DIAMETER); double currAngle = current.Angle.Value; double lastAngle = previous.Angle.Value; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs index e828eba1cc61..9b1dabd782db 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/FlashlightEvaluator.cs @@ -67,7 +67,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, IReadOnly smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); // We also want to nerf stacks so that only the first object of the stack is accounted for. - double stackNerf = Math.Min(1.0, (currentObj.LazyJumpDistance / scalingFactor) / 25.0); + double stackNerf = Math.Min(1.0, (currentObj.TailJumpDistance / scalingFactor) / 25.0); // Bonus based on how visible the object is. double opacityBonus = 1.0 + max_opacity_bonus * (1.0 - osuCurrent.OpacityAt(currentHitObject.StartTime, mods.OfType().Any(m => !m.OnlyFadeApproachCircles.Value))); @@ -99,10 +99,10 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, IReadOnly if (osuCurrent.BaseObject is Slider osuSlider) { // Invert the scaling factor to determine the true travel distance independent of circle size. - double pixelTravelDistance = osuCurrent.LazyTravelDistance / scalingFactor; + double pixelTravelDistance = osuCurrent.SliderBodyDistance / scalingFactor; // Reward sliders based on velocity. - sliderBonus = Math.Pow(Math.Max(0.0, pixelTravelDistance / osuCurrent.TravelTime - min_velocity), 0.5); + sliderBonus = Math.Pow(Math.Max(0.0, pixelTravelDistance / osuCurrent.SliderTravelTime - min_velocity), 0.5); // Longer sliders require more memorisation. sliderBonus *= pixelTravelDistance; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/ReadingEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/ReadingEvaluator.cs index fe6d49661b9c..7f202e0add7f 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/ReadingEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/ReadingEvaluator.cs @@ -31,7 +31,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool hidd var currObj = (OsuDifficultyHitObject)current; var nextObj = (OsuDifficultyHitObject)current.Next(0); - double velocity = Math.Max(1, currObj.LazyJumpDistance / currObj.AdjustedDeltaTime); // Only allow velocity to buff + double velocity = Math.Max(1, currObj.TailJumpDistance / currObj.AdjustedDeltaTime); // Only allow velocity to buff double currentVisibleObjectDensity = retrieveCurrentVisibleObjectDensity(currObj); double pastObjectDifficultyInfluence = getPastObjectDifficultyInfluence(currObj); @@ -72,7 +72,7 @@ private static double calculateDensityDifficulty(OsuDifficultyHitObject? nextObj if (nextObj != null) { // Reduce difficulty if movement to next object is small - futureObjectDifficultyInfluence *= DifficultyCalculationUtils.Smootherstep(nextObj.LazyJumpDistance, 15, distance_influence_threshold); + futureObjectDifficultyInfluence *= DifficultyCalculationUtils.Smootherstep(nextObj.TailJumpDistance, 15, distance_influence_threshold); } // Value higher note densities exponentially @@ -134,7 +134,7 @@ private static double calculateHiddenDifficulty(OsuDifficultyHitObject currObj, var previousObj = (OsuDifficultyHitObject)currObj.Previous(0); // Buff perfect stacks only if current note is completely invisible at the time you click the previous note. - if (currObj.LazyJumpDistance == 0 && currObj.OpacityAt(previousObj.BaseObject.StartTime, true) == 0 && previousObj.StartTime > currObj.StartTime - currObj.Preempt) + if (currObj.TailJumpDistance == 0 && currObj.OpacityAt(previousObj.BaseObject.StartTime, true) == 0 && previousObj.StartTime > currObj.StartTime - currObj.Preempt) hiddenDifficulty += hidden_multiplier * 2500 / Math.Pow(currObj.AdjustedDeltaTime, 1.5); // Perfect stacks are harder the less time between notes return hiddenDifficulty; @@ -149,7 +149,7 @@ private static double getPastObjectDifficultyInfluence(OsuDifficultyHitObject cu double loopDifficulty = currObj.OpacityAt(loopObj.BaseObject.StartTime, false); // When aiming an object small distances mean previous objects may be cheesed, so it doesn't matter whether they were arranged confusingly. - loopDifficulty *= DifficultyCalculationUtils.Smootherstep(loopObj.LazyJumpDistance, 15, distance_influence_threshold); + loopDifficulty *= DifficultyCalculationUtils.Smootherstep(loopObj.TailJumpDistance, 15, distance_influence_threshold); // Account less for objects close to the max reading window double timeBetweenCurrAndLoopObj = currObj.StartTime - loopObj.StartTime; @@ -245,7 +245,7 @@ private static double getConstantAngleNerfFactor(OsuDifficultyHitObject current) angleDifferenceAlternating = double.Lerp(Math.PI, 0.1 * angleDifferenceAlternating, weight); } - double stackFactor = DifficultyCalculationUtils.Smootherstep(loopObj.LazyJumpDistance, 0, OsuDifficultyHitObject.NORMALISED_RADIUS); + double stackFactor = DifficultyCalculationUtils.Smootherstep(loopObj.TailJumpDistance, 0, OsuDifficultyHitObject.NORMALISED_RADIUS); constantAngleCount += Math.Cos(3 * Math.Min(double.DegreesToRadians(30), Math.Min(angleDifference, angleDifferenceAlternating) * stackFactor)) * longIntervalFactor; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Speed/RhythmEvaluator.cs b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Speed/RhythmEvaluator.cs index e2ee41162b41..8a1c3ee79589 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Speed/RhythmEvaluator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Evaluators/Speed/RhythmEvaluator.cs @@ -86,7 +86,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current) // for example a slider-circle-circle pattern should be evaluated as a regular triple and not as a single->double if (prevObj.BaseObject is Slider) { - double sliderLazyEndDelta = currObj.MinimumJumpTime; + double sliderLazyEndDelta = currObj.TailDeltaTime; double sliderLazyDeltaDifference = Math.Max(sliderLazyEndDelta, currDelta) / Math.Min(sliderLazyEndDelta, currDelta); double sliderRealEndDelta = currObj.LastObjectEndDeltaTime; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 221b2c637852..d2b668d203ca 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -46,51 +46,35 @@ public class OsuDifficultyHitObject : DifficultyHitObject /// public readonly double Preempt; - /// - /// Normalised distance from the start position of the previous to the start position of this . - /// - public double JumpDistance { get; private set; } - /// /// Normalised distance from the "lazy" end position of the previous to the start position of this . /// /// The "lazy" end position is the position at which the cursor ends up if the previous hitobject is followed with as minimal movement as possible (i.e. on the edge of slider follow circles). /// /// - public double LazyJumpDistance { get; private set; } + public double TailJumpDistance { get; private set; } /// - /// Normalised shortest distance to consider for a jump between the previous and this . + /// Amount of time elapsed between the end of and the start of , + /// adjusted by clockrate and capped to a minimum of ms. /// - /// - /// This is bounded from above by , and is smaller than the former if a more natural path is able to be taken through the previous . - /// - /// - /// Suppose a linear slider - circle pattern. - ///
- /// Following the slider lazily (see: ) will result in underestimating the true end position of the slider as being closer towards the start position. - /// As a result, overestimates the jump distance because the player is able to take a more natural path by following through the slider to its end, - /// such that the jump is felt as only starting from the slider's true end position. - ///
- /// Now consider a slider - circle pattern where the circle is stacked along the path inside the slider. - /// In this case, the lazy end position correctly estimates the true end position of the slider and provides the more natural movement path. - ///
- public double MinimumJumpDistance { get; private set; } + public double TailDeltaTime; /// - /// The time taken to travel through , with a minimum value of 25ms. + /// The distance travelled by the cursor upon completion of this if it is a + /// and was hit with as few movements as possible. /// - public double MinimumJumpTime { get; private set; } + public double SliderBodyDistance { get; private set; } /// - /// Normalised distance between the start and end position of this . + /// The time taken to travel through , with a minimum value of 25ms for objects. /// - public double TravelDistance { get; private set; } + public double SliderTravelTime { get; private set; } /// - /// The time taken to travel through , with a minimum value of 25ms for objects. + /// Normalised distance between the start and end position of this . /// - public double TravelTime { get; private set; } + public double SliderBonusDistance { get; private set; } /// /// The position of the cursor at the point of completion of this if it is a @@ -98,18 +82,6 @@ public class OsuDifficultyHitObject : DifficultyHitObject /// public Vector2? LazyEndPosition { get; private set; } - /// - /// The distance travelled by the cursor upon completion of this if it is a - /// and was hit with as few movements as possible. - /// - public double LazyTravelDistance { get; private set; } - - /// - /// The time taken by the cursor upon completion of this if it is a - /// and was hit with as few movements as possible. - /// - public double LazyTravelTime { get; private set; } - /// /// Angle the player has to take to hit this . /// Calculated as the angle between the circles (current-2, current-1, current). @@ -127,6 +99,17 @@ public class OsuDifficultyHitObject : DifficultyHitObject /// public double SmallCircleBonus { get; private set; } + /// + /// Normalised distance the cursor travels from the previous to the start position of this , + /// including the slider body. + /// + private double distanceWithSlider { get; set; } + + /// + /// Normalised distance from the start position of the previous to the start position of this . + /// + private double distanceWithoutSlider { get; set; } + private readonly OsuDifficultyHitObject? lastLastDifficultyObject; private readonly OsuDifficultyHitObject? lastDifficultyObject; @@ -144,7 +127,7 @@ public OsuDifficultyHitObject(HitObject hitObject, HitObject lastObject, double Preempt = BaseObject.TimePreempt / clockRate; - computeSliderCursorPosition(); + computeSliderCursorPosition(clockRate); setDistances(clockRate); } @@ -205,11 +188,10 @@ private void setDistances(double clockRate) if (BaseObject is Slider currentSlider) { // Bonus for repeat sliders until a better per nested object strain system can be achieved. - TravelDistance = LazyTravelDistance * Math.Max(1, Math.Pow(currentSlider.RepeatCount, 0.3)); - TravelTime = Math.Max(LazyTravelTime / clockRate, MIN_DELTA_TIME); + SliderBonusDistance = SliderBodyDistance * Math.Max(1, Math.Pow(currentSlider.RepeatCount, 0.3)); } - MinimumJumpTime = AdjustedDeltaTime; + TailDeltaTime = AdjustedDeltaTime; // We don't need to calculate either angle or distance when one of the last->curr objects is a spinner if (BaseObject is Spinner || LastObject is Spinner) @@ -220,44 +202,22 @@ private void setDistances(double clockRate) Vector2 lastCursorPosition = lastDifficultyObject != null ? getEndCursorPosition(lastDifficultyObject) : LastObject.StackedPosition; - JumpDistance = (LastObject.StackedPosition - BaseObject.StackedPosition).Length * scalingFactor; - LazyJumpDistance = (BaseObject.StackedPosition - lastCursorPosition).Length * scalingFactor; - MinimumJumpDistance = LazyJumpDistance; + distanceWithoutSlider = (BaseObject.StackedPosition - LastObject.StackedPosition).Length * scalingFactor; + TailJumpDistance = (BaseObject.StackedPosition - lastCursorPosition).Length * scalingFactor; - if (LastObject is Slider lastSlider && lastDifficultyObject != null) + if (LastObject is Slider && lastDifficultyObject != null) { - double lastTravelTime = Math.Max(lastDifficultyObject.LazyTravelTime / clockRate, MIN_DELTA_TIME); - MinimumJumpTime = Math.Max(AdjustedDeltaTime - lastTravelTime, MIN_DELTA_TIME); - - // - // There are two types of slider-to-object patterns to consider in order to better approximate the real movement a player will take to jump between the hitobjects. - // - // 1. The anti-flow pattern, where players cut the slider short in order to move to the next hitobject. - // - // <======o==> ← slider - // | ← most natural jump path - // o ← a follow-up hitcircle - // - // In this case the most natural jump path is approximated by LazyJumpDistance. - // - // 2. The flow pattern, where players follow through the slider to its visual extent into the next hitobject. - // - // <======o==>---o - // ↑ - // most natural jump path - // - // In this case the most natural jump path is better approximated by a new distance called "tailJumpDistance" - the distance between the slider's tail and the next hitobject. - // - // Thus, the player is assumed to jump the minimum of these two distances in all cases. - // - - float tailJumpDistance = Vector2.Subtract(lastSlider.TailCircle.StackedPosition, BaseObject.StackedPosition).Length * scalingFactor; - MinimumJumpDistance = Math.Max(0, Math.Min(LazyJumpDistance - (maximum_slider_radius - assumed_slider_radius), tailJumpDistance - maximum_slider_radius)); + TailDeltaTime = Math.Max(TailDeltaTime - lastDifficultyObject.SliderTravelTime, MIN_DELTA_TIME); + distanceWithSlider = TailJumpDistance + lastDifficultyObject.SliderBodyDistance; + } + else + { + distanceWithSlider = distanceWithoutSlider; } if (lastLastDifficultyObject != null && lastLastDifficultyObject.BaseObject is not Spinner) { - if (lastDifficultyObject!.BaseObject is Slider prevSlider && lastDifficultyObject.TravelDistance > 0) + if (lastDifficultyObject!.BaseObject is Slider prevSlider && lastDifficultyObject.SliderBonusDistance > 0) lastCursorPosition = prevSlider.HeadCircle.StackedPosition; Vector2 lastLastCursorPosition = getEndCursorPosition(lastLastDifficultyObject); @@ -272,7 +232,7 @@ private void setDistances(double clockRate) } } - private void computeSliderCursorPosition() + private void computeSliderCursorPosition(double clockRate) { if (BaseObject is not Slider slider) return; @@ -324,9 +284,10 @@ private void computeSliderCursorPosition() nestedObjects = reordered; } - LazyTravelTime = trackingEndTime - slider.StartTime; + double lazyTravelTime = trackingEndTime - slider.StartTime; + SliderTravelTime = Math.Max(lazyTravelTime / clockRate, MIN_DELTA_TIME); - double endTimeMin = LazyTravelTime / slider.SpanDuration; + double endTimeMin = lazyTravelTime / slider.SpanDuration; if (endTimeMin % 2 >= 1) endTimeMin = 1 - endTimeMin % 1; else @@ -372,7 +333,7 @@ private void computeSliderCursorPosition() // this finds the positional delta from the required radius and the current position, and updates the currCursorPosition accordingly, as well as rewarding distance. currCursorPosition = Vector2.Add(currCursorPosition, Vector2.Multiply(currMovement, (float)((currMovementLength - requiredMovement) / currMovementLength))); currMovementLength *= (currMovementLength - requiredMovement) / currMovementLength; - LazyTravelDistance += currMovementLength; + SliderBodyDistance += currMovementLength; } if (i == nestedObjects.Count - 1) @@ -384,7 +345,7 @@ private double calculateSliderAngle(OsuDifficultyHitObject lastDifficultyObject, { Vector2 lastCursorPosition = getEndCursorPosition(lastDifficultyObject); - if (lastDifficultyObject.BaseObject is Slider prevSlider && lastDifficultyObject.TravelDistance > 0) + if (lastDifficultyObject.BaseObject is Slider prevSlider && lastDifficultyObject.SliderBonusDistance > 0) { OsuHitObject secondLastNestedObject = (OsuHitObject)prevSlider.NestedHitObjects[^2]; lastLastCursorPosition = secondLastNestedObject.StackedPosition; @@ -408,5 +369,7 @@ private Vector2 getEndCursorPosition(OsuDifficultyHitObject difficultyHitObject) { return difficultyHitObject.LazyEndPosition ?? difficultyHitObject.BaseObject.StackedPosition; } + + public double GetDistance(bool withSlider) => withSlider ? distanceWithSlider : distanceWithoutSlider; } }