diff --git a/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiBeatmapConverter.cs b/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiBeatmapConverter.cs index fd4512b1c..40e6e7a1e 100644 --- a/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiBeatmapConverter.cs +++ b/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiBeatmapConverter.cs @@ -155,36 +155,20 @@ private IEnumerable convertSlider(HitObject original) twin = true; } + // See if we can convert to a slide object if (special && slider.Duration >= 350) { - List slides = new List(); - if (EnabledExperiments.HasFlag(ConversionExperiments.twinSlides)) + var result = tryConvertSliderToSlide(original, nodeSamples, twin, breakNote).ToList(); + if (result.Any()) { - if (twin) - slides.Add((Slide)createSlideNote(original, nodeSamples, true, breakNote)); - else - foreach (var note in createTapsFromTicks(original, nodeSamples)) - yield return note; - } - - slides.Add((Slide)createSlideNote(original, nodeSamples, false, breakNote)); - - // Make sure duplicates are cleared - if (slides.Count == 2 && slides.First().Lane == slides.Last().Lane) - { - // Make sure that both slides patterns are unique - if (!slides.First().SlideInfoList.Exists(x => x.ID == slides.Last().SlideInfoList.First().ID)) - slides.First().SlideInfoList.AddRange(slides.Last().SlideInfoList); + foreach (var ho in result) + yield return ho; - slides.Remove(slides.Last()); + yield break; } - - foreach (var slide in slides) - yield return slide; - - yield break; } + // Fallback to hold notes if (EnabledExperiments.HasFlag(ConversionExperiments.twinNotes)) if (twin) yield return createHoldNote(original, nodeSamples, true, breakNote); @@ -195,6 +179,51 @@ private IEnumerable convertSlider(HitObject original) yield return createHoldNote(original, nodeSamples, false, breakNote); } + private IEnumerable tryConvertSliderToSlide(HitObject original, IList> nodeSamples, bool twin = false, bool isBreak = false) + { + List slides = new List(); + List taps = new List(); + if (EnabledExperiments.HasFlag(ConversionExperiments.twinSlides)) + { + if (twin) + slides.Add((Slide)createSlideNote(original, nodeSamples, true, isBreak)); + else + taps.AddRange(createTapsFromTicks(original, nodeSamples)); + } + + slides.Add((Slide)createSlideNote(original, nodeSamples, false, isBreak)); + + // If there is a SlideFan, we always prioritize that, and ignore the rest + foreach (var slide in slides) + if (slide.SlideInfoList[0].ID == SlidePaths.FANID) + { + yield return slide; + yield break; + } + + // If both slides have the same start lane, we attempt to merge them + if (slides.Count == 2 && slides[0].Lane == slides[1].Lane) + { + bool isSamePattern = slides[0].SlideInfoList[0].ID == slides[1].SlideInfoList[0].ID; + bool isSameOrientation = slides[0].SlideInfoList[0].Mirrored == slides[1].SlideInfoList[0].Mirrored; + + // We merge both slides only if they both have the same pattern AND orientation + if (!isSamePattern || !isSameOrientation) + slides[0].SlideInfoList.AddRange(slides[1].SlideInfoList); + + slides.RemoveAt(1); + } + + if (slides.Count > 0) + { + foreach (var slide in slides) + yield return slide; + + foreach (var tap in taps) + yield return tap; + } + } + #endregion #region SentakkiHitObject creation methods @@ -260,7 +289,7 @@ private SentakkiHitObject createSlideNote(HitObject original, IList pathEnumerable = SlidePaths.VALIDPATHS.Where(p => ((IHasDuration)original).Duration >= p.Item1.MinDuration && ((IHasDuration)original).Duration <= p.Item1.MaxDuration); if (!EnabledExperiments.HasFlag(ConversionExperiments.fanSlides)) - pathEnumerable = pathEnumerable.Where(s => s != SlidePaths.VALIDPATHS.Last()); + pathEnumerable = pathEnumerable.Where(s => s != SlidePaths.VALIDPATHS[^1]); validPaths = pathEnumerable.ToList(); } @@ -285,7 +314,7 @@ private SentakkiHitObject createSlideNote(HitObject original, IList createTapsFromTicks(HitObject original, IList> nodeSamples) + private IEnumerable createTapsFromTicks(HitObject original, IList> nodeSamples) { if (original is not IHasPathWithRepeats) yield break;