mirror of
https://github.com/ppy/osu-tools.git
synced 2025-06-07 23:07:01 +09:00
Merge branch 'master' into fix_simulate_mania
This commit is contained in:
commit
70d5baa375
13 changed files with 529 additions and 133 deletions
|
@ -47,6 +47,7 @@ namespace PerformanceCalculator.Difficulty
|
|||
mod.RequiresConfiguration,
|
||||
mod.UserPlayable,
|
||||
mod.ValidForMultiplayer,
|
||||
mod.ValidForFreestyleAsRequiredMod,
|
||||
mod.ValidForMultiplayerAsFreeMod,
|
||||
mod.AlwaysValidForSubmission,
|
||||
});
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Alba.CsConsoleFormat" Version="1.0.0" />
|
||||
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" />
|
||||
<PackageReference Include="ppy.osu.Game" Version="2025.227.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Osu" Version="2025.227.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Taiko" Version="2025.227.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Catch" Version="2025.227.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Mania" Version="2025.227.0" />
|
||||
<PackageReference Include="ppy.osu.Game" Version="2025.530.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Osu" Version="2025.530.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Taiko" Version="2025.530.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Catch" Version="2025.530.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Mania" Version="2025.530.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Alba.CsConsoleFormat;
|
||||
using JetBrains.Annotations;
|
||||
using McMaster.Extensions.CommandLineUtils;
|
||||
|
@ -27,6 +28,9 @@ namespace PerformanceCalculator.Profile
|
|||
[AllowedValues("0", "1", "2", "3")]
|
||||
public int? Ruleset { get; }
|
||||
|
||||
private const int max_api_scores = 200;
|
||||
private const int max_api_scores_in_one_query = 100;
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
var displayPlays = new List<UserPlayInfo>();
|
||||
|
@ -39,22 +43,28 @@ namespace PerformanceCalculator.Profile
|
|||
|
||||
Console.WriteLine("Getting user top scores...");
|
||||
|
||||
foreach (var play in GetJsonFromApi<List<SoloScoreInfo>>($"users/{userData.Id}/scores/best?mode={rulesetApiName}&limit=100"))
|
||||
var apiScores = new List<SoloScoreInfo>();
|
||||
|
||||
for (int i = 0; i < max_api_scores; i += max_api_scores_in_one_query)
|
||||
{
|
||||
apiScores.AddRange(GetJsonFromApi<List<SoloScoreInfo>>($"users/{userData.Id}/scores/best?mode={rulesetApiName}&limit={max_api_scores_in_one_query}&offset={i}"));
|
||||
Thread.Sleep(200);
|
||||
}
|
||||
|
||||
foreach (var play in apiScores)
|
||||
{
|
||||
var working = ProcessorWorkingBeatmap.FromFileOrId(play.BeatmapID.ToString());
|
||||
|
||||
Mod[] mods = play.Mods.Select(x => x.ToMod(ruleset)).ToArray();
|
||||
|
||||
var scoreInfo = play.ToScoreInfo(mods);
|
||||
var scoreInfo = play.ToScoreInfo(mods, working.BeatmapInfo);
|
||||
scoreInfo.Ruleset = ruleset.RulesetInfo;
|
||||
|
||||
var score = new ProcessorScoreDecoder(working).Parse(scoreInfo);
|
||||
|
||||
var difficultyCalculator = ruleset.CreateDifficultyCalculator(working);
|
||||
var difficultyAttributes = difficultyCalculator.Calculate(scoreInfo.Mods);
|
||||
var performanceCalculator = ruleset.CreatePerformanceCalculator();
|
||||
|
||||
var ppAttributes = performanceCalculator?.Calculate(score.ScoreInfo, difficultyAttributes);
|
||||
var ppAttributes = performanceCalculator?.Calculate(scoreInfo, difficultyAttributes);
|
||||
var thisPlay = new UserPlayInfo
|
||||
{
|
||||
Beatmap = working.BeatmapInfo,
|
||||
|
|
|
@ -40,6 +40,10 @@ namespace PerformanceCalculator.Simulate
|
|||
[Option(Template = "-X|--misses <misses>", Description = "Number of misses. Defaults to 0.")]
|
||||
public int Misses { get; }
|
||||
|
||||
[UsedImplicitly]
|
||||
[Option(Template = "-l|--legacy-total-score <score>", Description = "Amount of legacy total score.")]
|
||||
public long? LegacyTotalScore { get; }
|
||||
|
||||
//
|
||||
// Options implemented in the ruleset-specific commands
|
||||
// -> Catch renames Mehs/Goods to (tiny-)droplets
|
||||
|
@ -73,6 +77,7 @@ namespace PerformanceCalculator.Simulate
|
|||
Accuracy = GetAccuracy(beatmap, statistics, mods),
|
||||
MaxCombo = Combo ?? (int)Math.Round(PercentCombo / 100 * beatmapMaxCombo),
|
||||
Statistics = statistics,
|
||||
LegacyTotalScore = LegacyTotalScore,
|
||||
Mods = mods
|
||||
};
|
||||
|
||||
|
|
|
@ -2,21 +2,26 @@
|
|||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
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.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Utils;
|
||||
using osuTK;
|
||||
|
@ -24,7 +29,7 @@ using PerformanceCalculatorGUI.Components.TextBoxes;
|
|||
|
||||
namespace PerformanceCalculatorGUI.Components
|
||||
{
|
||||
public partial class BeatmapCard : OsuClickableContainer
|
||||
public partial class BeatmapCard : OsuClickableContainer, IHasCustomTooltip<ProcessorWorkingBeatmap>
|
||||
{
|
||||
private readonly ProcessorWorkingBeatmap beatmap;
|
||||
|
||||
|
@ -40,6 +45,10 @@ namespace PerformanceCalculatorGUI.Components
|
|||
[Resolved]
|
||||
private Bindable<IReadOnlyList<Mod>> mods { get; set; }
|
||||
|
||||
public ITooltip<ProcessorWorkingBeatmap> GetCustomTooltip() => new BeatmapCardTooltip(colourProvider);
|
||||
public ProcessorWorkingBeatmap TooltipContent { get; }
|
||||
|
||||
private ModSettingChangeTracker modSettingChangeTracker;
|
||||
private OsuSpriteText bpmText = null!;
|
||||
|
||||
public BeatmapCard(ProcessorWorkingBeatmap beatmap)
|
||||
|
@ -49,6 +58,7 @@ namespace PerformanceCalculatorGUI.Components
|
|||
RelativeSizeAxes = Axes.X;
|
||||
Height = 40;
|
||||
CornerRadius = ExtendedLabelledTextBox.CORNER_RADIUS;
|
||||
TooltipContent = beatmap;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -115,7 +125,13 @@ namespace PerformanceCalculatorGUI.Components
|
|||
|
||||
Action = () => { host.OpenUrlExternally($"https://osu.ppy.sh/beatmaps/{beatmap.BeatmapInfo.OnlineID}"); };
|
||||
|
||||
mods.BindValueChanged(_ => updateBpm());
|
||||
mods.BindValueChanged(_ =>
|
||||
{
|
||||
modSettingChangeTracker?.Dispose();
|
||||
modSettingChangeTracker = new ModSettingChangeTracker(mods.Value);
|
||||
modSettingChangeTracker.SettingChanged += _ => updateBpm();
|
||||
updateBpm();
|
||||
}, true);
|
||||
|
||||
updateBpm();
|
||||
}
|
||||
|
@ -146,5 +162,146 @@ namespace PerformanceCalculatorGUI.Components
|
|||
|
||||
bpmText.Text = labelText;
|
||||
}
|
||||
|
||||
public partial class BeatmapCardTooltip : VisibilityContainer, ITooltip<ProcessorWorkingBeatmap>
|
||||
{
|
||||
public BeatmapCardTooltip(OverlayColourProvider colourProvider)
|
||||
{
|
||||
this.colourProvider = colourProvider;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Masking = true;
|
||||
CornerRadius = 8;
|
||||
}
|
||||
|
||||
protected override void PopIn() => this.FadeIn(150, Easing.OutQuint);
|
||||
protected override void PopOut() => this.Delay(150).FadeOut(500, Easing.OutQuint);
|
||||
|
||||
public void Move(Vector2 pos) => Position = pos;
|
||||
|
||||
private ProcessorWorkingBeatmap beatmap;
|
||||
|
||||
private VerticalAttributeDisplay keyCountDisplay = null!;
|
||||
private VerticalAttributeDisplay circleSizeDisplay = null!;
|
||||
private VerticalAttributeDisplay drainRateDisplay = null!;
|
||||
private VerticalAttributeDisplay approachRateDisplay = null!;
|
||||
private VerticalAttributeDisplay overallDifficultyDisplay = null!;
|
||||
|
||||
[Resolved]
|
||||
private Bindable<IReadOnlyList<Mod>> mods { get; set; }
|
||||
|
||||
private readonly OverlayColourProvider colourProvider;
|
||||
|
||||
private ModSettingChangeTracker modSettingChangeTracker;
|
||||
|
||||
[Resolved]
|
||||
private IBindable<RulesetInfo> ruleset { get; set; }
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
mods.BindValueChanged(_ =>
|
||||
{
|
||||
modSettingChangeTracker?.Dispose();
|
||||
modSettingChangeTracker = new ModSettingChangeTracker(mods.Value);
|
||||
modSettingChangeTracker.SettingChanged += _ => updateValues();
|
||||
updateValues();
|
||||
}, true);
|
||||
|
||||
ruleset.BindValueChanged(_ => updateValues());
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e) => true;
|
||||
|
||||
protected override bool OnClick(ClickEvent e) => true;
|
||||
|
||||
private void updateValues() => Scheduler.AddOnce(() =>
|
||||
{
|
||||
if (beatmap?.BeatmapInfo == null)
|
||||
return;
|
||||
|
||||
double rate = ModUtils.CalculateRateWithMods(mods.Value);
|
||||
|
||||
BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(beatmap.BeatmapInfo.Difficulty);
|
||||
BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(originalDifficulty);
|
||||
|
||||
foreach (var mod in mods.Value.OfType<IApplicableToDifficulty>())
|
||||
mod.ApplyToDifficulty(adjustedDifficulty);
|
||||
|
||||
Ruleset rulesetInstance = ruleset.Value.CreateInstance();
|
||||
adjustedDifficulty = rulesetInstance.GetRateAdjustedDisplayDifficulty(adjustedDifficulty, rate);
|
||||
|
||||
if (ruleset.Value.OnlineID >= 0)
|
||||
{
|
||||
if (ruleset.Value.ShortName is "osu" or "fruits")
|
||||
{
|
||||
circleSizeDisplay.Show();
|
||||
circleSizeDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.CircleSize, adjustedDifficulty.CircleSize);
|
||||
circleSizeDisplay.Current.Value = adjustedDifficulty.CircleSize;
|
||||
|
||||
approachRateDisplay.Show();
|
||||
approachRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate);
|
||||
approachRateDisplay.Current.Value = adjustedDifficulty.ApproachRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
circleSizeDisplay.Hide();
|
||||
approachRateDisplay.Hide();
|
||||
}
|
||||
|
||||
if (ruleset.Value.ShortName == "mania")
|
||||
{
|
||||
ILegacyRuleset legacyRuleset = (ILegacyRuleset)ruleset.Value.CreateInstance();
|
||||
int keyCount = legacyRuleset.GetKeyCount(beatmap.BeatmapInfo, mods.Value);
|
||||
int keyCountOriginal = legacyRuleset.GetKeyCount(beatmap.BeatmapInfo, []);
|
||||
|
||||
keyCountDisplay.Show();
|
||||
keyCountDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(keyCountOriginal, keyCount);
|
||||
keyCountDisplay.Current.Value = keyCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyCountDisplay.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
drainRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.DrainRate, adjustedDifficulty.DrainRate);
|
||||
overallDifficultyDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty);
|
||||
|
||||
drainRateDisplay.Current.Value = adjustedDifficulty.DrainRate;
|
||||
overallDifficultyDisplay.Current.Value = adjustedDifficulty.OverallDifficulty;
|
||||
});
|
||||
|
||||
public void SetContent(ProcessorWorkingBeatmap content)
|
||||
{
|
||||
if (content == beatmap && Children.Any())
|
||||
return;
|
||||
|
||||
beatmap = content;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background6
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Padding = new MarginPadding(8),
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
keyCountDisplay = new VerticalAttributeDisplay("Keys") { AutoSizeAxes = Axes.Both, Alpha = 0 },
|
||||
circleSizeDisplay = new VerticalAttributeDisplay("CS") { AutoSizeAxes = Axes.Both, Alpha = 0 },
|
||||
drainRateDisplay = new VerticalAttributeDisplay("HP") { AutoSizeAxes = Axes.Both },
|
||||
overallDifficultyDisplay = new VerticalAttributeDisplay("OD") { AutoSizeAxes = Axes.Both },
|
||||
approachRateDisplay = new VerticalAttributeDisplay("AR") { AutoSizeAxes = Axes.Both, Alpha = 0 },
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ using osu.Game.Rulesets.Difficulty;
|
|||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Utils;
|
||||
using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
using PerformanceCalculatorGUI.Components.TextBoxes;
|
||||
|
||||
|
@ -74,6 +75,7 @@ namespace PerformanceCalculatorGUI.Components
|
|||
public partial class ExtendedProfileScore : CompositeDrawable
|
||||
{
|
||||
private const int height = 35;
|
||||
private const int avatar_size = 35;
|
||||
private const int performance_width = 100;
|
||||
private const int rank_difference_width = 35;
|
||||
private const int small_text_font_size = 11;
|
||||
|
@ -82,6 +84,8 @@ namespace PerformanceCalculatorGUI.Components
|
|||
|
||||
public readonly ExtendedScore Score;
|
||||
|
||||
public readonly bool ShowAvatar;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
|
@ -90,17 +94,20 @@ namespace PerformanceCalculatorGUI.Components
|
|||
|
||||
private OsuSpriteText positionChangeText;
|
||||
|
||||
public ExtendedProfileScore(ExtendedScore score)
|
||||
public ExtendedProfileScore(ExtendedScore score, bool showAvatar = false)
|
||||
{
|
||||
Score = score;
|
||||
ShowAvatar = showAvatar;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
private void load(GameHost host, RulesetStore rulesets)
|
||||
{
|
||||
int avatarPadding = ShowAvatar ? avatar_size : 0;
|
||||
|
||||
AddInternal(new ExtendedProfileItemContainer
|
||||
{
|
||||
OnHoverAction = () =>
|
||||
|
@ -111,8 +118,17 @@ namespace PerformanceCalculatorGUI.Components
|
|||
{
|
||||
positionChangeText.Text = $"{Score.PositionChange.Value:+0;-0;-}";
|
||||
},
|
||||
Children = new Drawable[]
|
||||
Children = new[]
|
||||
{
|
||||
ShowAvatar
|
||||
? new ClickableAvatar(Score.SoloScore.User, true)
|
||||
{
|
||||
Masking = true,
|
||||
CornerRadius = ExtendedLabelledTextBox.CORNER_RADIUS,
|
||||
Size = new Vector2(avatar_size),
|
||||
Action = () => { host.OpenUrlExternally($"https://osu.ppy.sh/users/{Score.SoloScore.User?.Id}"); }
|
||||
}
|
||||
: Empty(),
|
||||
new Container
|
||||
{
|
||||
Name = "Rank difference",
|
||||
|
@ -120,6 +136,7 @@ namespace PerformanceCalculatorGUI.Components
|
|||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Width = rank_difference_width,
|
||||
Margin = new MarginPadding { Left = avatarPadding },
|
||||
Child = positionChangeText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
|
@ -132,7 +149,7 @@ namespace PerformanceCalculatorGUI.Components
|
|||
{
|
||||
Name = "Score info",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = rank_difference_width, Right = performance_width },
|
||||
Padding = new MarginPadding { Left = rank_difference_width + avatarPadding, Right = performance_width },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
// 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.Color4Extensions;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace PerformanceCalculatorGUI.Components.TextBoxes
|
||||
{
|
||||
public partial class ReadonlyOsuTextBox : OsuTextBox
|
||||
{
|
||||
private readonly string text;
|
||||
private readonly bool hasBackground;
|
||||
|
||||
public ReadonlyOsuTextBox(string text, bool hasBackground = true)
|
||||
{
|
||||
this.text = text;
|
||||
this.hasBackground = hasBackground;
|
||||
|
||||
Text = text;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
if (!hasBackground)
|
||||
BackgroundUnfocused = BackgroundUnfocused.Opacity(0);
|
||||
|
||||
base.LoadComplete();
|
||||
}
|
||||
|
||||
protected override void OnUserTextAdded(string added)
|
||||
{
|
||||
NotifyInputError();
|
||||
Text = text;
|
||||
}
|
||||
|
||||
protected override void OnUserTextRemoved(string removed)
|
||||
{
|
||||
NotifyInputError();
|
||||
Text = text;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,10 +6,10 @@
|
|||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game" Version="2025.227.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Osu" Version="2025.227.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Taiko" Version="2025.227.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Catch" Version="2025.227.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Mania" Version="2025.227.0" />
|
||||
<PackageReference Include="ppy.osu.Game" Version="2025.530.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Osu" Version="2025.530.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Taiko" Version="2025.530.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Catch" Version="2025.530.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Rulesets.Mania" Version="2025.530.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace PerformanceCalculatorGUI
|
|||
public Score Parse(ScoreInfo scoreInfo)
|
||||
{
|
||||
var score = new Score { ScoreInfo = scoreInfo };
|
||||
score.ScoreInfo.LegacyTotalScore = score.ScoreInfo.TotalScore;
|
||||
score.ScoreInfo.LegacyTotalScore ??= score.ScoreInfo.TotalScore;
|
||||
PopulateMaximumStatistics(score.ScoreInfo, beatmap);
|
||||
StandardisedScoreMigrationTools.UpdateFromLegacy(score.ScoreInfo, beatmap);
|
||||
return score;
|
||||
|
|
|
@ -26,6 +26,7 @@ using osu.Game.Screens.Edit;
|
|||
using osu.Game.Screens.Edit.Components;
|
||||
using osu.Game.Screens.Edit.Components.Timelines.Summary;
|
||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace PerformanceCalculatorGUI.Screens.ObjectInspection
|
||||
|
@ -133,7 +134,7 @@ namespace PerformanceCalculatorGUI.Screens.ObjectInspection
|
|||
timeline = new Timeline(new TimelineBlueprintContainer())
|
||||
}
|
||||
},
|
||||
rulesetContainer = new Container
|
||||
rulesetContainer = new RulesetSkinProvidingContainer(rulesetInstance, playableBeatmap, null)
|
||||
{
|
||||
Origin = Anchor.TopRight,
|
||||
Anchor = Anchor.TopRight,
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
|
||||
private StatefulButton calculationButton;
|
||||
private SwitchButton includePinnedCheckbox;
|
||||
private SwitchButton onlyDisplayBestCheckbox;
|
||||
private VerboseLoadingLayer loadingLayer;
|
||||
|
||||
private GridContainer layout;
|
||||
|
@ -48,7 +49,7 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
private Container userPanelContainer;
|
||||
private UserCard userPanel;
|
||||
|
||||
private string currentUser;
|
||||
private string[] currentUsers = Array.Empty<string>();
|
||||
|
||||
private CancellationTokenSource calculationCancellatonToken;
|
||||
|
||||
|
@ -73,6 +74,8 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
public override bool ShouldShowConfirmationDialogOnSwitch => false;
|
||||
|
||||
private const float username_container_height = 40;
|
||||
private const int max_api_scores = 200;
|
||||
private const int max_api_scores_in_one_query = 100;
|
||||
|
||||
public ProfileScreen()
|
||||
{
|
||||
|
@ -121,15 +124,15 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Anchor = Anchor.TopLeft,
|
||||
Label = "Username",
|
||||
PlaceholderText = "peppy",
|
||||
Label = "Username(s)",
|
||||
PlaceholderText = "peppy, rloseise, peppy2",
|
||||
CommitOnFocusLoss = false
|
||||
},
|
||||
calculationButton = new StatefulButton("Start calculation")
|
||||
{
|
||||
Width = 150,
|
||||
Height = username_container_height,
|
||||
Action = () => { calculateProfile(usernameTextBox.Current.Value); }
|
||||
Action = () => { calculateProfiles(usernameTextBox.Current.Value.Split(", ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,6 +175,20 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
Font = OsuFont.Torus.With(weight: FontWeight.SemiBold, size: 14),
|
||||
UseFullGlyphHeight = false,
|
||||
Text = "Include pinned scores"
|
||||
},
|
||||
onlyDisplayBestCheckbox = new SwitchButton
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Current = { Value = true },
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.Torus.With(weight: FontWeight.SemiBold, size: 14),
|
||||
UseFullGlyphHeight = false,
|
||||
Text = "Only display best score on each beatmap"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -207,17 +224,20 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
}
|
||||
};
|
||||
|
||||
usernameTextBox.OnCommit += (_, _) => { calculateProfile(usernameTextBox.Current.Value); };
|
||||
usernameTextBox.OnCommit += (_, _) => { calculateProfiles(usernameTextBox.Current.Value.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); };
|
||||
sorting.ValueChanged += e => { updateSorting(e.NewValue); };
|
||||
includePinnedCheckbox.Current.ValueChanged += e => { calculateProfile(currentUser); };
|
||||
includePinnedCheckbox.Current.ValueChanged += e => { calculateProfiles(currentUsers); };
|
||||
onlyDisplayBestCheckbox.Current.ValueChanged += e => { calculateProfiles(currentUsers); };
|
||||
|
||||
if (RuntimeInfo.IsDesktop)
|
||||
HotReloadCallbackReceiver.CompilationFinished += _ => Schedule(() => { calculateProfile(currentUser); });
|
||||
HotReloadCallbackReceiver.CompilationFinished += _ => Schedule(() => { calculateProfiles(currentUsers); });
|
||||
}
|
||||
|
||||
private void calculateProfile(string username)
|
||||
private void calculateProfiles(string[] usernames)
|
||||
{
|
||||
if (string.IsNullOrEmpty(username))
|
||||
currentUsers = usernames.Distinct().ToArray();
|
||||
|
||||
if (usernames.Length < 1)
|
||||
{
|
||||
usernameTextBox.FlashColour(Color4.Red, 1);
|
||||
return;
|
||||
|
@ -236,22 +256,11 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
Schedule(() => loadingLayer.Text.Value = "Getting user data...");
|
||||
|
||||
var player = await apiManager.GetJsonFromApi<APIUser>($"users/{username}/{ruleset.Value.ShortName}").ConfigureAwait(false);
|
||||
|
||||
currentUser = player.Username;
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
if (userPanel != null)
|
||||
userPanelContainer.Remove(userPanel, true);
|
||||
|
||||
userPanelContainer.Add(userPanel = new UserCard(player)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X
|
||||
});
|
||||
|
||||
sortingTabControl.Alpha = 1.0f;
|
||||
sortingTabControl.Current.Value = ProfileSortCriteria.Local;
|
||||
|
||||
|
@ -268,16 +277,32 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
return;
|
||||
|
||||
var plays = new List<ExtendedScore>();
|
||||
|
||||
var players = new List<APIUser>();
|
||||
var rulesetInstance = ruleset.Value.CreateInstance();
|
||||
|
||||
foreach (string username in currentUsers)
|
||||
{
|
||||
try
|
||||
{
|
||||
Schedule(() => loadingLayer.Text.Value = $"Getting {username} user data...");
|
||||
|
||||
var player = await apiManager.GetJsonFromApi<APIUser>($"users/{username}/{ruleset.Value.ShortName}").ConfigureAwait(false);
|
||||
players.Add(player);
|
||||
|
||||
Schedule(() => loadingLayer.Text.Value = $"Calculating {player.Username} top scores...");
|
||||
|
||||
var apiScores = await apiManager.GetJsonFromApi<List<SoloScoreInfo>>($"users/{player.OnlineID}/scores/best?mode={ruleset.Value.ShortName}&limit=100").ConfigureAwait(false);
|
||||
var apiScores = new List<SoloScoreInfo>();
|
||||
|
||||
for (int i = 0; i < max_api_scores; i += max_api_scores_in_one_query)
|
||||
{
|
||||
apiScores.AddRange(await apiManager.GetJsonFromApi<List<SoloScoreInfo>>($"users/{player.OnlineID}/scores/best?mode={ruleset.Value.ShortName}&limit={max_api_scores_in_one_query}&offset={i}").ConfigureAwait(false));
|
||||
await Task.Delay(200, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (includePinnedCheckbox.Current.Value)
|
||||
{
|
||||
var pinnedScores = await apiManager.GetJsonFromApi<List<SoloScoreInfo>>($"users/{player.OnlineID}/scores/pinned?mode={ruleset.Value.ShortName}&limit=100").ConfigureAwait(false);
|
||||
var pinnedScores = await apiManager.GetJsonFromApi<List<SoloScoreInfo>>($"users/{player.OnlineID}/scores/pinned?mode={ruleset.Value.ShortName}&limit={max_api_scores_in_one_query}")
|
||||
.ConfigureAwait(false);
|
||||
apiScores = apiScores.Concat(pinnedScores.Where(p => !apiScores.Any(b => b.ID == p.ID)).ToArray()).ToList();
|
||||
}
|
||||
|
||||
|
@ -308,13 +333,51 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
|
||||
var extendedScore = new ExtendedScore(score, livePp, perfAttributes);
|
||||
plays.Add(extendedScore);
|
||||
|
||||
Schedule(() => scores.Add(new ExtendedProfileScore(extendedScore)));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(ex.ToString(), level: LogLevel.Error);
|
||||
notificationDisplay.Display(new Notification($"Failed to calculate {username}: {ex.Message}"));
|
||||
}
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
bool calculatingSingleProfile = players.Count == 1;
|
||||
|
||||
// Add user card if only calculating single profile
|
||||
if (calculatingSingleProfile)
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
userPanelContainer.Add(userPanel = new UserCard(players[0])
|
||||
{
|
||||
RelativeSizeAxes = Axes.X
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Filter plays if only displaying best score on each beatmap
|
||||
if (onlyDisplayBestCheckbox.Current.Value)
|
||||
{
|
||||
Schedule(() => loadingLayer.Text.Value = "Filtering plays");
|
||||
|
||||
var filteredPlays = new List<ExtendedScore>();
|
||||
|
||||
// List of all beatmap IDs in plays without duplicates
|
||||
var beatmapIDs = plays.Select(x => x.SoloScore.BeatmapID).Distinct().ToList();
|
||||
|
||||
foreach (int id in beatmapIDs)
|
||||
{
|
||||
var bestPlayOnBeatmap = plays.Where(x => x.SoloScore.BeatmapID == id).OrderByDescending(x => x.SoloScore.PP).First();
|
||||
filteredPlays.Add(bestPlayOnBeatmap);
|
||||
}
|
||||
|
||||
plays = filteredPlays;
|
||||
}
|
||||
|
||||
var localOrdered = plays.OrderByDescending(x => x.SoloScore.PP).ToList();
|
||||
var liveOrdered = plays.OrderByDescending(x => x.LivePP ?? 0).ToList();
|
||||
|
||||
|
@ -322,6 +385,8 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
{
|
||||
foreach (var play in plays)
|
||||
{
|
||||
scores.Add(new ExtendedProfileScore(play, !calculatingSingleProfile));
|
||||
|
||||
if (play.LivePP != null)
|
||||
{
|
||||
play.Position.Value = localOrdered.IndexOf(play) + 1;
|
||||
|
@ -330,6 +395,10 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
}
|
||||
});
|
||||
|
||||
if (calculatingSingleProfile)
|
||||
{
|
||||
var player = players.First();
|
||||
|
||||
decimal totalLocalPP = 0;
|
||||
for (int i = 0; i < localOrdered.Count; i++)
|
||||
totalLocalPP += (decimal)(Math.Pow(0.95, i) * (localOrdered[i].SoloScore.PP ?? 0));
|
||||
|
@ -353,6 +422,7 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
PlaycountPP = playcountBonusPP
|
||||
};
|
||||
});
|
||||
}
|
||||
}, token).ContinueWith(t =>
|
||||
{
|
||||
Logger.Log(t.Exception?.ToString(), level: LogLevel.Error);
|
||||
|
|
96
PerformanceCalculatorGUI/Screens/Simulate/AttributesTable.cs
Normal file
96
PerformanceCalculatorGUI/Screens/Simulate/AttributesTable.cs
Normal file
|
@ -0,0 +1,96 @@
|
|||
// 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 Humanizer;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using PerformanceCalculatorGUI.Components.TextBoxes;
|
||||
|
||||
namespace PerformanceCalculatorGUI.Screens.Simulate
|
||||
{
|
||||
public partial class AttributesTable : Container
|
||||
{
|
||||
public readonly Bindable<Dictionary<string, object>> Attributes = new Bindable<Dictionary<string, object>>();
|
||||
private const float row_height = 35;
|
||||
|
||||
private FillFlowContainer backgroundFlow;
|
||||
private GridContainer grid;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
CornerRadius = ExtendedLabelledTextBox.CORNER_RADIUS;
|
||||
Masking = true;
|
||||
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
backgroundFlow = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical
|
||||
},
|
||||
grid = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize), new Dimension() }
|
||||
}
|
||||
});
|
||||
|
||||
Attributes.BindValueChanged(onAttributesChanged);
|
||||
}
|
||||
|
||||
private void onAttributesChanged(ValueChangedEvent<Dictionary<string, object>> changedEvent)
|
||||
{
|
||||
grid.RowDimensions = Enumerable.Repeat(new Dimension(GridSizeMode.Absolute, row_height), changedEvent.NewValue.Count).ToArray();
|
||||
grid.Content = changedEvent.NewValue.Select(s => createRowContent(s.Key, s.Value)).ToArray();
|
||||
|
||||
backgroundFlow.Children = changedEvent.NewValue.Select((_, i) => new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = row_height,
|
||||
Colour = colourProvider.Background4.Opacity(i % 2 == 0 ? 0.7f : 0.9f),
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
private Drawable[] createRowContent(string label, object value) => new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold),
|
||||
Text = label.Humanize().ToLowerInvariant(),
|
||||
Margin = new MarginPadding { Left = 15, Right = 10 },
|
||||
UseFullGlyphHeight = true
|
||||
},
|
||||
new ReadonlyOsuTextBox(FormattableString.Invariant($"{value:N2}"), false)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Height = 1,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
SelectAllOnFocus = true,
|
||||
FontSize = 18,
|
||||
CornerRadius = ExtendedLabelledTextBox.CORNER_RADIUS
|
||||
},
|
||||
}.ToArray();
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Humanizer;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
|
@ -40,6 +39,7 @@ using PerformanceCalculatorGUI.Components;
|
|||
using PerformanceCalculatorGUI.Components.TextBoxes;
|
||||
using PerformanceCalculatorGUI.Configuration;
|
||||
using PerformanceCalculatorGUI.Screens.ObjectInspection;
|
||||
using PerformanceCalculatorGUI.Screens.Simulate;
|
||||
|
||||
namespace PerformanceCalculatorGUI.Screens
|
||||
{
|
||||
|
@ -71,10 +71,10 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
private SwitchButton fullScoreDataSwitch;
|
||||
|
||||
private DifficultyAttributes difficultyAttributes;
|
||||
private FillFlowContainer difficultyAttributesContainer;
|
||||
private FillFlowContainer performanceAttributesContainer;
|
||||
private AttributesTable difficultyAttributesContainer;
|
||||
|
||||
private PerformanceCalculator performanceCalculator;
|
||||
private AttributesTable performanceAttributesContainer;
|
||||
|
||||
[Cached]
|
||||
private Bindable<DifficultyCalculator> difficultyCalculator = new Bindable<DifficultyCalculator>();
|
||||
|
@ -418,37 +418,23 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Margin = new MarginPadding { Left = 10f, Top = 5f, Bottom = 10.0f },
|
||||
Margin = new MarginPadding { Left = 10f, Vertical = 5f },
|
||||
Origin = Anchor.TopLeft,
|
||||
Height = 20,
|
||||
Text = "Difficulty Attributes"
|
||||
},
|
||||
difficultyAttributesContainer = new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Anchor = Anchor.TopLeft,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(0, 2f)
|
||||
},
|
||||
difficultyAttributesContainer = new AttributesTable(),
|
||||
new OsuSpriteText
|
||||
{
|
||||
Margin = new MarginPadding(10.0f),
|
||||
Margin = new MarginPadding { Left = 10f, Vertical = 5f },
|
||||
Origin = Anchor.TopLeft,
|
||||
Height = 20,
|
||||
Text = "Performance Attributes"
|
||||
},
|
||||
performanceAttributesContainer = new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Anchor = Anchor.TopLeft,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(0, 2f)
|
||||
},
|
||||
performanceAttributesContainer = new AttributesTable(),
|
||||
new OsuSpriteText
|
||||
{
|
||||
Margin = new MarginPadding(10.0f),
|
||||
Margin = new MarginPadding { Left = 10f, Vertical = 5f },
|
||||
Origin = Anchor.TopLeft,
|
||||
Height = 20,
|
||||
Text = "Strain graph (alt+scroll to zoom)"
|
||||
|
@ -582,6 +568,7 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
// recreate calculators to update DHOs
|
||||
createCalculators();
|
||||
|
||||
modSettingChangeTracker?.Dispose();
|
||||
modSettingChangeTracker = new ModSettingChangeTracker(mods.NewValue);
|
||||
modSettingChangeTracker.SettingChanged += m =>
|
||||
{
|
||||
|
@ -592,7 +579,7 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
updateMissesTextboxes();
|
||||
calculateDifficulty();
|
||||
calculatePerformance();
|
||||
}, 100);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
calculateDifficulty();
|
||||
|
@ -647,6 +634,7 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
resetCalculations();
|
||||
}
|
||||
|
||||
beatmapTitle.Clear();
|
||||
beatmapTitle.Add(new BeatmapCard(working));
|
||||
|
||||
loadBackground();
|
||||
|
@ -672,14 +660,7 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
try
|
||||
{
|
||||
difficultyAttributes = difficultyCalculator.Value.Calculate(appliedMods.Value);
|
||||
difficultyAttributesContainer.Children = AttributeConversion.ToDictionary(difficultyAttributes).Select(x =>
|
||||
new ExtendedLabelledTextBox
|
||||
{
|
||||
ReadOnly = true,
|
||||
Label = x.Key.Humanize().ToLowerInvariant(),
|
||||
Text = FormattableString.Invariant($"{x.Value:N2}")
|
||||
}
|
||||
).ToArray();
|
||||
difficultyAttributesContainer.Attributes.Value = AttributeConversion.ToDictionary(difficultyAttributes);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -752,17 +733,11 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
Statistics = statistics,
|
||||
Mods = appliedMods.Value.ToArray(),
|
||||
TotalScore = score,
|
||||
Ruleset = ruleset.Value
|
||||
Ruleset = ruleset.Value,
|
||||
LegacyTotalScore = legacyTotalScore,
|
||||
}, difficultyAttributes);
|
||||
|
||||
performanceAttributesContainer.Children = AttributeConversion.ToDictionary(ppAttributes).Select(x =>
|
||||
new ExtendedLabelledTextBox
|
||||
{
|
||||
ReadOnly = true,
|
||||
Label = x.Key.Humanize().ToLowerInvariant(),
|
||||
Text = FormattableString.Invariant($"{x.Value:N2}")
|
||||
}
|
||||
).ToArray();
|
||||
performanceAttributesContainer.Attributes.Value = AttributeConversion.ToDictionary(ppAttributes);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -920,7 +895,10 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
private void resetCalculations()
|
||||
{
|
||||
createCalculators();
|
||||
|
||||
resetMods();
|
||||
legacyTotalScore = null;
|
||||
|
||||
calculateDifficulty();
|
||||
calculatePerformance();
|
||||
populateScoreParams();
|
||||
|
@ -1011,6 +989,8 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
notificationDisplay.Display(new Notification(message));
|
||||
}
|
||||
|
||||
private long? legacyTotalScore;
|
||||
|
||||
private void populateSettingsFromScore(long scoreId)
|
||||
{
|
||||
if (scoreIdPopulateButton.State.Value == ButtonState.Loading)
|
||||
|
@ -1035,6 +1015,8 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
ruleset.Value = rulesets.GetRuleset(scoreInfo.RulesetID);
|
||||
appliedMods.Value = scoreInfo.Mods.Select(x => x.ToMod(ruleset.Value.CreateInstance())).ToList();
|
||||
|
||||
legacyTotalScore = scoreInfo.LegacyTotalScore;
|
||||
|
||||
fullScoreDataSwitch.Current.Value = true;
|
||||
|
||||
// TODO: this shouldn't be done in 2 lines
|
||||
|
@ -1062,6 +1044,21 @@ namespace PerformanceCalculatorGUI.Screens
|
|||
mehsTextBox.Text = mehs.ToString();
|
||||
}
|
||||
|
||||
if (ruleset.Value?.ShortName == "fruits")
|
||||
{
|
||||
if (scoreInfo.Statistics.TryGetValue(HitResult.LargeTickHit, out int largeTickHits))
|
||||
{
|
||||
goodsTextBox.Value.Value = largeTickHits;
|
||||
goodsTextBox.Text = largeTickHits.ToString();
|
||||
}
|
||||
|
||||
if (scoreInfo.Statistics.TryGetValue(HitResult.SmallTickHit, out int smallTickHits))
|
||||
{
|
||||
mehsTextBox.Value.Value = smallTickHits;
|
||||
mehsTextBox.Text = smallTickHits.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
if (scoreInfo.Statistics.TryGetValue(HitResult.LargeTickMiss, out int largeTickMisses))
|
||||
{
|
||||
largeTickMissesTextBox.Value.Value = largeTickMisses;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue