diff --git a/osu.Game.Rulesets.PumpTrainer/Beatmaps/PumpTrainerBeatmapConverter.cs b/osu.Game.Rulesets.PumpTrainer/Beatmaps/PumpTrainerBeatmapConverter.cs index cae3581..5785905 100644 --- a/osu.Game.Rulesets.PumpTrainer/Beatmaps/PumpTrainerBeatmapConverter.cs +++ b/osu.Game.Rulesets.PumpTrainer/Beatmaps/PumpTrainerBeatmapConverter.cs @@ -16,13 +16,6 @@ public class PumpTrainerBeatmapConverter : BeatmapConverter generator.Settings; private NextHitObjectGenerator generator = new(); - /// - /// For osu! sliders with no repeats, this represents whether to convert the end of the slider to a hitobject. - /// Even if this is true, the slider must be at least length 1/4 of a beat for its end to be converted. - /// (For repeating sliders, each slider end is always converted to hitobjects whether this is true or false.) - /// - public bool CountNormalSliderEnds = true; - public PumpTrainerBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) : base(beatmap, ruleset) { @@ -41,14 +34,14 @@ protected override IEnumerable ConvertHitObject(HitObject if (original is IHasRepeats hasRepeats) { - // This is a slider. (Even a sliders with no repeats is IHasRepeats) + // This is a slider. (Even a slider with no repeats is IHasRepeats) int hitObjectsToReturnAfterFirst = hasRepeats.RepeatCount + 1; // +1 for the last hit object double durationBetweenHitObjects = (hasRepeats.EndTime - original.StartTime) / hitObjectsToReturnAfterFirst; TimingControlPoint currentTimingPoint = beatmap.ControlPointInfo.TimingPointAt(original.StartTime); - const double rounding_error = 5; + const double rounding_error = 5; // Use this rounding error "generously" for '<=' and '>=', and "not generously" for '<' and '>' if (hitObjectsToReturnAfterFirst > 1) { @@ -68,15 +61,44 @@ protected override IEnumerable ConvertHitObject(HitObject yield return generator.GetNextHitObject(newHitObjectTime, beatmap); } } - else if (CountNormalSliderEnds) + else if (durationBetweenHitObjects >= currentTimingPoint.BeatLength / 4 - rounding_error) { - // This is a slider with no repeats. Only create a hitobject for the ending of the slider if - // the slider length is at least a 1/4 beat. + // This is a slider with no repeats, and that's at least a 1/4 beat (same as buzz slider protection). + + double hitObjectTimeForSliderEnd = hasRepeats.EndTime; - if (durationBetweenHitObjects > currentTimingPoint.BeatLength / 4 - rounding_error) + if (durationBetweenHitObjects > currentTimingPoint.BeatLength / 2 + rounding_error) { - yield return generator.GetNextHitObject(hasRepeats.EndTime, beatmap); + // The slider is longer than 1/2 a beat. + // Round down to the nearest 1/4. + + double newEndTime = original.StartTime; + + while (newEndTime < hitObjectTimeForSliderEnd) + { + newEndTime += currentTimingPoint.BeatLength / 2; + } + + hitObjectTimeForSliderEnd = newEndTime - currentTimingPoint.BeatLength / 2; } + else if (durationBetweenHitObjects > currentTimingPoint.BeatLength / 4 + rounding_error + && durationBetweenHitObjects < currentTimingPoint.BeatLength / 2 - rounding_error) + { + // The slider length is longer than 1/4 but shorter than 1/2, so it has to be 3/8 or something. + // Round down to the nearest 1/4. + // Good test map: https://osu.ppy.sh/beatmapsets/929924#osu/2073258 + + double newEndTime = original.StartTime; + + while (newEndTime < hitObjectTimeForSliderEnd) + { + newEndTime += currentTimingPoint.BeatLength / 4; + } + + hitObjectTimeForSliderEnd = newEndTime - currentTimingPoint.BeatLength / 4; + } + + yield return generator.GetNextHitObject(hitObjectTimeForSliderEnd, beatmap); } } } diff --git a/osu.Game.Rulesets.PumpTrainer/Mods/PumpTrainerModIgnoreOsuSliderEnds.cs b/osu.Game.Rulesets.PumpTrainer/Mods/PumpTrainerModIgnoreOsuSliderEnds.cs deleted file mode 100644 index 83bd042..0000000 --- a/osu.Game.Rulesets.PumpTrainer/Mods/PumpTrainerModIgnoreOsuSliderEnds.cs +++ /dev/null @@ -1,24 +0,0 @@ -using osu.Framework.Localisation; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.PumpTrainer.Beatmaps; - -namespace osu.Game.Rulesets.PumpTrainer.Mods -{ - public class PumpTrainerModIgnoreOsuSliderEnds : Mod, IApplicableToBeatmapConverter - { - public override string Name => "Ignore osu! Slider Ends"; - public override string Acronym => "SE"; - public override LocalisableString Description => - "Recommended for certain osu! \"tech\"-style maps."; - public override double ScoreMultiplier => 1; - public override ModType Type => ModType.DifficultyReduction; - - public void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter) - { - var pumpBeatmapConverter = (PumpTrainerBeatmapConverter)beatmapConverter; - - pumpBeatmapConverter.CountNormalSliderEnds = false; - } - } -} diff --git a/osu.Game.Rulesets.PumpTrainer/PumpTrainerRuleset.cs b/osu.Game.Rulesets.PumpTrainer/PumpTrainerRuleset.cs index 9a16eb2..e117718 100644 --- a/osu.Game.Rulesets.PumpTrainer/PumpTrainerRuleset.cs +++ b/osu.Game.Rulesets.PumpTrainer/PumpTrainerRuleset.cs @@ -38,7 +38,6 @@ public override IEnumerable GetModsFor(ModType type) { new PumpTrainerModBanSinglesTwists(), new PumpTrainerModBanFarColumns(), - new PumpTrainerModIgnoreOsuSliderEnds(), new PumpTrainerModHalfTime(), };