diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index fa8974f55a..2c45b3642d 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -23,6 +23,8 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; using osu.Game.Input.Bindings; using osu.Game.Screens.Select.Carousel; +using osu.Game.Online.API; +using osu.Game.Users; namespace osu.Game.Screens.Select { @@ -31,6 +33,8 @@ namespace osu.Game.Screens.Select private const float bleed_top = FilterControl.HEIGHT; private const float bleed_bottom = Footer.HEIGHT; + private readonly Bindable localUser = new Bindable(); + /// /// Triggered when the loaded change and are completely loaded. /// @@ -140,7 +144,7 @@ namespace osu.Game.Screens.Select private BeatmapManager beatmaps { get; set; } [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuConfigManager config) + private void load(OsuConfigManager config, IAPIProvider api) { config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm); config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled); @@ -154,6 +158,8 @@ namespace osu.Game.Screens.Select beatmaps.BeatmapRestored += beatmapRestored; loadBeatmapSets(GetLoadableBeatmaps()); + + localUser.BindTo(api.LocalUser); } protected virtual IEnumerable GetLoadableBeatmaps() => beatmaps.GetAllUsableBeatmapSetsEnumerable(); @@ -588,7 +594,7 @@ namespace osu.Game.Screens.Select b.Metadata = beatmapSet.Metadata; } - var set = new CarouselBeatmapSet(beatmapSet); + var set = new CarouselBeatmapSet(beatmapSet, localUser); foreach (var c in set.Beatmaps) { diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 8e323c66e2..9f1c39c578 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -4,19 +4,23 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Beatmaps; using osu.Game.Screens.Select.Filter; +using osu.Game.Users; namespace osu.Game.Screens.Select.Carousel { public class CarouselBeatmapSet : CarouselGroupEagerSelect { + private readonly Bindable localUser; + public IEnumerable Beatmaps => InternalChildren.OfType(); public BeatmapSetInfo BeatmapSet; - public CarouselBeatmapSet(BeatmapSetInfo beatmapSet) + public CarouselBeatmapSet(BeatmapSetInfo beatmapSet, Bindable localUser) { BeatmapSet = beatmapSet ?? throw new ArgumentNullException(nameof(beatmapSet)); @@ -24,10 +28,32 @@ namespace osu.Game.Screens.Select.Carousel .Where(b => !b.Hidden) .Select(b => new CarouselBeatmap(b)) .ForEach(AddChild); + + this.localUser = localUser; } protected override DrawableCarouselItem CreateDrawableRepresentation() => new DrawableCarouselBeatmapSet(this); + protected override CarouselItem GetNextToSelect() + { + if (LastSelected == null) + { + decimal? pp = localUser.Value?.Statistics?.PP ?? 60; // TODO: This needs to get ruleset specific statistics + + var recommendedDifficulty = Math.Pow((double)pp, 0.4) * 0.195; + return Children.OfType() + .Where(b => !b.Filtered.Value) + .OrderBy(b => + { + var difference = b.Beatmap.StarDifficulty - recommendedDifficulty; + return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder + }) + .FirstOrDefault(); + } + + return base.GetNextToSelect(); + } + public override int CompareTo(FilterCriteria criteria, CarouselItem other) { if (!(other is CarouselBeatmapSet otherSet)) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 6ce12f7b89..262bea9c71 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -90,11 +90,15 @@ namespace osu.Game.Screens.Select.Carousel PerformSelection(); } + protected virtual CarouselItem GetNextToSelect() + { + return Children.Skip(lastSelectedIndex).FirstOrDefault(i => !i.Filtered.Value) ?? + Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex).FirstOrDefault(i => !i.Filtered.Value); + } + protected virtual void PerformSelection() { - CarouselItem nextToSelect = - Children.Skip(lastSelectedIndex).FirstOrDefault(i => !i.Filtered.Value) ?? - Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex).FirstOrDefault(i => !i.Filtered.Value); + CarouselItem nextToSelect = GetNextToSelect(); if (nextToSelect != null) nextToSelect.State.Value = CarouselItemState.Selected;