mirror of
https://github.com/ppy/osu.git
synced 2026-05-13 20:33:35 +08:00
Add matchmaking profile badge (#37241)
Using the same styling as osu!web + daily challenge. <img width="1920" height="1034" alt="Screenshot_20260409-164600" src="https://github.com/user-attachments/assets/97e2270e-af9f-478d-b2d6-c9fb8be16720" /> --------- Co-authored-by: Dean Herbert <pe@ppy.sh>
This commit is contained in:
committed by
GitHub
Unverified
parent
b838564039
commit
93b7c3324d
@@ -0,0 +1,89 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Profile;
|
||||
using osu.Game.Overlays.Profile.Header.Components;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public partial class TestSceneUserProfileMatchmakingStatsDisplay : OsuManualInputManagerTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly Bindable<UserProfileData?> userProfileData = new Bindable<UserProfileData?>(new UserProfileData(new APIUser(), new OsuRuleset().RulesetInfo));
|
||||
|
||||
[Cached]
|
||||
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("create", () =>
|
||||
{
|
||||
Clear();
|
||||
Add(new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background2,
|
||||
});
|
||||
Add(new MatchmakingStatsDisplay
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(1f),
|
||||
User = { BindTarget = userProfileData },
|
||||
});
|
||||
});
|
||||
|
||||
AddStep("set stats", () => userProfileData.Value = new UserProfileData(new APIUser
|
||||
{
|
||||
MatchmakingStatistics =
|
||||
[
|
||||
new APIUserMatchmakingStatistics
|
||||
{
|
||||
Plays = 10,
|
||||
FirstPlacements = 8,
|
||||
Rank = 1000,
|
||||
Rating = 2000,
|
||||
TotalPoints = 500,
|
||||
Pool =
|
||||
{
|
||||
Name = "Active Pool"
|
||||
}
|
||||
},
|
||||
new APIUserMatchmakingStatistics
|
||||
{
|
||||
Plays = 5,
|
||||
FirstPlacements = 4,
|
||||
Rank = 500,
|
||||
Rating = 1000,
|
||||
TotalPoints = 250,
|
||||
Pool =
|
||||
{
|
||||
Name = "Inactive Pool"
|
||||
}
|
||||
},
|
||||
new APIUserMatchmakingStatistics
|
||||
{
|
||||
Rating = 1500,
|
||||
IsRatingProvisional = true,
|
||||
Pool =
|
||||
{
|
||||
Name = "Provisional"
|
||||
}
|
||||
}
|
||||
]
|
||||
}, new OsuRuleset().RulesetInfo));
|
||||
|
||||
AddStep("clear stats", () => userProfileData.Value = null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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 Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
public class APIMatchmakingPool
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("active")]
|
||||
public bool Active { get; set; }
|
||||
|
||||
[JsonProperty("ruleset_id")]
|
||||
public int RulesetId { get; set; }
|
||||
|
||||
[JsonProperty("variant_id")]
|
||||
public int VariantId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -297,6 +297,9 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
[JsonProperty("daily_challenge_user_stats")]
|
||||
public APIUserDailyChallengeStatistics DailyChallengeStatistics = new APIUserDailyChallengeStatistics();
|
||||
|
||||
[JsonProperty("matchmaking_stats")]
|
||||
public APIUserMatchmakingStatistics[] MatchmakingStatistics = [];
|
||||
|
||||
public override string ToString() => Username;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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 Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
public class APIUserMatchmakingStatistics
|
||||
{
|
||||
[JsonProperty("user_id")]
|
||||
public int UserId;
|
||||
|
||||
[JsonProperty("pool_id")]
|
||||
public int PoolId { get; set; }
|
||||
|
||||
[JsonProperty("rating")]
|
||||
public int Rating { get; set; }
|
||||
|
||||
[JsonProperty("rank")]
|
||||
public int Rank { get; set; }
|
||||
|
||||
[JsonProperty("plays")]
|
||||
public int Plays { get; set; }
|
||||
|
||||
[JsonProperty("total_points")]
|
||||
public int TotalPoints { get; set; }
|
||||
|
||||
[JsonProperty("first_placements")]
|
||||
public int FirstPlacements { get; set; }
|
||||
|
||||
[JsonProperty("is_rating_provisional")]
|
||||
public bool IsRatingProvisional { get; set; }
|
||||
|
||||
[JsonProperty("pool")]
|
||||
public APIMatchmakingPool Pool { get; set; } = new APIMatchmakingPool();
|
||||
}
|
||||
}
|
||||
@@ -70,11 +70,24 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
{
|
||||
Title = UsersStrings.ShowRankCountrySimple,
|
||||
},
|
||||
new DailyChallengeStatsDisplay
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
User = { BindTarget = User },
|
||||
Spacing = new Vector2(20),
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new MatchmakingStatsDisplay
|
||||
{
|
||||
User = { BindTarget = User }
|
||||
},
|
||||
new DailyChallengeStatsDisplay
|
||||
{
|
||||
User = { BindTarget = User },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
// 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.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Header.Components
|
||||
{
|
||||
public partial class MatchmakingStatsDisplay : CompositeDrawable, IHasCustomTooltip<MatchmakingStatsTooltipData>
|
||||
{
|
||||
public readonly Bindable<UserProfileData?> User = new Bindable<UserProfileData?>();
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
|
||||
private OsuSpriteText rankText = null!;
|
||||
|
||||
public MatchmakingStatsDisplay()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
CornerRadius = 6,
|
||||
BorderThickness = 2,
|
||||
BorderColour = colourProvider.Background4,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background4,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Padding = new MarginPadding(3f),
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "Quick Play",
|
||||
Margin = new MarginPadding { Horizontal = 5f, Vertical = 7f },
|
||||
Font = OsuFont.GetFont(size: 12)
|
||||
},
|
||||
new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
CornerRadius = 3,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background6,
|
||||
},
|
||||
rankText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
UseFullGlyphHeight = false,
|
||||
Colour = colourProvider.Content2,
|
||||
Margin = new MarginPadding { Horizontal = 10f, Vertical = 5f }
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
User.BindValueChanged(_ => updateDisplay(), true);
|
||||
}
|
||||
|
||||
private void updateDisplay()
|
||||
{
|
||||
if (User.Value == null)
|
||||
{
|
||||
Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
APIUserMatchmakingStatistics[] stats = User.Value.User.MatchmakingStatistics;
|
||||
|
||||
if (stats.Length == 0)
|
||||
{
|
||||
Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
APIUserMatchmakingStatistics[] mostRelevantStats = stats.OrderByDescending(s => s.Pool.Active).ThenByDescending(s => s.Pool.Id).ToArray();
|
||||
APIUserMatchmakingStatistics mostRelevantStat = mostRelevantStats.First();
|
||||
|
||||
rankText.Text = $"#{mostRelevantStat.Rank:N0}";
|
||||
|
||||
TooltipContent = new MatchmakingStatsTooltipData(colourProvider, mostRelevantStats);
|
||||
|
||||
Show();
|
||||
}
|
||||
|
||||
public ITooltip<MatchmakingStatsTooltipData> GetCustomTooltip() => new MatchmakingStatsTooltip();
|
||||
|
||||
public MatchmakingStatsTooltipData? TooltipContent { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
// 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.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Header.Components
|
||||
{
|
||||
public partial class MatchmakingStatsTooltip : VisibilityContainer, ITooltip<MatchmakingStatsTooltipData>
|
||||
{
|
||||
private Box background = null!;
|
||||
private Container<TableContainer> tableContainer = null!;
|
||||
|
||||
public MatchmakingStatsTooltip()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
CornerRadius = 20f;
|
||||
Masking = true;
|
||||
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
Radius = 30f,
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
tableContainer = new Container<TableContainer>
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(15f),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void SetContent(MatchmakingStatsTooltipData content)
|
||||
{
|
||||
var statistics = content.Statistics;
|
||||
var colourProvider = content.ColourProvider;
|
||||
|
||||
background.Colour = colourProvider.Background4;
|
||||
|
||||
tableContainer.Child = new MatchmakingStatsTooltipTable(colourProvider)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Columns =
|
||||
[
|
||||
new TableColumn(dimension: new Dimension(GridSizeMode.AutoSize)),
|
||||
new TableColumn(dimension: new Dimension(GridSizeMode.AutoSize)),
|
||||
new TableColumn("Wins", dimension: new Dimension(GridSizeMode.AutoSize)),
|
||||
new TableColumn("Plays", dimension: new Dimension(GridSizeMode.AutoSize)),
|
||||
new TableColumn("Points", dimension: new Dimension(GridSizeMode.AutoSize)),
|
||||
new TableColumn("Rating", dimension: new Dimension(GridSizeMode.AutoSize)),
|
||||
],
|
||||
RowSize = new Dimension(GridSizeMode.AutoSize),
|
||||
Content = statistics.Select(s => createRow(colourProvider, s)).ToArray().ToRectangular()
|
||||
};
|
||||
}
|
||||
|
||||
private Drawable[] createRow(OverlayColourProvider colourProvider, APIUserMatchmakingStatistics stat)
|
||||
{
|
||||
return
|
||||
[
|
||||
new StatisticText(colourProvider)
|
||||
{
|
||||
Text = stat.Pool.Name,
|
||||
Colour = Color4.White
|
||||
},
|
||||
new StatisticText(colourProvider) { Text = $"#{stat.Rank:N0}" },
|
||||
new StatisticText(colourProvider) { Text = stat.FirstPlacements.ToString("N0") },
|
||||
new StatisticText(colourProvider) { Text = stat.Plays.ToString("N0") },
|
||||
new StatisticText(colourProvider) { Text = stat.TotalPoints.ToString("N0") },
|
||||
new StatisticText(colourProvider) { Text = stat.Rating.ToString("N0") + (stat.IsRatingProvisional ? "*" : string.Empty) }
|
||||
];
|
||||
}
|
||||
|
||||
protected override void PopIn() => this.FadeIn(200, Easing.OutQuint);
|
||||
|
||||
protected override void PopOut() => this.FadeOut(200, Easing.OutQuint);
|
||||
|
||||
public void Move(Vector2 pos) => Position = pos;
|
||||
|
||||
private partial class MatchmakingStatsTooltipTable : TableContainer
|
||||
{
|
||||
private readonly OverlayColourProvider colourProvider;
|
||||
|
||||
public MatchmakingStatsTooltipTable(OverlayColourProvider colourProvider)
|
||||
{
|
||||
this.colourProvider = colourProvider;
|
||||
}
|
||||
|
||||
protected override Drawable CreateHeader(int index, TableColumn? column)
|
||||
{
|
||||
return new StatisticText(colourProvider)
|
||||
{
|
||||
Text = column?.Header ?? string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private partial class StatisticText : OsuSpriteText
|
||||
{
|
||||
public StatisticText(OverlayColourProvider colourProvider)
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 12);
|
||||
Padding = new MarginPadding { Horizontal = 5, Vertical = 2 };
|
||||
Colour = colourProvider.Content2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public record MatchmakingStatsTooltipData(OverlayColourProvider ColourProvider, APIUserMatchmakingStatistics[] Statistics);
|
||||
}
|
||||
Reference in New Issue
Block a user