From 5ae56df32b777f840ed689a88c4f3cd1c48f349d Mon Sep 17 00:00:00 2001 From: Milo Date: Mon, 11 Nov 2024 18:52:30 -0400 Subject: [PATCH] implement proposed osu!mania mod, `Full Perfect` --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 898 +++++++++--------- .../Mods/ManiaModFullPerfect.cs | 40 + 2 files changed, 489 insertions(+), 449 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Mods/ManiaModFullPerfect.cs diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index cdc7b0a951..897989773e 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -1,449 +1,449 @@ -// Copyright (c) ppy Pty Ltd . 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 osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Bindings; -using osu.Framework.Localisation; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Legacy; -using osu.Game.Configuration; -using osu.Game.Graphics; -using osu.Game.Overlays.Settings; -using osu.Game.Rulesets.Configuration; -using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Filter; -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Configuration; -using osu.Game.Rulesets.Mania.Difficulty; -using osu.Game.Rulesets.Mania.Edit; -using osu.Game.Rulesets.Mania.Edit.Setup; -using osu.Game.Rulesets.Mania.Mods; -using osu.Game.Rulesets.Mania.Replays; -using osu.Game.Rulesets.Mania.Scoring; -using osu.Game.Rulesets.Mania.Skinning.Argon; -using osu.Game.Rulesets.Mania.Skinning.Default; -using osu.Game.Rulesets.Mania.Skinning.Legacy; -using osu.Game.Rulesets.Mania.UI; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Replays.Types; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.Scoring.Legacy; -using osu.Game.Rulesets.UI; -using osu.Game.Scoring; -using osu.Game.Screens.Edit.Setup; -using osu.Game.Screens.Ranking.Statistics; -using osu.Game.Skinning; - -namespace osu.Game.Rulesets.Mania -{ - public class ManiaRuleset : Ruleset, ILegacyRuleset - { - /// - /// The maximum number of supported keys in a single stage. - /// - public const int MAX_STAGE_KEYS = 10; - - public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList? mods = null) => new DrawableManiaRuleset(this, beatmap, mods); - - public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(); - - public override HealthProcessor CreateHealthProcessor(double drainStartTime) => new ManiaHealthProcessor(drainStartTime); - - public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap, this); - - public override PerformanceCalculator CreatePerformanceCalculator() => new ManiaPerformanceCalculator(); - - public const string SHORT_NAME = "mania"; - - public override string RulesetAPIVersionSupported => CURRENT_RULESET_API_VERSION; - - public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this); - - public override IBeatmapVerifier CreateBeatmapVerifier() => new ManiaBeatmapVerifier(); - - public override ISkin? CreateSkinTransformer(ISkin skin, IBeatmap beatmap) - { - switch (skin) - { - case TrianglesSkin: - return new ManiaTrianglesSkinTransformer(skin, beatmap); - - case ArgonSkin: - return new ManiaArgonSkinTransformer(skin, beatmap); - - case DefaultLegacySkin: - return new ManiaClassicSkinTransformer(skin, beatmap); - - case LegacySkin: - return new ManiaLegacySkinTransformer(skin, beatmap); - } - - return null; - } - - public override IEnumerable ConvertFromLegacyMods(LegacyMods mods) - { - if (mods.HasFlag(LegacyMods.Nightcore)) - yield return new ManiaModNightcore(); - else if (mods.HasFlag(LegacyMods.DoubleTime)) - yield return new ManiaModDoubleTime(); - - if (mods.HasFlag(LegacyMods.Perfect)) - yield return new ManiaModPerfect(); - else if (mods.HasFlag(LegacyMods.SuddenDeath)) - yield return new ManiaModSuddenDeath(); - - if (mods.HasFlag(LegacyMods.Cinema)) - yield return new ManiaModCinema(); - else if (mods.HasFlag(LegacyMods.Autoplay)) - yield return new ManiaModAutoplay(); - - if (mods.HasFlag(LegacyMods.Easy)) - yield return new ManiaModEasy(); - - if (mods.HasFlag(LegacyMods.FadeIn)) - yield return new ManiaModFadeIn(); - - if (mods.HasFlag(LegacyMods.Flashlight)) - yield return new ManiaModFlashlight(); - - if (mods.HasFlag(LegacyMods.HalfTime)) - yield return new ManiaModHalfTime(); - - if (mods.HasFlag(LegacyMods.HardRock)) - yield return new ManiaModHardRock(); - - if (mods.HasFlag(LegacyMods.Hidden)) - yield return new ManiaModHidden(); - - if (mods.HasFlag(LegacyMods.Key1)) - yield return new ManiaModKey1(); - - if (mods.HasFlag(LegacyMods.Key2)) - yield return new ManiaModKey2(); - - if (mods.HasFlag(LegacyMods.Key3)) - yield return new ManiaModKey3(); - - if (mods.HasFlag(LegacyMods.Key4)) - yield return new ManiaModKey4(); - - if (mods.HasFlag(LegacyMods.Key5)) - yield return new ManiaModKey5(); - - if (mods.HasFlag(LegacyMods.Key6)) - yield return new ManiaModKey6(); - - if (mods.HasFlag(LegacyMods.Key7)) - yield return new ManiaModKey7(); - - if (mods.HasFlag(LegacyMods.Key8)) - yield return new ManiaModKey8(); - - if (mods.HasFlag(LegacyMods.Key9)) - yield return new ManiaModKey9(); - - if (mods.HasFlag(LegacyMods.KeyCoop)) - yield return new ManiaModDualStages(); - - if (mods.HasFlag(LegacyMods.NoFail)) - yield return new ManiaModNoFail(); - - if (mods.HasFlag(LegacyMods.Random)) - yield return new ManiaModRandom(); - - if (mods.HasFlag(LegacyMods.Mirror)) - yield return new ManiaModMirror(); - - if (mods.HasFlag(LegacyMods.ScoreV2)) - yield return new ModScoreV2(); - } - - public override LegacyMods ConvertToLegacyMods(Mod[] mods) - { - var value = base.ConvertToLegacyMods(mods); - - foreach (var mod in mods) - { - switch (mod) - { - case ManiaModKey1: - value |= LegacyMods.Key1; - break; - - case ManiaModKey2: - value |= LegacyMods.Key2; - break; - - case ManiaModKey3: - value |= LegacyMods.Key3; - break; - - case ManiaModKey4: - value |= LegacyMods.Key4; - break; - - case ManiaModKey5: - value |= LegacyMods.Key5; - break; - - case ManiaModKey6: - value |= LegacyMods.Key6; - break; - - case ManiaModKey7: - value |= LegacyMods.Key7; - break; - - case ManiaModKey8: - value |= LegacyMods.Key8; - break; - - case ManiaModKey9: - value |= LegacyMods.Key9; - break; - - case ManiaModDualStages: - value |= LegacyMods.KeyCoop; - break; - - case ManiaModFadeIn: - value |= LegacyMods.FadeIn; - value &= ~LegacyMods.Hidden; // this is toggled on in the base call due to inheritance, but we don't want that. - break; - - case ManiaModMirror: - value |= LegacyMods.Mirror; - break; - - case ManiaModRandom: - value |= LegacyMods.Random; - break; - } - } - - return value; - } - - public override IEnumerable GetModsFor(ModType type) - { - switch (type) - { - case ModType.DifficultyReduction: - return new Mod[] - { - new ManiaModEasy(), - new ManiaModNoFail(), - new MultiMod(new ManiaModHalfTime(), new ManiaModDaycore()), - new ManiaModNoRelease(), - }; - - case ModType.DifficultyIncrease: - return new Mod[] - { - new ManiaModHardRock(), - new MultiMod(new ManiaModSuddenDeath(), new ManiaModPerfect()), - new MultiMod(new ManiaModDoubleTime(), new ManiaModNightcore()), - new MultiMod(new ManiaModFadeIn(), new ManiaModHidden(), new ManiaModCover()), - new ManiaModFlashlight(), - new ModAccuracyChallenge(), - }; - - case ModType.Conversion: - return new Mod[] - { - new ManiaModRandom(), - new ManiaModDualStages(), - new ManiaModMirror(), - new ManiaModDifficultyAdjust(), - new ManiaModClassic(), - new ManiaModInvert(), - new ManiaModConstantSpeed(), - new ManiaModHoldOff(), - new MultiMod( - new ManiaModKey1(), - new ManiaModKey2(), - new ManiaModKey3(), - new ManiaModKey4(), - new ManiaModKey5(), - new ManiaModKey6(), - new ManiaModKey7(), - new ManiaModKey8(), - new ManiaModKey9(), - new ManiaModKey10() - ), - }; - - case ModType.Automation: - return new Mod[] - { - new MultiMod(new ManiaModAutoplay(), new ManiaModCinema()), - }; - - case ModType.Fun: - return new Mod[] - { - new MultiMod(new ModWindUp(), new ModWindDown()), - new ManiaModMuted(), - new ModAdaptiveSpeed() - }; - - case ModType.System: - return new Mod[] - { - new ModScoreV2(), - }; - - default: - return Array.Empty(); - } - } - - public override string Description => "osu!mania"; - - public override string ShortName => SHORT_NAME; - - public override string PlayingVerb => "Smashing keys"; - - public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetMania }; - - public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new ManiaDifficultyCalculator(RulesetInfo, beatmap); - - public int LegacyID => 3; - - public ILegacyScoreSimulator CreateLegacyScoreSimulator() => new ManiaLegacyScoreSimulator(); - - public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame(); - - public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new ManiaRulesetConfigManager(settings, RulesetInfo); - - public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this); - - public override IEnumerable AvailableVariants - { - get - { - for (int i = 1; i <= MAX_STAGE_KEYS; i++) - yield return (int)PlayfieldType.Single + i; - for (int i = 2; i <= MAX_STAGE_KEYS * 2; i += 2) - yield return (int)PlayfieldType.Dual + i; - } - } - - public override IEnumerable GetDefaultKeyBindings(int variant = 0) - { - switch (getPlayfieldType(variant)) - { - case PlayfieldType.Single: - return new SingleStageVariantGenerator(variant).GenerateMappings(); - - case PlayfieldType.Dual: - return new DualStageVariantGenerator(getDualStageKeyCount(variant)).GenerateMappings(); - } - - return Array.Empty(); - } - - public override LocalisableString GetVariantName(int variant) - { - switch (getPlayfieldType(variant)) - { - default: - return $"{variant}K"; - - case PlayfieldType.Dual: - { - int keys = getDualStageKeyCount(variant); - return $"{keys}K + {keys}K"; - } - } - } - - /// - /// Finds the number of keys for each stage in a variant. - /// - /// The variant. - private int getDualStageKeyCount(int variant) => (variant - (int)PlayfieldType.Dual) / 2; - - /// - /// Finds the that corresponds to a variant value. - /// - /// The variant value. - /// The that corresponds to . - private PlayfieldType getPlayfieldType(int variant) - { - return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast().OrderDescending().First(v => variant >= v); - } - - protected override IEnumerable GetValidHitResults() - { - return new[] - { - HitResult.Perfect, - HitResult.Great, - HitResult.Good, - HitResult.Ok, - HitResult.Meh, - - // HitResult.SmallBonus is used for awarding perfect bonus score but is not included here as - // it would be a bit redundant to show this to the user. - }; - } - - public override StatisticItem[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => new[] - { - new StatisticItem("Performance Breakdown", () => new PerformanceBreakdownChart(score, playableBeatmap) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y - }), - new StatisticItem("Timing Distribution", () => new HitEventTimingDistributionGraph(score.HitEvents) - { - RelativeSizeAxes = Axes.X, - Height = 250 - }, true), - new StatisticItem("Statistics", () => new SimpleStatisticTable(2, new SimpleStatisticItem[] - { - new AverageHitError(score.HitEvents), - new UnstableRate(score.HitEvents) - }), true) - }; - - public override IRulesetFilterCriteria CreateRulesetFilterCriteria() - { - return new ManiaFilterCriteria(); - } - - public override IEnumerable CreateEditorSetupSections() => - [ - new MetadataSection(), - new ManiaDifficultySection(), - new ResourcesSection(), - new DesignSection(), - ]; - - public int GetKeyCount(IBeatmapInfo beatmapInfo, IReadOnlyList? mods = null) - => ManiaBeatmapConverter.GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo), mods); - } - - public enum PlayfieldType - { - /// - /// Columns are grouped into a single stage. - /// Number of columns in this stage lies at (item - Single). - /// - Single = 0, - - /// - /// Columns are grouped into two stages. - /// Overall number of columns lies at (item - Dual), further computation is required for - /// number of columns in each individual stage. - /// - Dual = 1000, - } -} +// Copyright (c) ppy Pty Ltd . 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 osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Bindings; +using osu.Framework.Localisation; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Configuration; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Filter; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Configuration; +using osu.Game.Rulesets.Mania.Difficulty; +using osu.Game.Rulesets.Mania.Edit; +using osu.Game.Rulesets.Mania.Edit.Setup; +using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Rulesets.Mania.Replays; +using osu.Game.Rulesets.Mania.Scoring; +using osu.Game.Rulesets.Mania.Skinning.Argon; +using osu.Game.Rulesets.Mania.Skinning.Default; +using osu.Game.Rulesets.Mania.Skinning.Legacy; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Replays.Types; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Scoring.Legacy; +using osu.Game.Rulesets.UI; +using osu.Game.Scoring; +using osu.Game.Screens.Edit.Setup; +using osu.Game.Screens.Ranking.Statistics; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Mania +{ + public class ManiaRuleset : Ruleset, ILegacyRuleset + { + /// + /// The maximum number of supported keys in a single stage. + /// + public const int MAX_STAGE_KEYS = 10; + + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList? mods = null) => new DrawableManiaRuleset(this, beatmap, mods); + + public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(); + + public override HealthProcessor CreateHealthProcessor(double drainStartTime) => new ManiaHealthProcessor(drainStartTime); + + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap, this); + + public override PerformanceCalculator CreatePerformanceCalculator() => new ManiaPerformanceCalculator(); + + public const string SHORT_NAME = "mania"; + + public override string RulesetAPIVersionSupported => CURRENT_RULESET_API_VERSION; + + public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this); + + public override IBeatmapVerifier CreateBeatmapVerifier() => new ManiaBeatmapVerifier(); + + public override ISkin? CreateSkinTransformer(ISkin skin, IBeatmap beatmap) + { + switch (skin) + { + case TrianglesSkin: + return new ManiaTrianglesSkinTransformer(skin, beatmap); + + case ArgonSkin: + return new ManiaArgonSkinTransformer(skin, beatmap); + + case DefaultLegacySkin: + return new ManiaClassicSkinTransformer(skin, beatmap); + + case LegacySkin: + return new ManiaLegacySkinTransformer(skin, beatmap); + } + + return null; + } + + public override IEnumerable ConvertFromLegacyMods(LegacyMods mods) + { + if (mods.HasFlag(LegacyMods.Nightcore)) + yield return new ManiaModNightcore(); + else if (mods.HasFlag(LegacyMods.DoubleTime)) + yield return new ManiaModDoubleTime(); + + if (mods.HasFlag(LegacyMods.Perfect)) + yield return new ManiaModPerfect(); + else if (mods.HasFlag(LegacyMods.SuddenDeath)) + yield return new ManiaModSuddenDeath(); + + if (mods.HasFlag(LegacyMods.Cinema)) + yield return new ManiaModCinema(); + else if (mods.HasFlag(LegacyMods.Autoplay)) + yield return new ManiaModAutoplay(); + + if (mods.HasFlag(LegacyMods.Easy)) + yield return new ManiaModEasy(); + + if (mods.HasFlag(LegacyMods.FadeIn)) + yield return new ManiaModFadeIn(); + + if (mods.HasFlag(LegacyMods.Flashlight)) + yield return new ManiaModFlashlight(); + + if (mods.HasFlag(LegacyMods.HalfTime)) + yield return new ManiaModHalfTime(); + + if (mods.HasFlag(LegacyMods.HardRock)) + yield return new ManiaModHardRock(); + + if (mods.HasFlag(LegacyMods.Hidden)) + yield return new ManiaModHidden(); + + if (mods.HasFlag(LegacyMods.Key1)) + yield return new ManiaModKey1(); + + if (mods.HasFlag(LegacyMods.Key2)) + yield return new ManiaModKey2(); + + if (mods.HasFlag(LegacyMods.Key3)) + yield return new ManiaModKey3(); + + if (mods.HasFlag(LegacyMods.Key4)) + yield return new ManiaModKey4(); + + if (mods.HasFlag(LegacyMods.Key5)) + yield return new ManiaModKey5(); + + if (mods.HasFlag(LegacyMods.Key6)) + yield return new ManiaModKey6(); + + if (mods.HasFlag(LegacyMods.Key7)) + yield return new ManiaModKey7(); + + if (mods.HasFlag(LegacyMods.Key8)) + yield return new ManiaModKey8(); + + if (mods.HasFlag(LegacyMods.Key9)) + yield return new ManiaModKey9(); + + if (mods.HasFlag(LegacyMods.KeyCoop)) + yield return new ManiaModDualStages(); + + if (mods.HasFlag(LegacyMods.NoFail)) + yield return new ManiaModNoFail(); + + if (mods.HasFlag(LegacyMods.Random)) + yield return new ManiaModRandom(); + + if (mods.HasFlag(LegacyMods.Mirror)) + yield return new ManiaModMirror(); + + if (mods.HasFlag(LegacyMods.ScoreV2)) + yield return new ModScoreV2(); + } + + public override LegacyMods ConvertToLegacyMods(Mod[] mods) + { + var value = base.ConvertToLegacyMods(mods); + + foreach (var mod in mods) + { + switch (mod) + { + case ManiaModKey1: + value |= LegacyMods.Key1; + break; + + case ManiaModKey2: + value |= LegacyMods.Key2; + break; + + case ManiaModKey3: + value |= LegacyMods.Key3; + break; + + case ManiaModKey4: + value |= LegacyMods.Key4; + break; + + case ManiaModKey5: + value |= LegacyMods.Key5; + break; + + case ManiaModKey6: + value |= LegacyMods.Key6; + break; + + case ManiaModKey7: + value |= LegacyMods.Key7; + break; + + case ManiaModKey8: + value |= LegacyMods.Key8; + break; + + case ManiaModKey9: + value |= LegacyMods.Key9; + break; + + case ManiaModDualStages: + value |= LegacyMods.KeyCoop; + break; + + case ManiaModFadeIn: + value |= LegacyMods.FadeIn; + value &= ~LegacyMods.Hidden; // this is toggled on in the base call due to inheritance, but we don't want that. + break; + + case ManiaModMirror: + value |= LegacyMods.Mirror; + break; + + case ManiaModRandom: + value |= LegacyMods.Random; + break; + } + } + + return value; + } + + public override IEnumerable GetModsFor(ModType type) + { + switch (type) + { + case ModType.DifficultyReduction: + return new Mod[] + { + new ManiaModEasy(), + new ManiaModNoFail(), + new MultiMod(new ManiaModHalfTime(), new ManiaModDaycore()), + new ManiaModNoRelease(), + }; + + case ModType.DifficultyIncrease: + return new Mod[] + { + new ManiaModHardRock(), + new MultiMod(new ManiaModSuddenDeath(), new ManiaModPerfect(), new ManiaModFullPerfect()), + new MultiMod(new ManiaModDoubleTime(), new ManiaModNightcore()), + new MultiMod(new ManiaModFadeIn(), new ManiaModHidden(), new ManiaModCover()), + new ManiaModFlashlight(), + new ModAccuracyChallenge(), + }; + + case ModType.Conversion: + return new Mod[] + { + new ManiaModRandom(), + new ManiaModDualStages(), + new ManiaModMirror(), + new ManiaModDifficultyAdjust(), + new ManiaModClassic(), + new ManiaModInvert(), + new ManiaModConstantSpeed(), + new ManiaModHoldOff(), + new MultiMod( + new ManiaModKey1(), + new ManiaModKey2(), + new ManiaModKey3(), + new ManiaModKey4(), + new ManiaModKey5(), + new ManiaModKey6(), + new ManiaModKey7(), + new ManiaModKey8(), + new ManiaModKey9(), + new ManiaModKey10() + ), + }; + + case ModType.Automation: + return new Mod[] + { + new MultiMod(new ManiaModAutoplay(), new ManiaModCinema()), + }; + + case ModType.Fun: + return new Mod[] + { + new MultiMod(new ModWindUp(), new ModWindDown()), + new ManiaModMuted(), + new ModAdaptiveSpeed() + }; + + case ModType.System: + return new Mod[] + { + new ModScoreV2(), + }; + + default: + return Array.Empty(); + } + } + + public override string Description => "osu!mania"; + + public override string ShortName => SHORT_NAME; + + public override string PlayingVerb => "Smashing keys"; + + public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetMania }; + + public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => new ManiaDifficultyCalculator(RulesetInfo, beatmap); + + public int LegacyID => 3; + + public ILegacyScoreSimulator CreateLegacyScoreSimulator() => new ManiaLegacyScoreSimulator(); + + public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame(); + + public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new ManiaRulesetConfigManager(settings, RulesetInfo); + + public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this); + + public override IEnumerable AvailableVariants + { + get + { + for (int i = 1; i <= MAX_STAGE_KEYS; i++) + yield return (int)PlayfieldType.Single + i; + for (int i = 2; i <= MAX_STAGE_KEYS * 2; i += 2) + yield return (int)PlayfieldType.Dual + i; + } + } + + public override IEnumerable GetDefaultKeyBindings(int variant = 0) + { + switch (getPlayfieldType(variant)) + { + case PlayfieldType.Single: + return new SingleStageVariantGenerator(variant).GenerateMappings(); + + case PlayfieldType.Dual: + return new DualStageVariantGenerator(getDualStageKeyCount(variant)).GenerateMappings(); + } + + return Array.Empty(); + } + + public override LocalisableString GetVariantName(int variant) + { + switch (getPlayfieldType(variant)) + { + default: + return $"{variant}K"; + + case PlayfieldType.Dual: + { + int keys = getDualStageKeyCount(variant); + return $"{keys}K + {keys}K"; + } + } + } + + /// + /// Finds the number of keys for each stage in a variant. + /// + /// The variant. + private int getDualStageKeyCount(int variant) => (variant - (int)PlayfieldType.Dual) / 2; + + /// + /// Finds the that corresponds to a variant value. + /// + /// The variant value. + /// The that corresponds to . + private PlayfieldType getPlayfieldType(int variant) + { + return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast().OrderDescending().First(v => variant >= v); + } + + protected override IEnumerable GetValidHitResults() + { + return new[] + { + HitResult.Perfect, + HitResult.Great, + HitResult.Good, + HitResult.Ok, + HitResult.Meh, + + // HitResult.SmallBonus is used for awarding perfect bonus score but is not included here as + // it would be a bit redundant to show this to the user. + }; + } + + public override StatisticItem[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => new[] + { + new StatisticItem("Performance Breakdown", () => new PerformanceBreakdownChart(score, playableBeatmap) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }), + new StatisticItem("Timing Distribution", () => new HitEventTimingDistributionGraph(score.HitEvents) + { + RelativeSizeAxes = Axes.X, + Height = 250 + }, true), + new StatisticItem("Statistics", () => new SimpleStatisticTable(2, new SimpleStatisticItem[] + { + new AverageHitError(score.HitEvents), + new UnstableRate(score.HitEvents) + }), true) + }; + + public override IRulesetFilterCriteria CreateRulesetFilterCriteria() + { + return new ManiaFilterCriteria(); + } + + public override IEnumerable CreateEditorSetupSections() => + [ + new MetadataSection(), + new ManiaDifficultySection(), + new ResourcesSection(), + new DesignSection(), + ]; + + public int GetKeyCount(IBeatmapInfo beatmapInfo, IReadOnlyList? mods = null) + => ManiaBeatmapConverter.GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo), mods); + } + + public enum PlayfieldType + { + /// + /// Columns are grouped into a single stage. + /// Number of columns in this stage lies at (item - Single). + /// + Single = 0, + + /// + /// Columns are grouped into two stages. + /// Overall number of columns lies at (item - Dual), further computation is required for + /// number of columns in each individual stage. + /// + Dual = 1000, + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModFullPerfect.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModFullPerfect.cs new file mode 100644 index 0000000000..05db2b33af --- /dev/null +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModFullPerfect.cs @@ -0,0 +1,40 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text.4 + +using System; +using System.Linq; +using osu.Framework.Localisation; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModFullPerfect : ManiaModPerfect + { + + public override string Name => "Full Perfect"; + public override string Acronym => "FP"; + public override LocalisableString Description => @"Placeholder"; + public override double ScoreMultiplier => 1; + + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] + { + typeof(ManiaModPerfect), + }).ToArray(); + + + protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) + { + if (!isRelevantResult(result.Judgement.MinResult) && !isRelevantResult(result.Judgement.MaxResult) && !isRelevantResult(result.Type)) + return false; + + if (result.Judgement.MaxResult == HitResult.Perfect) + return result.Type < HitResult.Perfect; + + return result.Type != result.Judgement.MaxResult; + } + + private bool isRelevantResult(HitResult result) => result.AffectsAccuracy() || result.AffectsCombo(); + } +}