1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-16 15:43:04 +08:00
Files
osu-lazer/osu.Game/Screens/SelectV2/BeatmapMetadataWedge.cs
T

426 lines
19 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.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Graphics.Containers;
using osu.Game.Localisation;
using osu.Game.Online;
using osu.Game.Online.API;
using osu.Game.Online.Chat;
using osu.Game.Resources.Localisation.Web;
using osuTK;
namespace osu.Game.Screens.SelectV2
{
public partial class BeatmapMetadataWedge : VisibilityContainer
{
private MetadataDisplay creator = null!;
private MetadataDisplay source = null!;
private MetadataDisplay genre = null!;
private MetadataDisplay language = null!;
private MetadataDisplay userTags = null!;
private MetadataDisplay mapperTags = null!;
private MetadataDisplay submitted = null!;
private MetadataDisplay ranked = null!;
private Drawable ratingsWedge = null!;
private SuccessRateDisplay successRateDisplay = null!;
private UserRatingDisplay userRatingDisplay = null!;
private RatingSpreadDisplay ratingSpreadDisplay = null!;
private Drawable failRetryWedge = null!;
private FailRetryDisplay failRetryDisplay = null!;
public bool RatingsVisible => ratingsWedge.Alpha > 0;
public bool FailRetryVisible => failRetryWedge.Alpha > 0;
protected override bool StartHidden => true;
[Resolved]
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
[Resolved]
private IBindable<SongSelect.BeatmapSetLookupResult> onlineLookupResult { get; set; } = null!;
[Resolved]
private IAPIProvider api { get; set; } = null!;
[Resolved]
private RealmAccess realm { get; set; } = null!;
private IBindable<APIState> apiState = null!;
[Resolved]
private ILinkHandler? linkHandler { get; set; }
[Resolved]
private ISongSelect? songSelect { get; set; }
private Sample? wedgeAppearSample;
private Sample? wedgeHideSample;
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Top = 4f };
Width = 0.9f;
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 4f),
Shear = OsuGame.SHEAR,
Children = new[]
{
new ShearAligningWrapper(new Container
{
CornerRadius = 10,
Masking = true,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new WedgeBackground(),
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Shear = -OsuGame.SHEAR,
Padding = new MarginPadding { Left = SongSelect.WEDGE_CONTENT_MARGIN, Right = 35, Vertical = 16 },
Children = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
AutoSizeDuration = (float)transition_duration / 3,
AutoSizeEasing = Easing.OutQuint,
Children = new Drawable[]
{
new GridContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
ColumnDimensions = new[]
{
new Dimension(),
new Dimension(),
new Dimension(),
},
Content = new[]
{
new[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Children = new[]
{
creator = new MetadataDisplay(EditorSetupStrings.Creator),
genre = new MetadataDisplay(BeatmapsetsStrings.ShowInfoGenre),
},
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Children = new[]
{
source = new MetadataDisplay(BeatmapsetsStrings.ShowInfoSource),
language = new MetadataDisplay(BeatmapsetsStrings.ShowInfoLanguage),
},
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Children = new[]
{
submitted = new MetadataDisplay(SongSelectStrings.Submitted),
ranked = new MetadataDisplay(SongSelectStrings.Ranked),
},
},
},
},
},
userTags = new MetadataDisplay(BeatmapsetsStrings.ShowInfoUserTags)
{
Alpha = 0,
},
mapperTags = new MetadataDisplay(BeatmapsetsStrings.ShowInfoMapperTags),
},
},
},
},
},
}),
new ShearAligningWrapper(ratingsWedge = new Container
{
Alpha = 0f,
CornerRadius = 10,
Masking = true,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new WedgeBackground(),
new GridContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Shear = -OsuGame.SHEAR,
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
ColumnDimensions = new[]
{
new Dimension(),
new Dimension(GridSizeMode.Absolute, 10),
new Dimension(),
new Dimension(GridSizeMode.Absolute, 10),
new Dimension(),
},
Padding = new MarginPadding { Left = SongSelect.WEDGE_CONTENT_MARGIN, Right = 40f, Vertical = 16 },
Content = new[]
{
new[]
{
successRateDisplay = new SuccessRateDisplay(),
Empty(),
userRatingDisplay = new UserRatingDisplay(),
Empty(),
ratingSpreadDisplay = new RatingSpreadDisplay(),
},
},
},
}
}),
new ShearAligningWrapper(failRetryWedge = new Container
{
Alpha = 0f,
CornerRadius = 10,
Masking = true,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new WedgeBackground(),
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Shear = -OsuGame.SHEAR,
Padding = new MarginPadding { Left = SongSelect.WEDGE_CONTENT_MARGIN, Right = 40f, Vertical = 16 },
Child = failRetryDisplay = new FailRetryDisplay(),
},
},
}),
}
};
wedgeAppearSample = audio.Samples.Get(@"SongSelect/metadata-wedge-pop-in");
wedgeHideSample = audio.Samples.Get(@"SongSelect/metadata-wedge-pop-out");
}
protected override void LoadComplete()
{
base.LoadComplete();
beatmap.BindValueChanged(_ => updateDisplay());
onlineLookupResult.BindValueChanged(_ => updateDisplay());
apiState = api.State.GetBoundCopy();
apiState.BindValueChanged(_ => Scheduler.AddOnce(updateDisplay), true);
}
private const double transition_duration = 300;
protected override void PopIn()
{
this.FadeIn(transition_duration, Easing.OutQuint)
.MoveToX(0, transition_duration, Easing.OutQuint);
updateSubWedgeVisibility();
}
protected override void PopOut()
{
this.FadeOut(transition_duration, Easing.OutQuint)
.MoveToX(-100, transition_duration, Easing.OutQuint);
updateSubWedgeVisibility();
}
private void updateSubWedgeVisibility()
{
// We could consider hiding individual wedges based on zero data in the future.
// Needs some experimentation on what looks good.
var beatmapInfo = beatmap.Value.BeatmapInfo;
var currentOnlineBeatmap = onlineLookupResult.Value?.Result?.Beatmaps.SingleOrDefault(b => b.OnlineID == beatmapInfo.OnlineID);
if (State.Value == Visibility.Visible && currentOnlineBeatmap != null)
{
// play show sounds only if the wedges were previously hidden
if (ratingsWedge.Alpha < 1)
playWedgeAppearSound();
ratingsWedge.FadeIn(transition_duration, Easing.OutQuint)
.MoveToX(0, transition_duration, Easing.OutQuint);
failRetryWedge.Delay(100)
.FadeIn(transition_duration, Easing.OutQuint)
.MoveToX(0, transition_duration, Easing.OutQuint);
}
else
{
// play hide sounds only if the wedges were previously visible
if (ratingsWedge.Alpha > 0)
playWedgeHideSound();
failRetryWedge.FadeOut(transition_duration, Easing.OutQuint)
.MoveToX(-50, transition_duration, Easing.OutQuint);
ratingsWedge.Delay(100)
.FadeOut(transition_duration, Easing.OutQuint)
.MoveToX(-50, transition_duration, Easing.OutQuint);
}
}
private void playWedgeAppearSound()
{
var wedgeAppearChannel1 = wedgeAppearSample?.GetChannel();
if (wedgeAppearChannel1 == null)
return;
wedgeAppearChannel1.Balance.Value = -OsuGameBase.SFX_STEREO_STRENGTH / 2;
wedgeAppearChannel1.Frequency.Value = 0.98f + RNG.NextDouble(0.04f);
wedgeAppearChannel1.Play();
Scheduler.AddDelayed(() =>
{
var wedgeAppearChannel2 = wedgeAppearSample?.GetChannel();
if (wedgeAppearChannel2 == null)
return;
wedgeAppearChannel2.Balance.Value = -OsuGameBase.SFX_STEREO_STRENGTH / 2;
wedgeAppearChannel2.Frequency.Value = 0.90f + RNG.NextDouble(0.05f);
wedgeAppearChannel2.Play();
}, 100);
}
private void playWedgeHideSound()
{
var wedgeHideChannel = wedgeHideSample?.GetChannel();
if (wedgeHideChannel == null)
return;
wedgeHideChannel.Balance.Value = -OsuGameBase.SFX_STEREO_STRENGTH / 2;
wedgeHideChannel.Play();
}
private void updateDisplay()
{
var metadata = beatmap.Value.Metadata;
var beatmapSetInfo = beatmap.Value.BeatmapSetInfo;
creator.Data = (metadata.Author.Username, () => linkHandler?.HandleLink(new LinkDetails(LinkAction.OpenUserProfile, metadata.Author)));
if (!string.IsNullOrEmpty(metadata.Source))
source.Data = (metadata.Source, () => songSelect?.Search(metadata.Source));
else
source.Data = ("-", null);
if (!string.IsNullOrEmpty(metadata.Tags))
mapperTags.Tags = (metadata.Tags.Split(' '), t => songSelect?.Search(t));
else
mapperTags.Tags = (Array.Empty<string>(), _ => { });
submitted.Date = beatmapSetInfo.DateSubmitted;
ranked.Date = beatmapSetInfo.DateRanked;
updateOnlineDisplay();
}
private void updateOnlineDisplay()
{
if (onlineLookupResult.Value?.Status != SongSelect.BeatmapSetLookupStatus.Completed)
{
genre.Data = null;
language.Data = null;
userTags.Tags = null;
return;
}
if (onlineLookupResult.Value.Result == null)
{
genre.Data = ("-", null);
language.Data = ("-", null);
}
else
{
var beatmapInfo = beatmap.Value.BeatmapInfo;
var onlineBeatmapSet = onlineLookupResult.Value.Result;
var onlineBeatmap = onlineBeatmapSet.Beatmaps.SingleOrDefault(b => b.OnlineID == beatmapInfo.OnlineID);
genre.Data = (onlineBeatmapSet.Genre.Name, () => songSelect?.Search(onlineBeatmapSet.Genre.Name));
language.Data = (onlineBeatmapSet.Language.Name, () => songSelect?.Search(onlineBeatmapSet.Language.Name));
if (onlineBeatmap != null)
{
userRatingDisplay.Data = onlineBeatmapSet.Ratings;
ratingSpreadDisplay.Data = onlineBeatmapSet.Ratings;
successRateDisplay.Data = (onlineBeatmap.PassCount, onlineBeatmap.PlayCount);
failRetryDisplay.Data = onlineBeatmap.FailTimes ?? new APIFailTimes();
}
}
updateUserTags();
updateSubWedgeVisibility();
}
private void updateUserTags()
{
string[] tags = realm.Run(r =>
{
// need to refetch because `beatmap.Value.BeatmapInfo` is not going to have the latest tags
r.Refresh();
var refetchedBeatmap = r.Find<BeatmapInfo>(beatmap.Value.BeatmapInfo.ID);
return refetchedBeatmap?.Metadata.UserTags.ToArray() ?? [];
});
if (tags.Length == 0)
{
userTags.FadeOut(transition_duration, Easing.OutQuint);
return;
}
userTags.FadeIn(transition_duration, Easing.OutQuint);
userTags.Tags = (tags, t => songSelect?.Search($@"tag=""{t}""!"));
}
}
}