diff --git a/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs new file mode 100644 index 0000000000..a206aafb8a --- /dev/null +++ b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs @@ -0,0 +1,46 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Screens.Edit.Setup; + +namespace osu.Game.Rulesets.Mania.Edit.Setup +{ + public class ManiaSetupSection : RulesetSetupSection + { + private LabelledSwitchButton specialStyle; + + public ManiaSetupSection() + : base(new ManiaRuleset().RulesetInfo) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + specialStyle = new LabelledSwitchButton + { + Label = "Use special (N+1) style", + Description = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 5k (4+1) or 8key (7+1) configurations.", + Current = { Value = Beatmap.BeatmapInfo.SpecialStyle } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + specialStyle.Current.BindValueChanged(_ => updateBeatmap()); + } + + private void updateBeatmap() + { + Beatmap.BeatmapInfo.SpecialStyle = specialStyle.Current.Value; + } + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index f4b6e10af4..1f79dae280 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -27,11 +27,13 @@ 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.Scoring; using osu.Game.Rulesets.Mania.Skinning.Legacy; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; using osu.Game.Scoring; +using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Ranking.Statistics; namespace osu.Game.Rulesets.Mania @@ -390,6 +392,8 @@ namespace osu.Game.Rulesets.Mania { return new ManiaFilterCriteria(); } + + public override RulesetSetupSection CreateEditorSetupSection() => new ManiaSetupSection(); } public enum PlayfieldType diff --git a/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs b/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs new file mode 100644 index 0000000000..8cb778a2e1 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Setup/OsuSetupSection.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Screens.Edit.Setup; + +namespace osu.Game.Rulesets.Osu.Edit.Setup +{ + public class OsuSetupSection : RulesetSetupSection + { + private LabelledSliderBar stackLeniency; + + public OsuSetupSection() + : base(new OsuRuleset().RulesetInfo) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Children = new[] + { + stackLeniency = new LabelledSliderBar + { + Label = "Stack Leniency", + Description = "In play mode, osu! automatically stacks notes which occur at the same location. Increasing this value means it is more likely to snap notes of further time-distance.", + Current = new BindableFloat(Beatmap.BeatmapInfo.StackLeniency) + { + Default = 0.7f, + MinValue = 0, + MaxValue = 1, + Precision = 0.1f + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + stackLeniency.Current.BindValueChanged(_ => updateBeatmap()); + } + + private void updateBeatmap() + { + Beatmap.BeatmapInfo.StackLeniency = stackLeniency.Current.Value; + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index b13cdff1ec..f4a93a571d 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -30,9 +30,11 @@ using osu.Game.Skinning; using System; using System.Linq; using osu.Framework.Extensions.EnumExtensions; +using osu.Game.Rulesets.Osu.Edit.Setup; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning.Legacy; using osu.Game.Rulesets.Osu.Statistics; +using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Ranking.Statistics; namespace osu.Game.Rulesets.Osu @@ -305,5 +307,7 @@ namespace osu.Game.Rulesets.Osu } }; } + + public override RulesetSetupSection CreateEditorSetupSection() => new OsuSetupSection(); } } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs index 9253023c9a..c3c803ff23 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs @@ -4,8 +4,13 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Taiko; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Setup; @@ -23,15 +28,31 @@ namespace osu.Game.Tests.Visual.Editing editorBeatmap = new EditorBeatmap(new OsuBeatmap()); } - [BackgroundDependencyLoader] - private void load() - { - Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); + [Test] + public void TestOsu() => runForRuleset(new OsuRuleset().RulesetInfo); - Child = new SetupScreen + [Test] + public void TestTaiko() => runForRuleset(new TaikoRuleset().RulesetInfo); + + [Test] + public void TestCatch() => runForRuleset(new CatchRuleset().RulesetInfo); + + [Test] + public void TestMania() => runForRuleset(new ManiaRuleset().RulesetInfo); + + private void runForRuleset(RulesetInfo rulesetInfo) + { + AddStep("create screen", () => { - State = { Value = Visibility.Visible }, - }; + editorBeatmap.BeatmapInfo.Ruleset = rulesetInfo; + + Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); + + Child = new SetupScreen + { + State = { Value = Visibility.Visible }, + }; + }); } } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 80be61ead1..de62cf8d33 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -28,6 +28,7 @@ using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Testing; using osu.Game.Extensions; using osu.Game.Rulesets.Filter; +using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Ranking.Statistics; namespace osu.Game.Rulesets @@ -315,5 +316,11 @@ namespace osu.Game.Rulesets /// [CanBeNull] public virtual IRulesetFilterCriteria CreateRulesetFilterCriteria() => null; + + /// + /// Can be overridden to add a ruleset-specific section to the editor beatmap setup screen. + /// + [CanBeNull] + public virtual RulesetSetupSection CreateEditorSetupSection() => null; } } diff --git a/osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs b/osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs new file mode 100644 index 0000000000..935842ff99 --- /dev/null +++ b/osu.Game/Screens/Edit/Setup/RulesetSetupSection.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; +using osu.Game.Rulesets; + +namespace osu.Game.Screens.Edit.Setup +{ + public abstract class RulesetSetupSection : SetupSection + { + public sealed override LocalisableString Title => $"Ruleset ({rulesetInfo.Name})"; + + private readonly RulesetInfo rulesetInfo; + + protected RulesetSetupSection(RulesetInfo rulesetInfo) + { + this.rulesetInfo = rulesetInfo; + } + } +} diff --git a/osu.Game/Screens/Edit/Setup/SetupScreen.cs b/osu.Game/Screens/Edit/Setup/SetupScreen.cs index 746cf38867..04767f1786 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreen.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreen.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics.Containers; @@ -10,7 +11,7 @@ namespace osu.Game.Screens.Edit.Setup public class SetupScreen : EditorRoundedScreen { [Cached] - private SectionsContainer sections = new SectionsContainer(); + private SectionsContainer sections { get; } = new SetupScreenSectionsContainer(); [Cached] private SetupScreenHeader header = new SetupScreenHeader(); @@ -21,24 +22,27 @@ namespace osu.Game.Screens.Edit.Setup } [BackgroundDependencyLoader] - private void load() + private void load(EditorBeatmap beatmap) { - AddRange(new Drawable[] + var sectionsEnumerable = new List { - sections = new SetupScreenSectionsContainer - { - FixedHeader = header, - RelativeSizeAxes = Axes.Both, - Children = new SetupSection[] - { - new ResourcesSection(), - new MetadataSection(), - new DifficultySection(), - new ColoursSection(), - new DesignSection(), - } - }, - }); + new ResourcesSection(), + new MetadataSection(), + new DifficultySection(), + new ColoursSection(), + new DesignSection(), + }; + + var rulesetSpecificSection = beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateEditorSetupSection(); + if (rulesetSpecificSection != null) + sectionsEnumerable.Add(rulesetSpecificSection); + + Add(sections.With(s => + { + s.RelativeSizeAxes = Axes.Both; + s.ChildrenEnumerable = sectionsEnumerable; + s.FixedHeader = header; + })); } private class SetupScreenSectionsContainer : SectionsContainer diff --git a/osu.Game/Screens/Edit/Setup/SetupSection.cs b/osu.Game/Screens/Edit/Setup/SetupSection.cs index 1f988d62e2..1dde6fb926 100644 --- a/osu.Game/Screens/Edit/Setup/SetupSection.cs +++ b/osu.Game/Screens/Edit/Setup/SetupSection.cs @@ -12,9 +12,9 @@ using osuTK; namespace osu.Game.Screens.Edit.Setup { - internal abstract class SetupSection : Container + public abstract class SetupSection : Container { - private readonly FillFlowContainer flow; + private FillFlowContainer flow; /// /// Used to align some of the child s together to achieve a grid-like look. @@ -31,7 +31,8 @@ namespace osu.Game.Screens.Edit.Setup public abstract LocalisableString Title { get; } - protected SetupSection() + [BackgroundDependencyLoader] + private void load() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y;