mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 10:18:22 +08:00
Merge pull request #16483 from hlysine/display-performance-attributes
Display performance breakdown in a tooltip
This commit is contained in:
commit
14c4e6fa66
@ -1,6 +1,7 @@
|
||||
// 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.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
|
||||
@ -16,5 +17,14 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
|
||||
[JsonProperty("scaled_score")]
|
||||
public double ScaledScore { get; set; }
|
||||
|
||||
public override IEnumerable<PerformanceDisplayAttribute> GetAttributesForDisplay()
|
||||
{
|
||||
foreach (var attribute in base.GetAttributesForDisplay())
|
||||
yield return attribute;
|
||||
|
||||
yield return new PerformanceDisplayAttribute(nameof(Difficulty), "Difficulty", Difficulty);
|
||||
yield return new PerformanceDisplayAttribute(nameof(Accuracy), "Accuracy", Accuracy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -366,6 +366,17 @@ namespace osu.Game.Rulesets.Mania
|
||||
|
||||
public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => new[]
|
||||
{
|
||||
new StatisticRow
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
new StatisticItem("Performance Breakdown", () => new PerformanceBreakdownChart(score, playableBeatmap)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
}),
|
||||
}
|
||||
},
|
||||
new StatisticRow
|
||||
{
|
||||
Columns = new[]
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
|
||||
@ -22,5 +23,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
[JsonProperty("effective_miss_count")]
|
||||
public double EffectiveMissCount { get; set; }
|
||||
|
||||
public override IEnumerable<PerformanceDisplayAttribute> GetAttributesForDisplay()
|
||||
{
|
||||
foreach (var attribute in base.GetAttributesForDisplay())
|
||||
yield return attribute;
|
||||
|
||||
yield return new PerformanceDisplayAttribute(nameof(Aim), "Aim", Aim);
|
||||
yield return new PerformanceDisplayAttribute(nameof(Speed), "Speed", Speed);
|
||||
yield return new PerformanceDisplayAttribute(nameof(Accuracy), "Accuracy", Accuracy);
|
||||
yield return new PerformanceDisplayAttribute(nameof(Flashlight), "Flashlight Bonus", Flashlight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -275,6 +275,17 @@ namespace osu.Game.Rulesets.Osu
|
||||
|
||||
return new[]
|
||||
{
|
||||
new StatisticRow
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
new StatisticItem("Performance Breakdown", () => new PerformanceBreakdownChart(score, playableBeatmap)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
}),
|
||||
}
|
||||
},
|
||||
new StatisticRow
|
||||
{
|
||||
Columns = new[]
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
|
||||
@ -13,5 +14,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
|
||||
[JsonProperty("accuracy")]
|
||||
public double Accuracy { get; set; }
|
||||
|
||||
public override IEnumerable<PerformanceDisplayAttribute> GetAttributesForDisplay()
|
||||
{
|
||||
foreach (var attribute in base.GetAttributesForDisplay())
|
||||
yield return attribute;
|
||||
|
||||
yield return new PerformanceDisplayAttribute(nameof(Difficulty), "Difficulty", Difficulty);
|
||||
yield return new PerformanceDisplayAttribute(nameof(Accuracy), "Accuracy", Accuracy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -209,6 +209,17 @@ namespace osu.Game.Rulesets.Taiko
|
||||
|
||||
return new[]
|
||||
{
|
||||
new StatisticRow
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
new StatisticItem("Performance Breakdown", () => new PerformanceBreakdownChart(score, playableBeatmap)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
}),
|
||||
}
|
||||
},
|
||||
new StatisticRow
|
||||
{
|
||||
Columns = new[]
|
||||
|
@ -130,7 +130,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
AddStep("click to right of panel", () =>
|
||||
{
|
||||
var expandedPanel = this.ChildrenOfType<ScorePanel>().Single(p => p.State == PanelState.Expanded);
|
||||
InputManager.MoveMouseTo(expandedPanel.ScreenSpaceDrawQuad.TopRight + new Vector2(100, 0));
|
||||
InputManager.MoveMouseTo(expandedPanel.ScreenSpaceDrawQuad.TopRight + new Vector2(50, 0));
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty
|
||||
@ -12,5 +13,15 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
/// </summary>
|
||||
[JsonProperty("pp")]
|
||||
public double Total { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Return a <see cref="PerformanceDisplayAttribute"/> for each attribute so that a performance breakdown can be displayed.
|
||||
/// Some attributes may be omitted if they are not meant for display.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual IEnumerable<PerformanceDisplayAttribute> GetAttributesForDisplay()
|
||||
{
|
||||
yield return new PerformanceDisplayAttribute(nameof(Total), "Achieved PP", Total);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
21
osu.Game/Rulesets/Difficulty/PerformanceBreakdown.cs
Normal file
21
osu.Game/Rulesets/Difficulty/PerformanceBreakdown.cs
Normal file
@ -0,0 +1,21 @@
|
||||
// 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.
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
/// <summary>
|
||||
/// Data for generating a performance breakdown by comparing performance to a perfect play.
|
||||
/// </summary>
|
||||
public class PerformanceBreakdown
|
||||
{
|
||||
/// <summary>
|
||||
/// Actual gameplay performance.
|
||||
/// </summary>
|
||||
public PerformanceAttributes Performance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Performance of a perfect play for comparison.
|
||||
/// </summary>
|
||||
public PerformanceAttributes PerfectPerformance { get; set; }
|
||||
}
|
||||
}
|
105
osu.Game/Rulesets/Difficulty/PerformanceBreakdownCalculator.cs
Normal file
105
osu.Game/Rulesets/Difficulty/PerformanceBreakdownCalculator.cs
Normal file
@ -0,0 +1,105 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
public class PerformanceBreakdownCalculator
|
||||
{
|
||||
private readonly IBeatmap playableBeatmap;
|
||||
private readonly BeatmapDifficultyCache difficultyCache;
|
||||
private readonly ScorePerformanceCache performanceCache;
|
||||
|
||||
public PerformanceBreakdownCalculator(IBeatmap playableBeatmap, BeatmapDifficultyCache difficultyCache, ScorePerformanceCache performanceCache)
|
||||
{
|
||||
this.playableBeatmap = playableBeatmap;
|
||||
this.difficultyCache = difficultyCache;
|
||||
this.performanceCache = performanceCache;
|
||||
}
|
||||
|
||||
[ItemCanBeNull]
|
||||
public async Task<PerformanceBreakdown> CalculateAsync(ScoreInfo score, CancellationToken cancellationToken = default)
|
||||
{
|
||||
PerformanceAttributes[] performanceArray = await Task.WhenAll(
|
||||
// compute actual performance
|
||||
performanceCache.CalculatePerformanceAsync(score, cancellationToken),
|
||||
// compute performance for perfect play
|
||||
getPerfectPerformance(score, cancellationToken)
|
||||
).ConfigureAwait(false);
|
||||
|
||||
return new PerformanceBreakdown { Performance = performanceArray[0], PerfectPerformance = performanceArray[1] };
|
||||
}
|
||||
|
||||
[ItemCanBeNull]
|
||||
private Task<PerformanceAttributes> getPerfectPerformance(ScoreInfo score, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
Ruleset ruleset = score.Ruleset.CreateInstance();
|
||||
ScoreInfo perfectPlay = score.DeepClone();
|
||||
perfectPlay.Accuracy = 1;
|
||||
perfectPlay.Passed = true;
|
||||
|
||||
// calculate max combo
|
||||
// todo: Get max combo from difficulty calculator instead when diffcalc properly supports lazer-first scores
|
||||
perfectPlay.MaxCombo = calculateMaxCombo(playableBeatmap);
|
||||
|
||||
// create statistics assuming all hit objects have perfect hit result
|
||||
var statistics = playableBeatmap.HitObjects
|
||||
.SelectMany(getPerfectHitResults)
|
||||
.GroupBy(hr => hr, (hr, list) => (hitResult: hr, count: list.Count()))
|
||||
.ToDictionary(pair => pair.hitResult, pair => pair.count);
|
||||
perfectPlay.Statistics = statistics;
|
||||
|
||||
// calculate total score
|
||||
ScoreProcessor scoreProcessor = ruleset.CreateScoreProcessor();
|
||||
scoreProcessor.HighestCombo.Value = perfectPlay.MaxCombo;
|
||||
scoreProcessor.Mods.Value = perfectPlay.Mods;
|
||||
perfectPlay.TotalScore = (long)scoreProcessor.GetImmediateScore(ScoringMode.Standardised, perfectPlay.MaxCombo, statistics);
|
||||
|
||||
// compute rank achieved
|
||||
// default to SS, then adjust the rank with mods
|
||||
perfectPlay.Rank = ScoreRank.X;
|
||||
|
||||
foreach (IApplicableToScoreProcessor mod in perfectPlay.Mods.OfType<IApplicableToScoreProcessor>())
|
||||
{
|
||||
perfectPlay.Rank = mod.AdjustRank(perfectPlay.Rank, 1);
|
||||
}
|
||||
|
||||
// calculate performance for this perfect score
|
||||
var difficulty = await difficultyCache.GetDifficultyAsync(
|
||||
playableBeatmap.BeatmapInfo,
|
||||
score.Ruleset,
|
||||
score.Mods,
|
||||
cancellationToken
|
||||
).ConfigureAwait(false);
|
||||
|
||||
// ScorePerformanceCache is not used to avoid caching multiple copies of essentially identical perfect performance attributes
|
||||
return difficulty == null ? null : ruleset.CreatePerformanceCalculator(difficulty.Value.Attributes, perfectPlay)?.Calculate();
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
private int calculateMaxCombo(IBeatmap beatmap)
|
||||
{
|
||||
return beatmap.HitObjects.SelectMany(getPerfectHitResults).Count(r => r.AffectsCombo());
|
||||
}
|
||||
|
||||
private IEnumerable<HitResult> getPerfectHitResults(HitObject hitObject)
|
||||
{
|
||||
foreach (HitObject nested in hitObject.NestedHitObjects)
|
||||
yield return nested.CreateJudgement().MaxResult;
|
||||
|
||||
yield return hitObject.CreateJudgement().MaxResult;
|
||||
}
|
||||
}
|
||||
}
|
33
osu.Game/Rulesets/Difficulty/PerformanceDisplayAttribute.cs
Normal file
33
osu.Game/Rulesets/Difficulty/PerformanceDisplayAttribute.cs
Normal file
@ -0,0 +1,33 @@
|
||||
// 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.
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
/// <summary>
|
||||
/// Data for displaying a performance attribute to user. Includes a display name for clarity.
|
||||
/// </summary>
|
||||
public class PerformanceDisplayAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the attribute property in <see cref="PerformanceAttributes"/>.
|
||||
/// </summary>
|
||||
public string PropertyName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A custom display name for the attribute.
|
||||
/// </summary>
|
||||
public string DisplayName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The associated attribute value.
|
||||
/// </summary>
|
||||
public double Value { get; }
|
||||
|
||||
public PerformanceDisplayAttribute(string propertyName, string displayName, double value)
|
||||
{
|
||||
PropertyName = propertyName;
|
||||
DisplayName = displayName;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
|
||||
namespace osu.Game.Scoring
|
||||
{
|
||||
@ -15,7 +16,7 @@ namespace osu.Game.Scoring
|
||||
/// A component which performs and acts as a central cache for performance calculations of locally databased scores.
|
||||
/// Currently not persisted between game sessions.
|
||||
/// </summary>
|
||||
public class ScorePerformanceCache : MemoryCachingComponent<ScorePerformanceCache.PerformanceCacheLookup, double?>
|
||||
public class ScorePerformanceCache : MemoryCachingComponent<ScorePerformanceCache.PerformanceCacheLookup, PerformanceAttributes>
|
||||
{
|
||||
[Resolved]
|
||||
private BeatmapDifficultyCache difficultyCache { get; set; }
|
||||
@ -27,10 +28,10 @@ namespace osu.Game.Scoring
|
||||
/// </summary>
|
||||
/// <param name="score">The score to do the calculation on. </param>
|
||||
/// <param name="token">An optional <see cref="CancellationToken"/> to cancel the operation.</param>
|
||||
public Task<double?> CalculatePerformanceAsync([NotNull] ScoreInfo score, CancellationToken token = default) =>
|
||||
public Task<PerformanceAttributes> CalculatePerformanceAsync([NotNull] ScoreInfo score, CancellationToken token = default) =>
|
||||
GetAsync(new PerformanceCacheLookup(score), token);
|
||||
|
||||
protected override async Task<double?> ComputeValueAsync(PerformanceCacheLookup lookup, CancellationToken token = default)
|
||||
protected override async Task<PerformanceAttributes> ComputeValueAsync(PerformanceCacheLookup lookup, CancellationToken token = default)
|
||||
{
|
||||
var score = lookup.ScoreInfo;
|
||||
|
||||
@ -44,7 +45,7 @@ namespace osu.Game.Scoring
|
||||
|
||||
var calculator = score.Ruleset.CreateInstance().CreatePerformanceCalculator(attributes.Value.Attributes, score);
|
||||
|
||||
return calculator?.Calculate().Total;
|
||||
return calculator?.Calculate();
|
||||
}
|
||||
|
||||
public readonly struct PerformanceCacheLookup
|
||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics
|
||||
else
|
||||
{
|
||||
performanceCache.CalculatePerformanceAsync(score, cancellationTokenSource.Token)
|
||||
.ContinueWith(t => Schedule(() => setPerformanceValue(t.GetResultSafely())), cancellationTokenSource.Token);
|
||||
.ContinueWith(t => Schedule(() => setPerformanceValue(t.GetResultSafely().Total)), cancellationTokenSource.Token);
|
||||
}
|
||||
}
|
||||
|
||||
|
247
osu.Game/Screens/Ranking/Statistics/PerformanceBreakdownChart.cs
Normal file
247
osu.Game/Screens/Ranking/Statistics/PerformanceBreakdownChart.cs
Normal file
@ -0,0 +1,247 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Scoring;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Ranking.Statistics
|
||||
{
|
||||
public class PerformanceBreakdownChart : Container
|
||||
{
|
||||
private readonly ScoreInfo score;
|
||||
private readonly IBeatmap playableBeatmap;
|
||||
|
||||
private Drawable spinner;
|
||||
private Drawable content;
|
||||
private GridContainer chart;
|
||||
private OsuSpriteText achievedPerformance;
|
||||
private OsuSpriteText maximumPerformance;
|
||||
|
||||
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
[Resolved]
|
||||
private ScorePerformanceCache performanceCache { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private BeatmapDifficultyCache difficultyCache { get; set; }
|
||||
|
||||
public PerformanceBreakdownChart(ScoreInfo score, IBeatmap playableBeatmap)
|
||||
{
|
||||
this.score = score;
|
||||
this.playableBeatmap = playableBeatmap;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new[]
|
||||
{
|
||||
spinner = new LoadingSpinner(true)
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre
|
||||
},
|
||||
content = new FillFlowContainer
|
||||
{
|
||||
Alpha = 0,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Width = 0.6f,
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Spacing = new Vector2(15, 15),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.8f,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 18),
|
||||
Text = "Achieved PP",
|
||||
Colour = Color4Extensions.FromHex("#66FFCC")
|
||||
},
|
||||
achievedPerformance = new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.CentreRight,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 18),
|
||||
Colour = Color4Extensions.FromHex("#66FFCC")
|
||||
}
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 18),
|
||||
Text = "Maximum",
|
||||
Colour = OsuColour.Gray(0.7f)
|
||||
},
|
||||
maximumPerformance = new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 18),
|
||||
Colour = OsuColour.Gray(0.7f)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
chart = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
spinner.Show();
|
||||
|
||||
new PerformanceBreakdownCalculator(playableBeatmap, difficultyCache, performanceCache)
|
||||
.CalculateAsync(score, cancellationTokenSource.Token)
|
||||
.ContinueWith(t => Schedule(() => setPerformanceValue(t.GetResultSafely())));
|
||||
}
|
||||
|
||||
private void setPerformanceValue(PerformanceBreakdown breakdown)
|
||||
{
|
||||
spinner.Hide();
|
||||
content.FadeIn(200);
|
||||
|
||||
var displayAttributes = breakdown.Performance.GetAttributesForDisplay();
|
||||
var perfectDisplayAttributes = breakdown.PerfectPerformance.GetAttributesForDisplay();
|
||||
|
||||
setTotalValues(
|
||||
displayAttributes.First(a => a.PropertyName == nameof(PerformanceAttributes.Total)),
|
||||
perfectDisplayAttributes.First(a => a.PropertyName == nameof(PerformanceAttributes.Total))
|
||||
);
|
||||
|
||||
var rowDimensions = new List<Dimension>();
|
||||
var rows = new List<Drawable[]>();
|
||||
|
||||
foreach (PerformanceDisplayAttribute attr in displayAttributes)
|
||||
{
|
||||
if (attr.PropertyName == nameof(PerformanceAttributes.Total)) continue;
|
||||
|
||||
var row = createAttributeRow(attr, perfectDisplayAttributes.First(a => a.PropertyName == attr.PropertyName));
|
||||
|
||||
if (row != null)
|
||||
{
|
||||
rows.Add(row);
|
||||
rowDimensions.Add(new Dimension(GridSizeMode.AutoSize));
|
||||
}
|
||||
}
|
||||
|
||||
chart.RowDimensions = rowDimensions.ToArray();
|
||||
chart.Content = rows.ToArray();
|
||||
}
|
||||
|
||||
private void setTotalValues(PerformanceDisplayAttribute attribute, PerformanceDisplayAttribute perfectAttribute)
|
||||
{
|
||||
achievedPerformance.Text = Math.Round(attribute.Value, MidpointRounding.AwayFromZero).ToLocalisableString();
|
||||
maximumPerformance.Text = Math.Round(perfectAttribute.Value, MidpointRounding.AwayFromZero).ToLocalisableString();
|
||||
}
|
||||
|
||||
[CanBeNull]
|
||||
private Drawable[] createAttributeRow(PerformanceDisplayAttribute attribute, PerformanceDisplayAttribute perfectAttribute)
|
||||
{
|
||||
// Don't display the attribute if its maximum is 0
|
||||
// For example, flashlight bonus would be zero if flashlight mod isn't on
|
||||
if (Precision.AlmostEquals(perfectAttribute.Value, 0f))
|
||||
return null;
|
||||
|
||||
float percentage = (float)(attribute.Value / perfectAttribute.Value);
|
||||
|
||||
return new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular),
|
||||
Text = attribute.DisplayName,
|
||||
Colour = Colour4.White
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = 10, Right = 10 },
|
||||
Child = new Bar
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
CornerRadius = 2.5f,
|
||||
Masking = true,
|
||||
Height = 5,
|
||||
BackgroundColour = Color4.White.Opacity(0.5f),
|
||||
AccentColour = Color4Extensions.FromHex("#66FFCC"),
|
||||
Length = percentage
|
||||
}
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Origin = Anchor.CentreRight,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.SemiBold),
|
||||
Text = percentage.ToLocalisableString("0%"),
|
||||
Colour = Colour4.White
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
cancellationTokenSource?.Cancel();
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user