diff --git a/PerformanceCalculator/Simulate/OsuSimulateCommand.cs b/PerformanceCalculator/Simulate/OsuSimulateCommand.cs index e42511b..10151ee 100644 --- a/PerformanceCalculator/Simulate/OsuSimulateCommand.cs +++ b/PerformanceCalculator/Simulate/OsuSimulateCommand.cs @@ -3,11 +3,13 @@ using System; using System.Collections.Generic; +using System.Linq; using JetBrains.Annotations; using McMaster.Extensions.CommandLineUtils; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; namespace PerformanceCalculator.Simulate @@ -31,6 +33,14 @@ namespace PerformanceCalculator.Simulate [Option(Template = "-C|--percent-combo ", Description = "Percentage of beatmap maximum combo achieved. Alternative to combo option. Enter as decimal 0-100.")] public override double PercentCombo { get; } = 100; + [UsedImplicitly] + [Option(Template = "-L|--large-tick-misses ", Description = "Number of large tick misses. Defaults to 0.")] + private int largeTickMisses { get; } + + [UsedImplicitly] + [Option(Template = "-S|--slider-tail-misses ", Description = "Number of slider tail misses. Defaults to 0.")] + private int sliderTailMisses { get; } + public override Ruleset Ruleset => new OsuRuleset(); protected override int GetMaxCombo(IBeatmap beatmap) => beatmap.GetMaxCombo(); @@ -116,6 +126,8 @@ namespace PerformanceCalculator.Simulate { HitResult.Great, countGreat }, { HitResult.Ok, countGood ?? 0 }, { HitResult.Meh, countMeh ?? 0 }, + { HitResult.LargeTickMiss, largeTickMisses }, + { HitResult.SliderTailHit, beatmap.HitObjects.Count(x => x is Slider) - sliderTailMisses }, { HitResult.Miss, countMiss } }; } diff --git a/PerformanceCalculatorGUI/RulesetHelper.cs b/PerformanceCalculatorGUI/RulesetHelper.cs index e32f8a6..1529d03 100644 --- a/PerformanceCalculatorGUI/RulesetHelper.cs +++ b/PerformanceCalculatorGUI/RulesetHelper.cs @@ -15,6 +15,7 @@ using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko; using osu.Game.Rulesets.Taiko.Objects; @@ -103,11 +104,11 @@ namespace PerformanceCalculatorGUI return (int)Math.Round(1000000 * scoreMultiplier); } - public static Dictionary GenerateHitResultsForRuleset(RulesetInfo ruleset, double accuracy, IBeatmap beatmap, int countMiss, int? countMeh, int? countGood, int? countLargeTickMisses) + public static Dictionary GenerateHitResultsForRuleset(RulesetInfo ruleset, double accuracy, IBeatmap beatmap, int countMiss, int? countMeh, int? countGood, int countLargeTickMisses, int countSliderTailMisses) { return ruleset.OnlineID switch { - 0 => generateOsuHitResults(accuracy, beatmap, countMiss, countMeh, countGood, countLargeTickMisses), + 0 => generateOsuHitResults(accuracy, beatmap, countMiss, countMeh, countGood, countLargeTickMisses, countSliderTailMisses), 1 => generateTaikoHitResults(accuracy, beatmap, countMiss, countGood), 2 => generateCatchHitResults(accuracy, beatmap, countMiss, countMeh, countGood), 3 => generateManiaHitResults(accuracy, beatmap, countMiss), @@ -115,7 +116,7 @@ namespace PerformanceCalculatorGUI }; } - private static Dictionary generateOsuHitResults(double accuracy, IBeatmap beatmap, int countMiss, int? countMeh, int? countGood, int? countLargeTickMisses) + private static Dictionary generateOsuHitResults(double accuracy, IBeatmap beatmap, int countMiss, int? countMeh, int? countGood, int countLargeTickMisses, int countSliderTailMisses) { int countGreat; @@ -191,12 +192,15 @@ namespace PerformanceCalculatorGUI countGreat = (int)(totalResultCount - countGood - countMeh - countMiss); } + int sliderTailHits = beatmap.HitObjects.Count(x => x is Slider) - countSliderTailMisses; + return new Dictionary { { HitResult.Great, countGreat }, { HitResult.Ok, countGood ?? 0 }, { HitResult.Meh, countMeh ?? 0 }, - { HitResult.LargeTickMiss, countLargeTickMisses ?? 0 }, + { HitResult.LargeTickMiss, countLargeTickMisses }, + { HitResult.SliderTailHit, sliderTailHits }, { HitResult.Miss, countMiss } }; } diff --git a/PerformanceCalculatorGUI/Screens/SimulateScreen.cs b/PerformanceCalculatorGUI/Screens/SimulateScreen.cs index 2d70fc1..786b6de 100644 --- a/PerformanceCalculatorGUI/Screens/SimulateScreen.cs +++ b/PerformanceCalculatorGUI/Screens/SimulateScreen.cs @@ -28,6 +28,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; @@ -51,8 +52,10 @@ namespace PerformanceCalculatorGUI.Screens private LabelledTextBox beatmapIdTextBox; private SwitchButton beatmapImportTypeSwitch; + private GridContainer missesContainer; private LimitedLabelledNumberBox missesTextBox; private LimitedLabelledNumberBox largeTickMissesTextBox; + private LimitedLabelledNumberBox sliderTailMissesTextBox; private LimitedLabelledNumberBox comboTextBox; private LimitedLabelledNumberBox scoreTextBox; @@ -263,12 +266,13 @@ namespace PerformanceCalculatorGUI.Screens PlaceholderText = "0", MinValue = 0 }, - new GridContainer + missesContainer = new GridContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, ColumnDimensions = new[] { + new Dimension(), new Dimension(), new Dimension() }, @@ -292,6 +296,14 @@ namespace PerformanceCalculatorGUI.Screens Label = "Large Tick Misses", PlaceholderText = "0", MinValue = 0 + }, + sliderTailMissesTextBox = new LimitedLabelledNumberBox + { + RelativeSizeAxes = Axes.X, + Anchor = Anchor.TopLeft, + Label = "Slider Tail Misses", + PlaceholderText = "0", + MinValue = 0 } } } @@ -472,6 +484,7 @@ namespace PerformanceCalculatorGUI.Screens mehsTextBox.Value.BindValueChanged(_ => debouncedCalculatePerformance()); missesTextBox.Value.BindValueChanged(_ => debouncedCalculatePerformance()); largeTickMissesTextBox.Value.BindValueChanged(_ => debouncedCalculatePerformance()); + sliderTailMissesTextBox.Value.BindValueChanged(_ => debouncedCalculatePerformance()); comboTextBox.Value.BindValueChanged(_ => debouncedCalculatePerformance()); scoreTextBox.Value.BindValueChanged(_ => debouncedCalculatePerformance()); @@ -511,14 +524,35 @@ namespace PerformanceCalculatorGUI.Screens private void modsChanged(ValueChangedEvent> mods) { + void updateMissesTextboxes() + { + if (ruleset.Value.ShortName == "osu") + { + // Large tick misses and slider tail misses are only relevant in PP if slider head accuracy exists + if (mods.NewValue.OfType().Any(m => m.NoSliderHeadAccuracy.Value)) + { + missesContainer.Content = new[] { new[] { missesTextBox } }; + missesContainer.ColumnDimensions = [new Dimension()]; + } + else + { + missesContainer.Content = new[] { new[] { missesTextBox, largeTickMissesTextBox, sliderTailMissesTextBox } }; + missesContainer.ColumnDimensions = [new Dimension(), new Dimension(), new Dimension()]; + } + } + } + modSettingChangeTracker?.Dispose(); if (working is null) return; + updateMissesTextboxes(); + modSettingChangeTracker = new ModSettingChangeTracker(mods.NewValue); modSettingChangeTracker.SettingChanged += m => { + updateMissesTextboxes(); debouncedStatisticsUpdate?.Cancel(); debouncedStatisticsUpdate = Scheduler.AddDelayed(() => { @@ -663,7 +697,7 @@ namespace PerformanceCalculatorGUI.Screens if (ruleset.Value.OnlineID != -1) { // official rulesets can generate more precise hits from accuracy - statistics = RulesetHelper.GenerateHitResultsForRuleset(ruleset.Value, accuracyTextBox.Value.Value / 100.0, beatmap, missesTextBox.Value.Value, countMeh, countGood, largeTickMissesTextBox.Value.Value); + statistics = RulesetHelper.GenerateHitResultsForRuleset(ruleset.Value, accuracyTextBox.Value.Value / 100.0, beatmap, missesTextBox.Value.Value, countMeh, countGood, largeTickMissesTextBox.Value.Value, sliderTailMissesTextBox.Value.Value); accuracy = RulesetHelper.GetAccuracyForRuleset(ruleset.Value, statistics); } @@ -700,6 +734,7 @@ namespace PerformanceCalculatorGUI.Screens comboTextBox.Hide(); missesTextBox.Hide(); largeTickMissesTextBox.Hide(); + sliderTailMissesTextBox.Hide(); scoreTextBox.Hide(); if (ruleset.Value.ShortName == "osu" || ruleset.Value.ShortName == "taiko" || ruleset.Value.ShortName == "fruits") @@ -714,6 +749,7 @@ namespace PerformanceCalculatorGUI.Screens if (ruleset.Value.ShortName == "osu") { largeTickMissesTextBox.Show(); + sliderTailMissesTextBox.Show(); } } else if (ruleset.Value.ShortName == "mania") @@ -736,6 +772,7 @@ namespace PerformanceCalculatorGUI.Screens comboTextBox.Show(); missesTextBox.Show(); largeTickMissesTextBox.Show(); + sliderTailMissesTextBox.Show(); scoreTextBox.Text = string.Empty; scoreTextBox.Show();