@@ -16,13 +16,6 @@ public class PumpTrainerBeatmapConverter : BeatmapConverter<PumpTrainerHitObject
1616 public PumpTrainerBeatmapConverterSettings Settings => generator . Settings ;
1717 private NextHitObjectGenerator generator = new ( ) ;
1818
19- /// <summary>
20- /// For osu! sliders with no repeats, this represents whether to convert the end of the slider to a hitobject.
21- /// Even if this is true, the slider must be at least length 1/4 of a beat for its end to be converted.
22- /// (For repeating sliders, each slider end is always converted to hitobjects whether this is true or false.)
23- /// </summary>
24- public bool CountNormalSliderEnds = true ;
25-
2619 public PumpTrainerBeatmapConverter ( IBeatmap beatmap , Ruleset ruleset )
2720 : base ( beatmap , ruleset )
2821 {
@@ -41,14 +34,14 @@ protected override IEnumerable<PumpTrainerHitObject> ConvertHitObject(HitObject
4134
4235 if ( original is IHasRepeats hasRepeats )
4336 {
44- // This is a slider. (Even a sliders with no repeats is IHasRepeats)
37+ // This is a slider. (Even a slider with no repeats is IHasRepeats)
4538
4639 int hitObjectsToReturnAfterFirst = hasRepeats . RepeatCount + 1 ; // +1 for the last hit object
4740
4841 double durationBetweenHitObjects = ( hasRepeats . EndTime - original . StartTime ) / hitObjectsToReturnAfterFirst ;
4942 TimingControlPoint currentTimingPoint = beatmap . ControlPointInfo . TimingPointAt ( original . StartTime ) ;
5043
51- const double rounding_error = 5 ;
44+ const double rounding_error = 5 ; // Use this rounding error "generously" for '<=' and '>=', and "not generously" for '<' and '>'
5245
5346 if ( hitObjectsToReturnAfterFirst > 1 )
5447 {
@@ -68,15 +61,44 @@ protected override IEnumerable<PumpTrainerHitObject> ConvertHitObject(HitObject
6861 yield return generator . GetNextHitObject ( newHitObjectTime , beatmap ) ;
6962 }
7063 }
71- else if ( CountNormalSliderEnds )
64+ else if ( durationBetweenHitObjects >= currentTimingPoint . BeatLength / 4 - rounding_error )
7265 {
73- // This is a slider with no repeats. Only create a hitobject for the ending of the slider if
74- // the slider length is at least a 1/4 beat.
66+ // This is a slider with no repeats, and that's at least a 1/4 beat (same as buzz slider protection).
67+
68+ double hitObjectTimeForSliderEnd = hasRepeats . EndTime ;
7569
76- if ( durationBetweenHitObjects > currentTimingPoint . BeatLength / 4 - rounding_error )
70+ if ( durationBetweenHitObjects > currentTimingPoint . BeatLength / 2 + rounding_error )
7771 {
78- yield return generator . GetNextHitObject ( hasRepeats . EndTime , beatmap ) ;
72+ // The slider is longer than 1/2 a beat.
73+ // Round down to the nearest 1/4.
74+
75+ double newEndTime = original . StartTime ;
76+
77+ while ( newEndTime < hitObjectTimeForSliderEnd )
78+ {
79+ newEndTime += currentTimingPoint . BeatLength / 2 ;
80+ }
81+
82+ hitObjectTimeForSliderEnd = newEndTime - currentTimingPoint . BeatLength / 2 ;
7983 }
84+ else if ( durationBetweenHitObjects > currentTimingPoint . BeatLength / 4 + rounding_error
85+ && durationBetweenHitObjects < currentTimingPoint . BeatLength / 2 - rounding_error )
86+ {
87+ // The slider length is longer than 1/4 but shorter than 1/2, so it has to be 3/8 or something.
88+ // Round down to the nearest 1/4.
89+ // Good test map: https://osu.ppy.sh/beatmapsets/929924#osu/2073258
90+
91+ double newEndTime = original . StartTime ;
92+
93+ while ( newEndTime < hitObjectTimeForSliderEnd )
94+ {
95+ newEndTime += currentTimingPoint . BeatLength / 4 ;
96+ }
97+
98+ hitObjectTimeForSliderEnd = newEndTime - currentTimingPoint . BeatLength / 4 ;
99+ }
100+
101+ yield return generator . GetNextHitObject ( hitObjectTimeForSliderEnd , beatmap ) ;
80102 }
81103 }
82104 }
0 commit comments