diff --git a/osu.Desktop.Tests/Visual/TestCaseOnlineBeatmapSetOverlay.cs b/osu.Desktop.Tests/Visual/TestCaseOnlineBeatmapSetOverlay.cs index 2bbe6ea952..2d26c6c518 100644 --- a/osu.Desktop.Tests/Visual/TestCaseOnlineBeatmapSetOverlay.cs +++ b/osu.Desktop.Tests/Visual/TestCaseOnlineBeatmapSetOverlay.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -45,6 +46,8 @@ namespace osu.Desktop.VisualTests.Tests FavouriteCount = 356, Submitted = new DateTime(2016, 2, 10), Ranked = new DateTime(2016, 6, 19), + Length = 118000, + BPM = 236, Author = new User { Username = @"Fresh Chicken", @@ -70,6 +73,20 @@ namespace osu.Desktop.VisualTests.Tests OverallDifficulty = 6.5f, ApproachRate = 5, }, + OnlineInfo = new BeatmapOnlineInfo + { + HasVideo = false, + CircleCount = 265, + SliderCount = 71, + PlayCount = 47906, + PassCount = 19899, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 10), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, }, new BeatmapInfo { @@ -84,6 +101,20 @@ namespace osu.Desktop.VisualTests.Tests OverallDifficulty = 7, ApproachRate = 5, }, + OnlineInfo = new BeatmapOnlineInfo + { + HasVideo = false, + CircleCount = 592, + SliderCount = 62, + PlayCount = 162021, + PassCount = 72116, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 10), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, }, new BeatmapInfo { @@ -98,6 +129,20 @@ namespace osu.Desktop.VisualTests.Tests OverallDifficulty = 7.5f, ApproachRate = 5, }, + OnlineInfo = new BeatmapOnlineInfo + { + HasVideo = false, + CircleCount = 1042, + SliderCount = 79, + PlayCount = 225178, + PassCount = 73001, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 10), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, }, new BeatmapInfo { @@ -112,6 +157,20 @@ namespace osu.Desktop.VisualTests.Tests OverallDifficulty = 8, ApproachRate = 5, }, + OnlineInfo = new BeatmapOnlineInfo + { + HasVideo = false, + CircleCount = 1352, + SliderCount = 69, + PlayCount = 131545, + PassCount = 42703, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 10), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, }, new BeatmapInfo { @@ -126,6 +185,20 @@ namespace osu.Desktop.VisualTests.Tests OverallDifficulty = 8.5f, ApproachRate = 5, }, + OnlineInfo = new BeatmapOnlineInfo + { + HasVideo = false, + CircleCount = 1730, + SliderCount = 115, + PlayCount = 117673, + PassCount = 24241, + }, + Metrics = new BeatmapMetrics + { + Ratings = Enumerable.Range(0, 10), + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6), + }, }, }, }); diff --git a/osu.Game/Beatmaps/BeatmapOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs index e8f40a7e07..c9794107ce 100644 --- a/osu.Game/Beatmaps/BeatmapOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs @@ -10,6 +10,23 @@ namespace osu.Game.Beatmaps /// public class BeatmapOnlineInfo { + /// + /// Whether or not this beatmap has a background video. + /// + public bool HasVideo { get; set; } + + /// + /// The amount of circles in this beatmap. + /// + [JsonProperty(@"count_circles")] + public int CircleCount { get; set; } + + /// + /// The amount of sliders in this beatmap. + /// + [JsonProperty(@"count_sliders")] + public int SliderCount { get; set; } + /// /// The amount of plays this beatmap has. /// diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index 40755b468f..8009ea5b9e 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -44,6 +44,16 @@ namespace osu.Game.Beatmaps [JsonProperty(@"previewUrl")] public string Preview { get; set; } + /// + /// The length in milliseconds of this beatmap's song. + /// + public double Length { get; set; } + + /// + /// The beats per minute of this beatmap set's song. + /// + public double BPM { get; set; } + /// /// The amount of plays this beatmap set has. /// diff --git a/osu.Game/Overlays/OnlineBeatmapSet/BasicStats.cs b/osu.Game/Overlays/OnlineBeatmapSet/BasicStats.cs new file mode 100644 index 0000000000..fa45a04b40 --- /dev/null +++ b/osu.Game/Overlays/OnlineBeatmapSet/BasicStats.cs @@ -0,0 +1,124 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Overlays.OnlineBeatmapSet +{ + public class BasicStats : Container + { + private readonly Statistic length, bpm, circleCount, sliderCount; + + private BeatmapInfo beatmap; + public BeatmapInfo Beatmap + { + get { return beatmap; } + set + { + if (value == beatmap) return; + beatmap = value; + circleCount.Value = string.Format(@"{0:n0}", beatmap.OnlineInfo.CircleCount); + sliderCount.Value = string.Format(@"{0:n0}", beatmap.OnlineInfo.SliderCount); + } + } + + public BasicStats(BeatmapSetInfo set) + { + var statWidth = 0.25f; + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new[] + { + length = new Statistic(FontAwesome.fa_clock_o, "Length") + { + Width = statWidth, + Value = TimeSpan.FromMilliseconds(set.OnlineInfo.Length).ToString(@"m\:ss"), + }, + bpm = new Statistic(FontAwesome.fa_circle, "BPM") + { + Width = statWidth, + Value = set.OnlineInfo.BPM.ToString(@"0.##"), + }, + circleCount = new Statistic(FontAwesome.fa_circle_o, "Circle Count") { Width = statWidth }, + sliderCount = new Statistic(FontAwesome.fa_circle, "Slider Count") { Width = statWidth }, + }, + }; + } + + private class Statistic : Container, IHasTooltip + { + private readonly string name; + private readonly OsuSpriteText value; + + public string TooltipText => name; + public string Value + { + get { return value.Text; } + set { this.value.Text = value; } + } + + public Statistic(FontAwesome icon, string name) + { + this.name = name; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + Icon = FontAwesome.fa_square, + Size = new Vector2(13), + Rotation = 45, + Colour = OsuColour.FromHex(@"441288"), + }, + new SpriteIcon + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + Icon = icon, + Size = new Vector2(13), + Colour = OsuColour.FromHex(@"f7dd55"), + Scale = new Vector2(0.8f), + }, + value = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + TextSize = 13, + Font = @"Exo2.0-Bold", + Margin = new MarginPadding { Left = 10 }, + }, + }, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + value.Colour = colour.Yellow; + } + } + } +} diff --git a/osu.Game/Overlays/OnlineBeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/OnlineBeatmapSet/BeatmapPicker.cs index 2a0e137a4e..3a1fe9bd07 100644 --- a/osu.Game/Overlays/OnlineBeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/OnlineBeatmapSet/BeatmapPicker.cs @@ -107,8 +107,6 @@ namespace osu.Game.Overlays.OnlineBeatmapSet starRating.FadeIn(100); }, }); - - Beatmap.TriggerChange(); } [BackgroundDependencyLoader] @@ -117,6 +115,14 @@ namespace osu.Game.Overlays.OnlineBeatmapSet starRating.Colour = colours.Yellow; } + protected override void LoadComplete() + { + base.LoadComplete(); + + // done here so everything can bind in intialization and get the first trigger + Beatmap.TriggerChange(); + } + private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap.Version; private class TilesFillFlowContainer : FillFlowContainer diff --git a/osu.Game/Overlays/OnlineBeatmapSet/Details.cs b/osu.Game/Overlays/OnlineBeatmapSet/Details.cs index 69a489d05a..300eb3a8e1 100644 --- a/osu.Game/Overlays/OnlineBeatmapSet/Details.cs +++ b/osu.Game/Overlays/OnlineBeatmapSet/Details.cs @@ -7,12 +7,32 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Screens.Select.Details; namespace osu.Game.Overlays.OnlineBeatmapSet { public class Details : FillFlowContainer { - public Details() + private readonly BasicStats basic; + private readonly AdvancedStats advanced; + private readonly UserRatings ratings; + + private BeatmapInfo beatmap; + public BeatmapInfo Beatmap + { + get { return beatmap; } + set + { + if (value == beatmap) return; + beatmap = value; + + basic.Beatmap = advanced.Beatmap = Beatmap; + ratings.Metrics = Beatmap.Metrics; + } + } + + public Details(BeatmapSetInfo set) { Width = OnlineBeatmapSetOverlay.RIGHT_WIDTH; AutoSizeAxes = Axes.Y; @@ -20,36 +40,40 @@ namespace osu.Game.Overlays.OnlineBeatmapSet Children = new Drawable[] { + new AsyncLoadWrapper(new PreviewButton(set) + { + RelativeSizeAxes = Axes.X, + }) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, new DetailBox { - Child = new Container + Child = basic = new BasicStats(set) { RelativeSizeAxes = Axes.X, - Height = 42, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Vertical = 10 }, + Padding = new MarginPadding { Horizontal = 15 }, }, }, new DetailBox { - Child = new Container + Child = advanced = new AdvancedStats { RelativeSizeAxes = Axes.X, - Height = 35, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Vertical = 7.5f }, }, }, new DetailBox { - Child = new Container + Child = ratings = new UserRatings { RelativeSizeAxes = Axes.X, - Height = 110, - }, - }, - new DetailBox - { - Child = new Container - { - RelativeSizeAxes = Axes.X, - Height = 115, + Height = 95, + Margin = new MarginPadding { Top = 10 }, }, }, }; diff --git a/osu.Game/Overlays/OnlineBeatmapSet/Header.cs b/osu.Game/Overlays/OnlineBeatmapSet/Header.cs index cea497af8c..8200a9a373 100644 --- a/osu.Game/Overlays/OnlineBeatmapSet/Header.cs +++ b/osu.Game/Overlays/OnlineBeatmapSet/Header.cs @@ -22,6 +22,8 @@ namespace osu.Game.Overlays.OnlineBeatmapSet private readonly Box tabsBg; + public readonly BeatmapPicker Picker; + public Header(BeatmapSetInfo set) { RelativeSizeAxes = Axes.X; @@ -35,6 +37,8 @@ namespace osu.Game.Overlays.OnlineBeatmapSet Offset = new Vector2(0f, 1f), }; + FillFlowContainer buttons; + Details details; Children = new Drawable[] { new Container @@ -101,7 +105,7 @@ namespace osu.Game.Overlays.OnlineBeatmapSet { RelativeSizeAxes = Axes.X, Height = 113, - Child = new BeatmapPicker(set), + Child = Picker = new BeatmapPicker(set), }, new OsuSpriteText { @@ -122,7 +126,7 @@ namespace osu.Game.Overlays.OnlineBeatmapSet Margin = new MarginPadding { Top = 20 }, Child = new AuthorInfo(set.OnlineInfo), }, - new FillFlowContainer + buttons = new FillFlowContainer { RelativeSizeAxes = Axes.X, Height = 45, @@ -131,14 +135,12 @@ namespace osu.Game.Overlays.OnlineBeatmapSet Children = new HeaderButton[] { new FavouriteButton(), - new DownloadButton("Download", ""), - new DownloadButton("osu!direct", ""), }, }, }, }, }, - new Details + details = new Details(set) { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, @@ -147,6 +149,25 @@ namespace osu.Game.Overlays.OnlineBeatmapSet }, }, }; + + Picker.Beatmap.ValueChanged += b => + { + details.Beatmap = b; + + buttons.Child = new FavouriteButton(); + if (b.OnlineInfo.HasVideo) + { + buttons.AddRange(new[] + { + new DownloadButton("Download", "with Video"), + new DownloadButton("Download", "without Video"), + }); + } + else + { + buttons.Add(new DownloadButton("Download", @"")); + } + }; } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/OnlineBeatmapSet/Info.cs b/osu.Game/Overlays/OnlineBeatmapSet/Info.cs index b4c505b136..e311b6e886 100644 --- a/osu.Game/Overlays/OnlineBeatmapSet/Info.cs +++ b/osu.Game/Overlays/OnlineBeatmapSet/Info.cs @@ -22,9 +22,16 @@ namespace osu.Game.Overlays.OnlineBeatmapSet private readonly BeatmapSetInfo set; private readonly Box successRateBackground; + private readonly SuccessRate successRate; private readonly FillFlowContainer metadataFlow; private readonly ScrollContainer descriptionScroll; + public BeatmapInfo Beatmap + { + get { return successRate.Beatmap; } + set { successRate.Beatmap = value; } + } + public Info(BeatmapSetInfo set) { this.set = set; @@ -85,12 +92,17 @@ namespace osu.Game.Overlays.OnlineBeatmapSet Origin = Anchor.TopRight, RelativeSizeAxes = Axes.Y, Width = OnlineBeatmapSetOverlay.RIGHT_WIDTH, - Children = new[] + Children = new Drawable[] { successRateBackground = new Box { RelativeSizeAxes = Axes.Both, }, + successRate = new SuccessRate + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 20, Horizontal = 15 }, + }, }, }, }, diff --git a/osu.Game/Overlays/OnlineBeatmapSet/PreviewButton.cs b/osu.Game/Overlays/OnlineBeatmapSet/PreviewButton.cs new file mode 100644 index 0000000000..a86623048a --- /dev/null +++ b/osu.Game/Overlays/OnlineBeatmapSet/PreviewButton.cs @@ -0,0 +1,137 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Track; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Overlays.OnlineBeatmapSet +{ + public class PreviewButton : OsuClickableContainer + { + private readonly BeatmapSetInfo set; + private readonly Box bg, progress; + private readonly SpriteIcon icon; + + private AudioManager audio; + private Track preview; + + private bool playing = false; + public bool Playing + { + get { return playing; } + set + { + if (value == playing) return; + playing = value; + + if (Playing) + { + icon.Icon = FontAwesome.fa_stop; + progress.FadeIn(100); + + loadPreview(); + preview.Start(); + } + else + { + icon.Icon = FontAwesome.fa_play; + progress.FadeOut(100); + preview.Stop(); + } + } + } + + public PreviewButton(BeatmapSetInfo set) + { + this.set = set; + Height = 42; + + Children = new Drawable[] + { + bg = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.25f), + }, + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = 3, + Child = progress = new Box + { + RelativeSizeAxes = Axes.Both, + Width = 0f, + Alpha = 0f, + }, + }, + icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.fa_play, + Size = new Vector2(18), + Shadow = false, + }, + }; + + Action = () => Playing = !Playing; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, AudioManager audio) + { + this.audio = audio; + progress.Colour = colours.Yellow; + + loadPreview(); + } + + protected override void Update() + { + base.Update(); + + if (Playing) + { + progress.Width = (float)(preview.CurrentTime / preview.Length); + if (preview.HasCompleted) Playing = false; + } + } + + protected override bool OnHover(InputState state) + { + bg.FadeColour(Color4.Black.Opacity(0.5f), 100); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + bg.FadeColour(Color4.Black.Opacity(0.25f), 100); + } + + private void loadPreview() + { + if (preview?.HasCompleted ?? true) + { + preview = audio.Track.Get(set.OnlineInfo.Preview); + preview.Volume.Value = 0.5; + } + else + { + preview.Seek(0); + } + } + } +} diff --git a/osu.Game/Overlays/OnlineBeatmapSet/SuccessRate.cs b/osu.Game/Overlays/OnlineBeatmapSet/SuccessRate.cs new file mode 100644 index 0000000000..88aef753ff --- /dev/null +++ b/osu.Game/Overlays/OnlineBeatmapSet/SuccessRate.cs @@ -0,0 +1,117 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Beatmaps; +using osu.Game.Screens.Select.Details; +using System; + +namespace osu.Game.Overlays.OnlineBeatmapSet +{ + public class SuccessRate : Container + { + private readonly FillFlowContainer header; + private readonly OsuSpriteText successRateLabel, successPercent, graphLabel; + private readonly Bar successRate; + private readonly Container percentContainer; + private readonly FailRetryGraph graph; + + private BeatmapInfo beatmap; + public BeatmapInfo Beatmap + { + get { return beatmap; } + set + { + if (value == beatmap) return; + beatmap = value; + + var rate = (float)beatmap.OnlineInfo.PassCount / beatmap.OnlineInfo.PlayCount; + successPercent.Text = $"{Math.Round(rate * 100)}%"; + successRate.Length = rate; + + graph.Metrics = Beatmap.Metrics; + } + } + + public SuccessRate() + { + Children = new Drawable[] + { + header = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + successRateLabel = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Success Rate", + TextSize = 13, + }, + successRate = new Bar + { + RelativeSizeAxes = Axes.X, + Height = 5, + Margin = new MarginPadding { Top = 5 }, + }, + percentContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = successPercent = new OsuSpriteText + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopCentre, + TextSize = 13, + }, + }, + graphLabel = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Points of Failure", + TextSize = 13, + Margin = new MarginPadding { Vertical = 20 }, + }, + }, + }, + graph = new FailRetryGraph + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5; + successRate.AccentColour = colours.Green; + successRate.BackgroundColour = colours.GrayD; + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + graph.Padding = new MarginPadding { Top = header.DrawHeight }; + } + + protected override void Update() + { + base.Update(); + + percentContainer.Width = successRate.Length; + } + } +} diff --git a/osu.Game/Overlays/OnlineBeatmapSetOverlay.cs b/osu.Game/Overlays/OnlineBeatmapSetOverlay.cs index 4fae3cac35..6e3a2019d0 100644 --- a/osu.Game/Overlays/OnlineBeatmapSetOverlay.cs +++ b/osu.Game/Overlays/OnlineBeatmapSetOverlay.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Linq; using OpenTK; using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; @@ -68,12 +69,16 @@ namespace osu.Game.Overlays public void ShowBeatmapSet(BeatmapSetInfo set) { + Header header; + Info info; scrollContent.Children = new Drawable[] { - new Header(set), - new Info(set), + header = new Header(set), + info = new Info(set), }; + header.Picker.Beatmap.ValueChanged += b => info.Beatmap = b; + Show(); } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7db65a0b4c..42dd1ad032 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -545,6 +545,15 @@ + + + + + + + + + @@ -570,4 +579,4 @@ --> - + \ No newline at end of file