1
0
mirror of https://github.com/ppy/osu.git synced 2024-05-15 01:10:21 +08:00

Merge pull request #21776 from bdach/score-stats-display

Implement overall ranking display for solo results screen
This commit is contained in:
Dean Herbert 2022-12-25 21:43:12 +08:00 committed by GitHub
commit 831c7420b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 592 additions and 0 deletions

View File

@ -0,0 +1,117 @@
// 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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Online.Solo;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Statistics.User;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Ranking
{
public partial class TestSceneOverallRanking : OsuTestScene
{
private OverallRanking overallRanking = null!;
[Test]
public void TestUpdatePending()
{
createDisplay();
}
[Test]
public void TestAllIncreased()
{
createDisplay();
displayUpdate(
new UserStatistics
{
GlobalRank = 12_345,
Accuracy = 0.9899,
MaxCombo = 2_322,
RankedScore = 23_123_543_456,
TotalScore = 123_123_543_456,
PP = 5_072
},
new UserStatistics
{
GlobalRank = 1_234,
Accuracy = 0.9907,
MaxCombo = 2_352,
RankedScore = 23_124_231_435,
TotalScore = 123_124_231_435,
PP = 5_434
});
}
[Test]
public void TestAllDecreased()
{
createDisplay();
displayUpdate(
new UserStatistics
{
GlobalRank = 1_234,
Accuracy = 0.9907,
MaxCombo = 2_352,
RankedScore = 23_124_231_435,
TotalScore = 123_124_231_435,
PP = 5_434
},
new UserStatistics
{
GlobalRank = 12_345,
Accuracy = 0.9899,
MaxCombo = 2_322,
RankedScore = 23_123_543_456,
TotalScore = 123_123_543_456,
PP = 5_072
});
}
[Test]
public void TestNoChanges()
{
var statistics = new UserStatistics
{
GlobalRank = 12_345,
Accuracy = 0.9899,
MaxCombo = 2_322,
RankedScore = 23_123_543_456,
TotalScore = 123_123_543_456,
PP = 5_072
};
createDisplay();
displayUpdate(statistics, statistics);
}
[Test]
public void TestNotRanked()
{
var statistics = new UserStatistics
{
GlobalRank = null,
Accuracy = 0.9899,
MaxCombo = 2_322,
RankedScore = 23_123_543_456,
TotalScore = 123_123_543_456,
PP = null
};
createDisplay();
displayUpdate(statistics, statistics);
}
private void createDisplay() => AddStep("create display", () => Child = overallRanking = new OverallRanking
{
Width = 400,
Anchor = Anchor.Centre,
Origin = Anchor.Centre
});
private void displayUpdate(UserStatistics before, UserStatistics after) =>
AddStep("display update", () => overallRanking.StatisticsUpdate.Value = new SoloStatisticsUpdate(new ScoreInfo(), before, after));
}
}

View File

@ -0,0 +1,35 @@
// 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.Localisation;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Utils;
namespace osu.Game.Screens.Ranking.Statistics.User
{
public partial class AccuracyChangeRow : RankingChangeRow<double>
{
public AccuracyChangeRow()
: base(stats => stats.Accuracy)
{
}
protected override LocalisableString Label => UsersStrings.ShowStatsHitAccuracy;
protected override LocalisableString FormatCurrentValue(double current) => current.FormatAccuracy();
protected override int CalculateDifference(double previous, double current, out LocalisableString formattedDifference)
{
double difference = current - previous;
if (difference < 0)
formattedDifference = difference.FormatAccuracy();
else if (difference > 0)
formattedDifference = LocalisableString.Interpolate($@"+{difference.FormatAccuracy()}");
else
formattedDifference = string.Empty;
return current.CompareTo(previous);
}
}
}

View File

@ -0,0 +1,58 @@
// 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.Diagnostics;
using osu.Framework.Localisation;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Utils;
namespace osu.Game.Screens.Ranking.Statistics.User
{
public partial class GlobalRankChangeRow : RankingChangeRow<int?>
{
public GlobalRankChangeRow()
: base(stats => stats.GlobalRank)
{
}
protected override LocalisableString Label => UsersStrings.ShowRankGlobalSimple;
protected override LocalisableString FormatCurrentValue(int? current)
=> current == null ? string.Empty : current.Value.FormatRank();
protected override int CalculateDifference(int? previous, int? current, out LocalisableString formattedDifference)
{
if (previous == null && current == null)
{
formattedDifference = string.Empty;
return 0;
}
if (previous == null && current != null)
{
formattedDifference = LocalisableString.Interpolate($"+{current.Value.FormatRank()}");
return 1;
}
if (previous != null && current == null)
{
formattedDifference = LocalisableString.Interpolate($"-{previous.Value.FormatRank()}");
return -1;
}
Debug.Assert(previous != null && current != null);
// note that ranks work backwards, i.e. lower rank is _better_.
int difference = previous.Value - current.Value;
if (difference < 0)
formattedDifference = difference.FormatRank();
else if (difference > 0)
formattedDifference = LocalisableString.Interpolate($"+{difference.FormatRank()}");
else
formattedDifference = string.Empty;
return difference;
}
}
}

View File

@ -0,0 +1,34 @@
// 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.Localisation;
using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Screens.Ranking.Statistics.User
{
public partial class MaximumComboChangeRow : RankingChangeRow<int>
{
public MaximumComboChangeRow()
: base(stats => stats.MaxCombo)
{
}
protected override LocalisableString Label => UsersStrings.ShowStatsMaximumCombo;
protected override LocalisableString FormatCurrentValue(int current) => LocalisableString.Interpolate($@"{current:N0}x");
protected override int CalculateDifference(int previous, int current, out LocalisableString formattedDifference)
{
int difference = current - previous;
if (difference < 0)
formattedDifference = LocalisableString.Interpolate($@"{difference:N0}x");
else if (difference > 0)
formattedDifference = LocalisableString.Interpolate($@"+{difference:N0}x");
else
formattedDifference = string.Empty;
return current.CompareTo(previous);
}
}
}

View File

@ -0,0 +1,78 @@
// 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.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Solo;
using osuTK;
namespace osu.Game.Screens.Ranking.Statistics.User
{
public partial class OverallRanking : CompositeDrawable
{
private const float transition_duration = 300;
public Bindable<SoloStatisticsUpdate?> StatisticsUpdate { get; } = new Bindable<SoloStatisticsUpdate?>();
private LoadingLayer loadingLayer = null!;
private FillFlowContainer content = null!;
[BackgroundDependencyLoader]
private void load()
{
AutoSizeAxes = Axes.Y;
AutoSizeEasing = Easing.OutQuint;
AutoSizeDuration = transition_duration;
InternalChildren = new Drawable[]
{
loadingLayer = new LoadingLayer(withBox: false)
{
RelativeSizeAxes = Axes.Both,
},
content = new FillFlowContainer
{
AlwaysPresent = true,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(10),
Children = new Drawable[]
{
new GlobalRankChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
new AccuracyChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
new MaximumComboChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
new RankedScoreChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
new TotalScoreChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
new PerformancePointsChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } }
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
StatisticsUpdate.BindValueChanged(onUpdateReceived, true);
FinishTransforms(true);
}
private void onUpdateReceived(ValueChangedEvent<SoloStatisticsUpdate?> update)
{
if (update.NewValue == null)
{
loadingLayer.Show();
content.FadeOut(transition_duration, Easing.OutQuint);
}
else
{
loadingLayer.Hide();
content.FadeIn(transition_duration, Easing.OutQuint);
}
}
}
}

View File

@ -0,0 +1,56 @@
// 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.Diagnostics;
using osu.Framework.Localisation;
using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Screens.Ranking.Statistics.User
{
public partial class PerformancePointsChangeRow : RankingChangeRow<decimal?>
{
public PerformancePointsChangeRow()
: base(stats => stats.PP)
{
}
protected override LocalisableString Label => RankingsStrings.StatPerformance;
protected override LocalisableString FormatCurrentValue(decimal? current)
=> current == null ? string.Empty : LocalisableString.Interpolate($@"{current:N0}pp");
protected override int CalculateDifference(decimal? previous, decimal? current, out LocalisableString formattedDifference)
{
if (previous == null && current == null)
{
formattedDifference = string.Empty;
return 0;
}
if (previous == null && current != null)
{
formattedDifference = LocalisableString.Interpolate($"+{current.Value:N0}pp");
return 1;
}
if (previous != null && current == null)
{
formattedDifference = LocalisableString.Interpolate($"-{previous.Value:N0}pp");
return -1;
}
Debug.Assert(previous != null && current != null);
decimal difference = current.Value - previous.Value;
if (difference < 0)
formattedDifference = LocalisableString.Interpolate($@"{difference:N0}pp");
else if (difference > 0)
formattedDifference = LocalisableString.Interpolate($@"+{difference:N0}pp");
else
formattedDifference = string.Empty;
return current.Value.CompareTo(previous.Value);
}
}
}

View File

@ -0,0 +1,35 @@
// 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.Extensions.LocalisationExtensions;
using osu.Framework.Localisation;
using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Screens.Ranking.Statistics.User
{
public partial class RankedScoreChangeRow : RankingChangeRow<long>
{
public RankedScoreChangeRow()
: base(stats => stats.RankedScore)
{
}
protected override LocalisableString Label => UsersStrings.ShowStatsRankedScore;
protected override LocalisableString FormatCurrentValue(long current) => current.ToLocalisableString(@"N0");
protected override int CalculateDifference(long previous, long current, out LocalisableString formattedDifference)
{
long difference = current - previous;
if (difference < 0)
formattedDifference = difference.ToLocalisableString(@"N0");
else if (difference > 0)
formattedDifference = LocalisableString.Interpolate($@"+{difference:N0}");
else
formattedDifference = string.Empty;
return current.CompareTo(previous);
}
}
}

View File

@ -0,0 +1,144 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Solo;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Screens.Ranking.Statistics.User
{
public abstract partial class RankingChangeRow<T> : CompositeDrawable
{
public Bindable<SoloStatisticsUpdate?> StatisticsUpdate { get; } = new Bindable<SoloStatisticsUpdate?>();
private readonly Func<UserStatistics, T> accessor;
private OsuSpriteText currentValueText = null!;
private SpriteIcon changeIcon = null!;
private OsuSpriteText changeText = null!;
[Resolved]
private OsuColour colours { get; set; } = null!;
protected RankingChangeRow(
Func<UserStatistics, T> accessor)
{
this.accessor = accessor;
}
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChildren = new Drawable[]
{
new OsuSpriteText
{
Text = Label,
Font = OsuFont.Default.With(size: 18)
},
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(5),
Children = new Drawable[]
{
changeIcon = new SpriteIcon
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Size = new Vector2(18)
},
currentValueText = new OsuSpriteText
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Font = OsuFont.Default.With(size: 18, weight: FontWeight.Bold)
},
}
},
changeText = new OsuSpriteText
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Font = OsuFont.Default.With(weight: FontWeight.Bold)
}
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
StatisticsUpdate.BindValueChanged(onStatisticsUpdate, true);
}
private void onStatisticsUpdate(ValueChangedEvent<SoloStatisticsUpdate?> statisticsUpdate)
{
var update = statisticsUpdate.NewValue;
if (update == null)
return;
T previousValue = accessor.Invoke(update.Before);
T currentValue = accessor.Invoke(update.After);
int comparisonResult = CalculateDifference(previousValue, currentValue, out var formattedDifference);
Colour4 comparisonColour;
IconUsage icon;
if (comparisonResult < 0)
{
comparisonColour = colours.Red1;
icon = FontAwesome.Solid.ArrowDown;
}
else if (comparisonResult > 0)
{
comparisonColour = colours.Lime1;
icon = FontAwesome.Solid.ArrowUp;
}
else
{
comparisonColour = colours.Orange1;
icon = FontAwesome.Solid.Minus;
}
currentValueText.Text = FormatCurrentValue(currentValue);
changeIcon.Icon = icon;
changeIcon.Colour = comparisonColour;
changeText.Text = formattedDifference;
changeText.Colour = comparisonColour;
}
protected abstract LocalisableString Label { get; }
protected abstract LocalisableString FormatCurrentValue(T current);
protected abstract int CalculateDifference(T previous, T current, out LocalisableString formattedDifference);
}
}

View File

@ -0,0 +1,35 @@
// 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.Extensions.LocalisationExtensions;
using osu.Framework.Localisation;
using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Screens.Ranking.Statistics.User
{
public partial class TotalScoreChangeRow : RankingChangeRow<long>
{
public TotalScoreChangeRow()
: base(stats => stats.TotalScore)
{
}
protected override LocalisableString Label => UsersStrings.ShowStatsTotalScore;
protected override LocalisableString FormatCurrentValue(long current) => current.ToLocalisableString(@"N0");
protected override int CalculateDifference(long previous, long current, out LocalisableString formattedDifference)
{
long difference = current - previous;
if (difference < 0)
formattedDifference = difference.ToLocalisableString(@"N0");
else if (difference > 0)
formattedDifference = LocalisableString.Interpolate($@"+{difference:N0}");
else
formattedDifference = string.Empty;
return current.CompareTo(previous);
}
}
}