diff --git a/PerformanceCalculator/Simulate/CatchSimulateCommand.cs b/PerformanceCalculator/Simulate/CatchSimulateCommand.cs index 146508f5c..86fa47101 100644 --- a/PerformanceCalculator/Simulate/CatchSimulateCommand.cs +++ b/PerformanceCalculator/Simulate/CatchSimulateCommand.cs @@ -7,6 +7,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using System; using System.Collections.Generic; @@ -35,7 +36,7 @@ public class CatchSimulateCommand : SimulateCommand public override Ruleset Ruleset => new CatchRuleset(); - protected override Dictionary GenerateHitResults(IBeatmap beatmap) => generateHitResults(beatmap, Accuracy / 100, Misses, Mehs, Goods); + protected override Dictionary GenerateHitResults(IBeatmap beatmap, Mod[] mods) => generateHitResults(beatmap, Accuracy / 100, Misses, Mehs, Goods); private static Dictionary generateHitResults(IBeatmap beatmap, double accuracy, int countMiss, int? countMeh, int? countGood) { diff --git a/PerformanceCalculator/Simulate/ManiaSimulateCommand.cs b/PerformanceCalculator/Simulate/ManiaSimulateCommand.cs index 61c7f74a6..0287c50df 100644 --- a/PerformanceCalculator/Simulate/ManiaSimulateCommand.cs +++ b/PerformanceCalculator/Simulate/ManiaSimulateCommand.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; namespace PerformanceCalculator.Simulate @@ -35,7 +36,7 @@ public class ManiaSimulateCommand : SimulateCommand public override Ruleset Ruleset => new ManiaRuleset(); - protected override Dictionary GenerateHitResults(IBeatmap beatmap) => generateHitResults(beatmap, Accuracy / 100, Misses, Mehs, oks, Goods, greats); + protected override Dictionary GenerateHitResults(IBeatmap beatmap, Mod[] mods) => generateHitResults(beatmap, Accuracy / 100, Misses, Mehs, oks, Goods, greats); private static Dictionary generateHitResults(IBeatmap beatmap, double accuracy, int countMiss, int? countMeh, int? countOk, int? countGood, int? countGreat) { diff --git a/PerformanceCalculator/Simulate/OsuSimulateCommand.cs b/PerformanceCalculator/Simulate/OsuSimulateCommand.cs index ea7e83fcc..5ca1096a7 100644 --- a/PerformanceCalculator/Simulate/OsuSimulateCommand.cs +++ b/PerformanceCalculator/Simulate/OsuSimulateCommand.cs @@ -8,7 +8,9 @@ using McMaster.Extensions.CommandLineUtils; using osu.Game.Beatmaps; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; @@ -43,7 +45,17 @@ public class OsuSimulateCommand : SimulateCommand public override Ruleset Ruleset => new OsuRuleset(); - protected override Dictionary GenerateHitResults(IBeatmap beatmap) => generateHitResults(beatmap, Accuracy / 100, Misses, Mehs, Goods, largeTickMisses, sliderTailMisses); + protected override Dictionary GenerateHitResults(IBeatmap beatmap, Mod[] mods) + { + if (mods.OfType().Any(m => m.NoSliderHeadAccuracy.Value)) + { + return generateHitResults(beatmap, Accuracy / 100, Misses, Mehs, Goods, null, null); + } + else + { + return generateHitResults(beatmap, Accuracy / 100, Misses, Mehs, Goods, largeTickMisses, sliderTailMisses); + } + } private static Dictionary generateHitResults(IBeatmap beatmap, double accuracy, int countMiss, int? countMeh, int? countGood, int countLargeTickMisses, int countSliderTailMisses) { diff --git a/PerformanceCalculator/Simulate/SimulateCommand.cs b/PerformanceCalculator/Simulate/SimulateCommand.cs index 957b2f290..685432517 100644 --- a/PerformanceCalculator/Simulate/SimulateCommand.cs +++ b/PerformanceCalculator/Simulate/SimulateCommand.cs @@ -8,6 +8,7 @@ using McMaster.Extensions.CommandLineUtils; using osu.Game.Beatmaps; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; @@ -66,7 +67,7 @@ public override void Execute() var beatmap = workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); var beatmapMaxCombo = beatmap.GetMaxCombo(); - var statistics = GenerateHitResults(beatmap); + var statistics = GenerateHitResults(beatmap, mods); var scoreInfo = new ScoreInfo(beatmap.BeatmapInfo, ruleset.RulesetInfo) { Accuracy = GetAccuracy(beatmap, statistics), @@ -83,7 +84,7 @@ public override void Execute() OutputPerformance(scoreInfo, performanceAttributes, difficultyAttributes); } - protected abstract Dictionary GenerateHitResults(IBeatmap beatmap); + protected abstract Dictionary GenerateHitResults(IBeatmap beatmap, Mod[] mods); protected virtual double GetAccuracy(IBeatmap beatmap, Dictionary statistics) => 0; } diff --git a/PerformanceCalculator/Simulate/TaikoSimulateCommand.cs b/PerformanceCalculator/Simulate/TaikoSimulateCommand.cs index ac7d69448..bf537a7c8 100644 --- a/PerformanceCalculator/Simulate/TaikoSimulateCommand.cs +++ b/PerformanceCalculator/Simulate/TaikoSimulateCommand.cs @@ -7,6 +7,7 @@ using McMaster.Extensions.CommandLineUtils; using osu.Game.Beatmaps; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko; @@ -29,7 +30,7 @@ public class TaikoSimulateCommand : SimulateCommand public override Ruleset Ruleset => new TaikoRuleset(); - protected override Dictionary GenerateHitResults(IBeatmap beatmap) => generateHitResults(Accuracy / 100, beatmap, Misses, Goods); + protected override Dictionary GenerateHitResults(IBeatmap beatmap, Mod[] mods) => generateHitResults(Accuracy / 100, beatmap, Misses, Goods); private static Dictionary generateHitResults(double accuracy, IBeatmap beatmap, int countMiss, int? countGood) { diff --git a/PerformanceCalculatorGUI/RulesetHelper.cs b/PerformanceCalculatorGUI/RulesetHelper.cs index 1529d0390..d879fc219 100644 --- a/PerformanceCalculatorGUI/RulesetHelper.cs +++ b/PerformanceCalculatorGUI/RulesetHelper.cs @@ -104,7 +104,7 @@ public static int AdjustManiaScore(int score, IReadOnlyList mods) return (int)Math.Round(1000000 * scoreMultiplier); } - public static Dictionary GenerateHitResultsForRuleset(RulesetInfo ruleset, double accuracy, IBeatmap beatmap, int countMiss, int? countMeh, int? countGood, int countLargeTickMisses, int countSliderTailMisses) + public static Dictionary GenerateHitResultsForRuleset(RulesetInfo ruleset, double accuracy, IBeatmap beatmap, int countMiss, int? countMeh, int? countGood, int? countLargeTickMisses, int? countSliderTailMisses) { return ruleset.OnlineID switch { @@ -116,7 +116,7 @@ public static Dictionary GenerateHitResultsForRuleset(RulesetInf }; } - private static Dictionary generateOsuHitResults(double accuracy, IBeatmap beatmap, int countMiss, int? countMeh, int? countGood, int countLargeTickMisses, int countSliderTailMisses) + private static Dictionary generateOsuHitResults(double accuracy, IBeatmap beatmap, int countMiss, int? countMeh, int? countGood, int? countLargeTickMisses, int? countSliderTailMisses) { int countGreat; @@ -192,17 +192,21 @@ private static Dictionary generateOsuHitResults(double accuracy, countGreat = (int)(totalResultCount - countGood - countMeh - countMiss); } - int sliderTailHits = beatmap.HitObjects.Count(x => x is Slider) - countSliderTailMisses; - - return new Dictionary + var result = new Dictionary { { HitResult.Great, countGreat }, { HitResult.Ok, countGood ?? 0 }, { HitResult.Meh, countMeh ?? 0 }, - { HitResult.LargeTickMiss, countLargeTickMisses }, - { HitResult.SliderTailHit, sliderTailHits }, { HitResult.Miss, countMiss } }; + + if (countLargeTickMisses != null) + result[HitResult.LargeTickMiss] = countLargeTickMisses.Value; + + if (countSliderTailMisses != null) + result[HitResult.SliderTailHit] = beatmap.HitObjects.Count(x => x is Slider) - countSliderTailMisses.Value; + + return result; } private static Dictionary generateTaikoHitResults(double accuracy, IBeatmap beatmap, int countMiss, int? countGood) @@ -293,11 +297,11 @@ private static Dictionary generateManiaHitResults(double accurac }; } - public static double GetAccuracyForRuleset(RulesetInfo ruleset, Dictionary statistics) + public static double GetAccuracyForRuleset(RulesetInfo ruleset, IBeatmap beatmap, Dictionary statistics) { return ruleset.OnlineID switch { - 0 => getOsuAccuracy(statistics), + 0 => getOsuAccuracy(beatmap, statistics), 1 => getTaikoAccuracy(statistics), 2 => getCatchAccuracy(statistics), 3 => getManiaAccuracy(statistics), @@ -305,15 +309,35 @@ public static double GetAccuracyForRuleset(RulesetInfo ruleset, Dictionary statistics) + private static double getOsuAccuracy(IBeatmap beatmap, Dictionary statistics) { var countGreat = statistics[HitResult.Great]; var countGood = statistics[HitResult.Ok]; var countMeh = statistics[HitResult.Meh]; var countMiss = statistics[HitResult.Miss]; - var total = countGreat + countGood + countMeh + countMiss; - return (double)((6 * countGreat) + (2 * countGood) + countMeh) / (6 * total); + double total = 6 * countGreat + 2 * countGood + countMeh; + double max = 6 * (countGreat + countGood + countMeh + countMiss); + + if (statistics.ContainsKey(HitResult.SliderTailHit)) + { + var countSliders = beatmap.HitObjects.Count(x => x is Slider); + var countSliderTailHit = statistics[HitResult.SliderTailHit]; + + total += 3 * countSliderTailHit; + max += 3 * countSliders; + } + + if (statistics.ContainsKey(HitResult.LargeTickMiss)) + { + var countLargeTicks = beatmap.HitObjects.Sum(obj => obj.NestedHitObjects.Count(x => x is SliderTick or SliderRepeat)); + var countLargeTickHit = countLargeTicks - statistics[HitResult.LargeTickMiss]; + + total += 0.6 * countLargeTickHit; + max += 0.6 * countLargeTicks; + } + + return total / max; } private static double getTaikoAccuracy(Dictionary statistics) diff --git a/PerformanceCalculatorGUI/Screens/SimulateScreen.cs b/PerformanceCalculatorGUI/Screens/SimulateScreen.cs index 166e3be57..c067745a5 100644 --- a/PerformanceCalculatorGUI/Screens/SimulateScreen.cs +++ b/PerformanceCalculatorGUI/Screens/SimulateScreen.cs @@ -702,10 +702,18 @@ private void calculatePerformance() 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, sliderTailMissesTextBox.Value.Value); + if (appliedMods.Value.OfType().Any(m => m.NoSliderHeadAccuracy.Value)) + { + statistics = RulesetHelper.GenerateHitResultsForRuleset(ruleset.Value, accuracyTextBox.Value.Value / 100.0, beatmap, missesTextBox.Value.Value, countMeh, countGood, + null, null); + } + else + { + 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); + accuracy = RulesetHelper.GetAccuracyForRuleset(ruleset.Value, beatmap, statistics); } var ppAttributes = performanceCalculator?.Calculate(new ScoreInfo(beatmap.BeatmapInfo, ruleset.Value)