1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-17 21:13:01 +08:00

Merge pull request #33283 from peppy/last-played-split-out

Split out difficulties when sorting and grouping by "last played"
This commit is contained in:
Bartłomiej Dach
2025-05-29 14:48:48 +02:00
committed by GitHub
Unverified
2 changed files with 55 additions and 19 deletions
@@ -124,7 +124,15 @@ namespace osu.Game.Screens.SelectV2
public static bool ShouldGroupBeatmapsTogether(FilterCriteria criteria)
{
return criteria.Sort != SortMode.Difficulty && criteria.Group != GroupMode.Difficulty;
// In certain cases, we intentionally split out difficulties
// where it's more relevant or convenient to view them as individual items.
if (criteria.Sort == SortMode.Difficulty || criteria.Group == GroupMode.Difficulty)
return false;
if (criteria.Sort == SortMode.LastPlayed && criteria.Group == GroupMode.LastPlayed)
return false;
// In the majority case we group sets together for display.
return true;
}
private List<GroupMapping> getGroups(List<CarouselItem> items, FilterCriteria criteria)
@@ -152,12 +160,15 @@ namespace osu.Game.Screens.SelectV2
case GroupMode.LastPlayed:
return getGroupsBy(b =>
{
DateTimeOffset? maxLastPlayed = aggregateMax(b, items, bb => bb.LastPlayed);
var date = b.LastPlayed;
if (maxLastPlayed == null)
if (BeatmapSetsGroupedTogether)
date = aggregateMax(b, static b => (b.LastPlayed ?? DateTimeOffset.MinValue));
if (date == null || date == DateTimeOffset.MinValue)
return new GroupDefinition(int.MaxValue, "Never");
return defineGroupByDate(maxLastPlayed.Value);
return defineGroupByDate(date.Value);
}, items);
case GroupMode.RankedStatus:
@@ -166,8 +177,12 @@ namespace osu.Game.Screens.SelectV2
case GroupMode.BPM:
return getGroupsBy(b =>
{
double maxBPM = aggregateMax(b, items, bb => bb.BPM);
return defineGroupByBPM(maxBPM);
double bpm = b.BPM;
if (BeatmapSetsGroupedTogether)
bpm = aggregateMax(b, bb => bb.BPM);
return defineGroupByBPM(bpm);
}, items);
case GroupMode.Difficulty:
@@ -176,8 +191,12 @@ namespace osu.Game.Screens.SelectV2
case GroupMode.Length:
return getGroupsBy(b =>
{
double maxLength = aggregateMax(b, items, bb => bb.Length);
return defineGroupByLength(maxLength);
double length = b.Length;
if (BeatmapSetsGroupedTogether)
length = aggregateMax(b, bb => bb.Length);
return defineGroupByLength(length);
}, items);
case GroupMode.Collections:
@@ -334,10 +353,10 @@ namespace osu.Game.Screens.SelectV2
return new GroupDefinition(11, "Over 10 minutes");
}
private static T? aggregateMax<T>(BeatmapInfo b, IEnumerable<CarouselItem> items, Func<BeatmapInfo, T> func)
private static T? aggregateMax<T>(BeatmapInfo b, Func<BeatmapInfo, T> func)
{
var matchedBeatmaps = items.Select(i => i.Model).Cast<BeatmapInfo>().Where(beatmap => beatmap.BeatmapSet!.Equals(b.BeatmapSet));
return matchedBeatmaps.Max(func);
var beatmaps = b.BeatmapSet!.Beatmaps.Where(bb => !bb.Hidden);
return beatmaps.Max(func);
}
private record GroupMapping(GroupDefinition? Group, List<CarouselItem> ItemsInGroup);
@@ -27,19 +27,27 @@ namespace osu.Game.Screens.SelectV2
{
var criteria = getCriteria();
bool groupedSets = BeatmapCarouselFilterGrouping.ShouldGroupBeatmapsTogether(criteria);
return items.Order(Comparer<CarouselItem>.Create((a, b) =>
{
var ab = (BeatmapInfo)a.Model;
var bb = (BeatmapInfo)b.Model;
if (ab.BeatmapSet!.Equals(bb.BeatmapSet))
return compareDifficulty(ab, bb);
if (groupedSets)
{
if (ab.BeatmapSet!.Equals(bb.BeatmapSet))
return compareDifficulty(ab, bb, criteria.Sort);
return compare(ab, bb, criteria.Sort);
// If we're grouping by sets, all fallback sorts need to be aggregates for the set.
return compare(ab, bb, criteria.Sort, aggregate: true);
}
return compare(ab, bb, criteria.Sort, aggregate: false);
})).ToList();
}, cancellationToken).ConfigureAwait(false);
private static int compare(BeatmapInfo a, BeatmapInfo b, SortMode sort)
private static int compare(BeatmapInfo a, BeatmapInfo b, SortMode sort, bool aggregate)
{
int comparison;
@@ -80,15 +88,24 @@ namespace osu.Game.Screens.SelectV2
break;
case SortMode.LastPlayed:
comparison = -compareUsingAggregateMax(a, b, static b => (b.LastPlayed ?? DateTimeOffset.MinValue).ToUnixTimeSeconds());
if (aggregate)
comparison = compareUsingAggregateMax(b, a, static b => (b.LastPlayed ?? DateTimeOffset.MinValue).ToUnixTimeSeconds());
else
comparison = Nullable.Compare(b.LastPlayed, a.LastPlayed);
break;
case SortMode.BPM:
comparison = compareUsingAggregateMax(a, b, static b => b.BPM);
if (aggregate)
comparison = compareUsingAggregateMax(a, b, static b => b.BPM);
else
comparison = a.BPM.CompareTo(b.BPM);
break;
case SortMode.Length:
comparison = compareUsingAggregateMax(a, b, static b => b.Length);
if (aggregate)
comparison = compareUsingAggregateMax(a, b, static b => b.Length);
else
comparison = a.Length.CompareTo(b.Length);
break;
default:
@@ -108,7 +125,7 @@ namespace osu.Game.Screens.SelectV2
return comparison;
}
private static int compareDifficulty(BeatmapInfo a, BeatmapInfo b)
private static int compareDifficulty(BeatmapInfo a, BeatmapInfo b, SortMode sort)
{
int comparison = a.Ruleset.CompareTo(b.Ruleset);