mirror of
https://github.com/ppy/osu.git
synced 2026-06-04 12:24:42 +08:00
8050ee36ce
https://github.com/user-attachments/assets/edfc8d06-4f04-4876-84a5-dfc83a18f160 Of note: - Supports both native beatmaps and converts - Supports key mods (changing key mods will trigger song select refilter when key count grouping is engaged) - The option to group by keys is only visible when mania ruleset is active - If the user selects key count grouping and then switches to another ruleset, song select will fall back to no grouping, but this change will not be written back to config. Only the user changing the grouping mode manually will reflect in config changes. This is done so that key grouping persists across ruleset changes, and this even survives game restarts. --- I've only done some light behaviour testing on this because this feature needs a lot of subjective shot calls and I don't want to commit too deep before I get a temperature check on the shot calls I made here. In particular some performance profiling of https://github.com/ppy/osu/commit/7de8f70b1dbbdf2e3f13ba10faf25329abf6468d may be warranted.
336 lines
14 KiB
C#
336 lines
14 KiB
C#
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using osu.Framework.Allocation;
|
|
using osu.Framework.Bindables;
|
|
using osu.Framework.Graphics;
|
|
using osu.Framework.Graphics.Containers;
|
|
using osu.Framework.Graphics.Shapes;
|
|
using osu.Framework.Graphics.UserInterface;
|
|
using osu.Framework.Localisation;
|
|
using osu.Framework.Threading;
|
|
using osu.Game.Beatmaps;
|
|
using osu.Game.Beatmaps.Drawables;
|
|
using osu.Game.Graphics;
|
|
using osu.Game.Graphics.Carousel;
|
|
using osu.Game.Graphics.Containers;
|
|
using osu.Game.Graphics.Sprites;
|
|
using osu.Game.Overlays;
|
|
using osu.Game.Resources.Localisation.Web;
|
|
using osu.Game.Rulesets;
|
|
using osu.Game.Rulesets.Mods;
|
|
using osuTK;
|
|
|
|
namespace osu.Game.Screens.Select
|
|
{
|
|
public partial class PanelBeatmapStandalone : Panel
|
|
{
|
|
public const float HEIGHT = CarouselItem.DEFAULT_HEIGHT * 1.6f;
|
|
|
|
[Resolved]
|
|
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
|
|
|
|
[Resolved]
|
|
private IBindable<IReadOnlyList<Mod>> mods { get; set; } = null!;
|
|
|
|
[Resolved]
|
|
private OverlayColourProvider colourProvider { get; set; } = null!;
|
|
|
|
[Resolved]
|
|
private ISongSelect? songSelect { get; set; }
|
|
|
|
[Resolved]
|
|
private BeatmapManager beatmaps { get; set; } = null!;
|
|
|
|
[Resolved]
|
|
private BeatmapDifficultyCache difficultyCache { get; set; } = null!;
|
|
|
|
private IBindable<StarDifficulty>? starDifficultyBindable;
|
|
private CancellationTokenSource? starDifficultyCancellationSource;
|
|
|
|
private PanelSetBackground beatmapBackground = null!;
|
|
private ScheduledDelegate? scheduledBackgroundRetrieval;
|
|
|
|
private OsuSpriteText titleText = null!;
|
|
private OsuSpriteText artistText = null!;
|
|
private PanelUpdateBeatmapButton updateButton = null!;
|
|
private BeatmapSetOnlineStatusPill statusPill = null!;
|
|
|
|
private ConstrainedIconContainer difficultyIcon = null!;
|
|
private StarRatingDisplay starRatingDisplay = null!;
|
|
private SpreadDisplay spreadDisplay = null!;
|
|
private PanelLocalRankDisplay localRank = null!;
|
|
private OsuSpriteText keyCountText = null!;
|
|
private OsuSpriteText difficultyText = null!;
|
|
private OsuSpriteText authorText = null!;
|
|
private FillFlowContainer mainFill = null!;
|
|
|
|
private Box backgroundBorder = null!;
|
|
|
|
private BeatmapInfo beatmap => ((GroupedBeatmap)Item!.Model).Beatmap;
|
|
|
|
public PanelBeatmapStandalone()
|
|
{
|
|
PanelXOffset = 20;
|
|
}
|
|
|
|
[BackgroundDependencyLoader]
|
|
private void load()
|
|
{
|
|
Height = HEIGHT;
|
|
|
|
Icon = difficultyIcon = new ConstrainedIconContainer
|
|
{
|
|
Size = new Vector2(12),
|
|
Margin = new MarginPadding { Left = 4f, Right = 3f },
|
|
Colour = colourProvider.Background5,
|
|
};
|
|
|
|
Background = backgroundBorder = new Box
|
|
{
|
|
RelativeSizeAxes = Axes.Both,
|
|
};
|
|
|
|
Content.Children = new Drawable[]
|
|
{
|
|
beatmapBackground = new PanelSetBackground(),
|
|
new FillFlowContainer
|
|
{
|
|
AutoSizeAxes = Axes.Both,
|
|
Anchor = Anchor.CentreLeft,
|
|
Origin = Anchor.CentreLeft,
|
|
Spacing = new Vector2(5),
|
|
Margin = new MarginPadding { Left = 6.5f },
|
|
Direction = FillDirection.Horizontal,
|
|
Children = new Drawable[]
|
|
{
|
|
localRank = new PanelLocalRankDisplay
|
|
{
|
|
Scale = new Vector2(0.8f),
|
|
Origin = Anchor.CentreLeft,
|
|
Anchor = Anchor.CentreLeft,
|
|
},
|
|
mainFill = new FillFlowContainer
|
|
{
|
|
Anchor = Anchor.CentreLeft,
|
|
Origin = Anchor.CentreLeft,
|
|
Direction = FillDirection.Vertical,
|
|
Padding = new MarginPadding { Bottom = 4.8f },
|
|
AutoSizeAxes = Axes.Both,
|
|
Children = new Drawable[]
|
|
{
|
|
titleText = new OsuSpriteText
|
|
{
|
|
Font = OsuFont.Style.Heading2.With(typeface: Typeface.TorusAlternate, weight: FontWeight.Bold),
|
|
},
|
|
artistText = new OsuSpriteText
|
|
{
|
|
Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold),
|
|
Padding = new MarginPadding { Top = -2 },
|
|
},
|
|
new FillFlowContainer
|
|
{
|
|
Direction = FillDirection.Horizontal,
|
|
AutoSizeAxes = Axes.Both,
|
|
Padding = new MarginPadding { Top = 2, Bottom = 2 },
|
|
Children = new Drawable[]
|
|
{
|
|
statusPill = new BeatmapSetOnlineStatusPill
|
|
{
|
|
Animated = false,
|
|
Origin = Anchor.BottomLeft,
|
|
Anchor = Anchor.BottomLeft,
|
|
TextSize = OsuFont.Style.Caption2.Size,
|
|
Margin = new MarginPadding { Right = 4f },
|
|
},
|
|
updateButton = new PanelUpdateBeatmapButton
|
|
{
|
|
Scale = new Vector2(0.8f),
|
|
Anchor = Anchor.BottomLeft,
|
|
Origin = Anchor.BottomLeft,
|
|
Margin = new MarginPadding { Right = 4f, Bottom = -1f },
|
|
},
|
|
keyCountText = new OsuSpriteText
|
|
{
|
|
Font = OsuFont.Style.Body.With(weight: FontWeight.SemiBold),
|
|
Anchor = Anchor.BottomLeft,
|
|
Origin = Anchor.BottomLeft,
|
|
Alpha = 0,
|
|
},
|
|
difficultyText = new OsuSpriteText
|
|
{
|
|
Font = OsuFont.Style.Body.With(weight: FontWeight.SemiBold),
|
|
Anchor = Anchor.BottomLeft,
|
|
Origin = Anchor.BottomLeft,
|
|
Margin = new MarginPadding { Right = 3f },
|
|
},
|
|
authorText = new OsuSpriteText
|
|
{
|
|
Colour = colourProvider.Content2,
|
|
Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold),
|
|
Anchor = Anchor.BottomLeft,
|
|
Origin = Anchor.BottomLeft
|
|
}
|
|
}
|
|
},
|
|
new FillFlowContainer
|
|
{
|
|
Direction = FillDirection.Horizontal,
|
|
Spacing = new Vector2(3),
|
|
AutoSizeAxes = Axes.Both,
|
|
Children = new Drawable[]
|
|
{
|
|
starRatingDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Small, animated: true)
|
|
{
|
|
Origin = Anchor.CentreLeft,
|
|
Anchor = Anchor.CentreLeft,
|
|
Scale = new Vector2(0.875f),
|
|
},
|
|
spreadDisplay = new SpreadDisplay
|
|
{
|
|
Origin = Anchor.CentreLeft,
|
|
Anchor = Anchor.CentreLeft,
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
protected override void LoadComplete()
|
|
{
|
|
base.LoadComplete();
|
|
|
|
ruleset.BindValueChanged(_ => updateKeyCount());
|
|
mods.BindValueChanged(_ => updateKeyCount(), true);
|
|
|
|
Selected.BindValueChanged(s =>
|
|
{
|
|
Expanded.Value = s.NewValue;
|
|
spreadDisplay.Enabled.Value = s.NewValue;
|
|
}, true);
|
|
}
|
|
|
|
protected override void PrepareForUse()
|
|
{
|
|
base.PrepareForUse();
|
|
|
|
var beatmapSet = beatmap.BeatmapSet!;
|
|
|
|
scheduledBackgroundRetrieval = Scheduler.AddDelayed(b => beatmapBackground.Beatmap = beatmaps.GetWorkingBeatmap(b), beatmap, 50);
|
|
|
|
titleText.Text = new RomanisableString(beatmapSet.Metadata.TitleUnicode, beatmapSet.Metadata.Title);
|
|
artistText.Text = new RomanisableString(beatmapSet.Metadata.ArtistUnicode, beatmapSet.Metadata.Artist);
|
|
updateButton.BeatmapSet = beatmapSet;
|
|
statusPill.Status = beatmap.Status;
|
|
|
|
difficultyIcon.Icon = beatmap.Ruleset.CreateInstance().CreateIcon();
|
|
difficultyIcon.Show();
|
|
|
|
localRank.Beatmap = beatmap;
|
|
difficultyText.Text = beatmap.DifficultyName;
|
|
authorText.Text = BeatmapsetsStrings.ShowDetailsMappedBy(beatmap.Metadata.Author.Username);
|
|
|
|
computeStarRating();
|
|
spreadDisplay.Beatmap.Value = beatmap;
|
|
updateKeyCount();
|
|
}
|
|
|
|
protected override void FreeAfterUse()
|
|
{
|
|
base.FreeAfterUse();
|
|
|
|
scheduledBackgroundRetrieval?.Cancel();
|
|
scheduledBackgroundRetrieval = null;
|
|
beatmapBackground.Beatmap = null;
|
|
updateButton.BeatmapSet = null;
|
|
localRank.Beatmap = null;
|
|
starDifficultyBindable = null;
|
|
spreadDisplay.Beatmap.Value = null;
|
|
|
|
starDifficultyCancellationSource?.Cancel();
|
|
}
|
|
|
|
private void computeStarRating()
|
|
{
|
|
starDifficultyCancellationSource?.Cancel();
|
|
starDifficultyCancellationSource = new CancellationTokenSource();
|
|
|
|
if (Item == null)
|
|
return;
|
|
|
|
starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, starDifficultyCancellationSource.Token, SongSelect.DIFFICULTY_CALCULATION_DEBOUNCE);
|
|
starDifficultyBindable.BindValueChanged(starDifficulty =>
|
|
{
|
|
starRatingDisplay.Current.Value = starDifficulty.NewValue;
|
|
spreadDisplay.StarDifficulty.Value = starDifficulty.NewValue;
|
|
}, true);
|
|
}
|
|
|
|
protected override void Update()
|
|
{
|
|
base.Update();
|
|
|
|
if (Item?.IsVisible != true)
|
|
{
|
|
starDifficultyCancellationSource?.Cancel();
|
|
starDifficultyCancellationSource = null;
|
|
}
|
|
|
|
// Dirty hack to make sure we don't take up spacing in parent fill flow when not displaying a rank.
|
|
// I can't find a better way to do this.
|
|
mainFill.Margin = new MarginPadding { Left = 1 / starRatingDisplay.Scale.X * (localRank.HasRank ? 0 : -3) };
|
|
|
|
var diffColour = starRatingDisplay.DisplayedDifficultyColour;
|
|
|
|
AccentColour = diffColour;
|
|
spreadDisplay.Current.Colour = diffColour;
|
|
|
|
backgroundBorder.Colour = diffColour;
|
|
difficultyIcon.Colour = starRatingDisplay.DisplayedDifficultyTextColour;
|
|
}
|
|
|
|
private void updateKeyCount()
|
|
{
|
|
if (Item == null)
|
|
return;
|
|
|
|
var rulesetInstance = ruleset.Value.CreateInstance();
|
|
|
|
if (rulesetInstance.AvailableVariants.Count() > 1)
|
|
{
|
|
int variant = rulesetInstance.GetVariantForBeatmap(beatmap, mods.Value);
|
|
var variantName = rulesetInstance.GetVariantName(variant);
|
|
|
|
keyCountText.Alpha = 1;
|
|
keyCountText.Text = LocalisableString.Interpolate($"[{variantName}] ");
|
|
}
|
|
else
|
|
keyCountText.Alpha = 0;
|
|
}
|
|
|
|
public override MenuItem[] ContextMenuItems
|
|
{
|
|
get
|
|
{
|
|
if (Item == null)
|
|
return Array.Empty<MenuItem>();
|
|
|
|
List<MenuItem> items = new List<MenuItem>();
|
|
|
|
if (songSelect != null)
|
|
items.AddRange(songSelect.GetForwardActions(beatmap));
|
|
|
|
return items.ToArray();
|
|
}
|
|
}
|
|
}
|
|
}
|