diff --git a/osu.Game.Tests/Visual/RankedPlay/TestSceneCompactDivisionBadge.cs b/osu.Game.Tests/Visual/RankedPlay/TestSceneCompactDivisionBadge.cs new file mode 100644 index 000000000000..958439fb3d76 --- /dev/null +++ b/osu.Game.Tests/Visual/RankedPlay/TestSceneCompactDivisionBadge.cs @@ -0,0 +1,80 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.RankedPlay; +using osu.Game.Scoring; +using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Components; +using osuTK; + +namespace osu.Game.Tests.Visual.RankedPlay +{ + public partial class TestSceneCompactDivisionBadge : OsuTestScene + { + public TestSceneCompactDivisionBadge() + { + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(), + }, + Content = new[] + { + new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + Width = 150, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Full, + Spacing = new Vector2(10), + Children = DIVISIONS.Select(d => new CompactDivisionBadge(d)).ToArray(), + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10), + ChildrenEnumerable = Enumerable.Range(1, 100).Where(i => i == 1 || i == 100 || i % 5 == 0).Select(i => new CompactDivisionBadge( + new APIRankedPlayDivision { Tier = RankingTier.Lustrous, DisplayName = "Lustrous", StartRating = 2400, EndRating = 3000 }, + i + )), + } + } + }, + }; + } + + public static readonly APIRankedPlayDivision[] DIVISIONS = + [ + new APIRankedPlayDivision { Key = "Bronze-I", Tier = RankingTier.Bronze, Division = RankedPlayDivision.I, DisplayName = "Bronze I", StartRating = 600, EndRating = 699 }, + new APIRankedPlayDivision { Key = "Bronze-II", Tier = RankingTier.Bronze, Division = RankedPlayDivision.II, DisplayName = "Bronze II", StartRating = 700, EndRating = 799 }, + new APIRankedPlayDivision { Key = "Bronze-III", Tier = RankingTier.Bronze, Division = RankedPlayDivision.III, DisplayName = "Bronze III", StartRating = 800, EndRating = 899 }, + new APIRankedPlayDivision { Key = "Silver-I", Tier = RankingTier.Silver, Division = RankedPlayDivision.I, DisplayName = "Silver I", StartRating = 900, EndRating = 999 }, + new APIRankedPlayDivision { Key = "Silver-II", Tier = RankingTier.Silver, Division = RankedPlayDivision.II, DisplayName = "Silver II", StartRating = 1000, EndRating = 1099 }, + new APIRankedPlayDivision { Key = "Silver-III", Tier = RankingTier.Silver, Division = RankedPlayDivision.III, DisplayName = "Silver III", StartRating = 1100, EndRating = 1199 }, + new APIRankedPlayDivision { Key = "Gold-I", Tier = RankingTier.Gold, Division = RankedPlayDivision.I, DisplayName = "Gold I", StartRating = 1200, EndRating = 1299 }, + new APIRankedPlayDivision { Key = "Gold-II", Tier = RankingTier.Gold, Division = RankedPlayDivision.II, DisplayName = "Gold II", StartRating = 1300, EndRating = 1399 }, + new APIRankedPlayDivision { Key = "Gold-III", Tier = RankingTier.Gold, Division = RankedPlayDivision.III, DisplayName = "Gold III", StartRating = 1400, EndRating = 1499 }, + new APIRankedPlayDivision { Key = "Platinum-I", Tier = RankingTier.Platinum, Division = RankedPlayDivision.I, DisplayName = "Platinum I", StartRating = 1500, EndRating = 1599 }, + new APIRankedPlayDivision { Key = "Platinum-II", Tier = RankingTier.Platinum, Division = RankedPlayDivision.II, DisplayName = "Platinum II", StartRating = 1600, EndRating = 1699 }, + new APIRankedPlayDivision { Key = "Platinum-III", Tier = RankingTier.Platinum, Division = RankedPlayDivision.III, DisplayName = "Platinum III", StartRating = 1700, EndRating = 1799 }, + new APIRankedPlayDivision { Key = "Rhodium-I", Tier = RankingTier.Rhodium, Division = RankedPlayDivision.I, DisplayName = "Rhodium I", StartRating = 1800, EndRating = 1899 }, + new APIRankedPlayDivision { Key = "Rhodium-II", Tier = RankingTier.Rhodium, Division = RankedPlayDivision.II, DisplayName = "Rhodium II", StartRating = 1900, EndRating = 1999 }, + new APIRankedPlayDivision { Key = "Rhodium-III", Tier = RankingTier.Rhodium, Division = RankedPlayDivision.III, DisplayName = "Rhodium III", StartRating = 2000, EndRating = 2099 }, + new APIRankedPlayDivision { Key = "Radiant-I", Tier = RankingTier.Radiant, Division = RankedPlayDivision.I, DisplayName = "Radiant I", StartRating = 2100, EndRating = 2199 }, + new APIRankedPlayDivision { Key = "Radiant-II", Tier = RankingTier.Radiant, Division = RankedPlayDivision.II, DisplayName = "Radiant II", StartRating = 2200, EndRating = 2299 }, + new APIRankedPlayDivision { Key = "Radiant-III", Tier = RankingTier.Radiant, Division = RankedPlayDivision.III, DisplayName = "Radiant III", StartRating = 2300, EndRating = 2399 }, + ]; + } +} diff --git a/osu.Game.Tests/Visual/RankedPlay/TestSceneDivisionBadge.cs b/osu.Game.Tests/Visual/RankedPlay/TestSceneDivisionBadge.cs new file mode 100644 index 000000000000..a7004231112f --- /dev/null +++ b/osu.Game.Tests/Visual/RankedPlay/TestSceneDivisionBadge.cs @@ -0,0 +1,72 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; +using osu.Game.Scoring; +using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Components; +using osuTK; + +namespace osu.Game.Tests.Visual.RankedPlay +{ + public partial class TestSceneDivisionBadge : OsuTestScene + { + [Resolved] + private RulesetStore rulesetStore { get; set; } = null!; + + [TestCase("osu")] + [TestCase("taiko")] + [TestCase("fruits")] + [TestCase("mania")] + public void TestRuleset(string rulesetName) + { + var ruleset = rulesetStore.GetRuleset(rulesetName)!; + AddStep("create", () => + { + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(), + }, + Content = new[] + { + new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + Width = 400, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Full, + Spacing = new Vector2(10), + ChildrenEnumerable = TestSceneCompactDivisionBadge.DIVISIONS.Select(d => new DivisionBadge(d, ruleset)), + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10), + ChildrenEnumerable = Enumerable.Range(1, 100).Where(i => i == 1 || i == 100 || i % 20 == 0).Select(i => new DivisionBadge( + new APIRankedPlayDivision { Key = "Lustrous", Tier = RankingTier.Lustrous, DisplayName = "Lustrous", StartRating = 2400, EndRating = 3000 }, + ruleset, + i + )), + }, + }, + }, + }; + }); + } + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIRankedPlayDivision.cs b/osu.Game/Online/API/Requests/Responses/APIRankedPlayDivision.cs new file mode 100644 index 000000000000..2cb4febfbece --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIRankedPlayDivision.cs @@ -0,0 +1,34 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using osu.Game.Online.RankedPlay; +using osu.Game.Scoring; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIRankedPlayDivision + { + [JsonProperty(@"key")] + public string Key = string.Empty; + + [JsonProperty(@"tier")] + [JsonConverter(typeof(StringEnumConverter))] + public RankingTier Tier; + + [JsonProperty(@"division")] + public RankedPlayDivision Division; + + [JsonProperty(@"display_name")] + public string DisplayName = string.Empty; + + [JsonProperty(@"start_rating")] + public int StartRating; + + [JsonProperty("@end_rating")] + public int EndRating; + + public int Width => EndRating - StartRating + 1; + } +} diff --git a/osu.Game/Online/RankedPlay/RankedPlayDivision.cs b/osu.Game/Online/RankedPlay/RankedPlayDivision.cs new file mode 100644 index 000000000000..41ce8fc37159 --- /dev/null +++ b/osu.Game/Online/RankedPlay/RankedPlayDivision.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace osu.Game.Online.RankedPlay +{ + [SuppressMessage("ReSharper", "InconsistentNaming")] + [JsonConverter(typeof(StringEnumConverter))] + public enum RankedPlayDivision + { + I = 1, + II = 2, + III = 3, + } +} diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Components/CompactDivisionBadge.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Components/CompactDivisionBadge.cs new file mode 100644 index 000000000000..53232a83e1de --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Components/CompactDivisionBadge.cs @@ -0,0 +1,88 @@ +// Copyright (c) ppy Pty Ltd . 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.Extensions.Color4Extensions; +using osu.Framework.Extensions.LocalisationExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Localisation; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Scoring; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Components +{ + public partial class CompactDivisionBadge : CompositeDrawable, IHasTooltip + { + private readonly APIRankedPlayDivision division; + private readonly int? rank; + + public CompactDivisionBadge(APIRankedPlayDivision division, int? rank = null) + { + if (division.Tier == RankingTier.Lustrous && rank == null) + throw new ArgumentException("Must specify a rank for Lustrous tier."); + + this.division = division; + this.rank = rank; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + Size = new Vector2(36, 19.5f); + + InternalChild = new Sprite + { + RelativeSizeAxes = Axes.Both, + Texture = textures.Get(@$"Online/RankedPlay/Tiers/Compact/{division.Tier}"), + }; + + if (division.Tier != RankingTier.Lustrous) + { + AddInternal(new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(4), + ChildrenEnumerable = Enumerable.Range(1, (int)division.Division) + .Select(_ => new Container + { + Size = new Vector2(3, 10), + Masking = true, + CornerRadius = 1.5f, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.75f), + }, + }), + }); + } + else if (rank != null) + { + AddInternal(new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.Torus.With(size: 18, weight: FontWeight.Bold), + Colour = Colour4.Black.Opacity(0.75f), + Text = rank.ToLocalisableString(@"N0"), + }); + } + } + + public LocalisableString TooltipText => division.DisplayName; + } +} diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Components/DivisionBadge.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Components/DivisionBadge.cs new file mode 100644 index 000000000000..fb267be75207 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Components/DivisionBadge.cs @@ -0,0 +1,95 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Localisation; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; +using osu.Game.Scoring; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Components +{ + public partial class DivisionBadge : CompositeDrawable, IHasTooltip + { + private readonly APIRankedPlayDivision division; + private readonly RulesetInfo ruleset; + private readonly int? rank; + + public DivisionBadge(APIRankedPlayDivision division, RulesetInfo ruleset, int? rank = null) + { + if (division.Tier == RankingTier.Lustrous && rank == null) + throw new ArgumentException("Must specify a rank for Lustrous tier."); + + this.division = division; + this.ruleset = ruleset; + this.rank = rank; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + Size = division.Tier switch + { + RankingTier.Bronze => new Vector2(120, 96), + RankingTier.Silver => new Vector2(120, 99.75f), + RankingTier.Gold => new Vector2(120, 100.5f), + RankingTier.Platinum => new Vector2(120, 107.25f), + RankingTier.Rhodium => new Vector2(120, 108), + RankingTier.Radiant => new Vector2(120, 112.5f), + RankingTier.Lustrous => new Vector2(120, 122.5f), + _ => throw new ArgumentOutOfRangeException() + }; + + InternalChildren = new Drawable[] + { + new Sprite + { + RelativeSizeAxes = Axes.Both, + Texture = textures.Get($@"Online/RankedPlay/Tiers/Base/{division.Tier}"), + FillMode = FillMode.Fit, + }, + new Sprite + { + RelativeSizeAxes = Axes.Both, + Texture = textures.Get($@"Online/RankedPlay/Tiers/Stars/{division.Key}"), + FillMode = FillMode.Fit, + }, + new Sprite + { + Size = division.Tier switch + { + RankingTier.Bronze => new Vector2(33), + RankingTier.Silver => new Vector2(34.5f), + _ => new Vector2(36), + }, + Position = division.Tier switch + { + RankingTier.Bronze => new Vector2(43.5f, 24.75f), + RankingTier.Silver => new Vector2(42.75f, 27), + RankingTier.Gold => new Vector2(42, 23.25f), + RankingTier.Platinum => new Vector2(42, 28.5f), + RankingTier.Rhodium => new Vector2(42, 30), + RankingTier.Radiant => new Vector2(42, 33), + RankingTier.Lustrous => new Vector2(42, 43.5f), + _ => throw new ArgumentOutOfRangeException() + }, + Texture = textures.Get($@"Online/RankedPlay/Tiers/Rulesets/{ruleset.ShortName}"), + }, + new CompactDivisionBadge(division, rank) + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }, + }; + } + + public LocalisableString TooltipText => division.DisplayName; + } +}