mirror of
https://github.com/ppy/osu-tools.git
synced 2025-06-09 09:35:15 +09:00
775 lines
36 KiB
C#
775 lines
36 KiB
C#
// 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;
|
|
using osu.Framework.Allocation;
|
|
using osu.Framework.Audio;
|
|
using osu.Framework.Bindables;
|
|
using osu.Framework.Graphics;
|
|
using osu.Framework.Graphics.Containers;
|
|
using osu.Framework.Graphics.Shapes;
|
|
using osu.Framework.Graphics.Sprites;
|
|
using osu.Framework.Graphics.Textures;
|
|
using osu.Framework.Input.Events;
|
|
using osu.Framework.Input.States;
|
|
using osu.Framework.Threading;
|
|
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.Graphics.UserInterfaceV2;
|
|
using osu.Game.Overlays;
|
|
using osu.Game.Overlays.Mods;
|
|
using osu.Game.Rulesets;
|
|
using osu.Game.Rulesets.Difficulty;
|
|
using osu.Game.Rulesets.Difficulty.Skills;
|
|
using osu.Game.Rulesets.Mods;
|
|
using osu.Game.Scoring;
|
|
using osu.Game.Screens.Play.HUD;
|
|
using osu.Game.Utils;
|
|
using osuTK;
|
|
using PerformanceCalculatorGUI.Components;
|
|
using PerformanceCalculatorGUI.Configuration;
|
|
using PerformanceCalculatorGUI.Screens.ObjectInspection;
|
|
|
|
namespace PerformanceCalculatorGUI.Screens
|
|
{
|
|
public class SimulateScreen : PerformanceCalculatorScreen
|
|
{
|
|
private ProcessorWorkingBeatmap working;
|
|
|
|
private UserModSelectOverlay userModsSelectOverlay;
|
|
|
|
private GridContainer beatmapImportContainer;
|
|
private LabelledTextBox beatmapFileTextBox;
|
|
private LabelledTextBox beatmapIdTextBox;
|
|
private SwitchButton beatmapImportTypeSwitch;
|
|
|
|
private LimitedLabelledNumberBox missesTextBox;
|
|
private LimitedLabelledNumberBox comboTextBox;
|
|
private LimitedLabelledNumberBox scoreTextBox;
|
|
|
|
private GridContainer accuracyContainer;
|
|
private LimitedLabelledFractionalNumberBox accuracyTextBox;
|
|
private LimitedLabelledNumberBox goodsTextBox;
|
|
private LimitedLabelledNumberBox mehsTextBox;
|
|
private SwitchButton fullScoreDataSwitch;
|
|
|
|
private DifficultyAttributes difficultyAttributes;
|
|
private FillFlowContainer difficultyAttributesContainer;
|
|
private FillFlowContainer performanceAttributesContainer;
|
|
|
|
private PerformanceCalculator performanceCalculator;
|
|
|
|
[Cached]
|
|
private Bindable<DifficultyCalculator> difficultyCalculator = new();
|
|
|
|
private FillFlowContainer beatmapDataContainer;
|
|
private OsuSpriteText beatmapTitle;
|
|
|
|
private ModDisplay modDisplay;
|
|
|
|
private StrainVisualizer strainVisualizer;
|
|
private Container strainVisualizerContainer;
|
|
|
|
private ObjectInspector objectInspector;
|
|
|
|
private BufferedContainer background;
|
|
|
|
[Resolved]
|
|
private AudioManager audio { get; set; }
|
|
|
|
[Resolved]
|
|
private Bindable<IReadOnlyList<Mod>> appliedMods { get; set; }
|
|
|
|
[Resolved]
|
|
private Bindable<RulesetInfo> ruleset { get; set; }
|
|
|
|
[Resolved]
|
|
private LargeTextureStore textures { get; set; }
|
|
|
|
[Resolved]
|
|
private SettingsManager configManager { get; set; }
|
|
|
|
[Cached]
|
|
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
|
|
|
public override bool ShouldShowConfirmationDialogOnSwitch => working != null;
|
|
|
|
private const int file_selection_container_height = 40;
|
|
private const int map_title_container_height = 20;
|
|
|
|
public SimulateScreen()
|
|
{
|
|
RelativeSizeAxes = Axes.Both;
|
|
}
|
|
|
|
[BackgroundDependencyLoader]
|
|
private void load()
|
|
{
|
|
InternalChildren = new Drawable[]
|
|
{
|
|
new GridContainer
|
|
{
|
|
RelativeSizeAxes = Axes.Both,
|
|
ColumnDimensions = new[] { new Dimension() },
|
|
RowDimensions = new[] { new Dimension(GridSizeMode.Absolute, file_selection_container_height), new Dimension(GridSizeMode.Absolute, map_title_container_height), new Dimension() },
|
|
Content = new[]
|
|
{
|
|
new Drawable[]
|
|
{
|
|
beatmapImportContainer = new GridContainer
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
AutoSizeAxes = Axes.Y,
|
|
ColumnDimensions = new[]
|
|
{
|
|
new Dimension(),
|
|
new Dimension(GridSizeMode.Absolute),
|
|
new Dimension(GridSizeMode.AutoSize)
|
|
},
|
|
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
|
|
Content = new[]
|
|
{
|
|
new Drawable[]
|
|
{
|
|
beatmapFileTextBox = new FileChooserLabelledTextBox(configManager.GetBindable<string>(Settings.DefaultPath), ".osu")
|
|
{
|
|
Label = "Beatmap File",
|
|
FixedLabelWidth = 120f,
|
|
PlaceholderText = "Click to select a beatmap file"
|
|
},
|
|
beatmapIdTextBox = new LimitedLabelledNumberBox
|
|
{
|
|
Label = "Beatmap ID",
|
|
FixedLabelWidth = 120f,
|
|
PlaceholderText = "Enter beatmap ID",
|
|
CommitOnFocusLoss = false
|
|
},
|
|
beatmapImportTypeSwitch = new SwitchButton
|
|
{
|
|
Width = 80,
|
|
Height = 40
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
new Drawable[]
|
|
{
|
|
new Container
|
|
{
|
|
Name = "Beatmap title",
|
|
RelativeSizeAxes = Axes.Both,
|
|
Children = new Drawable[]
|
|
{
|
|
beatmapTitle = new OsuSpriteText
|
|
{
|
|
Anchor = Anchor.Centre,
|
|
Origin = Anchor.Centre,
|
|
Height = map_title_container_height,
|
|
Text = "No beatmap loaded!"
|
|
},
|
|
}
|
|
}
|
|
},
|
|
new Drawable[]
|
|
{
|
|
beatmapDataContainer = new FillFlowContainer
|
|
{
|
|
Name = "Beatmap data",
|
|
RelativeSizeAxes = Axes.Both,
|
|
Direction = FillDirection.Horizontal,
|
|
Children = new Drawable[]
|
|
{
|
|
new OsuScrollContainer(Direction.Vertical)
|
|
{
|
|
Name = "Score params",
|
|
RelativeSizeAxes = Axes.Both,
|
|
Width = 0.5f,
|
|
Child = new FillFlowContainer
|
|
{
|
|
Padding = new MarginPadding(15.0f),
|
|
RelativeSizeAxes = Axes.X,
|
|
AutoSizeAxes = Axes.Y,
|
|
Direction = FillDirection.Vertical,
|
|
Spacing = new Vector2(0, 2f),
|
|
Children = new Drawable[]
|
|
{
|
|
new OsuSpriteText
|
|
{
|
|
Margin = new MarginPadding(10.0f),
|
|
Origin = Anchor.TopLeft,
|
|
Height = 20,
|
|
Text = "Score params"
|
|
},
|
|
accuracyContainer = new GridContainer
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
AutoSizeAxes = Axes.Y,
|
|
ColumnDimensions = new[]
|
|
{
|
|
new Dimension(),
|
|
new Dimension(GridSizeMode.Absolute),
|
|
new Dimension(GridSizeMode.Absolute),
|
|
new Dimension(GridSizeMode.AutoSize)
|
|
},
|
|
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
|
|
Content = new[]
|
|
{
|
|
new Drawable[]
|
|
{
|
|
accuracyTextBox = new LimitedLabelledFractionalNumberBox
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
Anchor = Anchor.TopLeft,
|
|
Label = "Accuracy",
|
|
PlaceholderText = "100",
|
|
MaxValue = 100.0,
|
|
MinValue = 0.0,
|
|
Value = { Value = 100.0 }
|
|
},
|
|
goodsTextBox = new LimitedLabelledNumberBox
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
Anchor = Anchor.TopLeft,
|
|
Label = "Goods",
|
|
PlaceholderText = "0",
|
|
MinValue = 0
|
|
},
|
|
mehsTextBox = new LimitedLabelledNumberBox
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
Anchor = Anchor.TopLeft,
|
|
Label = "Mehs",
|
|
PlaceholderText = "0",
|
|
MinValue = 0
|
|
},
|
|
fullScoreDataSwitch = new SwitchButton
|
|
{
|
|
Width = 80,
|
|
Height = 40
|
|
}
|
|
}
|
|
}
|
|
},
|
|
missesTextBox = new LimitedLabelledNumberBox
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
Anchor = Anchor.TopLeft,
|
|
Label = "Misses",
|
|
PlaceholderText = "0",
|
|
MinValue = 0
|
|
},
|
|
comboTextBox = new LimitedLabelledNumberBox
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
Anchor = Anchor.TopLeft,
|
|
Label = "Combo",
|
|
PlaceholderText = "0",
|
|
MinValue = 0
|
|
},
|
|
scoreTextBox = new LimitedLabelledNumberBox
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
Anchor = Anchor.TopLeft,
|
|
Label = "Score",
|
|
PlaceholderText = "1000000",
|
|
MinValue = 0,
|
|
MaxValue = 1000000,
|
|
Value = { Value = 1000000 }
|
|
},
|
|
new FillFlowContainer
|
|
{
|
|
Name = "Mods container",
|
|
Height = 40,
|
|
Direction = FillDirection.Horizontal,
|
|
RelativeSizeAxes = Axes.X,
|
|
Anchor = Anchor.TopLeft,
|
|
AutoSizeAxes = Axes.Y,
|
|
Children = new Drawable[]
|
|
{
|
|
new OsuButton
|
|
{
|
|
Width = 100,
|
|
Margin = new MarginPadding(5.0f),
|
|
Action = () => { userModsSelectOverlay.Show(); },
|
|
BackgroundColour = colourProvider.Background1,
|
|
Text = "Mods"
|
|
},
|
|
modDisplay = new ModDisplay()
|
|
}
|
|
},
|
|
new ScalingContainer(ScalingMode.Everything)
|
|
{
|
|
Name = "Mod selection overlay",
|
|
RelativeSizeAxes = Axes.X,
|
|
Height = 400,
|
|
Child = userModsSelectOverlay = new ExtendedUserModSelectOverlay
|
|
{
|
|
RelativeSizeAxes = Axes.Both,
|
|
//AutoSizeAxes = Axes.Y,
|
|
Anchor = Anchor.TopLeft,
|
|
Origin = Anchor.TopLeft,
|
|
IsValidMod = (mod) => mod.HasImplementation && ModUtils.FlattenMod(mod).All(m => m.UserPlayable),
|
|
SelectedMods = { BindTarget = appliedMods }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
new OsuScrollContainer(Direction.Vertical)
|
|
{
|
|
Name = "Difficulty calculation results",
|
|
RelativeSizeAxes = Axes.Both,
|
|
Width = 0.5f,
|
|
Child = new FillFlowContainer
|
|
{
|
|
Padding = new MarginPadding(15.0f),
|
|
RelativeSizeAxes = Axes.X,
|
|
AutoSizeAxes = Axes.Y,
|
|
Direction = FillDirection.Vertical,
|
|
Spacing = new Vector2(0, 5f),
|
|
Children = new Drawable[]
|
|
{
|
|
new OsuSpriteText
|
|
{
|
|
Margin = new MarginPadding(10.0f),
|
|
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)
|
|
},
|
|
new OsuSpriteText
|
|
{
|
|
Margin = new MarginPadding(10.0f),
|
|
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)
|
|
},
|
|
new OsuSpriteText
|
|
{
|
|
Margin = new MarginPadding(10.0f),
|
|
Origin = Anchor.TopLeft,
|
|
Height = 20,
|
|
Text = "Strain graph"
|
|
},
|
|
strainVisualizerContainer = new Container
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
Anchor = Anchor.TopLeft,
|
|
AutoSizeAxes = Axes.Y,
|
|
Child = strainVisualizer = new StrainVisualizer()
|
|
},
|
|
new OsuButton
|
|
{
|
|
Anchor = Anchor.TopCentre,
|
|
Origin = Anchor.TopCentre,
|
|
Width = 250,
|
|
BackgroundColour = colourProvider.Background1,
|
|
Text = "Inspect Object Difficulty Data",
|
|
Action = () =>
|
|
{
|
|
if (objectInspector is not null)
|
|
RemoveInternal(objectInspector);
|
|
|
|
AddInternal(objectInspector = new ObjectInspector(working)
|
|
{
|
|
RelativeSizeAxes = Axes.Both,
|
|
Anchor = Anchor.Centre,
|
|
Origin = Anchor.Centre,
|
|
Size = new Vector2(0.95f)
|
|
});
|
|
objectInspector.Show();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
beatmapDataContainer.Hide();
|
|
userModsSelectOverlay.Hide();
|
|
|
|
beatmapFileTextBox.Current.BindValueChanged(filePath => { changeBeatmap(filePath.NewValue); });
|
|
beatmapIdTextBox.OnCommit += (_, _) => { changeBeatmap(beatmapIdTextBox.Current.Value); };
|
|
|
|
beatmapImportTypeSwitch.Current.BindValueChanged(val =>
|
|
{
|
|
if (val.NewValue)
|
|
{
|
|
beatmapImportContainer.ColumnDimensions = new[]
|
|
{
|
|
new Dimension(GridSizeMode.Absolute),
|
|
new Dimension(),
|
|
new Dimension(GridSizeMode.AutoSize)
|
|
};
|
|
|
|
fixupTextBox(beatmapIdTextBox);
|
|
}
|
|
else
|
|
{
|
|
beatmapImportContainer.ColumnDimensions = new[]
|
|
{
|
|
new Dimension(),
|
|
new Dimension(GridSizeMode.Absolute),
|
|
new Dimension(GridSizeMode.AutoSize)
|
|
};
|
|
}
|
|
});
|
|
|
|
accuracyTextBox.Value.BindValueChanged(_ => calculatePerformance());
|
|
goodsTextBox.Value.BindValueChanged(_ => calculatePerformance());
|
|
mehsTextBox.Value.BindValueChanged(_ => calculatePerformance());
|
|
missesTextBox.Value.BindValueChanged(_ => calculatePerformance());
|
|
comboTextBox.Value.BindValueChanged(_ => calculatePerformance());
|
|
scoreTextBox.Value.BindValueChanged(_ => calculatePerformance());
|
|
|
|
fullScoreDataSwitch.Current.BindValueChanged(val => updateAccuracyParams(val.NewValue));
|
|
|
|
appliedMods.BindValueChanged(modsChanged);
|
|
modDisplay.Current.BindTo(appliedMods);
|
|
|
|
ruleset.BindValueChanged(_ =>
|
|
{
|
|
createCalculators();
|
|
appliedMods.Value = Array.Empty<Mod>();
|
|
updateAccuracyParams(fullScoreDataSwitch.Current.Value);
|
|
calculateDifficulty();
|
|
});
|
|
|
|
if (RuntimeInfo.IsDesktop)
|
|
HotReloadCallbackReceiver.CompilationFinished += _ => Schedule(calculateDifficulty);
|
|
}
|
|
|
|
protected override void Dispose(bool isDisposing)
|
|
{
|
|
modSettingChangeTracker?.Dispose();
|
|
appliedMods.Value = Array.Empty<Mod>();
|
|
|
|
base.Dispose(isDisposing);
|
|
}
|
|
|
|
private ModSettingChangeTracker modSettingChangeTracker;
|
|
private ScheduledDelegate debouncedStatisticsUpdate;
|
|
|
|
private void modsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
|
{
|
|
modSettingChangeTracker?.Dispose();
|
|
|
|
modSettingChangeTracker = new ModSettingChangeTracker(mods.NewValue);
|
|
modSettingChangeTracker.SettingChanged += m =>
|
|
{
|
|
debouncedStatisticsUpdate?.Cancel();
|
|
debouncedStatisticsUpdate = Scheduler.AddDelayed(calculateDifficulty, 100);
|
|
};
|
|
|
|
calculateDifficulty();
|
|
}
|
|
|
|
private void resetBeatmap(string reason)
|
|
{
|
|
working = null;
|
|
beatmapTitle.Text = reason;
|
|
appliedMods.Value = Array.Empty<Mod>();
|
|
beatmapDataContainer.Hide();
|
|
|
|
if (background is not null)
|
|
{
|
|
RemoveInternal(background);
|
|
}
|
|
}
|
|
|
|
private void changeBeatmap(string beatmap)
|
|
{
|
|
beatmapDataContainer.Hide();
|
|
|
|
if (string.IsNullOrEmpty(beatmap))
|
|
{
|
|
resetBeatmap("Empty beatmap path!");
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
working = ProcessorWorkingBeatmap.FromFileOrId(beatmap, audio, configManager.GetBindable<string>(Settings.CachePath).Value);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
// TODO: better error display
|
|
resetBeatmap(e.Message);
|
|
return;
|
|
}
|
|
|
|
if (working is null)
|
|
return;
|
|
|
|
if (!working.BeatmapInfo.Ruleset.Equals(ruleset.Value))
|
|
{
|
|
ruleset.Value = working.BeatmapInfo.Ruleset;
|
|
appliedMods.Value = Array.Empty<Mod>();
|
|
}
|
|
|
|
beatmapTitle.Text = $"[{ruleset.Value.Name}] {working.BeatmapInfo.GetDisplayTitle()}";
|
|
|
|
createCalculators();
|
|
|
|
if (background is not null)
|
|
{
|
|
RemoveInternal(background);
|
|
}
|
|
|
|
if (working.BeatmapInfo?.BeatmapSet?.OnlineID is not null)
|
|
{
|
|
LoadComponentAsync(background = new BufferedContainer
|
|
{
|
|
RelativeSizeAxes = Axes.Both,
|
|
Depth = 99,
|
|
BlurSigma = new Vector2(6),
|
|
Children = new Drawable[]
|
|
{
|
|
new Sprite
|
|
{
|
|
RelativeSizeAxes = Axes.Both,
|
|
Texture = textures.Get($"https://assets.ppy.sh/beatmaps/{working.BeatmapInfo.BeatmapSet.OnlineID}/covers/cover.jpg"),
|
|
Anchor = Anchor.Centre,
|
|
Origin = Anchor.Centre,
|
|
FillMode = FillMode.Fill
|
|
},
|
|
new Box
|
|
{
|
|
RelativeSizeAxes = Axes.Both,
|
|
Colour = OsuColour.Gray(0),
|
|
Alpha = 0.85f
|
|
},
|
|
}
|
|
}).ContinueWith(_ =>
|
|
{
|
|
Schedule(() =>
|
|
{
|
|
AddInternal(background);
|
|
});
|
|
});
|
|
}
|
|
|
|
calculateDifficulty();
|
|
|
|
beatmapDataContainer.Show();
|
|
}
|
|
|
|
private void createCalculators()
|
|
{
|
|
if (working is null)
|
|
return;
|
|
|
|
var rulesetInstance = ruleset.Value.CreateInstance();
|
|
difficultyCalculator.Value = RulesetHelper.GetExtendedDifficultyCalculator(ruleset.Value, working);
|
|
performanceCalculator = rulesetInstance.CreatePerformanceCalculator();
|
|
}
|
|
|
|
private void calculateDifficulty()
|
|
{
|
|
if (working == null || difficultyCalculator.Value == null)
|
|
return;
|
|
|
|
try
|
|
{
|
|
difficultyAttributes = difficultyCalculator.Value.Calculate(appliedMods.Value);
|
|
|
|
populateScoreParams();
|
|
|
|
difficultyAttributesContainer.Children = AttributeConversion.ToDictionary(difficultyAttributes).Select(x =>
|
|
new LabelledTextBox
|
|
{
|
|
ReadOnly = true,
|
|
Label = x.Key.Humanize().ToLowerInvariant(),
|
|
Text = FormattableString.Invariant($"{x.Value:N2}")
|
|
}
|
|
).ToArray();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
// TODO: better error display
|
|
resetBeatmap(e.Message);
|
|
return;
|
|
}
|
|
|
|
if (difficultyCalculator.Value is IExtendedDifficultyCalculator extendedDifficultyCalculator)
|
|
strainVisualizer.Skills.Value = extendedDifficultyCalculator.GetSkills();
|
|
else
|
|
strainVisualizer.Skills.Value = Array.Empty<Skill>();
|
|
|
|
calculatePerformance();
|
|
}
|
|
|
|
private void calculatePerformance()
|
|
{
|
|
if (working == null || difficultyAttributes == null)
|
|
return;
|
|
|
|
int? countGood = null, countMeh = null;
|
|
|
|
if (fullScoreDataSwitch.Current.Value)
|
|
{
|
|
countGood = goodsTextBox.Value.Value;
|
|
countMeh = mehsTextBox.Value.Value;
|
|
}
|
|
|
|
var score = RulesetHelper.AdjustManiaScore(scoreTextBox.Value.Value, appliedMods.Value);
|
|
|
|
try
|
|
{
|
|
var beatmap = working.GetPlayableBeatmap(ruleset.Value, appliedMods.Value);
|
|
|
|
var statistics = RulesetHelper.GenerateHitResultsForRuleset(ruleset.Value, accuracyTextBox.Value.Value / 100.0, beatmap, missesTextBox.Value.Value, countMeh, countGood);
|
|
|
|
var ppAttributes = performanceCalculator?.Calculate(new ScoreInfo(beatmap.BeatmapInfo, ruleset.Value)
|
|
{
|
|
Accuracy = RulesetHelper.GetAccuracyForRuleset(ruleset.Value, statistics),
|
|
MaxCombo = comboTextBox.Value.Value,
|
|
Statistics = statistics,
|
|
Mods = appliedMods.Value.ToArray(),
|
|
TotalScore = score,
|
|
Ruleset = ruleset.Value
|
|
}, difficultyAttributes);
|
|
|
|
performanceAttributesContainer.Children = AttributeConversion.ToDictionary(ppAttributes).Select(x =>
|
|
new LabelledTextBox
|
|
{
|
|
ReadOnly = true,
|
|
Label = x.Key.Humanize().ToLowerInvariant(),
|
|
Text = FormattableString.Invariant($"{x.Value:N2}")
|
|
}
|
|
).ToArray();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
// TODO: better error display
|
|
resetBeatmap(e.Message);
|
|
}
|
|
}
|
|
|
|
private void populateScoreParams()
|
|
{
|
|
accuracyContainer.Hide();
|
|
comboTextBox.Hide();
|
|
missesTextBox.Hide();
|
|
scoreTextBox.Hide();
|
|
|
|
// TODO: other rulesets?
|
|
|
|
if (ruleset.Value.ShortName == "osu" || ruleset.Value.ShortName == "taiko" || ruleset.Value.ShortName == "fruits")
|
|
{
|
|
updateAccuracyParams(fullScoreDataSwitch.Current.Value);
|
|
accuracyContainer.Show();
|
|
|
|
comboTextBox.PlaceholderText = difficultyAttributes.MaxCombo.ToString();
|
|
comboTextBox.Text = string.Empty;
|
|
comboTextBox.MaxValue = comboTextBox.Value.Value = difficultyAttributes.MaxCombo;
|
|
comboTextBox.Show();
|
|
|
|
missesTextBox.MaxValue = difficultyAttributes.MaxCombo;
|
|
missesTextBox.Text = string.Empty;
|
|
missesTextBox.Show();
|
|
}
|
|
else if (ruleset.Value.ShortName == "mania")
|
|
{
|
|
scoreTextBox.Text = string.Empty;
|
|
scoreTextBox.Show();
|
|
}
|
|
}
|
|
|
|
private void updateAccuracyParams(bool useFullScoreData)
|
|
{
|
|
goodsTextBox.Text = string.Empty;
|
|
mehsTextBox.Text = string.Empty;
|
|
accuracyTextBox.Text = string.Empty;
|
|
|
|
if (useFullScoreData)
|
|
{
|
|
goodsTextBox.Label = ruleset.Value.ShortName switch
|
|
{
|
|
"osu" => "100s",
|
|
"taiko" => "Goods",
|
|
"fruits" => "Droplets",
|
|
_ => ""
|
|
};
|
|
|
|
mehsTextBox.Label = ruleset.Value.ShortName switch
|
|
{
|
|
"osu" => "50s",
|
|
"fruits" => "Tiny Droplets",
|
|
_ => ""
|
|
};
|
|
|
|
accuracyContainer.ColumnDimensions = ruleset.Value.ShortName switch
|
|
{
|
|
"osu" or "fruits" =>
|
|
new[]
|
|
{
|
|
new Dimension(GridSizeMode.Absolute),
|
|
new Dimension(),
|
|
new Dimension(),
|
|
new Dimension(GridSizeMode.AutoSize)
|
|
},
|
|
"taiko" =>
|
|
new[]
|
|
{
|
|
new Dimension(GridSizeMode.Absolute),
|
|
new Dimension(),
|
|
new Dimension(GridSizeMode.Absolute),
|
|
new Dimension(GridSizeMode.AutoSize)
|
|
},
|
|
_ => Array.Empty<Dimension>()
|
|
};
|
|
|
|
fixupTextBox(goodsTextBox);
|
|
fixupTextBox(mehsTextBox);
|
|
}
|
|
else
|
|
{
|
|
accuracyContainer.ColumnDimensions = new[]
|
|
{
|
|
new Dimension(),
|
|
new Dimension(GridSizeMode.Absolute),
|
|
new Dimension(GridSizeMode.Absolute),
|
|
new Dimension(GridSizeMode.AutoSize)
|
|
};
|
|
}
|
|
}
|
|
|
|
private void fixupTextBox(LabelledTextBox textbox)
|
|
{
|
|
// This is a hack around TextBox's way of updating layout and positioning of text
|
|
// It can only be triggered by a couple of input events and there's no way to invalidate it from the outside
|
|
// See: https://github.com/ppy/osu-framework/blob/fd5615732033c5ea650aa5cabc8595883a2b63f5/osu.Framework/Graphics/UserInterface/TextBox.cs#L528
|
|
textbox.TriggerEvent(new FocusEvent(new InputState()));
|
|
}
|
|
}
|
|
}
|