// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using JetBrains.Annotations; using McMaster.Extensions.CommandLineUtils; using osu.Game.Beatmaps; 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; using System.Linq; namespace PerformanceCalculator.Simulate { [Command(Name = "catch", Description = "Computes the performance (pp) of a simulated osu!catch play.")] public class CatchSimulateCommand : SimulateCommand { [UsedImplicitly] [Option(Template = "-c|--combo ", Description = "Maximum combo during play. Defaults to beatmap maximum.")] public override int? Combo { get; } [UsedImplicitly] [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 = "-T|--tiny-droplets ", Description = "Number of tiny droplets hit. Will override accuracy if used. Otherwise is automatically calculated.")] public override int? Mehs { get; } [UsedImplicitly] [Option(Template = "-D|--droplets ", Description = "Number of droplets hit. Will override accuracy if used. Otherwise is automatically calculated.")] public override int? Goods { get; } public override Ruleset Ruleset => new CatchRuleset(); 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) { int maxCombo = beatmap.GetMaxCombo(); int maxTinyDroplets = beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.OfType().Count()); int maxDroplets = beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.OfType().Count()) - maxTinyDroplets; int maxFruits = beatmap.HitObjects.Sum(h => h is Fruit ? 1 : (h as JuiceStream)?.NestedHitObjects.Count(n => n is Fruit) ?? 0); // Either given or max value minus misses int countDroplets = countGood ?? Math.Max(0, maxDroplets - countMiss); // Max value minus whatever misses are left. Negative if impossible missCount int countFruits = maxFruits - (countMiss - (maxDroplets - countDroplets)); // Either given or the max amount of hit objects with respect to accuracy minus the already calculated fruits and drops. // Negative if accuracy not feasable with missCount. int countTinyDroplets = countMeh ?? (int)Math.Round(accuracy * (maxCombo + maxTinyDroplets)) - countFruits - countDroplets; // Whatever droplets are left int countTinyMisses = maxTinyDroplets - countTinyDroplets; return new Dictionary { { HitResult.Great, countFruits }, { HitResult.LargeTickHit, countDroplets }, { HitResult.SmallTickHit, countTinyDroplets }, { HitResult.SmallTickMiss, countTinyMisses }, { HitResult.Miss, countMiss } }; } protected override double GetAccuracy(IBeatmap beatmap, Dictionary statistics, Mod[] mods) { double hits = statistics[HitResult.Great] + statistics[HitResult.LargeTickHit] + statistics[HitResult.SmallTickHit]; double total = hits + statistics[HitResult.Miss] + statistics[HitResult.SmallTickMiss]; return hits / total; } } }