diff --git a/osu-framework b/osu-framework index 209021fb49..2610a31337 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 209021fb491e21625127be5dbf5efb4c409e6f06 +Subproject commit 2610a3133721b0bc4af852342aa2a179d0e66497 diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 5f08048bf9..cdce598ce8 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -32,6 +32,10 @@ false + + $(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll + True + $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True diff --git a/osu.Game.Rulesets.Catch/packages.config b/osu.Game.Rulesets.Catch/packages.config index b39a85a382..e67d3e9b34 100644 --- a/osu.Game.Rulesets.Catch/packages.config +++ b/osu.Game.Rulesets.Catch/packages.config @@ -1,5 +1,6 @@  + \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 9b6b546b5f..b9e7f8e60f 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -32,6 +32,10 @@ false + + $(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll + True + $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True diff --git a/osu.Game.Rulesets.Mania/packages.config b/osu.Game.Rulesets.Mania/packages.config index b39a85a382..e67d3e9b34 100644 --- a/osu.Game.Rulesets.Mania/packages.config +++ b/osu.Game.Rulesets.Mania/packages.config @@ -1,5 +1,6 @@  + \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index a59d4607df..74a3883f0a 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -33,6 +33,10 @@ false + + $(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll + True + $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True diff --git a/osu.Game.Rulesets.Osu/packages.config b/osu.Game.Rulesets.Osu/packages.config index b39a85a382..e67d3e9b34 100644 --- a/osu.Game.Rulesets.Osu/packages.config +++ b/osu.Game.Rulesets.Osu/packages.config @@ -1,5 +1,6 @@  + \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 36ac9384cf..90256c7d63 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -32,6 +32,10 @@ false + + $(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll + True + $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True diff --git a/osu.Game.Rulesets.Taiko/packages.config b/osu.Game.Rulesets.Taiko/packages.config index b39a85a382..e67d3e9b34 100644 --- a/osu.Game.Rulesets.Taiko/packages.config +++ b/osu.Game.Rulesets.Taiko/packages.config @@ -1,5 +1,6 @@  + \ No newline at end of file diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 8f5fd3587a..8301f1f734 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -33,6 +33,10 @@ $(SolutionDir)\packages\DeepEqual.1.6.0.0\lib\net40\DeepEqual.dll + + $(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll + True + $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll True diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config index 62ddb99609..c16d10bf45 100644 --- a/osu.Game.Tests/packages.config +++ b/osu.Game.Tests/packages.config @@ -1,11 +1,12 @@ - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs similarity index 87% rename from osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs rename to osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs index 4632c6c5f0..b6bc348a52 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs @@ -14,7 +14,7 @@ namespace osu.Game.Input.Bindings /// A KeyBindingInputManager with a database backing for custom overrides. /// /// The type of the custom action. - public class DatabasedKeyBindingInputManager : KeyBindingContainer + public class DatabasedKeyBindingContainer : KeyBindingContainer where T : struct { private readonly RulesetInfo ruleset; @@ -31,7 +31,7 @@ namespace osu.Game.Input.Bindings /// A reference to identify the current . Used to lookup mappings. Null for global mappings. /// An optional variant for the specified . Used when a ruleset has more than one possible keyboard layouts. /// Specify how to deal with multiple matches of s and s. - public DatabasedKeyBindingInputManager(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None) + public DatabasedKeyBindingContainer(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None) : base(simultaneousMode) { this.ruleset = ruleset; diff --git a/osu.Game/Input/Bindings/GlobalKeyBindingInputManager.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs similarity index 91% rename from osu.Game/Input/Bindings/GlobalKeyBindingInputManager.cs rename to osu.Game/Input/Bindings/GlobalActionContainer.cs index dcebe939d4..46cda845aa 100644 --- a/osu.Game/Input/Bindings/GlobalKeyBindingInputManager.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -10,11 +10,11 @@ using osu.Framework.Input.Bindings; namespace osu.Game.Input.Bindings { - public class GlobalKeyBindingInputManager : DatabasedKeyBindingInputManager, IHandleGlobalInput + public class GlobalActionContainer : DatabasedKeyBindingContainer, IHandleGlobalInput { private readonly Drawable handler; - public GlobalKeyBindingInputManager(OsuGameBase game) + public GlobalActionContainer(OsuGameBase game) { if (game is IKeyBindingHandler) handler = game; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 9032cb46ce..2cdbe61059 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using osu.Framework.Configuration; using osu.Framework.Screens; using osu.Game.Configuration; @@ -27,6 +28,7 @@ using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; using osu.Game.Screens.Play; using osu.Game.Input.Bindings; +using osu.Game.Rulesets.Mods; using OpenTK.Graphics; namespace osu.Game @@ -80,6 +82,9 @@ namespace osu.Game private SettingsOverlay settings; + // todo: move this to SongSelect once Screen has the ability to unsuspend. + public readonly Bindable> SelectedMods = new Bindable>(new List()); + public OsuGame(string[] args = null) { this.args = args; @@ -111,7 +116,7 @@ namespace osu.Game Task.Run(() => BeatmapManager.Import(paths.ToArray())); } - dependencies.CacheAs(this); + dependencies.CacheAs(this); configRuleset = LocalConfig.GetBindable(OsuSetting.Ruleset); Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First(); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index d0b9634696..937b204c81 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -95,7 +95,7 @@ namespace osu.Game dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures")))); - dependencies.CacheAs(this); + dependencies.CacheAs(this); dependencies.Cache(LocalConfig); runMigrations(); @@ -212,10 +212,10 @@ namespace osu.Game { base.LoadComplete(); - GlobalKeyBindingInputManager globalBinding; + GlobalActionContainer globalBinding; CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both }; - CursorOverrideContainer.Child = globalBinding = new GlobalKeyBindingInputManager(this) + CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this) { RelativeSizeAxes = Axes.Both, Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both } diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs index f5b3096404..a4c1621266 100644 --- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.KeyBinding public override FontAwesome Icon => FontAwesome.fa_osu_hot; public override string Header => "Global"; - public GlobalKeyBindingsSection(GlobalKeyBindingInputManager manager) + public GlobalKeyBindingsSection(GlobalActionContainer manager) { Add(new DefaultBindingsSubsection(manager)); Add(new InGameKeyBindingsSubsection(manager)); @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.KeyBinding { protected override string Header => string.Empty; - public DefaultBindingsSubsection(GlobalKeyBindingInputManager manager) + public DefaultBindingsSubsection(GlobalActionContainer manager) : base(null) { Defaults = manager.GlobalKeyBindings; @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.KeyBinding { protected override string Header => "In Game"; - public InGameKeyBindingsSubsection(GlobalKeyBindingInputManager manager) : base(null) + public InGameKeyBindingsSubsection(GlobalActionContainer manager) : base(null) { Defaults = manager.InGameKeyBindings; } diff --git a/osu.Game/Overlays/KeyBindingOverlay.cs b/osu.Game/Overlays/KeyBindingOverlay.cs index 18e43ad39b..b311ee68c0 100644 --- a/osu.Game/Overlays/KeyBindingOverlay.cs +++ b/osu.Game/Overlays/KeyBindingOverlay.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays protected override Drawable CreateHeader() => new SettingsHeader("key configuration", "Customise your keys!"); [BackgroundDependencyLoader(permitNulls: true)] - private void load(RulesetStore rulesets, GlobalKeyBindingInputManager global) + private void load(RulesetStore rulesets, GlobalActionContainer global) { AddSection(new GlobalKeyBindingsSection(global)); diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 80823c56bf..91063bfa38 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -188,17 +188,19 @@ namespace osu.Game.Overlays.Mods start = Mods.Length - 1; for (int i = start; i < Mods.Length && i >= 0; i += direction) - { - if (Mods[i].HasImplementation) - { - changeSelectedIndex(i); - return; - } - } + if (SelectAt(i)) return; Deselect(); } + public bool SelectAt(int index) + { + if (!Mods[index].HasImplementation) return false; + + changeSelectedIndex(index); + return true; + } + public void Deselect() => changeSelectedIndex(-1); private void displayMod(Mod mod) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index a43c54f994..03c1f0468c 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -113,6 +113,23 @@ namespace osu.Game.Overlays.Mods } } + /// + /// Select one or more mods in this section. + /// + /// The types of s which should be deselected. + public void SelectTypes(IEnumerable mods) + { + foreach (var button in buttons) + { + for (int i = 0; i < button.Mods.Length; i++) + { + foreach (var mod in mods) + if (mod.GetType().IsInstanceOfType(button.Mods[i])) + button.SelectAt(i); + } + } + } + protected ModSection() { AutoSizeAxes = Axes.Y; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 96faa376ba..d7268fb186 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -51,6 +51,8 @@ namespace osu.Game.Overlays.Mods [BackgroundDependencyLoader(permitNulls: true)] private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets) { + SelectedMods.ValueChanged += selectedModsChanged; + LowMultiplierColour = colours.Red; HighMultiplierColour = colours.Green; @@ -63,6 +65,37 @@ namespace osu.Game.Overlays.Mods Ruleset.TriggerChange(); } + private void selectedModsChanged(IEnumerable obj) + { + foreach (ModSection section in ModSectionsContainer.Children) + section.SelectTypes(obj); + + updateMods(); + } + + private void updateMods() + { + double multiplier = 1.0; + bool ranked = true; + + foreach (Mod mod in SelectedMods.Value) + { + multiplier *= mod.ScoreMultiplier; + ranked &= mod.Ranked; + } + + MultiplierLabel.Text = $"{multiplier:N2}x"; + if (!ranked) + MultiplierLabel.Text += " (Unranked)"; + + if (multiplier > 1.0) + MultiplierLabel.FadeColour(HighMultiplierColour, 200); + else if (multiplier < 1.0) + MultiplierLabel.FadeColour(LowMultiplierColour, 200); + else + MultiplierLabel.FadeColour(Color4.White, 200); + } + protected override void PopOut() { base.PopOut(); @@ -97,6 +130,7 @@ namespace osu.Game.Overlays.Mods { foreach (ModSection section in ModSectionsContainer.Children) section.DeselectAll(); + refreshSelectedMods(); } @@ -119,30 +153,7 @@ namespace osu.Game.Overlays.Mods refreshSelectedMods(); } - private void refreshSelectedMods() - { - SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); - - double multiplier = 1.0; - bool ranked = true; - - foreach (Mod mod in SelectedMods.Value) - { - multiplier *= mod.ScoreMultiplier; - ranked &= mod.Ranked; - } - - MultiplierLabel.Text = $"{multiplier:N2}x"; - if (!ranked) - MultiplierLabel.Text += " (Unranked)"; - - if (multiplier > 1.0) - MultiplierLabel.FadeColour(HighMultiplierColour, 200); - else if (multiplier < 1.0) - MultiplierLabel.FadeColour(LowMultiplierColour, 200); - else - MultiplierLabel.FadeColour(Color4.White, 200); - } + private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray(); public ModSelectOverlay() { diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 223586a959..6e06ca6903 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.UI public abstract class RulesetInputManager : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler where T : struct { - public class RulesetKeyBindingContainer : DatabasedKeyBindingInputManager + public class RulesetKeyBindingContainer : DatabasedKeyBindingContainer { public RulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 6fdd38ce30..739bc39269 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -13,6 +13,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Overlays; using osu.Game.Overlays.Mods; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.Edit; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; @@ -47,10 +48,13 @@ namespace osu.Game.Screens.Select private SampleChannel sampleConfirm; [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay) + private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay, OsuGame game) { sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection"); + if (game != null) + modSelect.SelectedMods.BindTo(game.SelectedMods); + Footer.AddButton(@"mods", colours.Yellow, modSelect, Key.F1, float.MaxValue); BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1); @@ -121,6 +125,9 @@ namespace osu.Game.Screens.Select if (Beatmap.Value.Track != null) Beatmap.Value.Track.Looping = false; + Beatmap.Value.Mods.UnbindBindings(); + Beatmap.Value.Mods.Value = new Mod[] { }; + return false; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 79f00cc988..2421a4fdfe 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -181,7 +181,7 @@ namespace osu.Game.Screens.Select [BackgroundDependencyLoader(permitNulls: true)] private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours) { - dependencies.CacheAs(this); + dependencies.CacheAs(this); if (Footer != null) { diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0686d68968..594667ec20 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -91,6 +91,10 @@ $(SolutionDir)\packages\Humanizer.Core.2.2.0\lib\netstandard1.0\Humanizer.dll + + $(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll + True + $(SolutionDir)\packages\Microsoft.Data.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.Data.Sqlite.dll @@ -446,8 +450,8 @@ - - + + diff --git a/osu.Game/packages.config b/osu.Game/packages.config index 2938739eef..0216c8ae67 100644 --- a/osu.Game/packages.config +++ b/osu.Game/packages.config @@ -47,6 +47,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste +