internal DifficultyHitObjectOsu(HitObject BaseHitObject, float CircleRadius) { this.BaseHitObject = BaseHitObject; if (BaseHitObject.IsType(HitObjectType.Slider)) { SliderOsu Slider = (SliderOsu)BaseHitObject; MaxCombo = 1 + Slider.sliderScoreTimingPoints.Count; } else { MaxCombo = 1; } // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps. float ScalingFactor = (52.0f / CircleRadius); if (CircleRadius < 30) { float smallCircleBonus = Math.Min(30.0f - CircleRadius, 5.0f) / 50.0f; ScalingFactor *= 1.0f + smallCircleBonus; } NormalizedStartPosition = BaseHitObject.Position * ScalingFactor; // Calculate approximation of lazy movement on the slider if (BaseHitObject.IsType(HitObjectType.Slider)) { float SliderFollowCircleRadius = CircleRadius * 3; // Not sure if this is correct, but here we do not need 100% exact values. This comes pretty darn close in my tests. int SegmentLength = Math.Min(BaseHitObject.Length / BaseHitObject.SegmentCount, 60000); // We don't want infinite loops if someone decides to make a too long slider. (MillhioreF, I am talking about you! https://osu.ppy.sh/b/326585) int SegmentEndTime = BaseHitObject.StartTime + SegmentLength; // For simplifying this step we use actual osu! coordinates and simply scale the length, that we obtain by the ScalingFactor later Vector2 CursorPos = BaseHitObject.Position; // //Debug.Print("" + (BaseHitObject.StartTime + LAZY_SLIDER_STEP_LENGTH) + " " + SegmentEndTime + " " + BaseHitObject.EndTime + " " + BaseHitObject.SegmentCount); // Actual computation of the first lazy curve for (int Time = BaseHitObject.StartTime + LAZY_SLIDER_STEP_LENGTH; Time < SegmentEndTime; Time += LAZY_SLIDER_STEP_LENGTH) { Vector2 Difference = BaseHitObject.PositionAtTime(Time) - CursorPos; float Distance = Difference.Length(); // Did we move away too far? if (Distance > SliderFollowCircleRadius) { // Yep, we need to move the cursor Difference.Normalize(); // Obtain the direction of difference. We do no longer need the actual difference Distance -= SliderFollowCircleRadius; CursorPos += Difference * Distance; // We move the cursor just as far as needed to stay in the follow circle LazySliderLengthFirst += Distance; } } LazySliderLengthFirst *= ScalingFactor; // If we have an odd amount of repetitions the current position will be the end of the slider. Note that this will -always- be triggered if // BaseHitObject.SegmentCount <= 1, because BaseHitObject.SegmentCount can not be smaller than 1. Therefore NormalizedEndPosition will always be initialized if (BaseHitObject.SegmentCount % 2 == 1) { NormalizedEndPosition = CursorPos * ScalingFactor; } // If we have more than one segment, then we also need to compute the length ob subsequent lazy curves. They are different from the first one, since the first // one starts right at the beginning of the slider. if (BaseHitObject.SegmentCount > 1) { // Use the next segment SegmentEndTime += SegmentLength; for (int Time = SegmentEndTime - SegmentLength + LAZY_SLIDER_STEP_LENGTH; Time < SegmentEndTime; Time += LAZY_SLIDER_STEP_LENGTH) { Vector2 Difference = BaseHitObject.PositionAtTime(Time) - CursorPos; float Distance = Difference.Length(); // Did we move away too far? if (Distance > SliderFollowCircleRadius) { // Yep, we need to move the cursor Difference.Normalize(); // Obtain the direction of difference. We do no longer need the actual difference Distance -= SliderFollowCircleRadius; CursorPos += Difference * Distance; // We move the cursor just as far as needed to stay in the follow circle LazySliderLengthSubsequent += Distance; } } LazySliderLengthSubsequent *= ScalingFactor; // If we have an even amount of repetitions the current position will be the end of the slider if (BaseHitObject.SegmentCount % 2 == 0) { NormalizedEndPosition = CursorPos * ScalingFactor; } } } // We have a normal HitCircle or a spinner else { NormalizedEndPosition = NormalizedStartPosition; } }