// 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 osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; using osu.Game.Configuration; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Edit; 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.Scoring; using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; using osu.Game.Scoring; namespace osu.Game.Rulesets.Mania { public class ManiaRuleset : Ruleset, ILegacyRuleset { public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableManiaRuleset(this, beatmap, mods); public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new ManiaScoreProcessor(beatmap); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap, this); public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score); public const string SHORT_NAME = "mania"; public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this); public override ISkin CreateLegacySkinProvider(ISkinSource source) => new ManiaLegacySkinTransformer(source); public override IEnumerable ConvertLegacyMods(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.NoFail)) yield return new ManiaModNoFail(); if (mods.HasFlag(LegacyMods.Random)) yield return new ManiaModRandom(); } public override IEnumerable GetModsFor(ModType type) { switch (type) { case ModType.DifficultyReduction: return new Mod[] { new ManiaModEasy(), new ManiaModNoFail(), new MultiMod(new ManiaModHalfTime(), new ManiaModDaycore()), }; 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 ManiaModFlashlight(), }; case ModType.Conversion: return new Mod[] { new MultiMod(new ManiaModKey4(), new ManiaModKey5(), new ManiaModKey6(), new ManiaModKey7(), new ManiaModKey8(), new ManiaModKey9(), new ManiaModKey1(), new ManiaModKey2(), new ManiaModKey3()), new ManiaModRandom(), new ManiaModDualStages(), new ManiaModMirror(), new ManiaModDifficultyAdjust(), }; case ModType.Automation: return new Mod[] { new MultiMod(new ManiaModAutoplay(), new ManiaModCinema()), }; case ModType.Fun: return new Mod[] { new MultiMod(new ModWindUp(), new ModWindDown()) }; default: return Array.Empty(); } } public override string Description => "osu!mania"; public override string ShortName => SHORT_NAME; public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetMania }; public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(this, beatmap); public int LegacyID => 3; 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 <= 9; i++) yield return (int)PlayfieldType.Single + i; for (int i = 2; i <= 18; i += 2) yield return (int)PlayfieldType.Dual + i; } } public override IEnumerable GetDefaultKeyBindings(int variant = 0) { switch (getPlayfieldType(variant)) { case PlayfieldType.Single: return new VariantMappingGenerator { LeftKeys = new[] { InputKey.A, InputKey.S, InputKey.D, InputKey.F }, RightKeys = new[] { InputKey.J, InputKey.K, InputKey.L, InputKey.Semicolon }, SpecialKey = InputKey.Space, SpecialAction = ManiaAction.Special1, NormalActionStart = ManiaAction.Key1, }.GenerateKeyBindingsFor(variant, out _); case PlayfieldType.Dual: int keys = getDualStageKeyCount(variant); var stage1Bindings = new VariantMappingGenerator { LeftKeys = new[] { InputKey.Number1, InputKey.Number2, InputKey.Number3, InputKey.Number4, }, RightKeys = new[] { InputKey.Z, InputKey.X, InputKey.C, InputKey.V }, SpecialKey = InputKey.Tilde, SpecialAction = ManiaAction.Special1, NormalActionStart = ManiaAction.Key1 }.GenerateKeyBindingsFor(keys, out var nextNormal); var stage2Bindings = new VariantMappingGenerator { LeftKeys = new[] { InputKey.Number7, InputKey.Number8, InputKey.Number9, InputKey.Number0 }, RightKeys = new[] { InputKey.O, InputKey.P, InputKey.BracketLeft, InputKey.BracketRight }, SpecialKey = InputKey.BackSlash, SpecialAction = ManiaAction.Special2, NormalActionStart = nextNormal }.GenerateKeyBindingsFor(keys, out _); return stage1Bindings.Concat(stage2Bindings); } return Array.Empty(); } public override string GetVariantName(int variant) { switch (getPlayfieldType(variant)) { default: return $"{variant}K"; case PlayfieldType.Dual: { var 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().OrderByDescending(i => i).First(v => variant >= v); } private class VariantMappingGenerator { /// /// All the s available to the left hand. /// public InputKey[] LeftKeys; /// /// All the s available to the right hand. /// public InputKey[] RightKeys; /// /// The for the special key. /// public InputKey SpecialKey; /// /// The at which the normal columns should begin. /// public ManiaAction NormalActionStart; /// /// The for the special column. /// public ManiaAction SpecialAction; /// /// Generates a list of s for a specific number of columns. /// /// The number of columns that need to be bound. /// The next to use for normal columns. /// The keybindings. public IEnumerable GenerateKeyBindingsFor(int columns, out ManiaAction nextNormalAction) { ManiaAction currentNormalAction = NormalActionStart; var bindings = new List(); for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++) bindings.Add(new KeyBinding(LeftKeys[i], currentNormalAction++)); if (columns % 2 == 1) bindings.Add(new KeyBinding(SpecialKey, SpecialAction)); for (int i = 0; i < columns / 2; i++) bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++)); nextNormalAction = currentNormalAction; return bindings; } } } 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, } }