From 59b2f028289521f6eef68456662ca9255913287d Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Tue, 8 Oct 2019 18:34:09 +0800 Subject: [PATCH 01/84] initial implementation of customizable mods --- osu.Game/Overlays/Mods/ModControlSection.cs | 56 ++++++++++++++++++++ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 58 +++++++++++++++++++++ osu.Game/Rulesets/Mods/IModHasSettings.cs | 15 ++++++ osu.Game/Rulesets/Mods/Mod.cs | 2 +- 4 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Overlays/Mods/ModControlSection.cs create mode 100644 osu.Game/Rulesets/Mods/IModHasSettings.cs diff --git a/osu.Game/Overlays/Mods/ModControlSection.cs b/osu.Game/Overlays/Mods/ModControlSection.cs new file mode 100644 index 0000000000..df6f4d15b0 --- /dev/null +++ b/osu.Game/Overlays/Mods/ModControlSection.cs @@ -0,0 +1,56 @@ +// 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.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Mods; +using osuTK; + +namespace osu.Game.Overlays.Mods +{ + public class ModControlSection : Container + { + protected FillFlowContainer FlowContent; + protected override Container Content => FlowContent; + + public readonly Mod Mod; + + public ModControlSection(Mod mod) + { + Mod = mod; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + FlowContent = new FillFlowContainer + { + Margin = new MarginPadding { Top = 30 }, + Spacing = new Vector2(0, 5), + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }; + + if (Mod is IModHasSettings modHasSettings) + AddRange(modHasSettings.CreateControls()); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AddRangeInternal(new Drawable[] + { + new OsuSpriteText + { + Text = Mod.Name, + Font = OsuFont.GetFont(weight: FontWeight.Bold), + Colour = colours.Yellow, + }, + FlowContent + }); + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 9ff320841a..5493080964 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -31,6 +31,7 @@ namespace osu.Game.Overlays.Mods public class ModSelectOverlay : WaveOverlayContainer { protected readonly TriangleButton DeselectAllButton; + protected readonly TriangleButton CustomizeButton; protected readonly TriangleButton CloseButton; protected readonly OsuSpriteText MultiplierLabel; @@ -42,6 +43,10 @@ namespace osu.Game.Overlays.Mods protected readonly FillFlowContainer ModSectionsContainer; + protected readonly FillFlowContainer ModSettingsContent; + + protected readonly Container ModSettingsContainer; + protected readonly Bindable> SelectedMods = new Bindable>(Array.Empty()); protected readonly IBindable Ruleset = new Bindable(); @@ -226,6 +231,16 @@ namespace osu.Game.Overlays.Mods Right = 20 } }, + CustomizeButton = new TriangleButton + { + Width = 180, + Text = "Customization", + Action = () => ModSettingsContainer.Alpha = ModSettingsContainer.Alpha == 1 ? 0 : 1, + Margin = new MarginPadding + { + Right = 20 + } + }, CloseButton = new TriangleButton { Width = 180, @@ -271,6 +286,36 @@ namespace osu.Game.Overlays.Mods }, }, }, + ModSettingsContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Width = 0.25f, + Alpha = 0, + X = -100, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = new Color4(0, 0, 0, 192) + }, + new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = ModSettingsContent = new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0f, 10f), + Padding = new MarginPadding(20), + } + } + } + } }; } @@ -284,10 +329,23 @@ namespace osu.Game.Overlays.Mods Ruleset.BindTo(ruleset); if (mods != null) SelectedMods.BindTo(mods); + SelectedMods.ValueChanged += updateModSettings; + sampleOn = audio.Samples.Get(@"UI/check-on"); sampleOff = audio.Samples.Get(@"UI/check-off"); } + private void updateModSettings(ValueChangedEvent> selectedMods) + { + var added = selectedMods.NewValue.Except(selectedMods.OldValue).FirstOrDefault(); + var removed = selectedMods.OldValue.Except(selectedMods.NewValue).FirstOrDefault(); + + if (added is IModHasSettings) + ModSettingsContent.Add(new ModControlSection(added)); + else if (removed is IModHasSettings) + ModSettingsContent.Remove(ModSettingsContent.Children.Where(section => section.Mod == removed).FirstOrDefault()); + } + public void DeselectAll() { foreach (var section in ModSectionsContainer.Children) diff --git a/osu.Game/Rulesets/Mods/IModHasSettings.cs b/osu.Game/Rulesets/Mods/IModHasSettings.cs new file mode 100644 index 0000000000..004279f71d --- /dev/null +++ b/osu.Game/Rulesets/Mods/IModHasSettings.cs @@ -0,0 +1,15 @@ +// 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.Graphics; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// An interface for mods that allows user control over it's properties. + /// + public interface IModHasSettings + { + Drawable[] CreateControls(); + } +} diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 023d37497a..1c280c820d 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Mods /// /// Creates a copy of this initialised to a default state. /// - public virtual Mod CreateCopy() => (Mod)Activator.CreateInstance(GetType()); + public virtual Mod CreateCopy() => (Mod)MemberwiseClone(); public bool Equals(IMod other) => GetType() == other?.GetType(); } From 9375ef5eeae16c551b9e950becbb148a1a782dfd Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Tue, 8 Oct 2019 19:42:15 +0800 Subject: [PATCH 02/84] clear settings controls when changing rulesets --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 5493080964..a344bcc254 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -330,6 +330,7 @@ namespace osu.Game.Overlays.Mods if (mods != null) SelectedMods.BindTo(mods); SelectedMods.ValueChanged += updateModSettings; + Ruleset.ValueChanged += _ => ModSettingsContent.Clear(); sampleOn = audio.Samples.Get(@"UI/check-on"); sampleOff = audio.Samples.Get(@"UI/check-off"); From 2bc6932567548187d6696f4900ae98cfd1eaddc9 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 5 Nov 2019 00:55:55 +0800 Subject: [PATCH 03/84] make interface mod applicable --- osu.Game/Rulesets/Mods/IModHasSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/IModHasSettings.cs b/osu.Game/Rulesets/Mods/IModHasSettings.cs index 004279f71d..b5058de82b 100644 --- a/osu.Game/Rulesets/Mods/IModHasSettings.cs +++ b/osu.Game/Rulesets/Mods/IModHasSettings.cs @@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mods /// /// An interface for mods that allows user control over it's properties. /// - public interface IModHasSettings + public interface IModHasSettings : IApplicableMod { Drawable[] CreateControls(); } From a92b32f6dc94a164e5a2502e5f070240673f555e Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 5 Nov 2019 00:56:09 +0800 Subject: [PATCH 04/84] add basic tests --- .../TestSceneModCustomizationSettings.cs | 108 ++++++++++++++++++ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 8 +- 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs new file mode 100644 index 0000000000..ec5b3c1e16 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs @@ -0,0 +1,108 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Mods; +using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneModCustomizationSettings : OsuTestScene + { + private TestModSelectOverlay modSelect; + + [BackgroundDependencyLoader] + private void load() + { + Add(modSelect = new TestModSelectOverlay + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + }); + + var testMod = new TestModCustomizable(); + + AddStep("open", modSelect.Show); + AddAssert("button disabled", () => !modSelect.CustomizeButton.Enabled.Value); + AddStep("select mod", () => modSelect.SelectMod(testMod)); + AddAssert("button enabled", () => modSelect.CustomizeButton.Enabled.Value); + AddStep("open customization", () => modSelect.CustomizeButton.Click()); + AddAssert("controls exist", () => modSelect.GetControlSection(testMod) != null); + AddStep("deselect mod", () => modSelect.SelectMod(testMod)); + AddAssert("controls hidden", () => modSelect.ModSettingsContainer.Alpha == 0); + } + + private class TestModSelectOverlay : ModSelectOverlay + { + public new Container ModSettingsContainer => base.ModSettingsContainer; + public new TriangleButton CustomizeButton => base.CustomizeButton; + + public void SelectMod(Mod mod) => + ModSectionsContainer.Children.Single((s) => s.ModType == mod.Type) + .ButtonsContainer.OfType().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())).SelectNext(1); + + public ModControlSection GetControlSection(Mod mod) => + ModSettingsContent.Children.FirstOrDefault((s) => s.Mod == mod); + + protected override void LoadComplete() + { + base.LoadComplete(); + + foreach (var section in ModSectionsContainer) + if (section.ModType == ModType.Conversion) + section.Mods = new Mod[] { new TestModCustomizable() }; + else + section.Mods = new Mod[] { }; + } + } + + private class TestModCustomizable : Mod, IModHasSettings + { + public override string Name => "Customizable Mod"; + + public override string Acronym => "CM"; + + public override double ScoreMultiplier => 1.0; + + public override ModType Type => ModType.Conversion; + + public readonly BindableFloat sliderBindable = new BindableFloat + { + MinValue = 0, + MaxValue = 10, + }; + + public readonly BindableBool tickBindable = new BindableBool(); + + public Drawable[] CreateControls() + { + BindableFloat sliderControl = new BindableFloat(); + BindableBool tickControl = new BindableBool(); + + sliderControl.BindTo(sliderBindable); + tickControl.BindTo(tickBindable); + + return new Drawable[] + { + new SettingsSlider + { + LabelText = "Slider", + Bindable = sliderControl + }, + new SettingsCheckbox + { + LabelText = "Checkbox", + Bindable = tickControl + } + }; + } + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index a344bcc254..fc5ed1b3f3 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -331,6 +331,7 @@ namespace osu.Game.Overlays.Mods SelectedMods.ValueChanged += updateModSettings; Ruleset.ValueChanged += _ => ModSettingsContent.Clear(); + CustomizeButton.Enabled.Value = false; sampleOn = audio.Samples.Get(@"UI/check-on"); sampleOff = audio.Samples.Get(@"UI/check-off"); @@ -344,7 +345,12 @@ namespace osu.Game.Overlays.Mods if (added is IModHasSettings) ModSettingsContent.Add(new ModControlSection(added)); else if (removed is IModHasSettings) - ModSettingsContent.Remove(ModSettingsContent.Children.Where(section => section.Mod == removed).FirstOrDefault()); + ModSettingsContent.Remove(ModSettingsContent.Children.Where(section => section.Mod == removed).Single()); + + CustomizeButton.Enabled.Value = ModSettingsContent.Children.Count > 0; + + if (ModSettingsContainer.Alpha == 1 && ModSettingsContent.Children.Count == 0) + ModSettingsContainer.Alpha = 0; } public void DeselectAll() From 15e85234e4d4b80df421bb1c9d8383b35c5aab10 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 15 Nov 2019 11:12:51 +0800 Subject: [PATCH 05/84] remove unecessary step --- .../Visual/UserInterface/TestSceneModCustomizationSettings.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs index ec5b3c1e16..83d77cccdb 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs @@ -34,7 +34,6 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("select mod", () => modSelect.SelectMod(testMod)); AddAssert("button enabled", () => modSelect.CustomizeButton.Enabled.Value); AddStep("open customization", () => modSelect.CustomizeButton.Click()); - AddAssert("controls exist", () => modSelect.GetControlSection(testMod) != null); AddStep("deselect mod", () => modSelect.SelectMod(testMod)); AddAssert("controls hidden", () => modSelect.ModSettingsContainer.Alpha == 0); } From c4515429158bb5ed4a7a14b10b65f218fd8cd481 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 30 Nov 2019 03:01:07 +0300 Subject: [PATCH 06/84] Rankings overlay basic implementation --- .../Visual/Online/TestSceneRankingsOverlay.cs | 59 ++++++ osu.Game/Overlays/RankingsOverlay.cs | 198 ++++++++++++++++++ osu.Game/Users/Drawables/UpdateableFlag.cs | 14 ++ 3 files changed, 271 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs create mode 100644 osu.Game/Overlays/RankingsOverlay.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs new file mode 100644 index 0000000000..1f08fe7530 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs @@ -0,0 +1,59 @@ +// 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 osu.Game.Overlays.Rankings.Tables; +using osu.Framework.Allocation; +using osu.Game.Overlays; +using NUnit.Framework; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneRankingsOverlay : OsuTestScene + { + protected override bool UseOnlineAPI => true; + + public override IReadOnlyList RequiredTypes => new[] + { + typeof(PerformanceTable), + typeof(ScoresTable), + typeof(CountriesTable), + typeof(TableRowBackground), + typeof(UserBasedTable), + typeof(RankingsTable<>), + typeof(RankingsOverlay) + }; + + [Cached] + private RankingsOverlay rankingsOverlay; + + public TestSceneRankingsOverlay() + { + Add(rankingsOverlay = new RankingsOverlay()); + } + + [Test] + public void TestShow() + { + AddStep("Show", rankingsOverlay.Show); + } + + [Test] + public void TestShowCountry() + { + AddStep("Show US", () => rankingsOverlay.ShowCountry(new Country + { + FlagName = "US", + FullName = "United States" + })); + } + + [Test] + public void TestHide() + { + AddStep("Hide", rankingsOverlay.Hide); + } + } +} diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs new file mode 100644 index 0000000000..b9b2fe7232 --- /dev/null +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -0,0 +1,198 @@ +// 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.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Overlays.Rankings; +using osu.Game.Users; +using osu.Game.Rulesets; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using System.Threading; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Rankings.Tables; + +namespace osu.Game.Overlays +{ + public class RankingsOverlay : FullscreenOverlay + { + private readonly Bindable country = new Bindable(); + private readonly Bindable scope = new Bindable(); + private readonly Bindable ruleset = new Bindable(); + + private readonly BasicScrollContainer scrollFlow; + private readonly Box background; + private readonly Container contentPlaceholder; + private readonly DimmedLoadingLayer loading; + + private APIRequest request; + private CancellationTokenSource cancellationToken; + + [Resolved] + private IAPIProvider api { get; set; } + + public RankingsOverlay() + { + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + scrollFlow = new BasicScrollContainer + { + RelativeSizeAxes = Axes.Both, + ScrollbarVisible = false, + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new RankingsHeader + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Country = { BindTarget = country }, + Scope = { BindTarget = scope }, + Ruleset = { BindTarget = ruleset } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + contentPlaceholder = new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Margin = new MarginPadding { Vertical = 10 } + }, + loading = new DimmedLoadingLayer(), + } + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colour) + { + Waves.FirstWaveColour = colour.Green; + Waves.SecondWaveColour = colour.GreenLight; + Waves.ThirdWaveColour = colour.GreenDark; + Waves.FourthWaveColour = colour.GreenDarker; + + background.Colour = OsuColour.Gray(0.1f); + } + + protected override void LoadComplete() + { + country.BindValueChanged(_ => redraw(), true); + scope.BindValueChanged(_ => redraw(), true); + ruleset.BindValueChanged(_ => redraw(), true); + base.LoadComplete(); + } + + public void ShowCountry(Country requested) + { + if (requested == null) + return; + + Show(); + + if (country.Value?.FlagName == requested.FlagName) + return; + + country.Value = requested; + } + + private void redraw() + { + scrollFlow.ScrollToStart(); + + loading.Show(); + + cancellationToken?.Cancel(); + request?.Cancel(); + + cancellationToken = new CancellationTokenSource(); + + switch (scope.Value) + { + default: + contentPlaceholder.Clear(); + loading.Hide(); + return; + + case RankingsScope.Performance: + createPerformanceTable(); + return; + + case RankingsScope.Country: + createCountryTable(); + return; + + case RankingsScope.Score: + createScoreTable(); + return; + } + } + + private void createCountryTable() + { + request = new GetCountryRankingsRequest(ruleset.Value); + ((GetCountryRankingsRequest)request).Success += rankings => Schedule(() => + { + var table = new CountriesTable(1, rankings.Countries); + loadTable(table); + }); + + api.Queue(request); + } + + private void createPerformanceTable() + { + request = new GetUserRankingsRequest(ruleset.Value, country: country.Value?.FlagName); + ((GetUserRankingsRequest)request).Success += rankings => Schedule(() => + { + var table = new PerformanceTable(1, rankings.Users); + loadTable(table); + }); + + api.Queue(request); + } + + private void createScoreTable() + { + request = new GetUserRankingsRequest(ruleset.Value, UserRankingsType.Score); + ((GetUserRankingsRequest)request).Success += rankings => Schedule(() => + { + var table = new ScoresTable(1, rankings.Users); + loadTable(table); + }); + + api.Queue(request); + } + + private void loadTable(Drawable table) + { + LoadComponentAsync(table, t => + { + contentPlaceholder.Clear(); + contentPlaceholder.Add(t); + loading.Hide(); + }, cancellationToken.Token); + } + } +} diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs index abc16b2390..e7c23d3c23 100644 --- a/osu.Game/Users/Drawables/UpdateableFlag.cs +++ b/osu.Game/Users/Drawables/UpdateableFlag.cs @@ -1,8 +1,11 @@ // 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.Framework.Graphics.Containers; +using osu.Framework.Input.Events; +using osu.Game.Overlays; namespace osu.Game.Users.Drawables { @@ -34,5 +37,16 @@ namespace osu.Game.Users.Drawables RelativeSizeAxes = Axes.Both, }; } + + [Resolved(canBeNull: true)] + private RankingsOverlay rankingsOverlay { get; set; } + + protected override bool OnClick(ClickEvent e) + { + if (Country != null) + rankingsOverlay?.ShowCountry(Country); + + return true; + } } } From 0ac46755468b9bf840c816707674a626ae7e95c6 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 1 Dec 2019 03:52:41 +0300 Subject: [PATCH 07/84] Implement IEquatable --- osu.Game/Overlays/RankingsOverlay.cs | 3 --- osu.Game/Users/Country.cs | 5 ++++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index b9b2fe7232..d3586a538b 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -111,9 +111,6 @@ namespace osu.Game.Overlays Show(); - if (country.Value?.FlagName == requested.FlagName) - return; - country.Value = requested; } diff --git a/osu.Game/Users/Country.cs b/osu.Game/Users/Country.cs index 1dcce6e870..101d268a60 100644 --- a/osu.Game/Users/Country.cs +++ b/osu.Game/Users/Country.cs @@ -1,11 +1,12 @@ // 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 Newtonsoft.Json; namespace osu.Game.Users { - public class Country + public class Country : IEquatable { /// /// The name of this country. @@ -18,5 +19,7 @@ namespace osu.Game.Users /// [JsonProperty(@"code")] public string FlagName; + + public bool Equals(Country other) => FlagName == other.FlagName; } } From f375db368f1ac0208d7dd4dd5497da93ec6ab448 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 1 Dec 2019 03:56:03 +0300 Subject: [PATCH 08/84] Remove useless null check --- osu.Game/Users/Drawables/UpdateableFlag.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs index e7c23d3c23..1d30720889 100644 --- a/osu.Game/Users/Drawables/UpdateableFlag.cs +++ b/osu.Game/Users/Drawables/UpdateableFlag.cs @@ -43,9 +43,7 @@ namespace osu.Game.Users.Drawables protected override bool OnClick(ClickEvent e) { - if (Country != null) - rankingsOverlay?.ShowCountry(Country); - + rankingsOverlay?.ShowCountry(Country); return true; } } From 62daea195c5ab9b90d170c878c5fbaba5161e360 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 1 Dec 2019 04:09:45 +0300 Subject: [PATCH 09/84] Fix possible null --- osu.Game/Users/Country.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Users/Country.cs b/osu.Game/Users/Country.cs index 101d268a60..a9fcd69286 100644 --- a/osu.Game/Users/Country.cs +++ b/osu.Game/Users/Country.cs @@ -20,6 +20,6 @@ namespace osu.Game.Users [JsonProperty(@"code")] public string FlagName; - public bool Equals(Country other) => FlagName == other.FlagName; + public bool Equals(Country other) => FlagName == other?.FlagName; } } From 48732e49b94967317181137d08f2cbc642f54b9f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 5 Dec 2019 04:20:13 +0300 Subject: [PATCH 10/84] Improve async logic --- osu.Game/Overlays/RankingsOverlay.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index d3586a538b..948d165b82 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -15,6 +15,7 @@ using osu.Game.Online.API; using System.Threading; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Rankings.Tables; +using osu.Framework.Threading; namespace osu.Game.Overlays { @@ -30,6 +31,7 @@ namespace osu.Game.Overlays private readonly DimmedLoadingLayer loading; private APIRequest request; + private ScheduledDelegate showTableDelegate; private CancellationTokenSource cancellationToken; [Resolved] @@ -120,11 +122,10 @@ namespace osu.Game.Overlays loading.Show(); + showTableDelegate?.Cancel(); cancellationToken?.Cancel(); request?.Cancel(); - cancellationToken = new CancellationTokenSource(); - switch (scope.Value) { default: @@ -184,12 +185,12 @@ namespace osu.Game.Overlays private void loadTable(Drawable table) { - LoadComponentAsync(table, t => + showTableDelegate = Schedule(() => LoadComponentAsync(table, t => { contentPlaceholder.Clear(); contentPlaceholder.Add(t); loading.Hide(); - }, cancellationToken.Token); + }, (cancellationToken = new CancellationTokenSource()).Token)); } } } From f8f144b6c04671f0daf7155791e7314e5cdf26e3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 5 Dec 2019 05:20:22 +0300 Subject: [PATCH 11/84] Remove pointless ScheduledDelegate --- osu.Game/Overlays/RankingsOverlay.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index 948d165b82..0ec03ebcc9 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -15,7 +15,6 @@ using osu.Game.Online.API; using System.Threading; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Rankings.Tables; -using osu.Framework.Threading; namespace osu.Game.Overlays { @@ -31,7 +30,6 @@ namespace osu.Game.Overlays private readonly DimmedLoadingLayer loading; private APIRequest request; - private ScheduledDelegate showTableDelegate; private CancellationTokenSource cancellationToken; [Resolved] @@ -122,7 +120,6 @@ namespace osu.Game.Overlays loading.Show(); - showTableDelegate?.Cancel(); cancellationToken?.Cancel(); request?.Cancel(); @@ -185,12 +182,12 @@ namespace osu.Game.Overlays private void loadTable(Drawable table) { - showTableDelegate = Schedule(() => LoadComponentAsync(table, t => + LoadComponentAsync(table, t => { contentPlaceholder.Clear(); contentPlaceholder.Add(t); loading.Hide(); - }, (cancellationToken = new CancellationTokenSource()).Token)); + }, (cancellationToken = new CancellationTokenSource()).Token); } } } From 5f9b9631ef38bae53c320a9aa1512250399b1f47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Dec 2019 14:07:39 +0900 Subject: [PATCH 12/84] Move scope/country restrictions into RankingsOverlay --- osu.Game/Overlays/Rankings/HeaderTitle.cs | 10 +--------- osu.Game/Overlays/RankingsOverlay.cs | 24 +++++++++++++++++++---- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Rankings/HeaderTitle.cs b/osu.Game/Overlays/Rankings/HeaderTitle.cs index a1a893fa6b..b08a2a3900 100644 --- a/osu.Game/Overlays/Rankings/HeaderTitle.cs +++ b/osu.Game/Overlays/Rankings/HeaderTitle.cs @@ -74,13 +74,7 @@ namespace osu.Game.Overlays.Rankings base.LoadComplete(); } - private void onScopeChanged(ValueChangedEvent scope) - { - scopeText.Text = scope.NewValue.ToString(); - - if (scope.NewValue != RankingsScope.Performance) - Country.Value = null; - } + private void onScopeChanged(ValueChangedEvent scope) => scopeText.Text = scope.NewValue.ToString(); private void onCountryChanged(ValueChangedEvent country) { @@ -90,8 +84,6 @@ namespace osu.Game.Overlays.Rankings return; } - Scope.Value = RankingsScope.Performance; - flag.Country = country.NewValue; flag.Show(); } diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index 0ec03ebcc9..b989e9473b 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -98,9 +98,25 @@ namespace osu.Game.Overlays protected override void LoadComplete() { - country.BindValueChanged(_ => redraw(), true); - scope.BindValueChanged(_ => redraw(), true); - ruleset.BindValueChanged(_ => redraw(), true); + country.BindValueChanged(_ => + { + // if a country is requested, force performance scope. + if (country.Value != null) + scope.Value = RankingsScope.Performance; + + Scheduler.AddOnce(loadNewContent); + }, true); + scope.BindValueChanged(_ => + { + // country filtering is only valid for performance scope. + if (scope.Value != RankingsScope.Performance) + country.Value = null; + + Scheduler.AddOnce(loadNewContent); + }, true); + + ruleset.BindValueChanged(_ => Scheduler.AddOnce(loadNewContent), true); + base.LoadComplete(); } @@ -114,7 +130,7 @@ namespace osu.Game.Overlays country.Value = requested; } - private void redraw() + private void loadNewContent() { scrollFlow.ScrollToStart(); From cd473f207a1dab9f058cc1e370508fdc63e3a0a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Dec 2019 14:09:33 +0900 Subject: [PATCH 13/84] Use child set, not Clear/Add --- osu.Game/Overlays/RankingsOverlay.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index b989e9473b..154cf0e47b 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -200,8 +200,7 @@ namespace osu.Game.Overlays { LoadComponentAsync(table, t => { - contentPlaceholder.Clear(); - contentPlaceholder.Add(t); + contentPlaceholder.Child = t; loading.Hide(); }, (cancellationToken = new CancellationTokenSource()).Token); } From 6e9157d59c75cf680c11d216f68cd42b233b9dc2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Dec 2019 14:26:36 +0900 Subject: [PATCH 14/84] Standardise request/response handling --- .../API/Requests/GetUserRankingsRequest.cs | 7 +- osu.Game/Overlays/RankingsOverlay.cs | 97 ++++++++++--------- 2 files changed, 55 insertions(+), 49 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs index 9c3eba9fdc..6f657aee8d 100644 --- a/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs @@ -8,13 +8,14 @@ namespace osu.Game.Online.API.Requests { public class GetUserRankingsRequest : GetRankingsRequest { + public readonly UserRankingsType Type; + private readonly string country; - private readonly UserRankingsType type; public GetUserRankingsRequest(RulesetInfo ruleset, UserRankingsType type = UserRankingsType.Performance, int page = 1, string country = null) : base(ruleset, page) { - this.type = type; + this.Type = type; this.country = country; } @@ -28,7 +29,7 @@ namespace osu.Game.Online.API.Requests return req; } - protected override string TargetPostfix() => type.ToString().ToLowerInvariant(); + protected override string TargetPostfix() => Type.ToString().ToLowerInvariant(); } public enum UserRankingsType diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index 154cf0e47b..aa42b029e0 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -26,10 +26,10 @@ namespace osu.Game.Overlays private readonly BasicScrollContainer scrollFlow; private readonly Box background; - private readonly Container contentPlaceholder; + private readonly Container tableContainer; private readonly DimmedLoadingLayer loading; - private APIRequest request; + private APIRequest lastRequest; private CancellationTokenSource cancellationToken; [Resolved] @@ -68,7 +68,7 @@ namespace osu.Game.Overlays AutoSizeAxes = Axes.Y, Children = new Drawable[] { - contentPlaceholder = new Container + tableContainer = new Container { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -132,76 +132,81 @@ namespace osu.Game.Overlays private void loadNewContent() { - scrollFlow.ScrollToStart(); - loading.Show(); cancellationToken?.Cancel(); - request?.Cancel(); + lastRequest?.Cancel(); + var request = createScopedRequest(); + lastRequest = request; + + if (request == null) + { + loadTable(null); + return; + } + + request.Success += () => loadTable(createTableFromResponse(request)); + request.Failure += _ => loadTable(null); + + api.Queue(request); + } + + private APIRequest createScopedRequest() + { switch (scope.Value) { - default: - contentPlaceholder.Clear(); - loading.Hide(); - return; - case RankingsScope.Performance: - createPerformanceTable(); - return; + return new GetUserRankingsRequest(ruleset.Value, country: country.Value?.FlagName); case RankingsScope.Country: - createCountryTable(); - return; + return new GetCountryRankingsRequest(ruleset.Value); case RankingsScope.Score: - createScoreTable(); - return; + return new GetUserRankingsRequest(ruleset.Value, UserRankingsType.Score); } + + return null; } - private void createCountryTable() + private Drawable createTableFromResponse(APIRequest request) { - request = new GetCountryRankingsRequest(ruleset.Value); - ((GetCountryRankingsRequest)request).Success += rankings => Schedule(() => + switch (request) { - var table = new CountriesTable(1, rankings.Countries); - loadTable(table); - }); + case GetUserRankingsRequest userRequest: + switch (userRequest.Type) + { + case UserRankingsType.Performance: + return new PerformanceTable(1, userRequest.Result.Users); - api.Queue(request); - } + case UserRankingsType.Score: + return new ScoresTable(1, userRequest.Result.Users); + } - private void createPerformanceTable() - { - request = new GetUserRankingsRequest(ruleset.Value, country: country.Value?.FlagName); - ((GetUserRankingsRequest)request).Success += rankings => Schedule(() => - { - var table = new PerformanceTable(1, rankings.Users); - loadTable(table); - }); + return null; - api.Queue(request); - } + case GetCountryRankingsRequest countryRequest: + return new CountriesTable(1, countryRequest.Result.Countries); + } - private void createScoreTable() - { - request = new GetUserRankingsRequest(ruleset.Value, UserRankingsType.Score); - ((GetUserRankingsRequest)request).Success += rankings => Schedule(() => - { - var table = new ScoresTable(1, rankings.Users); - loadTable(table); - }); - - api.Queue(request); + return null; } private void loadTable(Drawable table) { + scrollFlow.ScrollToStart(); + + if (table == null) + { + tableContainer.Clear(); + loading.Hide(); + return; + } + LoadComponentAsync(table, t => { - contentPlaceholder.Child = t; loading.Hide(); + tableContainer.Child = table; }, (cancellationToken = new CancellationTokenSource()).Token); } } From de413418c7404f7c34fcb75ef16a6fac942ae5ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 5 Dec 2019 14:50:12 +0900 Subject: [PATCH 15/84] Remove redundant prefix --- osu.Game/Online/API/Requests/GetUserRankingsRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs index 6f657aee8d..143d21e40d 100644 --- a/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserRankingsRequest.cs @@ -15,7 +15,7 @@ namespace osu.Game.Online.API.Requests public GetUserRankingsRequest(RulesetInfo ruleset, UserRankingsType type = UserRankingsType.Performance, int page = 1, string country = null) : base(ruleset, page) { - this.Type = type; + Type = type; this.country = country; } From 33737d3d89b7381b29f695c5bfc6312464c74ff2 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 5 Dec 2019 09:53:25 +0300 Subject: [PATCH 16/84] Move tests to TestSceneRankingsOverlay due to refactoring --- .../Visual/Online/TestSceneRankingsHeader.cs | 2 - .../Online/TestSceneRankingsHeaderTitle.cs | 5 --- .../Visual/Online/TestSceneRankingsOverlay.cs | 39 ++++++++++++++++--- osu.Game/Overlays/RankingsOverlay.cs | 26 ++++++------- 4 files changed, 46 insertions(+), 26 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs index c0da605cdb..e708934bc3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs @@ -68,9 +68,7 @@ namespace osu.Game.Tests.Visual.Online }; AddStep("Set country", () => countryBindable.Value = country); - AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance); AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score); - AddAssert("Check country is Null", () => countryBindable.Value == null); AddStep("Set country with no flag", () => countryBindable.Value = unknownCountry); } } diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs index 849ca2defc..0edf104da0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs @@ -43,11 +43,6 @@ namespace osu.Game.Tests.Visual.Online FullName = "United States" }; - AddStep("Set country", () => countryBindable.Value = countryA); - AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance); - AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score); - AddAssert("Check country is Null", () => countryBindable.Value == null); - AddStep("Set country 1", () => countryBindable.Value = countryA); AddStep("Set country 2", () => countryBindable.Value = countryB); AddStep("Set null country", () => countryBindable.Value = null); diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs index 1f08fe7530..09f008b308 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs @@ -8,6 +8,8 @@ using osu.Framework.Allocation; using osu.Game.Overlays; using NUnit.Framework; using osu.Game.Users; +using osu.Framework.Bindables; +using osu.Game.Overlays.Rankings; namespace osu.Game.Tests.Visual.Online { @@ -29,9 +31,16 @@ namespace osu.Game.Tests.Visual.Online [Cached] private RankingsOverlay rankingsOverlay; + private readonly Bindable countryBindable = new Bindable(); + private readonly Bindable scope = new Bindable(); + public TestSceneRankingsOverlay() { - Add(rankingsOverlay = new RankingsOverlay()); + Add(rankingsOverlay = new TestRankingsOverlay + { + Country = { BindTarget = countryBindable }, + Scope = { BindTarget = scope }, + }); } [Test] @@ -40,14 +49,19 @@ namespace osu.Game.Tests.Visual.Online AddStep("Show", rankingsOverlay.Show); } + [Test] + public void TestFlagScopeDependency() + { + AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score); + AddAssert("Check country is Null", () => countryBindable.Value == null); + AddStep("Set country", () => countryBindable.Value = us_country); + AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance); + } + [Test] public void TestShowCountry() { - AddStep("Show US", () => rankingsOverlay.ShowCountry(new Country - { - FlagName = "US", - FullName = "United States" - })); + AddStep("Show US", () => rankingsOverlay.ShowCountry(us_country)); } [Test] @@ -55,5 +69,18 @@ namespace osu.Game.Tests.Visual.Online { AddStep("Hide", rankingsOverlay.Hide); } + + private static Country us_country = new Country + { + FlagName = "US", + FullName = "United States" + }; + + private class TestRankingsOverlay : RankingsOverlay + { + public new Bindable Country => base.Country; + + public new Bindable Scope => base.Scope; + } } } diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index aa42b029e0..e7c8b94a10 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -20,8 +20,8 @@ namespace osu.Game.Overlays { public class RankingsOverlay : FullscreenOverlay { - private readonly Bindable country = new Bindable(); - private readonly Bindable scope = new Bindable(); + protected readonly Bindable Country = new Bindable(); + protected readonly Bindable Scope = new Bindable(); private readonly Bindable ruleset = new Bindable(); private readonly BasicScrollContainer scrollFlow; @@ -58,8 +58,8 @@ namespace osu.Game.Overlays { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Country = { BindTarget = country }, - Scope = { BindTarget = scope }, + Country = { BindTarget = Country }, + Scope = { BindTarget = Scope }, Ruleset = { BindTarget = ruleset } }, new Container @@ -98,19 +98,19 @@ namespace osu.Game.Overlays protected override void LoadComplete() { - country.BindValueChanged(_ => + Country.BindValueChanged(_ => { // if a country is requested, force performance scope. - if (country.Value != null) - scope.Value = RankingsScope.Performance; + if (Country.Value != null) + Scope.Value = RankingsScope.Performance; Scheduler.AddOnce(loadNewContent); }, true); - scope.BindValueChanged(_ => + Scope.BindValueChanged(_ => { // country filtering is only valid for performance scope. - if (scope.Value != RankingsScope.Performance) - country.Value = null; + if (Scope.Value != RankingsScope.Performance) + Country.Value = null; Scheduler.AddOnce(loadNewContent); }, true); @@ -127,7 +127,7 @@ namespace osu.Game.Overlays Show(); - country.Value = requested; + Country.Value = requested; } private void loadNewContent() @@ -154,10 +154,10 @@ namespace osu.Game.Overlays private APIRequest createScopedRequest() { - switch (scope.Value) + switch (Scope.Value) { case RankingsScope.Performance: - return new GetUserRankingsRequest(ruleset.Value, country: country.Value?.FlagName); + return new GetUserRankingsRequest(ruleset.Value, country: Country.Value?.FlagName); case RankingsScope.Country: return new GetCountryRankingsRequest(ruleset.Value); From bf7c309d4c87c7e74f0bcca629c81976067d00dd Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 5 Dec 2019 10:05:04 +0300 Subject: [PATCH 17/84] Make field readonly --- osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs index 09f008b308..568e36df4c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsOverlay.cs @@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("Hide", rankingsOverlay.Hide); } - private static Country us_country = new Country + private static readonly Country us_country = new Country { FlagName = "US", FullName = "United States" From af35df4077015c5b45ebb5b8f304e9bdc58702f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 15:42:11 +0900 Subject: [PATCH 18/84] Add multiple mod testing and update test code style --- .../TestSceneModCustomizationSettings.cs | 56 +++++++++++-------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs index 83d77cccdb..f789e60252 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.UserInterface Anchor = Anchor.BottomCentre, }); - var testMod = new TestModCustomizable(); + var testMod = new TestModCustomizable1(); AddStep("open", modSelect.Show); AddAssert("button disabled", () => !modSelect.CustomizeButton.Enabled.Value); @@ -44,64 +44,74 @@ namespace osu.Game.Tests.Visual.UserInterface public new TriangleButton CustomizeButton => base.CustomizeButton; public void SelectMod(Mod mod) => - ModSectionsContainer.Children.Single((s) => s.ModType == mod.Type) - .ButtonsContainer.OfType().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())).SelectNext(1); + ModSectionsContainer.Children.Single(s => s.ModType == mod.Type) + .ButtonsContainer.OfType().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())).SelectNext(1); public ModControlSection GetControlSection(Mod mod) => - ModSettingsContent.Children.FirstOrDefault((s) => s.Mod == mod); + ModSettingsContent.Children.FirstOrDefault(s => s.Mod == mod); protected override void LoadComplete() { base.LoadComplete(); foreach (var section in ModSectionsContainer) + { if (section.ModType == ModType.Conversion) - section.Mods = new Mod[] { new TestModCustomizable() }; + { + section.Mods = new Mod[] + { + new TestModCustomizable1(), + new TestModCustomizable2() + }; + } else section.Mods = new Mod[] { }; + } } } - private class TestModCustomizable : Mod, IModHasSettings + private class TestModCustomizable1 : TestModCustomizable { - public override string Name => "Customizable Mod"; + public override string Name => "Customizable Mod 1"; - public override string Acronym => "CM"; + public override string Acronym => "CM1"; + } + private class TestModCustomizable2 : TestModCustomizable + { + public override string Name => "Customizable Mod 2"; + + public override string Acronym => "CM2"; + } + + private abstract class TestModCustomizable : Mod, IModHasSettings + { public override double ScoreMultiplier => 1.0; public override ModType Type => ModType.Conversion; - public readonly BindableFloat sliderBindable = new BindableFloat + public readonly BindableFloat SliderBindable = new BindableFloat { MinValue = 0, MaxValue = 10, }; - public readonly BindableBool tickBindable = new BindableBool(); + public readonly BindableBool TickBindable = new BindableBool(); - public Drawable[] CreateControls() - { - BindableFloat sliderControl = new BindableFloat(); - BindableBool tickControl = new BindableBool(); - - sliderControl.BindTo(sliderBindable); - tickControl.BindTo(tickBindable); - - return new Drawable[] + public Drawable[] CreateControls() => + new Drawable[] { new SettingsSlider { LabelText = "Slider", - Bindable = sliderControl + Bindable = SliderBindable }, new SettingsCheckbox { LabelText = "Checkbox", - Bindable = tickControl + Bindable = TickBindable } }; - } } } -} \ No newline at end of file +} diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index fc5ed1b3f3..d7d1135e81 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -345,7 +345,7 @@ namespace osu.Game.Overlays.Mods if (added is IModHasSettings) ModSettingsContent.Add(new ModControlSection(added)); else if (removed is IModHasSettings) - ModSettingsContent.Remove(ModSettingsContent.Children.Where(section => section.Mod == removed).Single()); + ModSettingsContent.Remove(ModSettingsContent.Children.Single(section => section.Mod == removed)); CustomizeButton.Enabled.Value = ModSettingsContent.Children.Count > 0; From 23e47530c50a38bd42874f85ec4c698b967f9e86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 17:09:24 +0900 Subject: [PATCH 19/84] Add a method for getting settings UI components automatically from a target class --- .../Configuration/SettingSourceAttribute.cs | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 osu.Game/Configuration/SettingSourceAttribute.cs diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs new file mode 100644 index 0000000000..fe582b1461 --- /dev/null +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -0,0 +1,85 @@ +// 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 System.Reflection; +using JetBrains.Annotations; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Overlays.Settings; + +namespace osu.Game.Configuration +{ + /// + /// An attribute to mark a bindable as being exposed to the user via settings controls. + /// Can be used in conjunction with to automatically create UI controls. + /// + [MeansImplicitUse] + [AttributeUsage(AttributeTargets.Property)] + public class SettingSourceAttribute : Attribute + { + public string Label { get; } + + public string Description { get; } + + public SettingSourceAttribute(string label, string description = null) + { + Label = label ?? string.Empty; + Description = description ?? string.Empty; + } + } + + public static class SettingSourceExtensions + { + public static IEnumerable CreateSettingsControls(this object obj) + { + var configProperties = obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttribute(true) != null); + + foreach (var property in configProperties) + { + var attr = property.GetCustomAttribute(true); + + switch (property.GetValue(obj)) + { + case BindableNumber bNumber: + yield return new SettingsSlider + { + LabelText = attr.Label, + Bindable = bNumber + }; + + break; + + case BindableNumber bNumber: + yield return new SettingsSlider + { + LabelText = attr.Label, + Bindable = bNumber + }; + + break; + + case BindableNumber bNumber: + yield return new SettingsSlider + { + LabelText = attr.Label, + Bindable = bNumber + }; + + break; + + case Bindable bBool: + yield return new SettingsCheckbox + { + LabelText = attr.Label, + Bindable = bBool + }; + + break; + } + } + } + } +} From a5d5099868f2c7d1a8672dc266030065366592c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 17:09:48 +0900 Subject: [PATCH 20/84] Use SettingsSource for mod cusomisation --- .../TestSceneModCustomizationSettings.cs | 30 +++++-------------- osu.Game/Overlays/Mods/ModControlSection.cs | 6 ++-- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 24 ++++++++------- osu.Game/Rulesets/Mods/IModHasSettings.cs | 15 ---------- 4 files changed, 25 insertions(+), 50 deletions(-) delete mode 100644 osu.Game/Rulesets/Mods/IModHasSettings.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs index f789e60252..8227fe8f7a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs @@ -6,9 +6,9 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; -using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; namespace osu.Game.Tests.Visual.UserInterface @@ -47,9 +47,6 @@ namespace osu.Game.Tests.Visual.UserInterface ModSectionsContainer.Children.Single(s => s.ModType == mod.Type) .ButtonsContainer.OfType().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())).SelectNext(1); - public ModControlSection GetControlSection(Mod mod) => - ModSettingsContent.Children.FirstOrDefault(s => s.Mod == mod); - protected override void LoadComplete() { base.LoadComplete(); @@ -84,34 +81,23 @@ namespace osu.Game.Tests.Visual.UserInterface public override string Acronym => "CM2"; } - private abstract class TestModCustomizable : Mod, IModHasSettings + private abstract class TestModCustomizable : Mod, IApplicableMod { public override double ScoreMultiplier => 1.0; public override ModType Type => ModType.Conversion; - public readonly BindableFloat SliderBindable = new BindableFloat + [SettingSource("Sample float", "Change something for a mod")] + public BindableFloat SliderBindable { get; } = new BindableFloat { MinValue = 0, MaxValue = 10, + Default = 5, + Value = 7 }; - public readonly BindableBool TickBindable = new BindableBool(); - - public Drawable[] CreateControls() => - new Drawable[] - { - new SettingsSlider - { - LabelText = "Slider", - Bindable = SliderBindable - }, - new SettingsCheckbox - { - LabelText = "Checkbox", - Bindable = TickBindable - } - }; + [SettingSource("Sample bool", "Clicking this changes a setting")] + public BindableBool TickBindable { get; } = new BindableBool(); } } } diff --git a/osu.Game/Overlays/Mods/ModControlSection.cs b/osu.Game/Overlays/Mods/ModControlSection.cs index df6f4d15b0..f4b588ddb3 100644 --- a/osu.Game/Overlays/Mods/ModControlSection.cs +++ b/osu.Game/Overlays/Mods/ModControlSection.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mods; @@ -34,8 +35,7 @@ namespace osu.Game.Overlays.Mods RelativeSizeAxes = Axes.X, }; - if (Mod is IModHasSettings modHasSettings) - AddRange(modHasSettings.CreateControls()); + AddRange(Mod.CreateSettingsControls()); } [BackgroundDependencyLoader] @@ -53,4 +53,4 @@ namespace osu.Game.Overlays.Mods }); } } -} \ No newline at end of file +} diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index d7d1135e81..cfad0126eb 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; @@ -236,6 +237,7 @@ namespace osu.Game.Overlays.Mods Width = 180, Text = "Customization", Action = () => ModSettingsContainer.Alpha = ModSettingsContainer.Alpha == 1 ? 0 : 1, + Enabled = { Value = false }, Margin = new MarginPadding { Right = 20 @@ -331,7 +333,6 @@ namespace osu.Game.Overlays.Mods SelectedMods.ValueChanged += updateModSettings; Ruleset.ValueChanged += _ => ModSettingsContent.Clear(); - CustomizeButton.Enabled.Value = false; sampleOn = audio.Samples.Get(@"UI/check-on"); sampleOff = audio.Samples.Get(@"UI/check-off"); @@ -339,18 +340,21 @@ namespace osu.Game.Overlays.Mods private void updateModSettings(ValueChangedEvent> selectedMods) { - var added = selectedMods.NewValue.Except(selectedMods.OldValue).FirstOrDefault(); - var removed = selectedMods.OldValue.Except(selectedMods.NewValue).FirstOrDefault(); + foreach (var added in selectedMods.NewValue.Except(selectedMods.OldValue)) + { + var controls = added.CreateSettingsControls().ToList(); + if (controls.Count > 0) + ModSettingsContent.Add(new ModControlSection(added) { Children = controls }); + } - if (added is IModHasSettings) - ModSettingsContent.Add(new ModControlSection(added)); - else if (removed is IModHasSettings) - ModSettingsContent.Remove(ModSettingsContent.Children.Single(section => section.Mod == removed)); + foreach (var removed in selectedMods.OldValue.Except(selectedMods.NewValue)) + ModSettingsContent.RemoveAll(section => section.Mod == removed); - CustomizeButton.Enabled.Value = ModSettingsContent.Children.Count > 0; + bool hasSettings = ModSettingsContent.Children.Count > 0; + CustomizeButton.Enabled.Value = hasSettings; - if (ModSettingsContainer.Alpha == 1 && ModSettingsContent.Children.Count == 0) - ModSettingsContainer.Alpha = 0; + if (!hasSettings) + ModSettingsContainer.Hide(); } public void DeselectAll() diff --git a/osu.Game/Rulesets/Mods/IModHasSettings.cs b/osu.Game/Rulesets/Mods/IModHasSettings.cs deleted file mode 100644 index b5058de82b..0000000000 --- a/osu.Game/Rulesets/Mods/IModHasSettings.cs +++ /dev/null @@ -1,15 +0,0 @@ -// 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.Graphics; - -namespace osu.Game.Rulesets.Mods -{ - /// - /// An interface for mods that allows user control over it's properties. - /// - public interface IModHasSettings : IApplicableMod - { - Drawable[] CreateControls(); - } -} From 9de032e35f1394571e3fcc05b4caa94a81b3e27d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 17:10:06 +0900 Subject: [PATCH 21/84] Fix SettingsItem bindable logic --- osu.Game/Overlays/Settings/SettingsItem.cs | 29 ++++++++-------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 8863e43cca..212ef5545d 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -53,27 +53,10 @@ namespace osu.Game.Overlays.Settings } } - // hold a reference to the provided bindable so we don't have to in every settings section. - private Bindable bindable; - public virtual Bindable Bindable { - get => bindable; - - set - { - if (bindable != null) - controlWithCurrent?.Current.UnbindFrom(bindable); - - bindable = value; - controlWithCurrent?.Current.BindTo(bindable); - - if (ShowsDefaultIndicator) - { - restoreDefaultButton.Bindable = bindable.GetBoundCopy(); - restoreDefaultButton.Bindable.TriggerChange(); - } - } + get => controlWithCurrent.Current; + set => controlWithCurrent.Current = value; } public virtual IEnumerable FilterTerms => Keywords == null ? new[] { LabelText } : new List(Keywords) { LabelText }.ToArray(); @@ -110,7 +93,15 @@ namespace osu.Game.Overlays.Settings private void load() { if (controlWithCurrent != null) + { controlWithCurrent.Current.DisabledChanged += disabled => { Colour = disabled ? Color4.Gray : Color4.White; }; + + if (ShowsDefaultIndicator) + { + restoreDefaultButton.Bindable = controlWithCurrent.Current; + restoreDefaultButton.Bindable.TriggerChange(); + } + } } private class RestoreDefaultValueButton : Container, IHasTooltip From 901eb5d99639edc7a21caa1f2c7577507415e42a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 18:03:50 +0900 Subject: [PATCH 22/84] Fix incorrect trigger logic --- osu.Game/Overlays/Settings/SettingsItem.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 212ef5545d..9c390c34ec 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -97,10 +97,7 @@ namespace osu.Game.Overlays.Settings controlWithCurrent.Current.DisabledChanged += disabled => { Colour = disabled ? Color4.Gray : Color4.White; }; if (ShowsDefaultIndicator) - { restoreDefaultButton.Bindable = controlWithCurrent.Current; - restoreDefaultButton.Bindable.TriggerChange(); - } } } @@ -116,6 +113,7 @@ namespace osu.Game.Overlays.Settings bindable = value; bindable.ValueChanged += _ => UpdateState(); bindable.DisabledChanged += _ => UpdateState(); + UpdateState(); } } From ed6d1ccd958622bbca51c8cab54f0c3dfd4a6296 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 17:09:24 +0900 Subject: [PATCH 23/84] Add a method for getting settings UI components automatically from a target class --- .../Configuration/SettingSourceAttribute.cs | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 osu.Game/Configuration/SettingSourceAttribute.cs diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs new file mode 100644 index 0000000000..fe582b1461 --- /dev/null +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -0,0 +1,85 @@ +// 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 System.Reflection; +using JetBrains.Annotations; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Overlays.Settings; + +namespace osu.Game.Configuration +{ + /// + /// An attribute to mark a bindable as being exposed to the user via settings controls. + /// Can be used in conjunction with to automatically create UI controls. + /// + [MeansImplicitUse] + [AttributeUsage(AttributeTargets.Property)] + public class SettingSourceAttribute : Attribute + { + public string Label { get; } + + public string Description { get; } + + public SettingSourceAttribute(string label, string description = null) + { + Label = label ?? string.Empty; + Description = description ?? string.Empty; + } + } + + public static class SettingSourceExtensions + { + public static IEnumerable CreateSettingsControls(this object obj) + { + var configProperties = obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttribute(true) != null); + + foreach (var property in configProperties) + { + var attr = property.GetCustomAttribute(true); + + switch (property.GetValue(obj)) + { + case BindableNumber bNumber: + yield return new SettingsSlider + { + LabelText = attr.Label, + Bindable = bNumber + }; + + break; + + case BindableNumber bNumber: + yield return new SettingsSlider + { + LabelText = attr.Label, + Bindable = bNumber + }; + + break; + + case BindableNumber bNumber: + yield return new SettingsSlider + { + LabelText = attr.Label, + Bindable = bNumber + }; + + break; + + case Bindable bBool: + yield return new SettingsCheckbox + { + LabelText = attr.Label, + Bindable = bBool + }; + + break; + } + } + } + } +} From 2fa0b30fa27801b00927b49025134cf8c8ee8ce1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 17:49:41 +0900 Subject: [PATCH 24/84] Add textbox and dropdown support --- .../Configuration/SettingSourceAttribute.cs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index fe582b1461..dceef1cb7d 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -8,6 +8,7 @@ using System.Reflection; using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; using osu.Game.Overlays.Settings; namespace osu.Game.Configuration @@ -40,8 +41,9 @@ namespace osu.Game.Configuration foreach (var property in configProperties) { var attr = property.GetCustomAttribute(true); + var prop = property.GetValue(obj); - switch (property.GetValue(obj)) + switch (prop) { case BindableNumber bNumber: yield return new SettingsSlider @@ -78,6 +80,30 @@ namespace osu.Game.Configuration }; break; + + case Bindable bString: + yield return new SettingsTextBox + { + LabelText = attr.Label, + Bindable = bString + }; + + break; + + case IBindable bindable: + + var dropdownType = typeof(SettingsEnumDropdown<>).MakeGenericType(bindable.GetType().GetGenericArguments()[0]); + + var dropdown = (Drawable)Activator.CreateInstance(dropdownType); + + dropdown.GetType().GetProperty(nameof(IHasCurrentValue.Current))?.SetValue(dropdown, obj); + + yield return dropdown; + + break; + + default: + throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} was attached to an unsupported type ({prop})"); } } } From f84705ab9605776f579f745ac6164b856bf5903e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 17:49:52 +0900 Subject: [PATCH 25/84] Add tests --- .../Settings/TestSceneSettingsSource.cs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs new file mode 100644 index 0000000000..e3dae9c27e --- /dev/null +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Configuration; +using osuTK; + +namespace osu.Game.Tests.Visual.Settings +{ + [TestFixture] + public class TestSceneSettingsSource : OsuTestScene + { + public TestSceneSettingsSource() + { + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(20), + Width = 0.5f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Padding = new MarginPadding(50), + ChildrenEnumerable = new TestTargetClass().CreateSettingsControls() + }, + }; + } + + private class TestTargetClass + { + [SettingSource("Sample bool", "Clicking this changes a setting")] + public BindableBool TickBindable { get; } = new BindableBool(); + + [SettingSource("Sample float", "Change something for a mod")] + public BindableFloat SliderBindable { get; } = new BindableFloat + { + MinValue = 0, + MaxValue = 10, + Default = 5, + Value = 7 + }; + + [SettingSource("Sample enum", "Change something for a mod")] + public Bindable EnumBindable { get; } = new Bindable + { + Default = TestEnum.Value1, + Value = TestEnum.Value2 + }; + + [SettingSource("Sample string", "Change something for a mod")] + public Bindable StringBindable { get; } = new Bindable(); + } + + private enum TestEnum + { + Value1, + Value2 + } + } +} From 46d055604a6f049735a6d2b0ed12d33ef5287dab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 18:57:11 +0900 Subject: [PATCH 26/84] Customize -> Customise --- ...ionSettings.cs => TestSceneModSettings.cs} | 26 +++++++++---------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 6 ++--- 2 files changed, 16 insertions(+), 16 deletions(-) rename osu.Game.Tests/Visual/UserInterface/{TestSceneModCustomizationSettings.cs => TestSceneModSettings.cs} (78%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs similarity index 78% rename from osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 8227fe8f7a..7a11ba5294 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModCustomizationSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -13,7 +13,7 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Tests.Visual.UserInterface { - public class TestSceneModCustomizationSettings : OsuTestScene + public class TestSceneModSettings : OsuTestScene { private TestModSelectOverlay modSelect; @@ -27,13 +27,13 @@ namespace osu.Game.Tests.Visual.UserInterface Anchor = Anchor.BottomCentre, }); - var testMod = new TestModCustomizable1(); + var testMod = new TestModCustomisable1(); AddStep("open", modSelect.Show); - AddAssert("button disabled", () => !modSelect.CustomizeButton.Enabled.Value); + AddAssert("button disabled", () => !modSelect.CustomiseButton.Enabled.Value); AddStep("select mod", () => modSelect.SelectMod(testMod)); - AddAssert("button enabled", () => modSelect.CustomizeButton.Enabled.Value); - AddStep("open customization", () => modSelect.CustomizeButton.Click()); + AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value); + AddStep("open Customisation", () => modSelect.CustomiseButton.Click()); AddStep("deselect mod", () => modSelect.SelectMod(testMod)); AddAssert("controls hidden", () => modSelect.ModSettingsContainer.Alpha == 0); } @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.UserInterface private class TestModSelectOverlay : ModSelectOverlay { public new Container ModSettingsContainer => base.ModSettingsContainer; - public new TriangleButton CustomizeButton => base.CustomizeButton; + public new TriangleButton CustomiseButton => base.CustomiseButton; public void SelectMod(Mod mod) => ModSectionsContainer.Children.Single(s => s.ModType == mod.Type) @@ -57,8 +57,8 @@ namespace osu.Game.Tests.Visual.UserInterface { section.Mods = new Mod[] { - new TestModCustomizable1(), - new TestModCustomizable2() + new TestModCustomisable1(), + new TestModCustomisable2() }; } else @@ -67,21 +67,21 @@ namespace osu.Game.Tests.Visual.UserInterface } } - private class TestModCustomizable1 : TestModCustomizable + private class TestModCustomisable1 : TestModCustomisable { - public override string Name => "Customizable Mod 1"; + public override string Name => "Customisable Mod 1"; public override string Acronym => "CM1"; } - private class TestModCustomizable2 : TestModCustomizable + private class TestModCustomisable2 : TestModCustomisable { - public override string Name => "Customizable Mod 2"; + public override string Name => "Customisable Mod 2"; public override string Acronym => "CM2"; } - private abstract class TestModCustomizable : Mod, IApplicableMod + private abstract class TestModCustomisable : Mod, IApplicableMod { public override double ScoreMultiplier => 1.0; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index cfad0126eb..e860463b23 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Mods public class ModSelectOverlay : WaveOverlayContainer { protected readonly TriangleButton DeselectAllButton; - protected readonly TriangleButton CustomizeButton; + protected readonly TriangleButton CustomiseButton; protected readonly TriangleButton CloseButton; protected readonly OsuSpriteText MultiplierLabel; @@ -232,7 +232,7 @@ namespace osu.Game.Overlays.Mods Right = 20 } }, - CustomizeButton = new TriangleButton + CustomiseButton = new TriangleButton { Width = 180, Text = "Customization", @@ -351,7 +351,7 @@ namespace osu.Game.Overlays.Mods ModSettingsContent.RemoveAll(section => section.Mod == removed); bool hasSettings = ModSettingsContent.Children.Count > 0; - CustomizeButton.Enabled.Value = hasSettings; + CustomiseButton.Enabled.Value = hasSettings; if (!hasSettings) ModSettingsContainer.Hide(); From 347373a3ba3ef852dabd35306721ff689b47b0af Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 19:04:55 +0900 Subject: [PATCH 27/84] Fix test failures --- .../UserInterface/TestSceneModSettings.cs | 3 +++ osu.Game/Overlays/Mods/ModSection.cs | 22 +++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 7a11ba5294..435dfd92be 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -31,6 +31,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("open", modSelect.Show); AddAssert("button disabled", () => !modSelect.CustomiseButton.Enabled.Value); + AddUntilStep("wait for button load", () => modSelect.ButtonsLoaded); AddStep("select mod", () => modSelect.SelectMod(testMod)); AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value); AddStep("open Customisation", () => modSelect.CustomiseButton.Click()); @@ -43,6 +44,8 @@ namespace osu.Game.Tests.Visual.UserInterface public new Container ModSettingsContainer => base.ModSettingsContainer; public new TriangleButton CustomiseButton => base.CustomiseButton; + public bool ButtonsLoaded => ModSectionsContainer.Children.All(c => c.ModIconsLoaded); + public void SelectMod(Mod mod) => ModSectionsContainer.Children.Single(s => s.ModType == mod.Type) .ButtonsContainer.OfType().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())).SelectNext(1); diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 9c0a164ad6..c55d1d8f70 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -57,6 +57,15 @@ namespace osu.Game.Overlays.Mods }).ToArray(); modsLoadCts?.Cancel(); + + if (modContainers.Length == 0) + { + ModIconsLoaded = true; + headerLabel.Hide(); + Hide(); + return; + } + ModIconsLoaded = false; LoadComponentsAsync(modContainers, c => @@ -67,17 +76,8 @@ namespace osu.Game.Overlays.Mods buttons = modContainers.OfType().ToArray(); - if (value.Any()) - { - headerLabel.FadeIn(200); - this.FadeIn(200); - } - else - { - // transition here looks weird as mods instantly disappear. - headerLabel.Hide(); - Hide(); - } + headerLabel.FadeIn(200); + this.FadeIn(200); } } From 5347e7c4a2aadd5694722786f57b45b8f2ab65d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 20:14:06 +0900 Subject: [PATCH 28/84] Apply code quality improvements --- osu.Game/Configuration/SettingSourceAttribute.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index dceef1cb7d..056fa8bcc0 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using JetBrains.Annotations; using osu.Framework.Bindables; @@ -36,11 +35,13 @@ namespace osu.Game.Configuration { public static IEnumerable CreateSettingsControls(this object obj) { - var configProperties = obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttribute(true) != null); - - foreach (var property in configProperties) + foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)) { var attr = property.GetCustomAttribute(true); + + if (attr == null) + continue; + var prop = property.GetValue(obj); switch (prop) @@ -91,9 +92,7 @@ namespace osu.Game.Configuration break; case IBindable bindable: - var dropdownType = typeof(SettingsEnumDropdown<>).MakeGenericType(bindable.GetType().GetGenericArguments()[0]); - var dropdown = (Drawable)Activator.CreateInstance(dropdownType); dropdown.GetType().GetProperty(nameof(IHasCurrentValue.Current))?.SetValue(dropdown, obj); From f65866648e3e914a358ec4a1639d24d4862c01c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 20:14:56 +0900 Subject: [PATCH 29/84] Use Array.Empty --- osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 435dfd92be..fc44c5f595 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.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; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -65,7 +66,7 @@ namespace osu.Game.Tests.Visual.UserInterface }; } else - section.Mods = new Mod[] { }; + section.Mods = Array.Empty(); } } } From eb074b7058cc6586f447d308131db79b8af5724e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Dec 2019 17:34:04 +0900 Subject: [PATCH 30/84] Allow mods to apply to track, not clock --- .../Visual/Gameplay/TestScenePlayerLoader.cs | 4 +- osu.Game/Overlays/MusicController.cs | 4 +- .../Difficulty/DifficultyCalculator.cs | 8 ++-- .../Difficulty/PerformanceCalculator.cs | 8 ++-- ...icableToClock.cs => IApplicableToTrack.cs} | 6 +-- osu.Game/Rulesets/Mods/ModDaycore.cs | 10 ++--- osu.Game/Rulesets/Mods/ModDoubleTime.cs | 2 +- osu.Game/Rulesets/Mods/ModHalfTime.cs | 2 +- osu.Game/Rulesets/Mods/ModNightcore.cs | 10 ++--- osu.Game/Rulesets/Mods/ModTimeAdjust.cs | 12 ++---- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 32 ++++------------ .../Screens/Play/GameplayClockContainer.cs | 37 +++++++++---------- 12 files changed, 52 insertions(+), 83 deletions(-) rename osu.Game/Rulesets/Mods/{IApplicableToClock.cs => IApplicableToTrack.cs} (69%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index dbea8d28a6..f02361e685 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -57,8 +57,8 @@ namespace osu.Game.Tests.Visual.Gameplay beforeLoadAction?.Invoke(); Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - foreach (var mod in Mods.Value.OfType()) - mod.ApplyToClock(Beatmap.Value.Track); + foreach (var mod in Mods.Value.OfType()) + mod.ApplyToTrack(Beatmap.Value.Track); InputManager.Child = container = new TestPlayerLoaderContainer( loader = new TestPlayerLoader(() => diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 5e0a67c2f7..bafdad3508 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -261,8 +261,8 @@ namespace osu.Game.Overlays if (allowRateAdjustments) { - foreach (var mod in mods.Value.OfType()) - mod.ApplyToClock(track); + foreach (var mod in mods.Value.OfType()) + mod.ApplyToTrack(track); } } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index e31c963403..1902de5bda 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Audio.Track; using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; @@ -41,10 +41,10 @@ namespace osu.Game.Rulesets.Difficulty IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); - var clock = new StopwatchClock(); - mods.OfType().ForEach(m => m.ApplyToClock(clock)); + var track = new TrackVirtual(10000); + mods.OfType().ForEach(m => m.ApplyToTrack(track)); - return calculate(playableBeatmap, mods, clock.Rate); + return calculate(playableBeatmap, mods, track.Rate); } /// diff --git a/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs b/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs index 9ab81b9580..ac3b817840 100644 --- a/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/PerformanceCalculator.cs @@ -3,8 +3,8 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework.Audio.Track; using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; @@ -35,9 +35,9 @@ namespace osu.Game.Rulesets.Difficulty protected virtual void ApplyMods(Mod[] mods) { - var clock = new StopwatchClock(); - mods.OfType().ForEach(m => m.ApplyToClock(clock)); - TimeRate = clock.Rate; + var track = new TrackVirtual(10000); + mods.OfType().ForEach(m => m.ApplyToTrack(track)); + TimeRate = track.Rate; } public abstract double Calculate(Dictionary categoryDifficulty = null); diff --git a/osu.Game/Rulesets/Mods/IApplicableToClock.cs b/osu.Game/Rulesets/Mods/IApplicableToTrack.cs similarity index 69% rename from osu.Game/Rulesets/Mods/IApplicableToClock.cs rename to osu.Game/Rulesets/Mods/IApplicableToTrack.cs index e5767b5fbf..4d6d958e82 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToClock.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToTrack.cs @@ -1,15 +1,15 @@ // 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.Timing; +using osu.Framework.Audio.Track; namespace osu.Game.Rulesets.Mods { /// /// An interface for mods that make adjustments to the track. /// - public interface IApplicableToClock : IApplicableMod + public interface IApplicableToTrack : IApplicableMod { - void ApplyToClock(IAdjustableClock clock); + void ApplyToTrack(Track track); } } diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs index 7e6d959119..dcb3cb5597 100644 --- a/osu.Game/Rulesets/Mods/ModDaycore.cs +++ b/osu.Game/Rulesets/Mods/ModDaycore.cs @@ -1,9 +1,8 @@ // 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.Audio; +using osu.Framework.Audio.Track; using osu.Framework.Graphics.Sprites; -using osu.Framework.Timing; namespace osu.Game.Rulesets.Mods { @@ -14,12 +13,9 @@ namespace osu.Game.Rulesets.Mods public override IconUsage Icon => FontAwesome.Solid.Question; public override string Description => "Whoaaaaa..."; - public override void ApplyToClock(IAdjustableClock clock) + public override void ApplyToTrack(Track track) { - if (clock is IHasPitchAdjust pitchAdjust) - pitchAdjust.PitchAdjust *= RateAdjust; - else - base.ApplyToClock(clock); + track.Frequency.Value *= RateAdjust; } } } diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs index a5e76e32b1..5e685b040e 100644 --- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -8,7 +8,7 @@ using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { - public abstract class ModDoubleTime : ModTimeAdjust, IApplicableToClock + public abstract class ModDoubleTime : ModTimeAdjust { public override string Name => "Double Time"; public override string Acronym => "DT"; diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs index 27369f4c30..d17ddd2253 100644 --- a/osu.Game/Rulesets/Mods/ModHalfTime.cs +++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs @@ -8,7 +8,7 @@ using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { - public abstract class ModHalfTime : ModTimeAdjust, IApplicableToClock + public abstract class ModHalfTime : ModTimeAdjust { public override string Name => "Half Time"; public override string Acronym => "HT"; diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index dc0fc33088..a4f1ef5a72 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -1,9 +1,8 @@ // 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.Audio; +using osu.Framework.Audio.Track; using osu.Framework.Graphics.Sprites; -using osu.Framework.Timing; using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods @@ -15,12 +14,9 @@ namespace osu.Game.Rulesets.Mods public override IconUsage Icon => OsuIcon.ModNightcore; public override string Description => "Uguuuuuuuu..."; - public override void ApplyToClock(IAdjustableClock clock) + public override void ApplyToTrack(Track track) { - if (clock is IHasPitchAdjust pitchAdjust) - pitchAdjust.PitchAdjust *= RateAdjust; - else - base.ApplyToClock(clock); + track.Frequency.Value *= RateAdjust; } } } diff --git a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs b/osu.Game/Rulesets/Mods/ModTimeAdjust.cs index 513883f552..f137a75ed8 100644 --- a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModTimeAdjust.cs @@ -2,23 +2,19 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework.Audio; -using osu.Framework.Timing; +using osu.Framework.Audio.Track; namespace osu.Game.Rulesets.Mods { - public abstract class ModTimeAdjust : Mod + public abstract class ModTimeAdjust : Mod, IApplicableToTrack { public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp) }; protected abstract double RateAdjust { get; } - public virtual void ApplyToClock(IAdjustableClock clock) + public virtual void ApplyToTrack(Track track) { - if (clock is IHasTempoAdjust tempo) - tempo.TempoAdjust *= RateAdjust; - else - clock.Rate *= RateAdjust; + track.TempoAdjust *= RateAdjust; } } } diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index e231225e3c..d95d354487 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -3,15 +3,14 @@ using System; using System.Linq; -using osu.Framework.Audio; -using osu.Framework.Timing; +using osu.Framework.Audio.Track; using osu.Game.Beatmaps; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mods { - public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToClock, IApplicableToBeatmap + public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToTrack, IApplicableToBeatmap { /// /// The point in the beatmap at which the final ramping rate should be reached. @@ -24,11 +23,11 @@ namespace osu.Game.Rulesets.Mods private double finalRateTime; private double beginRampTime; - private IAdjustableClock clock; + private Track track; - public virtual void ApplyToClock(IAdjustableClock clock) + public virtual void ApplyToTrack(Track track) { - this.clock = clock; + this.track = track; lastAdjust = 1; @@ -46,7 +45,7 @@ namespace osu.Game.Rulesets.Mods public virtual void Update(Playfield playfield) { - applyAdjustment((clock.CurrentTime - beginRampTime) / finalRateTime); + applyAdjustment((track.CurrentTime - beginRampTime) / finalRateTime); } private double lastAdjust = 1; @@ -59,23 +58,8 @@ namespace osu.Game.Rulesets.Mods { double adjust = 1 + (Math.Sign(FinalRateAdjustment) * Math.Clamp(amount, 0, 1) * Math.Abs(FinalRateAdjustment)); - switch (clock) - { - case IHasPitchAdjust pitch: - pitch.PitchAdjust /= lastAdjust; - pitch.PitchAdjust *= adjust; - break; - - case IHasTempoAdjust tempo: - tempo.TempoAdjust /= lastAdjust; - tempo.TempoAdjust *= adjust; - break; - - default: - clock.Rate /= lastAdjust; - clock.Rate *= adjust; - break; - } + track.Tempo.Value /= lastAdjust; + track.Tempo.Value *= lastAdjust; lastAdjust = adjust; } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 58c9a6a784..1508758c87 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -28,9 +28,9 @@ namespace osu.Game.Screens.Play private readonly IReadOnlyList mods; /// - /// The original source (usually a 's track). + /// The 's track. /// - private IAdjustableClock sourceClock; + private Track track; public readonly BindableBool IsPaused = new BindableBool(); @@ -72,8 +72,8 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both; - sourceClock = (IAdjustableClock)beatmap.Track ?? new StopwatchClock(); - (sourceClock as IAdjustableAudioComponent)?.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); + track = beatmap.Track; + track.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; @@ -127,11 +127,11 @@ namespace osu.Game.Screens.Play { Task.Run(() => { - sourceClock.Reset(); + track.Reset(); Schedule(() => { - adjustableClock.ChangeSource(sourceClock); + adjustableClock.ChangeSource(track); updateRate(); if (!IsPaused.Value) @@ -197,13 +197,13 @@ namespace osu.Game.Screens.Play /// public void StopUsingBeatmapClock() { - if (sourceClock != beatmap.Track) + if (track != beatmap.Track) return; removeSourceClockAdjustments(); - sourceClock = new TrackVirtual(beatmap.Track.Length); - adjustableClock.ChangeSource(sourceClock); + track = new TrackVirtual(beatmap.Track.Length); + adjustableClock.ChangeSource(track); } protected override void Update() @@ -218,18 +218,15 @@ namespace osu.Game.Screens.Play private void updateRate() { - if (sourceClock == null) return; + if (track == null) return; speedAdjustmentsApplied = true; - sourceClock.ResetSpeedAdjustments(); + track.ResetSpeedAdjustments(); - if (sourceClock is IHasTempoAdjust tempo) - tempo.TempoAdjust = UserPlaybackRate.Value; - else - sourceClock.Rate = UserPlaybackRate.Value; + track.Tempo.Value = UserPlaybackRate.Value; - foreach (var mod in mods.OfType()) - mod.ApplyToClock(sourceClock); + foreach (var mod in mods.OfType()) + mod.ApplyToTrack(track); } protected override void Dispose(bool isDisposing) @@ -237,18 +234,18 @@ namespace osu.Game.Screens.Play base.Dispose(isDisposing); removeSourceClockAdjustments(); - sourceClock = null; + track = null; } private void removeSourceClockAdjustments() { if (speedAdjustmentsApplied) { - sourceClock.ResetSpeedAdjustments(); + track.ResetSpeedAdjustments(); speedAdjustmentsApplied = false; } - (sourceClock as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); + (track as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); } } } From d650bfb6d6d3c56439c3a56e1c1afd940d9d989b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Dec 2019 18:05:14 +0900 Subject: [PATCH 31/84] Remove unnecessary cast --- osu.Game/Screens/Play/GameplayClockContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 1508758c87..2cc03ae453 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -245,7 +245,7 @@ namespace osu.Game.Screens.Play speedAdjustmentsApplied = false; } - (track as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); + track.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); } } } From 1e49078c526f2050a137f087cf0000f678f3484e Mon Sep 17 00:00:00 2001 From: mcendu Date: Mon, 9 Dec 2019 17:51:44 +0800 Subject: [PATCH 32/84] Add OsuCursorSprite --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs new file mode 100644 index 0000000000..56c04ce1fc --- /dev/null +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs @@ -0,0 +1,13 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Rulesets.Osu.UI.Cursor +{ + public class OsuCursorSprite : CompositeDrawable + { + public Drawable ExpandTarget { get; protected set; } + } +} From 22f2a4bed2284ab2fa81f7cfbb4a6a17f56ecb27 Mon Sep 17 00:00:00 2001 From: mcendu Date: Mon, 9 Dec 2019 17:53:16 +0800 Subject: [PATCH 33/84] Fix LegacyCursor's cursormiddle expanding --- osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs | 8 ++++---- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 18 +++++++++++------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs index 05b38ae195..cf133f54ea 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs @@ -5,13 +5,13 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Skinning; +using osu.Game.Rulesets.Osu.UI.Cursor; using osuTK; namespace osu.Game.Rulesets.Osu.Skinning { - public class LegacyCursor : CompositeDrawable + public class LegacyCursor : OsuCursorSprite { - private NonPlayfieldSprite cursor; private bool spin; public LegacyCursor() @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Skinning Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - cursor = new NonPlayfieldSprite + ExpandTarget = new NonPlayfieldSprite { Texture = skin.GetTexture("cursor"), Anchor = Anchor.Centre, @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Skinning protected override void LoadComplete() { if (spin) - cursor.Spin(10000, RotationDirection.Clockwise); + ExpandTarget.Spin(10000, RotationDirection.Clockwise); } } } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index 0aa8661fd3..4a67d23f57 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private bool cursorExpand; - private Container expandTarget; + private OsuCursorSprite cursorSprite; public OsuCursor() { @@ -37,17 +37,19 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor [BackgroundDependencyLoader] private void load() { - InternalChild = expandTarget = new Container + SkinnableDrawable cursor; + InternalChild = new Container { RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, Anchor = Anchor.Centre, - Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.Cursor), _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling) + Child = cursor = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.Cursor), _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling) { Origin = Anchor.Centre, Anchor = Anchor.Centre, } }; + cursorSprite = cursor.Drawable as OsuCursorSprite; } private const float pressed_scale = 1.2f; @@ -57,12 +59,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { if (!cursorExpand) return; - expandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 100, Easing.OutQuad); + cursorSprite.ExpandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 100, Easing.OutQuad); } - public void Contract() => expandTarget.ScaleTo(released_scale, 100, Easing.OutQuad); + public void Contract() => cursorSprite.ExpandTarget.ScaleTo(released_scale, 100, Easing.OutQuad); - private class DefaultCursor : CompositeDrawable + private class DefaultCursor : OsuCursorSprite { public DefaultCursor() { @@ -73,8 +75,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor InternalChildren = new Drawable[] { - new CircularContainer + ExpandTarget = new CircularContainer { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, Masking = true, BorderThickness = size / 6, From af90b45c407a0cbad892224204e83e5c6cfe0778 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 20:49:59 +0900 Subject: [PATCH 34/84] Refactor path visualisers to use bindables --- .../Components/PathControlPointPiece.cs | 116 ++++++++++++------ .../Components/PathControlPointVisualiser.cs | 34 +++-- 2 files changed, 98 insertions(+), 52 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 4fe02135c4..38da5939b6 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -11,6 +11,8 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osuTK; using osuTK.Graphics; @@ -20,10 +22,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { public class PathControlPointPiece : BlueprintPiece { - public Action RequestSelection; + public Action RequestSelection; public readonly BindableBool IsSelected = new BindableBool(); - public readonly int Index; + + public readonly PathControlPoint ControlPoint; private readonly Slider slider; private readonly Path path; @@ -36,10 +39,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components [Resolved] private OsuColour colours { get; set; } - public PathControlPointPiece(Slider slider, int index) + private IBindable sliderPosition; + private IBindable pathVersion; + + public PathControlPointPiece(Slider slider, PathControlPoint controlPoint) { this.slider = slider; - Index = index; + + ControlPoint = controlPoint; Origin = Anchor.Centre; AutoSizeAxes = Axes.Both; @@ -85,48 +92,41 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components }; } - protected override void Update() + protected override void LoadComplete() { - base.Update(); + base.LoadComplete(); - Position = slider.StackedPosition + slider.Path.ControlPoints[Index].Position.Value; + sliderPosition = slider.PositionBindable.GetBoundCopy(); + sliderPosition.BindValueChanged(_ => updateDisplay()); + pathVersion = slider.Path.Version.GetBoundCopy(); + pathVersion.BindValueChanged(_ => updateDisplay()); + + IsSelected.BindValueChanged(_ => updateMarkerDisplay()); + + updateDisplay(); + } + + private void updateDisplay() + { updateMarkerDisplay(); updateConnectingPath(); } - /// - /// Updates the state of the circular control point marker. - /// - private void updateMarkerDisplay() - { - markerRing.Alpha = IsSelected.Value ? 1 : 0; - - Color4 colour = slider.Path.ControlPoints[Index].Type.Value.HasValue ? colours.Red : colours.Yellow; - if (IsHovered || IsSelected.Value) - colour = Color4.White; - marker.Colour = colour; - } - - /// - /// Updates the path connecting this control point to the previous one. - /// - private void updateConnectingPath() - { - path.ClearVertices(); - - if (Index != slider.Path.ControlPoints.Count - 1) - { - path.AddVertex(Vector2.Zero); - path.AddVertex(slider.Path.ControlPoints[Index + 1].Position.Value - slider.Path.ControlPoints[Index].Position.Value); - } - - path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); - } - // The connecting path is excluded from positional input public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => marker.ReceivePositionalInputAt(screenSpacePos); + protected override bool OnHover(HoverEvent e) + { + updateMarkerDisplay(); + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateMarkerDisplay(); + } + protected override bool OnMouseDown(MouseDownEvent e) { if (RequestSelection == null) @@ -135,12 +135,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components switch (e.Button) { case MouseButton.Left: - RequestSelection.Invoke(Index, e); + RequestSelection.Invoke(this, e); return true; case MouseButton.Right: if (!IsSelected.Value) - RequestSelection.Invoke(Index, e); + RequestSelection.Invoke(this, e); return false; // Allow context menu to show } @@ -155,7 +155,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components protected override bool OnDrag(DragEvent e) { - if (Index == 0) + if (ControlPoint == slider.Path.ControlPoints[0]) { // Special handling for the head control point - the position of the slider changes which means the snapped position and time have to be taken into account (Vector2 snappedPosition, double snappedTime) = snapProvider?.GetSnappedPosition(e.MousePosition, slider.StartTime) ?? (e.MousePosition, slider.StartTime); @@ -169,11 +169,47 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components slider.Path.ControlPoints[i].Position.Value -= movementDelta; } else - slider.Path.ControlPoints[Index].Position.Value += e.Delta; + ControlPoint.Position.Value += e.Delta; return true; } protected override bool OnDragEnd(DragEndEvent e) => true; + + /// + /// Updates the state of the circular control point marker. + /// + private void updateMarkerDisplay() + { + Position = slider.StackedPosition + ControlPoint.Position.Value; + + markerRing.Alpha = IsSelected.Value ? 1 : 0; + + Color4 colour = ControlPoint.Type.Value != null ? colours.Red : colours.Yellow; + if (IsHovered || IsSelected.Value) + colour = Color4.White; + marker.Colour = colour; + } + + /// + /// Updates the path connecting this control point to the previous one. + /// + private void updateConnectingPath() + { + path.ClearVertices(); + + int index = slider.Path.ControlPoints.IndexOf(ControlPoint); + + if (index == -1) + return; + + if (++index != slider.Path.ControlPoints.Count) + { + path.AddVertex(Vector2.Zero); + path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value); + } + + path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); + } } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 434e74ddeb..eb6e3c01e1 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using Humanizer; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -32,6 +33,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components [Resolved(CanBeNull = true)] private IPlacementHandler placementHandler { get; set; } + private IBindableList controlPoints; + public PathControlPointVisualiser(Slider slider, bool allowSelection) { this.slider = slider; @@ -47,24 +50,31 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components base.LoadComplete(); inputManager = GetContainingInputManager(); + + controlPoints = slider.Path.ControlPoints.GetBoundCopy(); + controlPoints.ItemsAdded += addControlPoints; + controlPoints.ItemsRemoved += removeControlPoints; + + addControlPoints(controlPoints); } - protected override void Update() + private void addControlPoints(IEnumerable controlPoints) { - base.Update(); - - while (slider.Path.ControlPoints.Count > Pieces.Count) + foreach (var point in controlPoints) { - var piece = new PathControlPointPiece(slider, Pieces.Count); + var piece = new PathControlPointPiece(slider, point); if (allowSelection) piece.RequestSelection = selectPiece; Pieces.Add(piece); } + } - while (slider.Path.ControlPoints.Count < Pieces.Count) - Pieces.Remove(Pieces[Pieces.Count - 1]); + private void removeControlPoints(IEnumerable controlPoints) + { + foreach (var point in controlPoints) + Pieces.RemoveAll(p => p.ControlPoint == point); } protected override bool OnClick(ClickEvent e) @@ -87,20 +97,20 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components public bool OnReleased(PlatformAction action) => action.ActionMethod == PlatformActionMethod.Delete; - private void selectPiece(int index, MouseButtonEvent e) + private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e) { if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed) - Pieces[index].IsSelected.Toggle(); + piece.IsSelected.Toggle(); else { - foreach (var piece in Pieces) - piece.IsSelected.Value = piece.Index == index; + foreach (var p in Pieces) + p.IsSelected.Value = p == piece; } } private bool deleteSelected() { - List toRemove = Pieces.Where(p => p.IsSelected.Value).Select(p => slider.Path.ControlPoints[p.Index]).ToList(); + List toRemove = Pieces.Where(p => p.IsSelected.Value).Select(p => p.ControlPoint).ToList(); // Ensure that there are any points to be deleted if (toRemove.Count == 0) From 0ee303f7d6417130079bd7862938e78c403c0e19 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 9 Dec 2019 22:45:44 +0900 Subject: [PATCH 35/84] Remove unused using --- .../Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 38da5939b6..7c66f38854 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -12,7 +12,6 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osuTK; using osuTK.Graphics; From 9b318d2869a99a97f7e213946b7993fac71cf972 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Dec 2019 00:07:07 +0900 Subject: [PATCH 36/84] Add right-click menu item to change path type --- .../Components/PathControlPointPiece.cs | 2 +- .../Components/PathControlPointVisualiser.cs | 45 ++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 7c66f38854..c2aefac587 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components protected override bool OnHover(HoverEvent e) { updateMarkerDisplay(); - return true; + return false; } protected override void OnHoverLost(HoverLostEvent e) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index eb6e3c01e1..352bb56030 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.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; using System.Collections.Generic; using System.Linq; using Humanizer; @@ -15,6 +16,7 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit.Compose; using osuTK; @@ -161,9 +163,50 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components return new MenuItem[] { - new OsuMenuItem($"Delete {"control point".ToQuantity(selectedPoints)}", MenuItemType.Destructive, () => deleteSelected()) + new OsuMenuItem($"Delete {"control point".ToQuantity(selectedPoints)}", MenuItemType.Destructive, () => deleteSelected()), + new OsuMenuItem("Type") + { + Items = new[] + { + createMenuItemForPathType(null), + createMenuItemForPathType(PathType.Linear), + createMenuItemForPathType(PathType.PerfectCurve), + createMenuItemForPathType(PathType.Catmull) + } + } }; } } + + private MenuItem createMenuItemForPathType(PathType? type) + { + int totalCount = Pieces.Count(p => p.IsSelected.Value); + int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type.Value == type); + + var item = new PathTypeMenuItem(type, () => + { + foreach (var p in Pieces.Where(p => p.IsSelected.Value)) + p.ControlPoint.Type.Value = type; + }); + + if (countOfState == totalCount) + item.State.Value = TernaryState.True; + else if (countOfState > 0) + item.State.Value = TernaryState.Indeterminate; + else + item.State.Value = TernaryState.False; + + return item; + } + + private class PathTypeMenuItem : TernaryStateMenuItem + { + public PathTypeMenuItem(PathType? type, Action action) + : base(type == null ? "Inherit" : type.ToString().Humanize(), changeState, MenuItemType.Standard, _ => action?.Invoke()) + { + } + + private static TernaryState changeState(TernaryState state) => TernaryState.True; + } } } From 1b14b0e5b6cecdbfacbf44024f2ff0fa42252d93 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Dec 2019 00:08:38 +0900 Subject: [PATCH 37/84] Fix pieces blocking context menu --- .../Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 7c66f38854..c2aefac587 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components protected override bool OnHover(HoverEvent e) { updateMarkerDisplay(); - return true; + return false; } protected override void OnHoverLost(HoverLostEvent e) From dc9775742ca0dfc20f65e9de2d043c714a6f1a61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Dec 2019 02:23:17 +0900 Subject: [PATCH 38/84] Fix incorrect code migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Bartłomiej Dach --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index d95d354487..839b2ae36e 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Mods double adjust = 1 + (Math.Sign(FinalRateAdjustment) * Math.Clamp(amount, 0, 1) * Math.Abs(FinalRateAdjustment)); track.Tempo.Value /= lastAdjust; - track.Tempo.Value *= lastAdjust; + track.Tempo.Value *= adjust; lastAdjust = adjust; } From 94a298a82d5e965ff54e63e56b332c78af50f775 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Dec 2019 16:00:09 +0900 Subject: [PATCH 39/84] Refactor distance snap grid to not require hitobjects --- .../Edit/OsuDistanceSnapGrid.cs | 5 +- .../Editor/TestSceneDistanceSnapGrid.cs | 29 ++++++----- .../Components/CircularDistanceSnapGrid.cs | 25 +++++----- .../Compose/Components/DistanceSnapGrid.cs | 50 +++++++++---------- 4 files changed, 52 insertions(+), 57 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapGrid.cs index 9b00204d51..bde86a2890 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuDistanceSnapGrid.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 JetBrains.Annotations; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit.Compose.Components; @@ -8,8 +9,8 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuDistanceSnapGrid : CircularDistanceSnapGrid { - public OsuDistanceSnapGrid(OsuHitObject hitObject, OsuHitObject nextHitObject) - : base(hitObject, nextHitObject, hitObject.StackedEndPosition) + public OsuDistanceSnapGrid(OsuHitObject hitObject, [CanBeNull] OsuHitObject nextHitObject = null) + : base(hitObject.StackedPosition, hitObject.StartTime, nextHitObject?.StartTime) { Masking = true; } diff --git a/osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs index e4c987923c..39b4bf7218 100644 --- a/osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editor/TestSceneDistanceSnapGrid.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit; @@ -44,7 +43,7 @@ namespace osu.Game.Tests.Visual.Editor RelativeSizeAxes = Axes.Both, Colour = Color4.SlateGray }, - new TestDistanceSnapGrid(new HitObject(), grid_position) + new TestDistanceSnapGrid() }; }); @@ -73,7 +72,7 @@ namespace osu.Game.Tests.Visual.Editor RelativeSizeAxes = Axes.Both, Colour = Color4.SlateGray }, - new TestDistanceSnapGrid(new HitObject(), grid_position, new HitObject { StartTime = 100 }) + new TestDistanceSnapGrid(100) }; }); } @@ -82,68 +81,68 @@ namespace osu.Game.Tests.Visual.Editor { public new float DistanceSpacing => base.DistanceSpacing; - public TestDistanceSnapGrid(HitObject hitObject, Vector2 centrePosition, HitObject nextHitObject = null) - : base(hitObject, nextHitObject, centrePosition) + public TestDistanceSnapGrid(double? endTime = null) + : base(grid_position, 0, endTime) { } - protected override void CreateContent(Vector2 centrePosition) + protected override void CreateContent(Vector2 startPosition) { AddInternal(new Circle { Origin = Anchor.Centre, Size = new Vector2(5), - Position = centrePosition + Position = startPosition }); int beatIndex = 0; - for (float s = centrePosition.X + DistanceSpacing; s <= DrawWidth && beatIndex < MaxIntervals; s += DistanceSpacing, beatIndex++) + for (float s = startPosition.X + DistanceSpacing; s <= DrawWidth && beatIndex < MaxIntervals; s += DistanceSpacing, beatIndex++) { AddInternal(new Circle { Origin = Anchor.Centre, Size = new Vector2(5, 10), - Position = new Vector2(s, centrePosition.Y), + Position = new Vector2(s, startPosition.Y), Colour = GetColourForBeatIndex(beatIndex) }); } beatIndex = 0; - for (float s = centrePosition.X - DistanceSpacing; s >= 0 && beatIndex < MaxIntervals; s -= DistanceSpacing, beatIndex++) + for (float s = startPosition.X - DistanceSpacing; s >= 0 && beatIndex < MaxIntervals; s -= DistanceSpacing, beatIndex++) { AddInternal(new Circle { Origin = Anchor.Centre, Size = new Vector2(5, 10), - Position = new Vector2(s, centrePosition.Y), + Position = new Vector2(s, startPosition.Y), Colour = GetColourForBeatIndex(beatIndex) }); } beatIndex = 0; - for (float s = centrePosition.Y + DistanceSpacing; s <= DrawHeight && beatIndex < MaxIntervals; s += DistanceSpacing, beatIndex++) + for (float s = startPosition.Y + DistanceSpacing; s <= DrawHeight && beatIndex < MaxIntervals; s += DistanceSpacing, beatIndex++) { AddInternal(new Circle { Origin = Anchor.Centre, Size = new Vector2(10, 5), - Position = new Vector2(centrePosition.X, s), + Position = new Vector2(startPosition.X, s), Colour = GetColourForBeatIndex(beatIndex) }); } beatIndex = 0; - for (float s = centrePosition.Y - DistanceSpacing; s >= 0 && beatIndex < MaxIntervals; s -= DistanceSpacing, beatIndex++) + for (float s = startPosition.Y - DistanceSpacing; s >= 0 && beatIndex < MaxIntervals; s -= DistanceSpacing, beatIndex++) { AddInternal(new Circle { Origin = Anchor.Centre, Size = new Vector2(10, 5), - Position = new Vector2(centrePosition.X, s), + Position = new Vector2(startPosition.X, s), Colour = GetColourForBeatIndex(beatIndex) }); } diff --git a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs index 91e19f9cc0..23ed10b92d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/CircularDistanceSnapGrid.cs @@ -5,19 +5,18 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; -using osu.Game.Rulesets.Objects; using osuTK; namespace osu.Game.Screens.Edit.Compose.Components { public abstract class CircularDistanceSnapGrid : DistanceSnapGrid { - protected CircularDistanceSnapGrid(HitObject hitObject, HitObject nextHitObject, Vector2 centrePosition) - : base(hitObject, nextHitObject, centrePosition) + protected CircularDistanceSnapGrid(Vector2 startPosition, double startTime, double? endTime = null) + : base(startPosition, startTime, endTime) { } - protected override void CreateContent(Vector2 centrePosition) + protected override void CreateContent(Vector2 startPosition) { const float crosshair_thickness = 1; const float crosshair_max_size = 10; @@ -27,7 +26,7 @@ namespace osu.Game.Screens.Edit.Compose.Components new Box { Origin = Anchor.Centre, - Position = centrePosition, + Position = startPosition, Width = crosshair_thickness, EdgeSmoothness = new Vector2(1), Height = Math.Min(crosshair_max_size, DistanceSpacing * 2), @@ -35,15 +34,15 @@ namespace osu.Game.Screens.Edit.Compose.Components new Box { Origin = Anchor.Centre, - Position = centrePosition, + Position = startPosition, EdgeSmoothness = new Vector2(1), Width = Math.Min(crosshair_max_size, DistanceSpacing * 2), Height = crosshair_thickness, } }); - float dx = Math.Max(centrePosition.X, DrawWidth - centrePosition.X); - float dy = Math.Max(centrePosition.Y, DrawHeight - centrePosition.Y); + float dx = Math.Max(startPosition.X, DrawWidth - startPosition.X); + float dy = Math.Max(startPosition.Y, DrawHeight - startPosition.Y); float maxDistance = new Vector2(dx, dy).Length; int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceSpacing)); @@ -54,7 +53,7 @@ namespace osu.Game.Screens.Edit.Compose.Components AddInternal(new CircularProgress { Origin = Anchor.Centre, - Position = centrePosition, + Position = startPosition, Current = { Value = 1 }, Size = new Vector2(radius), InnerRadius = 4 * 1f / radius, @@ -66,9 +65,9 @@ namespace osu.Game.Screens.Edit.Compose.Components public override (Vector2 position, double time) GetSnappedPosition(Vector2 position) { if (MaxIntervals == 0) - return (CentrePosition, StartTime); + return (StartPosition, StartTime); - Vector2 direction = position - CentrePosition; + Vector2 direction = position - StartPosition; if (direction == Vector2.Zero) direction = new Vector2(0.001f, 0.001f); @@ -78,9 +77,9 @@ namespace osu.Game.Screens.Edit.Compose.Components int radialCount = Math.Clamp((int)MathF.Round(distance / radius), 1, MaxIntervals); Vector2 normalisedDirection = direction * new Vector2(1f / distance); - Vector2 snappedPosition = CentrePosition + normalisedDirection * radialCount * radius; + Vector2 snappedPosition = StartPosition + normalisedDirection * radialCount * radius; - return (snappedPosition, StartTime + SnapProvider.GetSnappedDurationFromDistance(StartTime, (snappedPosition - CentrePosition).Length)); + return (snappedPosition, StartTime + SnapProvider.GetSnappedDurationFromDistance(StartTime, (snappedPosition - StartPosition).Length)); } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 9508a2cdf0..00326d04f7 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -1,7 +1,6 @@ // 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 osu.Framework.Allocation; using osu.Framework.Caching; using osu.Framework.Graphics; @@ -9,7 +8,6 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Objects; using osuTK; namespace osu.Game.Screens.Edit.Compose.Components @@ -24,21 +22,21 @@ namespace osu.Game.Screens.Edit.Compose.Components /// protected float DistanceSpacing { get; private set; } - /// - /// The snapping time at . - /// - protected double StartTime { get; private set; } - /// /// The maximum number of distance snapping intervals allowed. /// protected int MaxIntervals { get; private set; } /// - /// The position which the grid is centred on. - /// The first beat snapping tick is located at + in the desired direction. + /// The position which the grid should start. + /// The first beat snapping tick is located at + away from this point. /// - protected readonly Vector2 CentrePosition; + protected readonly Vector2 StartPosition; + + /// + /// The snapping time at . + /// + protected readonly double StartTime; [Resolved] protected OsuColour Colours { get; private set; } @@ -53,25 +51,23 @@ namespace osu.Game.Screens.Edit.Compose.Components private BindableBeatDivisor beatDivisor { get; set; } private readonly Cached gridCache = new Cached(); - private readonly HitObject hitObject; - private readonly HitObject nextHitObject; + private readonly double? endTime; - protected DistanceSnapGrid(HitObject hitObject, [CanBeNull] HitObject nextHitObject, Vector2 centrePosition) + /// + /// Creates a new . + /// + /// The position at which the grid should start. The first tick is located one distance spacing length away from this point. + /// The snapping time at . + /// The time at which the snapping grid should end. If null, the grid will continue until the bounds of the screen are exceeded. + protected DistanceSnapGrid(Vector2 startPosition, double startTime, double? endTime = null) { - this.hitObject = hitObject; - this.nextHitObject = nextHitObject; - - CentrePosition = centrePosition; + this.endTime = endTime; + StartPosition = startPosition; + StartTime = startTime; RelativeSizeAxes = Axes.Both; } - [BackgroundDependencyLoader] - private void load() - { - StartTime = hitObject.GetEndTime(); - } - protected override void LoadComplete() { base.LoadComplete(); @@ -83,12 +79,12 @@ namespace osu.Game.Screens.Edit.Compose.Components { DistanceSpacing = SnapProvider.GetBeatSnapDistanceAt(StartTime); - if (nextHitObject == null) + if (endTime == null) MaxIntervals = int.MaxValue; else { // +1 is added since a snapped hitobject may have its start time slightly less than the snapped time due to floating point errors - double maxDuration = nextHitObject.StartTime - StartTime + 1; + double maxDuration = endTime.Value - StartTime + 1; MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(StartTime, DistanceSpacing)); } @@ -110,7 +106,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (!gridCache.IsValid) { ClearInternal(); - CreateContent(CentrePosition); + CreateContent(StartPosition); gridCache.Validate(); } } @@ -118,7 +114,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Creates the content which visualises the grid ticks. /// - protected abstract void CreateContent(Vector2 centrePosition); + protected abstract void CreateContent(Vector2 startPosition); /// /// Snaps a position to this grid. From 609c51130964ccb85e00160be1556ec4aa7bec61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Dec 2019 16:43:58 +0900 Subject: [PATCH 40/84] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 301c615ce4..914d352070 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -54,6 +54,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ef16738908..473ce82443 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 5090190f28..d03005012a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -74,7 +74,7 @@ - + @@ -82,7 +82,7 @@ - + From f7f4a57c5f6b1b3d64bec8c22461e261c05bae97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Dec 2019 19:41:18 +0900 Subject: [PATCH 41/84] Update bindable types in line with framework --- osu.Game/Rulesets/Mods/ModTimeAdjust.cs | 2 +- osu.Game/Rulesets/UI/DrawableRuleset.cs | 16 +++++++++++----- .../Tests/Visual/RateAdjustedBeatmapTestScene.cs | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs b/osu.Game/Rulesets/Mods/ModTimeAdjust.cs index f137a75ed8..7d0cc2a7c3 100644 --- a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModTimeAdjust.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mods public virtual void ApplyToTrack(Track track) { - track.TempoAdjust *= RateAdjust; + track.Tempo.Value *= RateAdjust; } } } diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 96275c1274..a856974292 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -511,15 +511,19 @@ namespace osu.Game.Rulesets.UI public IEnumerable GetAvailableResources() => throw new NotImplementedException(); - public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); + public void AddAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => throw new NotImplementedException(); - public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); + public void RemoveAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => throw new NotImplementedException(); - public BindableDouble Volume => throw new NotImplementedException(); + public BindableNumber Volume => throw new NotImplementedException(); - public BindableDouble Balance => throw new NotImplementedException(); + public BindableNumber Balance => throw new NotImplementedException(); - public BindableDouble Frequency => throw new NotImplementedException(); + public BindableNumber Frequency => throw new NotImplementedException(); + + public BindableNumber Tempo => throw new NotImplementedException(); + + public IBindable GetAggregate(AdjustableProperty type) => throw new NotImplementedException(); public IBindable AggregateVolume => throw new NotImplementedException(); @@ -527,6 +531,8 @@ namespace osu.Game.Rulesets.UI public IBindable AggregateFrequency => throw new NotImplementedException(); + public IBindable AggregateTempo => throw new NotImplementedException(); + public int PlaybackConcurrency { get => throw new NotImplementedException(); diff --git a/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs b/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs index 921a1d9789..ad24ffc7b8 100644 --- a/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs +++ b/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs @@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual base.Update(); // note that this will override any mod rate application - Beatmap.Value.Track.TempoAdjust = Clock.Rate; + Beatmap.Value.Track.Tempo.Value = Clock.Rate; } } } From 65f2d1f8757f809eb5f33b639abc5e7b338b6060 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Dec 2019 17:49:42 +0900 Subject: [PATCH 42/84] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 914d352070..3cd4dc48bf 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -54,6 +54,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 473ce82443..530d62f583 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index d03005012a..fb753b8c6f 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -74,7 +74,7 @@ - + @@ -82,7 +82,7 @@ - + From 55c938e5daa07a0a471d313645c172d5372ba73c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Dec 2019 18:08:11 +0900 Subject: [PATCH 43/84] Fix bindable usage --- osu.Game/Online/DownloadTrackingComposite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs index dcec17788a..9a0e112727 100644 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ b/osu.Game/Online/DownloadTrackingComposite.cs @@ -26,7 +26,7 @@ namespace osu.Game.Online /// protected readonly Bindable State = new Bindable(); - protected readonly Bindable Progress = new Bindable(); + protected readonly BindableNumber Progress = new BindableNumber { MinValue = 0, MaxValue = 1 }; protected DownloadTrackingComposite(TModel model = null) { From f593caf0eaa4d8750d314d16f17278c7fdc178d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 10 Dec 2019 18:08:51 +0900 Subject: [PATCH 44/84] Remove unused class --- .../Blueprints/Sliders/SliderPlacementBlueprint.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index c004b6db28..9b820261ab 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -1,7 +1,6 @@ // 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.Framework.Input; @@ -191,15 +190,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders Initial, Body, } - - private class Segment - { - public readonly List ControlPoints = new List(); - - public Segment(Vector2 offset) - { - ControlPoints.Add(offset); - } - } } } From 48976f5d0e0e14663ac8b5d5c1f106c0fd592f1f Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 9 Dec 2019 21:46:29 +0800 Subject: [PATCH 45/84] Add VS launcher profile for tournament client. --- osu.Desktop/Properties/launchSettings.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 osu.Desktop/Properties/launchSettings.json diff --git a/osu.Desktop/Properties/launchSettings.json b/osu.Desktop/Properties/launchSettings.json new file mode 100644 index 0000000000..5e768ec9fa --- /dev/null +++ b/osu.Desktop/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "osu! Desktop": { + "commandName": "Project" + }, + "osu! Tournament": { + "commandName": "Project", + "commandLineArgs": "--tournament" + } + } +} \ No newline at end of file From 06cde2b0c22feca53459df4213f46eb82ca6d0c1 Mon Sep 17 00:00:00 2001 From: mcendu Date: Tue, 10 Dec 2019 19:30:46 +0800 Subject: [PATCH 46/84] remove unused using directive --- osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs index cf133f54ea..4e027c4351 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Skinning; using osu.Game.Rulesets.Osu.UI.Cursor; using osuTK; From e37369304b7f75f67aafaa59947ca49b214a399e Mon Sep 17 00:00:00 2001 From: mcendu Date: Tue, 10 Dec 2019 19:45:06 +0800 Subject: [PATCH 47/84] property-ize expand target --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index 4a67d23f57..07cde5e407 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -20,7 +20,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private bool cursorExpand; - private OsuCursorSprite cursorSprite; + private SkinnableDrawable cursorSprite; + + private Drawable ExpandTarget => (cursorSprite.Drawable as OsuCursorSprite)?.ExpandTarget ?? cursorSprite; public OsuCursor() { @@ -37,19 +39,17 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor [BackgroundDependencyLoader] private void load() { - SkinnableDrawable cursor; InternalChild = new Container { RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, Anchor = Anchor.Centre, - Child = cursor = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.Cursor), _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling) + Child = cursorSprite = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.Cursor), _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling) { Origin = Anchor.Centre, Anchor = Anchor.Centre, } }; - cursorSprite = cursor.Drawable as OsuCursorSprite; } private const float pressed_scale = 1.2f; @@ -59,10 +59,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { if (!cursorExpand) return; - cursorSprite.ExpandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 100, Easing.OutQuad); + ExpandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 100, Easing.OutQuad); } - public void Contract() => cursorSprite.ExpandTarget.ScaleTo(released_scale, 100, Easing.OutQuad); + public void Contract() => ExpandTarget.ScaleTo(released_scale, 100, Easing.OutQuad); private class DefaultCursor : OsuCursorSprite { From 1afeaf31bcad901bceec6766d3ec7a6810b5f82b Mon Sep 17 00:00:00 2001 From: mcendu Date: Tue, 10 Dec 2019 19:58:56 +0800 Subject: [PATCH 48/84] make OsuCursorSprite abstract --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs index 56c04ce1fc..909c764b9e 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics.Containers; namespace osu.Game.Rulesets.Osu.UI.Cursor { - public class OsuCursorSprite : CompositeDrawable + public abstract class OsuCursorSprite : CompositeDrawable { public Drawable ExpandTarget { get; protected set; } } From b93bbf81aa4cba9b9ad840a4c804d7419c3a25be Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 Dec 2019 15:10:35 +0300 Subject: [PATCH 49/84] Add lighten background during breaks setting --- osu.Game/Configuration/OsuConfigManager.cs | 2 ++ .../Overlays/Settings/Sections/Gameplay/GeneralSettings.cs | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index b71463841a..947e864a87 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -80,6 +80,7 @@ namespace osu.Game.Configuration // Gameplay Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01); Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01); + Set(OsuSetting.LightenDuringBreaks, true); Set(OsuSetting.HitLighting, true); @@ -142,6 +143,7 @@ namespace osu.Game.Configuration AutoCursorSize, DimLevel, BlurLevel, + LightenDuringBreaks, ShowStoryboard, ShowVideoBackground, KeyOverlay, diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs index f4aa9a0144..3f8bc2b0c7 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/GeneralSettings.cs @@ -30,6 +30,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay KeyboardStep = 0.01f }, new SettingsCheckbox + { + LabelText = "Lighten playfield during breaks", + Bindable = config.GetBindable(OsuSetting.LightenDuringBreaks) + }, + new SettingsCheckbox { LabelText = "Show score overlay", Bindable = config.GetBindable(OsuSetting.ShowInterface) From bb078c2afc82b34bc1e9fae15cd8b09627d38892 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 Dec 2019 15:13:44 +0300 Subject: [PATCH 50/84] Lighten user-dim container if on break time --- .../Graphics/Containers/UserDimContainer.cs | 22 ++++++++++++++++--- .../Backgrounds/BackgroundScreenBeatmap.cs | 3 +++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index 7683bbcd63..20f5a4fd83 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -1,11 +1,13 @@ // 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.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Configuration; +using osu.Game.Screens.Play; namespace osu.Game.Graphics.Containers { @@ -14,7 +16,9 @@ namespace osu.Game.Graphics.Containers /// public abstract class UserDimContainer : Container { - protected const float BACKGROUND_FADE_DURATION = 800; + private const float break_lighten_amount = 0.3f; + + protected const double BACKGROUND_FADE_DURATION = 800; /// /// Whether or not user-configured dim levels should be applied to the container. @@ -26,6 +30,12 @@ namespace osu.Game.Graphics.Containers /// public readonly Bindable StoryboardReplacesBackground = new Bindable(); + /// + /// Whether player is in break time. + /// Must be bound to to allow for dim adjustments in gameplay. + /// + internal readonly IBindable IsBreakTime = new Bindable(); + /// /// Whether the content of this container is currently being displayed. /// @@ -33,11 +43,14 @@ namespace osu.Game.Graphics.Containers protected Bindable UserDimLevel { get; private set; } + protected Bindable LightenDuringBreaks { get; private set; } + protected Bindable ShowStoryboard { get; private set; } protected Bindable ShowVideo { get; private set; } - protected double DimLevel => EnableUserDim.Value ? UserDimLevel.Value : 0; + private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? break_lighten_amount : 0; + protected float DimLevel => Math.Max(EnableUserDim.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0); protected override Container Content => dimContent; @@ -55,11 +68,14 @@ namespace osu.Game.Graphics.Containers private void load(OsuConfigManager config) { UserDimLevel = config.GetBindable(OsuSetting.DimLevel); + LightenDuringBreaks = config.GetBindable(OsuSetting.LightenDuringBreaks); ShowStoryboard = config.GetBindable(OsuSetting.ShowStoryboard); ShowVideo = config.GetBindable(OsuSetting.ShowVideoBackground); EnableUserDim.ValueChanged += _ => UpdateVisuals(); UserDimLevel.ValueChanged += _ => UpdateVisuals(); + LightenDuringBreaks.ValueChanged += _ => UpdateVisuals(); + IsBreakTime.ValueChanged += _ => UpdateVisuals(); ShowStoryboard.ValueChanged += _ => UpdateVisuals(); ShowVideo.ValueChanged += _ => UpdateVisuals(); StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals(); @@ -84,7 +100,7 @@ namespace osu.Game.Graphics.Containers ContentDisplayed = ShowDimContent; dimContent.FadeTo(ContentDisplayed ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint); - dimContent.FadeColour(OsuColour.Gray(1 - (float)DimLevel), BACKGROUND_FADE_DURATION, Easing.OutQuint); + dimContent.FadeColour(OsuColour.Gray(1f - DimLevel), BACKGROUND_FADE_DURATION, Easing.OutQuint); } } } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 7b68460e6b..1ab3a5b533 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -38,6 +38,8 @@ namespace osu.Game.Screens.Backgrounds /// public readonly Bindable BlurAmount = new Bindable(); + internal readonly IBindable IsBreakTime = new Bindable(); + private readonly DimmableBackground dimmable; protected virtual DimmableBackground CreateFadeContainer() => new DimmableBackground { RelativeSizeAxes = Axes.Both }; @@ -48,6 +50,7 @@ namespace osu.Game.Screens.Backgrounds InternalChild = dimmable = CreateFadeContainer(); dimmable.EnableUserDim.BindTo(EnableUserDim); + dimmable.IsBreakTime.BindTo(IsBreakTime); dimmable.BlurAmount.BindTo(BlurAmount); } From 38f1a8bc170d11a3a4edacc4d058f7ef791fbd32 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 Dec 2019 15:14:47 +0300 Subject: [PATCH 51/84] Bind UserDimContainer.IsBreakTime from Player --- osu.Game/Screens/Play/Player.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d40c448452..9b372c3791 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -140,6 +140,11 @@ namespace osu.Game.Screens.Play // bind clock into components that require it DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); + // bind break into components that require it. + Background.IsBreakTime.BindTo(breakOverlay.IsBreakTime); + DimmableStoryboard.IsBreakTime.BindTo(breakOverlay.IsBreakTime); + DimmableVideo.IsBreakTime.BindTo(breakOverlay.IsBreakTime); + // Bind ScoreProcessor to ourselves ScoreProcessor.AllJudged += onCompletion; ScoreProcessor.Failed += onFail; From 63f66aa5fa9fa2c178910e6bb9f1a68de5e0a372 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 Dec 2019 15:25:03 +0300 Subject: [PATCH 52/84] Check by UserDimContainer.DimLevel instead --- .../Visual/Background/TestSceneUserDimContainer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index 8f71584b4d..910c462718 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -314,7 +314,7 @@ namespace osu.Game.Tests.Visual.Background config.BindWith(OsuSetting.BlurLevel, BlurLevel); } - public bool IsBackgroundDimmed() => ((FadeAccessibleBackground)Background).CurrentColour == OsuColour.Gray(1 - (float)DimLevel.Value); + public bool IsBackgroundDimmed() => ((FadeAccessibleBackground)Background).CurrentColour == OsuColour.Gray(1f - ((FadeAccessibleBackground)Background).CurrentDim); public bool IsBackgroundUndimmed() => ((FadeAccessibleBackground)Background).CurrentColour == Color4.White; @@ -404,6 +404,8 @@ namespace osu.Game.Tests.Visual.Background public float CurrentAlpha => dimmable.CurrentAlpha; + public float CurrentDim => dimmable.DimLevel; + public Vector2 CurrentBlur => Background.BlurSigma; private TestDimmableBackground dimmable; @@ -418,6 +420,8 @@ namespace osu.Game.Tests.Visual.Background { public Color4 CurrentColour => Content.Colour; public float CurrentAlpha => Content.Alpha; + + public new float DimLevel => base.DimLevel; } } } From dbe46c6cf7df3dc77eb66bdfd88679d7d88add3c Mon Sep 17 00:00:00 2001 From: mcendu Date: Tue, 10 Dec 2019 20:40:10 +0800 Subject: [PATCH 53/84] conform to coding styles --- osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs | 2 +- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs index 4e027c4351..02152fa51e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Skinning { spin = skin.GetConfig(OsuSkinConfiguration.CursorRotate)?.Value ?? true; - InternalChildren = new Drawable[] + InternalChildren = new[] { new NonPlayfieldSprite { diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index 07cde5e407..4f3d07f208 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private SkinnableDrawable cursorSprite; - private Drawable ExpandTarget => (cursorSprite.Drawable as OsuCursorSprite)?.ExpandTarget ?? cursorSprite; + private Drawable expandTarget => (cursorSprite.Drawable as OsuCursorSprite)?.ExpandTarget ?? cursorSprite; public OsuCursor() { @@ -59,10 +59,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { if (!cursorExpand) return; - ExpandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 100, Easing.OutQuad); + expandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 100, Easing.OutQuad); } - public void Contract() => ExpandTarget.ScaleTo(released_scale, 100, Easing.OutQuad); + public void Contract() => expandTarget.ScaleTo(released_scale, 100, Easing.OutQuad); private class DefaultCursor : OsuCursorSprite { @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Anchor = Anchor.Centre; Origin = Anchor.Centre; - InternalChildren = new Drawable[] + InternalChildren = new[] { ExpandTarget = new CircularContainer { From 53daa37eaa97e3575261c333f67b68cd6ab5ad92 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 10 Dec 2019 23:06:13 +0300 Subject: [PATCH 54/84] Fix failing tests --- osu.Game/Screens/Play/Player.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 9b372c3791..8684798632 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -140,11 +140,6 @@ namespace osu.Game.Screens.Play // bind clock into components that require it DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); - // bind break into components that require it. - Background.IsBreakTime.BindTo(breakOverlay.IsBreakTime); - DimmableStoryboard.IsBreakTime.BindTo(breakOverlay.IsBreakTime); - DimmableVideo.IsBreakTime.BindTo(breakOverlay.IsBreakTime); - // Bind ScoreProcessor to ourselves ScoreProcessor.AllJudged += onCompletion; ScoreProcessor.Failed += onFail; @@ -506,6 +501,11 @@ namespace osu.Game.Screens.Play Background.EnableUserDim.Value = true; Background.BlurAmount.Value = 0; + // bind component bindables. + Background.IsBreakTime.BindTo(breakOverlay.IsBreakTime); + DimmableStoryboard.IsBreakTime.BindTo(breakOverlay.IsBreakTime); + DimmableVideo.IsBreakTime.BindTo(breakOverlay.IsBreakTime); + Background.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); DimmableStoryboard.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); From a37af311d010a1122d64ff5b8ca1c025a2ec3931 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Dec 2019 13:19:02 +0900 Subject: [PATCH 55/84] Simplify settings update logic --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 47 +++++++++++----------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index e860463b23..ec5f99dc31 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -331,32 +331,10 @@ namespace osu.Game.Overlays.Mods Ruleset.BindTo(ruleset); if (mods != null) SelectedMods.BindTo(mods); - SelectedMods.ValueChanged += updateModSettings; - Ruleset.ValueChanged += _ => ModSettingsContent.Clear(); - sampleOn = audio.Samples.Get(@"UI/check-on"); sampleOff = audio.Samples.Get(@"UI/check-off"); } - private void updateModSettings(ValueChangedEvent> selectedMods) - { - foreach (var added in selectedMods.NewValue.Except(selectedMods.OldValue)) - { - var controls = added.CreateSettingsControls().ToList(); - if (controls.Count > 0) - ModSettingsContent.Add(new ModControlSection(added) { Children = controls }); - } - - foreach (var removed in selectedMods.OldValue.Except(selectedMods.NewValue)) - ModSettingsContent.RemoveAll(section => section.Mod == removed); - - bool hasSettings = ModSettingsContent.Children.Count > 0; - CustomiseButton.Enabled.Value = hasSettings; - - if (!hasSettings) - ModSettingsContainer.Hide(); - } - public void DeselectAll() { foreach (var section in ModSectionsContainer.Children) @@ -450,12 +428,14 @@ namespace osu.Game.Overlays.Mods refreshSelectedMods(); } - private void selectedModsChanged(ValueChangedEvent> e) + private void selectedModsChanged(ValueChangedEvent> mods) { foreach (var section in ModSectionsContainer.Children) - section.SelectTypes(e.NewValue.Select(m => m.GetType()).ToList()); + section.SelectTypes(mods.NewValue.Select(m => m.GetType()).ToList()); updateMods(); + + updateModSettings(mods); } private void updateMods() @@ -480,6 +460,25 @@ namespace osu.Game.Overlays.Mods UnrankedLabel.FadeTo(ranked ? 0 : 1, 200); } + private void updateModSettings(ValueChangedEvent> selectedMods) + { + foreach (var added in selectedMods.NewValue.Except(selectedMods.OldValue)) + { + var controls = added.CreateSettingsControls().ToList(); + if (controls.Count > 0) + ModSettingsContent.Add(new ModControlSection(added) { Children = controls }); + } + + foreach (var removed in selectedMods.OldValue.Except(selectedMods.NewValue)) + ModSettingsContent.RemoveAll(section => section.Mod == removed); + + bool hasSettings = ModSettingsContent.Children.Count > 0; + CustomiseButton.Enabled.Value = hasSettings; + + if (!hasSettings) + ModSettingsContainer.Hide(); + } + private void modButtonPressed(Mod selectedMod) { if (selectedMod != null) From 5624b9fd3f981451db3c52a7da0c94bc56971a20 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Dec 2019 13:19:13 +0900 Subject: [PATCH 56/84] Fix US english --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index ec5f99dc31..e8ea43e3f2 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -235,7 +235,7 @@ namespace osu.Game.Overlays.Mods CustomiseButton = new TriangleButton { Width = 180, - Text = "Customization", + Text = "Customisation", Action = () => ModSettingsContainer.Alpha = ModSettingsContainer.Alpha == 1 ? 0 : 1, Enabled = { Value = false }, Margin = new MarginPadding From 77b9989e115fa5681e17236415cf6c01f939b863 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Dec 2019 14:10:35 +0900 Subject: [PATCH 57/84] Fix some weird private field names --- .../MathUtils/FastRandom.cs | 20 +-- .../Components/ScrollingTeamContainer.cs | 121 +++++++++--------- osu.Game/Online/Multiplayer/PlaylistItem.cs | 22 ++-- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 34 +++-- 4 files changed, 94 insertions(+), 103 deletions(-) diff --git a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs index c721ff862a..46e427e1b7 100644 --- a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs +++ b/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs @@ -12,14 +12,14 @@ namespace osu.Game.Rulesets.Catch.MathUtils { private const double int_to_real = 1.0 / (int.MaxValue + 1.0); private const uint int_mask = 0x7FFFFFFF; - private const uint y = 842502087; - private const uint z = 3579807591; - private const uint w = 273326509; - private uint _x, _y = y, _z = z, _w = w; + private const uint y_initial = 842502087; + private const uint z_initial = 3579807591; + private const uint w_initial = 273326509; + private uint x, y = y_initial, z = z_initial, w = w_initial; public FastRandom(int seed) { - _x = (uint)seed; + x = (uint)seed; } public FastRandom() @@ -33,11 +33,11 @@ namespace osu.Game.Rulesets.Catch.MathUtils /// The random value. public uint NextUInt() { - uint t = _x ^ (_x << 11); - _x = _y; - _y = _z; - _z = _w; - return _w = _w ^ (_w >> 19) ^ t ^ (t >> 8); + uint t = x ^ (x << 11); + x = y; + y = z; + z = w; + return w = w ^ (w >> 19) ^ t ^ (t >> 8); } /// diff --git a/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs b/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs index a345f93896..3ff4718b75 100644 --- a/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs +++ b/osu.Game.Tournament/Screens/Drawings/Components/ScrollingTeamContainer.cs @@ -83,88 +83,81 @@ namespace osu.Game.Tournament.Screens.Drawings.Components }; } - private ScrollState _scrollState; + private ScrollState scrollState; - private ScrollState scrollState + private void setScrollState(ScrollState newstate) { - get => _scrollState; + if (scrollState == newstate) + return; - set + delayedStateChangeDelegate?.Cancel(); + + switch (scrollState = newstate) { - if (_scrollState == value) - return; + case ScrollState.Scrolling: + resetSelected(); - _scrollState = value; + OnScrollStarted?.Invoke(); - delayedStateChangeDelegate?.Cancel(); + speedTo(1000f, 200); + tracker.FadeOut(100); + break; - switch (value) - { - case ScrollState.Scrolling: - resetSelected(); + case ScrollState.Stopping: + speedTo(0f, 2000); + tracker.FadeIn(200); - OnScrollStarted?.Invoke(); + delayedStateChangeDelegate = Scheduler.AddDelayed(() => setScrollState(ScrollState.Stopped), 2300); + break; - speedTo(1000f, 200); - tracker.FadeOut(100); + case ScrollState.Stopped: + // Find closest to center + if (!Children.Any()) break; - case ScrollState.Stopping: - speedTo(0f, 2000); - tracker.FadeIn(200); + ScrollingTeam closest = null; - delayedStateChangeDelegate = Scheduler.AddDelayed(() => scrollState = ScrollState.Stopped, 2300); - break; + foreach (var c in Children) + { + if (!(c is ScrollingTeam stc)) + continue; - case ScrollState.Stopped: - // Find closest to center - if (!Children.Any()) - break; - - ScrollingTeam closest = null; - - foreach (var c in Children) + if (closest == null) { - if (!(c is ScrollingTeam stc)) - continue; - - if (closest == null) - { - closest = stc; - continue; - } - - float o = Math.Abs(c.Position.X + c.DrawWidth / 2f - DrawWidth / 2f); - float lastOffset = Math.Abs(closest.Position.X + closest.DrawWidth / 2f - DrawWidth / 2f); - - if (o < lastOffset) - closest = stc; + closest = stc; + continue; } - Trace.Assert(closest != null, "closest != null"); + float o = Math.Abs(c.Position.X + c.DrawWidth / 2f - DrawWidth / 2f); + float lastOffset = Math.Abs(closest.Position.X + closest.DrawWidth / 2f - DrawWidth / 2f); - // ReSharper disable once PossibleNullReferenceException - offset += DrawWidth / 2f - (closest.Position.X + closest.DrawWidth / 2f); + if (o < lastOffset) + closest = stc; + } - ScrollingTeam st = closest; + Trace.Assert(closest != null, "closest != null"); - availableTeams.RemoveAll(at => at == st.Team); + // ReSharper disable once PossibleNullReferenceException + offset += DrawWidth / 2f - (closest.Position.X + closest.DrawWidth / 2f); - st.Selected = true; - OnSelected?.Invoke(st.Team); + ScrollingTeam st = closest; - delayedStateChangeDelegate = Scheduler.AddDelayed(() => scrollState = ScrollState.Idle, 10000); - break; + availableTeams.RemoveAll(at => at == st.Team); - case ScrollState.Idle: - resetSelected(); + st.Selected = true; + OnSelected?.Invoke(st.Team); - OnScrollStarted?.Invoke(); + delayedStateChangeDelegate = Scheduler.AddDelayed(() => setScrollState(ScrollState.Idle), 10000); + break; - speedTo(40f, 200); - tracker.FadeOut(100); - break; - } + case ScrollState.Idle: + resetSelected(); + + OnScrollStarted?.Invoke(); + + speedTo(40f, 200); + tracker.FadeOut(100); + break; } } @@ -176,7 +169,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components availableTeams.Add(team); RemoveAll(c => c is ScrollingTeam); - scrollState = ScrollState.Idle; + setScrollState(ScrollState.Idle); } public void AddTeams(IEnumerable teams) @@ -192,7 +185,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components { availableTeams.Clear(); RemoveAll(c => c is ScrollingTeam); - scrollState = ScrollState.Idle; + setScrollState(ScrollState.Idle); } public void RemoveTeam(TournamentTeam team) @@ -217,7 +210,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components if (availableTeams.Count == 0) return; - scrollState = ScrollState.Scrolling; + setScrollState(ScrollState.Scrolling); } public void StopScrolling() @@ -232,13 +225,13 @@ namespace osu.Game.Tournament.Screens.Drawings.Components return; } - scrollState = ScrollState.Stopping; + setScrollState(ScrollState.Stopping); } protected override void LoadComplete() { base.LoadComplete(); - scrollState = ScrollState.Idle; + setScrollState(ScrollState.Idle); } protected override void UpdateAfterChildren() @@ -305,7 +298,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components private void speedTo(float value, double duration = 0, Easing easing = Easing.None) => this.TransformTo(nameof(speed), value, duration, easing); - private enum ScrollState + protected enum ScrollState { None, Idle, diff --git a/osu.Game/Online/Multiplayer/PlaylistItem.cs b/osu.Game/Online/Multiplayer/PlaylistItem.cs index e47d497d94..d13e8b31e6 100644 --- a/osu.Game/Online/Multiplayer/PlaylistItem.cs +++ b/osu.Game/Online/Multiplayer/PlaylistItem.cs @@ -45,23 +45,25 @@ namespace osu.Game.Online.Multiplayer [JsonProperty("beatmap")] private APIBeatmap apiBeatmap { get; set; } + private APIMod[] allowedModsBacking; + [JsonProperty("allowed_mods")] private APIMod[] allowedMods { get => AllowedMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray(); - set => _allowedMods = value; + set => allowedModsBacking = value; } + private APIMod[] requiredModsBacking; + [JsonProperty("required_mods")] private APIMod[] requiredMods { get => RequiredMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray(); - set => _requiredMods = value; + set => requiredModsBacking = value; } private BeatmapInfo beatmap; - private APIMod[] _allowedMods; - private APIMod[] _requiredMods; public void MapObjects(BeatmapManager beatmaps, RulesetStore rulesets) { @@ -70,20 +72,20 @@ namespace osu.Game.Online.Multiplayer Beatmap = apiBeatmap == null ? beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == BeatmapID) : apiBeatmap.ToBeatmap(rulesets); Ruleset = rulesets.GetRuleset(RulesetID); - if (_allowedMods != null) + if (allowedModsBacking != null) { AllowedMods.Clear(); - AllowedMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => _allowedMods.Any(m => m.Acronym == mod.Acronym))); + AllowedMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => allowedModsBacking.Any(m => m.Acronym == mod.Acronym))); - _allowedMods = null; + allowedModsBacking = null; } - if (_requiredMods != null) + if (requiredModsBacking != null) { RequiredMods.Clear(); - RequiredMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => _requiredMods.Any(m => m.Acronym == mod.Acronym))); + RequiredMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => requiredModsBacking.Any(m => m.Acronym == mod.Acronym))); - _requiredMods = null; + requiredModsBacking = null; } } diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index f54d638584..2fa5098890 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -188,26 +188,22 @@ namespace osu.Game.Screens.Play InternalButtons.Add(button); } - private int _selectionIndex = -1; + private int selectionIndex = -1; - private int selectionIndex + private void setSelected(int value) { - get => _selectionIndex; - set - { - if (_selectionIndex == value) - return; + if (selectionIndex == value) + return; - // Deselect the previously-selected button - if (_selectionIndex != -1) - InternalButtons[_selectionIndex].Selected.Value = false; + // Deselect the previously-selected button + if (selectionIndex != -1) + InternalButtons[selectionIndex].Selected.Value = false; - _selectionIndex = value; + selectionIndex = value; - // Select the newly-selected button - if (_selectionIndex != -1) - InternalButtons[_selectionIndex].Selected.Value = true; - } + // Select the newly-selected button + if (selectionIndex != -1) + InternalButtons[selectionIndex].Selected.Value = true; } protected override bool OnKeyDown(KeyDownEvent e) @@ -218,16 +214,16 @@ namespace osu.Game.Screens.Play { case Key.Up: if (selectionIndex == -1 || selectionIndex == 0) - selectionIndex = InternalButtons.Count - 1; + setSelected(InternalButtons.Count - 1); else - selectionIndex--; + setSelected(selectionIndex--); return true; case Key.Down: if (selectionIndex == -1 || selectionIndex == InternalButtons.Count - 1) - selectionIndex = 0; + setSelected(0); else - selectionIndex++; + setSelected(selectionIndex++); return true; } } From 9ebad16436cf7a079586c62216964f9f5648f20f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Dec 2019 14:34:17 +0900 Subject: [PATCH 58/84] Fix logic regression --- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 2fa5098890..adfbe977a4 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -216,14 +216,14 @@ namespace osu.Game.Screens.Play if (selectionIndex == -1 || selectionIndex == 0) setSelected(InternalButtons.Count - 1); else - setSelected(selectionIndex--); + setSelected(selectionIndex - 1); return true; case Key.Down: if (selectionIndex == -1 || selectionIndex == InternalButtons.Count - 1) setSelected(0); else - setSelected(selectionIndex++); + setSelected(selectionIndex + 1); return true; } } @@ -262,9 +262,9 @@ namespace osu.Game.Screens.Play private void buttonSelectionChanged(DialogButton button, bool isSelected) { if (!isSelected) - selectionIndex = -1; + setSelected(-1); else - selectionIndex = InternalButtons.IndexOf(button); + setSelected(InternalButtons.IndexOf(button)); } private void updateRetryCount() From 6c8f325063affe692a31ba0d3c1cd1688b3456c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Dec 2019 15:24:06 +0900 Subject: [PATCH 59/84] Add failing test --- .../Visual/Gameplay/TestScenePause.cs | 2 - .../Gameplay/TestScenePauseWhenInactive.cs | 49 +++++++++++++++++++ osu.Game/Tests/Visual/TestPlayer.cs | 7 ++- 3 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index 803cab9325..e04315894e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -285,8 +285,6 @@ namespace osu.Game.Tests.Visual.Gameplay protected class PausePlayer : TestPlayer { - public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer; - public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; public new HUDOverlay HUDOverlay => base.HUDOverlay; diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs new file mode 100644 index 0000000000..5f29b46fe6 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Platform; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual.Gameplay +{ + [HeadlessTest] // we alter unsafe properties on the game host to test inactive window state. + public class TestScenePauseWhenInactive : PlayerTestScene + { + protected new TestPlayer Player => (TestPlayer)base.Player; + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) + { + var beatmap = (Beatmap)base.CreateBeatmap(ruleset); + + beatmap.HitObjects.RemoveAll(h => h.StartTime < 30000); + + return beatmap; + } + + [Resolved] + private GameHost host { get; set; } + + public TestScenePauseWhenInactive() + : base(new OsuRuleset()) + { + } + + [Test] + public void TestDoesntPauseDuringIntro() + { + AddStep("set inactive", () => ((Bindable)host.IsActive).Value = false); + AddStep("resume player", () => Player.GameplayClockContainer.Start()); + AddUntilStep("wait for pause", () => Player.GameplayClockContainer.IsPaused.Value); + AddAssert("time of pause is after gameplay start time", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= Player.DrawableRuleset.GameplayStartTime); + } + + protected override Player CreatePlayer(Ruleset ruleset) => new TestPlayer(true, true, true); + } +} diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs index 31f6edadec..8e3821f1a0 100644 --- a/osu.Game/Tests/Visual/TestPlayer.cs +++ b/osu.Game/Tests/Visual/TestPlayer.cs @@ -8,13 +8,16 @@ namespace osu.Game.Tests.Visual { public class TestPlayer : Player { - protected override bool PauseOnFocusLost => false; + protected override bool PauseOnFocusLost { get; } public new DrawableRuleset DrawableRuleset => base.DrawableRuleset; - public TestPlayer(bool allowPause = true, bool showResults = true) + public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer; + + public TestPlayer(bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false) : base(allowPause, showResults) { + PauseOnFocusLost = pauseOnFocusLost; } } } From 75f92506451b47a1815530d00ffed7a74c23f41b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Dec 2019 15:45:50 +0900 Subject: [PATCH 60/84] Don't automatically pause when window is inactive if in break time --- osu.Game/Screens/Play/Player.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d40c448452..9feee82989 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -135,7 +135,7 @@ namespace osu.Game.Screens.Play addGameplayComponents(GameplayClockContainer, working); addOverlayComponents(GameplayClockContainer, working); - DrawableRuleset.HasReplayLoaded.BindValueChanged(e => HUDOverlay.HoldToQuit.PauseOnFocusLost = !e.NewValue && PauseOnFocusLost, true); + DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true); // bind clock into components that require it DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); @@ -146,6 +146,7 @@ namespace osu.Game.Screens.Play foreach (var mod in Mods.Value.OfType()) mod.ApplyToScoreProcessor(ScoreProcessor); + breakOverlay.IsBreakTime.ValueChanged += _ => updatePauseOnFocusLostState(); } private void addUnderlayComponents(Container target) @@ -241,6 +242,11 @@ namespace osu.Game.Screens.Play }); } + private void updatePauseOnFocusLostState() => + HUDOverlay.HoldToQuit.PauseOnFocusLost = PauseOnFocusLost + && !DrawableRuleset.HasReplayLoaded.Value + && !breakOverlay.IsBreakTime.Value; + private WorkingBeatmap loadBeatmap() { WorkingBeatmap working = Beatmap.Value; From 274958669c826236e48f681b8edb3c00ea8bf2b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Dec 2019 15:47:34 +0900 Subject: [PATCH 61/84] Add early assert as sanity check --- osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs index 5f29b46fe6..3513b6c25a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePauseWhenInactive.cs @@ -39,7 +39,9 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestDoesntPauseDuringIntro() { AddStep("set inactive", () => ((Bindable)host.IsActive).Value = false); + AddStep("resume player", () => Player.GameplayClockContainer.Start()); + AddAssert("ensure not paused", () => !Player.GameplayClockContainer.IsPaused.Value); AddUntilStep("wait for pause", () => Player.GameplayClockContainer.IsPaused.Value); AddAssert("time of pause is after gameplay start time", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= Player.DrawableRuleset.GameplayStartTime); } From d8cebd20edac75973ad00c65b35421fcab97a9e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Dec 2019 16:06:20 +0900 Subject: [PATCH 62/84] Add xmldoc --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs index 909c764b9e..573c408a78 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs @@ -8,6 +8,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { public abstract class OsuCursorSprite : CompositeDrawable { + /// + /// The an optional piece of the cursor to expand when in a clicked state. + /// If null, the whole cursor will be affected by expansion. + /// public Drawable ExpandTarget { get; protected set; } } } From 6b3c7c842198b1fe6e24314c6bad453cfd73d37b Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 19 Nov 2019 20:34:35 +0800 Subject: [PATCH 63/84] Remove usages of FileSafety class. --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 8 ++++---- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 4 ++-- osu.Game/Beatmaps/WorkingBeatmap.cs | 7 +++++-- osu.Game/Database/ArchiveModelManager.cs | 3 +-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 838b1c2f07..bffe999896 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using osu.Framework.IO.File; +using osu.Framework.Extensions; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Beatmaps.ControlPoints; @@ -112,7 +112,7 @@ namespace osu.Game.Beatmaps.Formats switch (pair.Key) { case @"AudioFilename": - metadata.AudioFile = FileSafety.PathStandardise(pair.Value); + metadata.AudioFile = pair.Value.PathStandardise(); break; case @"AudioLeadIn": @@ -300,12 +300,12 @@ namespace osu.Game.Beatmaps.Formats { case EventType.Background: string bgFilename = split[2].Trim('"'); - beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(bgFilename); + beatmap.BeatmapInfo.Metadata.BackgroundFile = bgFilename.PathStandardise(); break; case EventType.Video: string videoFilename = split[2].Trim('"'); - beatmap.BeatmapInfo.Metadata.VideoFile = FileSafety.PathStandardise(videoFilename); + beatmap.BeatmapInfo.Metadata.VideoFile = videoFilename.PathStandardise(); break; case EventType.Break: diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index f94ab3f27b..d79c0f7fa8 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -8,8 +8,8 @@ using System.IO; using System.Linq; using osuTK; using osuTK.Graphics; +using osu.Framework.Extensions; using osu.Framework.Graphics; -using osu.Framework.IO.File; using osu.Game.IO; using osu.Game.Storyboards; @@ -335,6 +335,6 @@ namespace osu.Game.Beatmaps.Formats } } - private string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('"')); + private string cleanFilename(string path) => path.Trim('"').PathStandardise(); } } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 44d6d33cef..f23669579a 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -7,7 +7,6 @@ using osu.Game.Rulesets.Mods; using System; using System.Collections.Generic; using osu.Game.Storyboards; -using osu.Framework.IO.File; using System.IO; using System.Linq; using System.Threading; @@ -83,7 +82,11 @@ namespace osu.Game.Beatmaps /// The absolute path of the output file. public string Save() { - var path = FileSafety.GetTempPath(Guid.NewGuid().ToString().Replace("-", string.Empty) + ".json"); + // copied from osu.Framework.IO.File.FileSafety.GetTempPath + string directory = Path.Combine(Path.GetTempPath(), @"osu!"); + Directory.CreateDirectory(directory); + + var path = Path.Combine(directory, Guid.NewGuid().ToString().Replace("-", string.Empty) + ".json"); using (var sw = new StreamWriter(path)) sw.WriteLine(Beatmap.Serialize()); return path; diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 7cce2fb92f..5cd2b947fe 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -13,7 +13,6 @@ using Microsoft.EntityFrameworkCore; using osu.Framework; using osu.Framework.Extensions; using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.IO.File; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Threading; @@ -493,7 +492,7 @@ namespace osu.Game.Database { fileInfos.Add(new TFileModel { - Filename = FileSafety.PathStandardise(file.Substring(prefix.Length)), + Filename = file.Substring(prefix.Length).PathStandardise(), FileInfo = files.Add(s) }); } From b86a3dbfabbc1db04314b6e6282cf88343e50eeb Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 11 Dec 2019 16:06:56 +0800 Subject: [PATCH 64/84] PathStandardise -> ToStandardisedPath --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 6 +++--- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 2 +- osu.Game/Database/ArchiveModelManager.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index bffe999896..f8275ec4f6 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -112,7 +112,7 @@ namespace osu.Game.Beatmaps.Formats switch (pair.Key) { case @"AudioFilename": - metadata.AudioFile = pair.Value.PathStandardise(); + metadata.AudioFile = pair.Value.ToStandardisedPath(); break; case @"AudioLeadIn": @@ -300,12 +300,12 @@ namespace osu.Game.Beatmaps.Formats { case EventType.Background: string bgFilename = split[2].Trim('"'); - beatmap.BeatmapInfo.Metadata.BackgroundFile = bgFilename.PathStandardise(); + beatmap.BeatmapInfo.Metadata.BackgroundFile = bgFilename.ToStandardisedPath(); break; case EventType.Video: string videoFilename = split[2].Trim('"'); - beatmap.BeatmapInfo.Metadata.VideoFile = videoFilename.PathStandardise(); + beatmap.BeatmapInfo.Metadata.VideoFile = videoFilename.ToStandardisedPath(); break; case EventType.Break: diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index d79c0f7fa8..ccd46ab559 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -335,6 +335,6 @@ namespace osu.Game.Beatmaps.Formats } } - private string cleanFilename(string path) => path.Trim('"').PathStandardise(); + private string cleanFilename(string path) => path.Trim('"').ToStandardisedPath(); } } diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 5cd2b947fe..fd455d7cd5 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -492,7 +492,7 @@ namespace osu.Game.Database { fileInfos.Add(new TFileModel { - Filename = file.Substring(prefix.Length).PathStandardise(), + Filename = file.Substring(prefix.Length).ToStandardisedPath(), FileInfo = files.Add(s) }); } From cffeceb2290c4ce9b6ce37dacdd7d766e00d216e Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 11 Dec 2019 16:24:22 +0800 Subject: [PATCH 65/84] Remove unnecessary comment. --- osu.Game/Beatmaps/WorkingBeatmap.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index f23669579a..4452d26fcd 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -82,7 +82,6 @@ namespace osu.Game.Beatmaps /// The absolute path of the output file. public string Save() { - // copied from osu.Framework.IO.File.FileSafety.GetTempPath string directory = Path.Combine(Path.GetTempPath(), @"osu!"); Directory.CreateDirectory(directory); From 48f1dad4aa4546402ed6c0bd860ae54598c7365d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Dec 2019 17:25:06 +0900 Subject: [PATCH 66/84] Remove abstract ScoreProcessor class --- .../Scoring/CatchScoreProcessor.cs | 10 +- .../UI/DrawableCatchRuleset.cs | 2 +- .../Scoring/ManiaScoreProcessor.cs | 12 +- .../UI/DrawableManiaRuleset.cs | 2 +- .../Scoring/OsuScoreProcessor.cs | 10 +- .../UI/DrawableOsuRuleset.cs | 2 +- .../Scoring/TaikoScoreProcessor.cs | 12 +- .../UI/DrawableTaikoRuleset.cs | 2 +- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 276 ++++++++---------- osu.Game/Rulesets/UI/DrawableRuleset.cs | 26 +- osu.Game/Screens/Play/Player.cs | 3 + 11 files changed, 165 insertions(+), 192 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 18785d65ea..f67ca1213e 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -2,23 +2,21 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Catch.Scoring { - public class CatchScoreProcessor : ScoreProcessor + public class CatchScoreProcessor : ScoreProcessor { - public CatchScoreProcessor(DrawableRuleset drawableRuleset) - : base(drawableRuleset) + public CatchScoreProcessor(IBeatmap beatmap) + : base(beatmap) { } private float hpDrainRate; - protected override void ApplyBeatmap(Beatmap beatmap) + protected override void ApplyBeatmap(IBeatmap beatmap) { base.ApplyBeatmap(beatmap); diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index 6b7f00c5d0..f5bddeb2cb 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.UI TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); } - public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this); + public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(Beatmap); protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index 49894a644c..a678ef60e7 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -3,13 +3,11 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Mania.Scoring { - internal class ManiaScoreProcessor : ScoreProcessor + internal class ManiaScoreProcessor : ScoreProcessor { /// /// The hit HP multiplier at OD = 0. @@ -51,12 +49,12 @@ namespace osu.Game.Rulesets.Mania.Scoring /// private double hpMultiplier = 1; - public ManiaScoreProcessor(DrawableRuleset drawableRuleset) - : base(drawableRuleset) + public ManiaScoreProcessor(IBeatmap beatmap) + : base(beatmap) { } - protected override void ApplyBeatmap(Beatmap beatmap) + protected override void ApplyBeatmap(IBeatmap beatmap) { base.ApplyBeatmap(beatmap); @@ -65,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.Scoring hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max); } - protected override void SimulateAutoplay(Beatmap beatmap) + protected override void SimulateAutoplay(IBeatmap beatmap) { while (true) { diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index d371c1f7a8..0607bf0abd 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Mania.UI protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages); - public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); + public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(Beatmap); public override int Variant => (int)(Beatmap.Stages.Count == 1 ? PlayfieldType.Single : PlayfieldType.Dual) + Beatmap.TotalColumns; diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index affe18a30d..6779271cb3 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -5,22 +5,20 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu.Scoring { - internal class OsuScoreProcessor : ScoreProcessor + internal class OsuScoreProcessor : ScoreProcessor { - public OsuScoreProcessor(DrawableRuleset drawableRuleset) - : base(drawableRuleset) + public OsuScoreProcessor(IBeatmap beatmap) + : base(beatmap) { } private float hpDrainRate; - protected override void ApplyBeatmap(Beatmap beatmap) + protected override void ApplyBeatmap(IBeatmap beatmap) { base.ApplyBeatmap(beatmap); diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index 49aea52902..5bb728a9b0 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.UI public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor - public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(this); + public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(Beatmap); protected override Playfield CreatePlayfield() => new OsuPlayfield(); diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index 75a27ff639..ae593d2e3a 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -1,15 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Objects; -using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Taiko.Scoring { - internal class TaikoScoreProcessor : ScoreProcessor + internal class TaikoScoreProcessor : ScoreProcessor { /// /// A value used for calculating . @@ -31,16 +31,16 @@ namespace osu.Game.Rulesets.Taiko.Scoring /// private double hpMissMultiplier; - public TaikoScoreProcessor(DrawableRuleset drawableRuleset) - : base(drawableRuleset) + public TaikoScoreProcessor(IBeatmap beatmap) + : base(beatmap) { } - protected override void ApplyBeatmap(Beatmap beatmap) + protected override void ApplyBeatmap(IBeatmap beatmap) { base.ApplyBeatmap(beatmap); - hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98)); + hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.OfType().Count() * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98)); hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120); } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index fc109bf6a6..d4ea9a043a 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.UI new BarLineGenerator(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar))); } - public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this); + public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(Beatmap); public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer(); diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 18c2a2ca01..a8a2294498 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -13,13 +13,16 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.UI; using osu.Game.Scoring; namespace osu.Game.Rulesets.Scoring { - public abstract class ScoreProcessor + public class ScoreProcessor { + private const double base_portion = 0.3; + private const double combo_portion = 0.7; + private const double max_score = 1000000; + /// /// Invoked when the is in a failed state. /// This may occur regardless of whether an event is invoked. @@ -67,11 +70,6 @@ namespace osu.Game.Rulesets.Scoring /// public readonly Bindable> Mods = new Bindable>(Array.Empty()); - /// - /// Create a for this processor. - /// - public virtual HitWindows CreateHitWindows() => new HitWindows(); - /// /// The current rank. /// @@ -90,132 +88,23 @@ namespace osu.Game.Rulesets.Scoring /// /// Whether all s have been processed. /// - public virtual bool HasCompleted => false; - - /// - /// The total number of judged s at the current point in time. - /// - public int JudgedHits { get; protected set; } + public bool HasCompleted => JudgedHits == MaxHits; /// /// Whether this ScoreProcessor has already triggered the failed state. /// - public virtual bool HasFailed { get; private set; } + public bool HasFailed { get; private set; } /// - /// The default conditions for failing. + /// The maximum number of hits that can be judged. /// - protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value); - - protected ScoreProcessor() - { - Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); }; - Accuracy.ValueChanged += delegate - { - Rank.Value = rankFrom(Accuracy.Value); - foreach (var mod in Mods.Value.OfType()) - Rank.Value = mod.AdjustRank(Rank.Value, Accuracy.Value); - }; - } - - private ScoreRank rankFrom(double acc) - { - if (acc == 1) - return ScoreRank.X; - if (acc > 0.95) - return ScoreRank.S; - if (acc > 0.9) - return ScoreRank.A; - if (acc > 0.8) - return ScoreRank.B; - if (acc > 0.7) - return ScoreRank.C; - - return ScoreRank.D; - } - - /// - /// Resets this ScoreProcessor to a default state. - /// - /// Whether to store the current state of the for future use. - protected virtual void Reset(bool storeResults) - { - TotalScore.Value = 0; - Accuracy.Value = 1; - Health.Value = 1; - Combo.Value = 0; - Rank.Value = ScoreRank.X; - HighestCombo.Value = 0; - - JudgedHits = 0; - - HasFailed = false; - } - - /// - /// Checks if the score is in a failed state and notifies subscribers. - /// - /// This can only ever notify subscribers once. - /// - /// - protected void UpdateFailed(JudgementResult result) - { - if (HasFailed) - return; - - if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true) - return; - - if (Failed?.Invoke() != false) - HasFailed = true; - } - - /// - /// Notifies subscribers of that a new judgement has occurred. - /// - /// The judgement scoring result to notify subscribers of. - protected void NotifyNewJudgement(JudgementResult result) - { - NewJudgement?.Invoke(result); - - if (HasCompleted) - AllJudged?.Invoke(); - } - - /// - /// Retrieve a score populated with data for the current play this processor is responsible for. - /// - public virtual void PopulateScore(ScoreInfo score) - { - score.TotalScore = (long)Math.Round(TotalScore.Value); - score.Combo = Combo.Value; - score.MaxCombo = HighestCombo.Value; - score.Accuracy = Math.Round(Accuracy.Value, 4); - score.Rank = Rank.Value; - score.Date = DateTimeOffset.Now; - - var hitWindows = CreateHitWindows(); - - foreach (var result in Enum.GetValues(typeof(HitResult)).OfType().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r))) - score.Statistics[result] = GetStatistic(result); - } - - public abstract int GetStatistic(HitResult result); - - public abstract double GetStandardisedScore(); - } - - public class ScoreProcessor : ScoreProcessor - where TObject : HitObject - { - private const double base_portion = 0.3; - private const double combo_portion = 0.7; - private const double max_score = 1000000; - - public sealed override bool HasCompleted => JudgedHits == MaxHits; - protected int MaxHits { get; private set; } + /// + /// The total number of judged s at the current point in time. + /// + public int JudgedHits { get; private set; } + private double maxHighestCombo; private double maxBaseScore; @@ -225,17 +114,22 @@ namespace osu.Game.Rulesets.Scoring private double scoreMultiplier = 1; - public ScoreProcessor(DrawableRuleset drawableRuleset) + public ScoreProcessor(IBeatmap beatmap) { Debug.Assert(base_portion + combo_portion == 1.0); - drawableRuleset.OnNewResult += applyResult; - drawableRuleset.OnRevertResult += revertResult; + Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue); + Accuracy.ValueChanged += accuracy => + { + Rank.Value = rankFrom(accuracy.NewValue); + foreach (var mod in Mods.Value.OfType()) + Rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue); + }; - ApplyBeatmap(drawableRuleset.Beatmap); + ApplyBeatmap(beatmap); Reset(false); - SimulateAutoplay(drawableRuleset.Beatmap); + SimulateAutoplay(beatmap); Reset(true); if (maxBaseScore == 0 || maxHighestCombo == 0) @@ -257,19 +151,19 @@ namespace osu.Game.Rulesets.Scoring } /// - /// Applies any properties of the which affect scoring to this . + /// Applies any properties of the which affect scoring to this . /// - /// The to read properties from. - protected virtual void ApplyBeatmap(Beatmap beatmap) + /// The to read properties from. + protected virtual void ApplyBeatmap(IBeatmap beatmap) { } /// - /// Simulates an autoplay of the to determine scoring values. + /// Simulates an autoplay of the to determine scoring values. /// /// This provided temporarily. DO NOT USE. - /// The to simulate. - protected virtual void SimulateAutoplay(Beatmap beatmap) + /// The to simulate. + protected virtual void SimulateAutoplay(IBeatmap beatmap) { foreach (var obj in beatmap.HitObjects) simulate(obj); @@ -289,7 +183,7 @@ namespace osu.Game.Rulesets.Scoring result.Type = judgement.MaxResult; - applyResult(result); + ApplyResult(result); } } @@ -297,22 +191,26 @@ namespace osu.Game.Rulesets.Scoring /// Applies the score change of a to this . /// /// The to apply. - private void applyResult(JudgementResult result) + public void ApplyResult(JudgementResult result) { - ApplyResult(result); - updateScore(); + ApplyResultInternal(result); - UpdateFailed(result); - NotifyNewJudgement(result); + updateScore(); + updateFailed(result); + + NewJudgement?.Invoke(result); + + if (HasCompleted) + AllJudged?.Invoke(); } /// /// Reverts the score change of a that was applied to this . /// /// The judgement scoring result. - private void revertResult(JudgementResult result) + public void RevertResult(JudgementResult result) { - RevertResult(result); + RevertResultInternal(result); updateScore(); } @@ -322,10 +220,10 @@ namespace osu.Game.Rulesets.Scoring /// Applies the score change of a to this . /// /// - /// Any changes applied via this method can be reverted via . + /// Any changes applied via this method can be reverted via . /// /// The to apply. - protected virtual void ApplyResult(JudgementResult result) + protected virtual void ApplyResultInternal(JudgementResult result) { result.ComboAtJudgement = Combo.Value; result.HighestComboAtJudgement = HighestCombo.Value; @@ -372,10 +270,10 @@ namespace osu.Game.Rulesets.Scoring } /// - /// Reverts the score change of a that was applied to this via . + /// Reverts the score change of a that was applied to this via . /// /// The judgement scoring result. - protected virtual void RevertResult(JudgementResult result) + protected virtual void RevertResultInternal(JudgementResult result) { Combo.Value = result.ComboAtJudgement; HighestCombo.Value = result.HighestComboAtJudgement; @@ -432,11 +330,49 @@ namespace osu.Game.Rulesets.Scoring } } - public override int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result); + /// + /// Checks if the score is in a failed state and notifies subscribers. + /// + /// This can only ever notify subscribers once. + /// + /// + private void updateFailed(JudgementResult result) + { + if (HasFailed) + return; - public override double GetStandardisedScore() => getScore(ScoringMode.Standardised); + if (!DefaultFailCondition && FailConditions?.Invoke(this, result) != true) + return; - protected override void Reset(bool storeResults) + if (Failed?.Invoke() != false) + HasFailed = true; + } + + private ScoreRank rankFrom(double acc) + { + if (acc == 1) + return ScoreRank.X; + if (acc > 0.95) + return ScoreRank.S; + if (acc > 0.9) + return ScoreRank.A; + if (acc > 0.8) + return ScoreRank.B; + if (acc > 0.7) + return ScoreRank.C; + + return ScoreRank.D; + } + + public int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result); + + public double GetStandardisedScore() => getScore(ScoringMode.Standardised); + + /// + /// Resets this ScoreProcessor to a default state. + /// + /// Whether to store the current state of the for future use. + protected virtual void Reset(bool storeResults) { scoreResultCounts.Clear(); @@ -447,13 +383,49 @@ namespace osu.Game.Rulesets.Scoring maxBaseScore = baseScore; } - base.Reset(storeResults); - + JudgedHits = 0; baseScore = 0; rollingMaxBaseScore = 0; bonusScore = 0; + + TotalScore.Value = 0; + Accuracy.Value = 1; + Health.Value = 1; + Combo.Value = 0; + Rank.Value = ScoreRank.X; + HighestCombo.Value = 0; + + HasFailed = false; } + /// + /// Retrieve a score populated with data for the current play this processor is responsible for. + /// + public virtual void PopulateScore(ScoreInfo score) + { + score.TotalScore = (long)Math.Round(TotalScore.Value); + score.Combo = Combo.Value; + score.MaxCombo = HighestCombo.Value; + score.Accuracy = Math.Round(Accuracy.Value, 4); + score.Rank = Rank.Value; + score.Date = DateTimeOffset.Now; + + var hitWindows = CreateHitWindows(); + + foreach (var result in Enum.GetValues(typeof(HitResult)).OfType().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r))) + score.Statistics[result] = GetStatistic(result); + } + + /// + /// The default conditions for failing. + /// + protected virtual bool DefaultFailCondition => Precision.AlmostBigger(Health.MinValue, Health.Value); + + /// + /// Create a for this processor. + /// + public virtual HitWindows CreateHitWindows() => new HitWindows(); + /// /// Creates the that represents the scoring result for a . /// diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index a856974292..10657f6b39 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -45,6 +45,10 @@ namespace osu.Game.Rulesets.UI public abstract class DrawableRuleset : DrawableRuleset, IProvideCursor, ICanAttachKeyCounter where TObject : HitObject { + public override event Action OnNewResult; + + public override event Action OnRevertResult; + /// /// The selected variant. /// @@ -91,16 +95,6 @@ namespace osu.Game.Rulesets.UI } } - /// - /// Invoked when a has been applied by a . - /// - public event Action OnNewResult; - - /// - /// Invoked when a is being reverted by a . - /// - public event Action OnRevertResult; - /// /// The beatmap. /// @@ -309,7 +303,7 @@ namespace osu.Game.Rulesets.UI /// The Playfield. protected abstract Playfield CreatePlayfield(); - public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(this); + public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(Beatmap); /// /// Applies the active mods to this DrawableRuleset. @@ -366,6 +360,16 @@ namespace osu.Game.Rulesets.UI /// public abstract class DrawableRuleset : CompositeDrawable { + /// + /// Invoked when a has been applied by a . + /// + public abstract event Action OnNewResult; + + /// + /// Invoked when a is being reverted by a . + /// + public abstract event Action OnRevertResult; + /// /// Whether a replay is currently loaded. /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index d40c448452..d3df137328 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -140,6 +140,9 @@ namespace osu.Game.Screens.Play // bind clock into components that require it DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused); + DrawableRuleset.OnNewResult += ScoreProcessor.ApplyResult; + DrawableRuleset.OnRevertResult += ScoreProcessor.RevertResult; + // Bind ScoreProcessor to ourselves ScoreProcessor.AllJudged += onCompletion; ScoreProcessor.Failed += onFail; From 97ca2e2753151ec527264d0226a5e0b2459f6cd9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Dec 2019 17:58:59 +0900 Subject: [PATCH 67/84] Add missing bezier option to menu --- .../Blueprints/Sliders/Components/PathControlPointVisualiser.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index d706fb51d5..ab6064602b 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -171,6 +171,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components createMenuItemForPathType(null), createMenuItemForPathType(PathType.Linear), createMenuItemForPathType(PathType.PerfectCurve), + createMenuItemForPathType(PathType.Bezier), createMenuItemForPathType(PathType.Catmull) } } From 03040d175092313cd9a5657fa1f9915917d22927 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Dec 2019 18:18:16 +0900 Subject: [PATCH 68/84] Don't show inherit menu item when first control point is selected --- .../Components/PathControlPointVisualiser.cs | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index ab6064602b..e406bb6426 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -27,7 +27,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components public class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler, IHasContextMenu { internal readonly Container Pieces; + private readonly Slider slider; + private readonly bool allowSelection; private InputManager inputManager; @@ -82,7 +84,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components protected override bool OnClick(ClickEvent e) { foreach (var piece in Pieces) + { piece.IsSelected.Value = false; + } + return false; } @@ -156,24 +161,29 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (!Pieces.Any(p => p.IsHovered)) return null; - int selectedPoints = Pieces.Count(p => p.IsSelected.Value); + var selectedPieces = Pieces.Where(p => p.IsSelected.Value).ToList(); + int count = selectedPieces.Count; - if (selectedPoints == 0) + if (count == 0) return null; + List items = new List(); + + if (!selectedPieces.Contains(Pieces[0])) + items.Add(createMenuItemForPathType(null)); + + // todo: hide/disable items which aren't valid for selected points + items.Add(createMenuItemForPathType(PathType.Linear)); + items.Add(createMenuItemForPathType(PathType.PerfectCurve)); + items.Add(createMenuItemForPathType(PathType.Bezier)); + items.Add(createMenuItemForPathType(PathType.Catmull)); + return new MenuItem[] { - new OsuMenuItem($"Delete {"control point".ToQuantity(selectedPoints, selectedPoints > 1 ? ShowQuantityAs.Numeric : ShowQuantityAs.None)}", MenuItemType.Destructive, () => deleteSelected()), + new OsuMenuItem($"Delete {"control point".ToQuantity(count, count > 1 ? ShowQuantityAs.Numeric : ShowQuantityAs.None)}", MenuItemType.Destructive, () => deleteSelected()), new OsuMenuItem("Type") { - Items = new[] - { - createMenuItemForPathType(null), - createMenuItemForPathType(PathType.Linear), - createMenuItemForPathType(PathType.PerfectCurve), - createMenuItemForPathType(PathType.Bezier), - createMenuItemForPathType(PathType.Catmull) - } + Items = items } }; } From d82ba3e7f7ebb69b29a7529299dbe3aaa800a373 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Dec 2019 18:20:28 +0900 Subject: [PATCH 69/84] Curve -> Curve type --- .../Blueprints/Sliders/Components/PathControlPointVisualiser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index e406bb6426..22155ab7af 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components return new MenuItem[] { new OsuMenuItem($"Delete {"control point".ToQuantity(count, count > 1 ? ShowQuantityAs.Numeric : ShowQuantityAs.None)}", MenuItemType.Destructive, () => deleteSelected()), - new OsuMenuItem("Type") + new OsuMenuItem("Curve type") { Items = items } From 23959f3a3ccb462a21c52460163b70bc474e8fa1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Dec 2019 18:52:38 +0900 Subject: [PATCH 70/84] Move control point removal to SliderSelectionBlueprint --- .../Components/PathControlPointVisualiser.cs | 32 ++----------- .../Sliders/SliderSelectionBlueprint.cs | 47 +++++++++++++++++-- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 22155ab7af..cd19653a2e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using Humanizer; -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -18,8 +17,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Screens.Edit.Compose; -using osuTK; using osuTK.Input; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components @@ -34,11 +31,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private InputManager inputManager; - [Resolved(CanBeNull = true)] - private IPlacementHandler placementHandler { get; set; } - private IBindableList controlPoints; + public Action> RemoveControlPointsRequested; + public PathControlPointVisualiser(Slider slider, bool allowSelection) { this.slider = slider; @@ -123,29 +119,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (toRemove.Count == 0) return false; - foreach (var c in toRemove) - { - // The first control point in the slider must have a type, so take it from the previous "first" one - // Todo: Should be handled within SliderPath itself - if (c == slider.Path.ControlPoints[0] && slider.Path.ControlPoints.Count > 1 && slider.Path.ControlPoints[1].Type.Value == null) - slider.Path.ControlPoints[1].Type.Value = slider.Path.ControlPoints[0].Type.Value; - - slider.Path.ControlPoints.Remove(c); - } - - // If there are 0 or 1 remaining control points, the slider is in a degenerate (single point) form and should be deleted - if (slider.Path.ControlPoints.Count <= 1) - { - placementHandler?.Delete(slider); - return true; - } - - // The path will have a non-zero offset if the head is removed, but sliders don't support this behaviour since the head is positioned at the slider's position - // So the slider needs to be offset by this amount instead, and all control points offset backwards such that the path is re-positioned at (0, 0) - Vector2 first = slider.Path.ControlPoints[0].Position.Value; - foreach (var c in slider.Path.ControlPoints) - c.Position.Value -= first; - slider.Position += first; + RemoveControlPointsRequested?.Invoke(toRemove); // Since pieces are re-used, they will not point to the deleted control points while remaining selected foreach (var piece in Pieces) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 68873093a6..3165c441fb 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.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 System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -14,6 +15,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Screens.Edit.Compose; using osuTK; using osuTK.Input; @@ -29,6 +31,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders [Resolved(CanBeNull = true)] private HitObjectComposer composer { get; set; } + [Resolved(CanBeNull = true)] + private IPlacementHandler placementHandler { get; set; } + public SliderSelectionBlueprint(DrawableSlider slider) : base(slider) { @@ -40,6 +45,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders HeadBlueprint = CreateCircleSelectionBlueprint(slider, SliderPosition.Start), TailBlueprint = CreateCircleSelectionBlueprint(slider, SliderPosition.End), ControlPointVisualiser = new PathControlPointVisualiser(sliderObject, true) + { + RemoveControlPointsRequested = removeControlPoints + } }; } @@ -97,6 +105,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders return true; } + private BindableList controlPoints => HitObject.Path.ControlPoints; + private int addControlPoint(Vector2 position) { position -= HitObject.Position; @@ -104,9 +114,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders int insertionIndex = 0; float minDistance = float.MaxValue; - for (int i = 0; i < HitObject.Path.ControlPoints.Count - 1; i++) + for (int i = 0; i < controlPoints.Count - 1; i++) { - float dist = new Line(HitObject.Path.ControlPoints[i].Position.Value, HitObject.Path.ControlPoints[i + 1].Position.Value).DistanceToPoint(position); + float dist = new Line(controlPoints[i].Position.Value, controlPoints[i + 1].Position.Value).DistanceToPoint(position); if (dist < minDistance) { @@ -116,11 +126,42 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders } // Move the control points from the insertion index onwards to make room for the insertion - HitObject.Path.ControlPoints.Insert(insertionIndex, new PathControlPoint { Position = { Value = position } }); + controlPoints.Insert(insertionIndex, new PathControlPoint { Position = { Value = position } }); return insertionIndex; } + private void removeControlPoints(List toRemove) + { + // Ensure that there are any points to be deleted + if (toRemove.Count == 0) + return; + + foreach (var c in toRemove) + { + // The first control point in the slider must have a type, so take it from the previous "first" one + // Todo: Should be handled within SliderPath itself + if (c == controlPoints[0] && controlPoints.Count > 1 && controlPoints[1].Type.Value == null) + controlPoints[1].Type.Value = controlPoints[0].Type.Value; + + controlPoints.Remove(c); + } + + // If there are 0 or 1 remaining control points, the slider is in a degenerate (single point) form and should be deleted + if (controlPoints.Count <= 1) + { + placementHandler?.Delete(HitObject); + return; + } + + // The path will have a non-zero offset if the head is removed, but sliders don't support this behaviour since the head is positioned at the slider's position + // So the slider needs to be offset by this amount instead, and all control points offset backwards such that the path is re-positioned at (0, 0) + Vector2 first = controlPoints[0].Position.Value; + foreach (var c in controlPoints) + c.Position.Value -= first; + HitObject.Position += first; + } + private void updatePath() { HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance; From 9963d18d17c00777a68d36768429c5ee9ebd4006 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 11 Dec 2019 19:13:04 +0900 Subject: [PATCH 71/84] Add whitespace --- osu.Game/Overlays/RankingsOverlay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index e7c8b94a10..c8874ef891 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -106,6 +106,7 @@ namespace osu.Game.Overlays Scheduler.AddOnce(loadNewContent); }, true); + Scope.BindValueChanged(_ => { // country filtering is only valid for performance scope. From bc02cfc2e24cdaa26832f004b8ad8910b5b8342c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 12 Dec 2019 00:30:16 +0300 Subject: [PATCH 72/84] TestSceneUserDimContainer -> TestSceneUserDimBackgrounds --- ...tSceneUserDimContainer.cs => TestSceneUserDimBackgrounds.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/Visual/Background/{TestSceneUserDimContainer.cs => TestSceneUserDimBackgrounds.cs} (99%) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs similarity index 99% rename from osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs rename to osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 910c462718..2d2726bbd3 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -36,7 +36,7 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual.Background { [TestFixture] - public class TestSceneUserDimContainer : ManualInputManagerTestScene + public class TestSceneUserDimBackgrounds : ManualInputManagerTestScene { public override IReadOnlyList RequiredTypes => new[] { From 4f6b85e5ea19381ab19a2ebdab80336bcf4a59e0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 12 Dec 2019 00:32:39 +0300 Subject: [PATCH 73/84] Add test ensuring correct break lightening behaviour --- .../Background/TestSceneUserDimContainer.cs | 82 +++++++++++++++++++ .../Graphics/Containers/UserDimContainer.cs | 5 +- 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs new file mode 100644 index 0000000000..0aad9bed75 --- /dev/null +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -0,0 +1,82 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual.Background +{ + public class TestSceneUserDimContainer : OsuTestScene + { + private readonly TestUserDimContainer container; + private readonly BindableBool isBreakTime = new BindableBool(); + private readonly Bindable lightenDuringBreaks = new Bindable(); + + public TestSceneUserDimContainer() + { + Add(container = new TestUserDimContainer + { + RelativeSizeAxes = Axes.Both, + Child = new Box + { + Colour = Color4.White, + RelativeSizeAxes = Axes.Both, + }, + }); + + container.IsBreakTime.BindTo(isBreakTime); + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.BindWith(OsuSetting.LightenDuringBreaks, lightenDuringBreaks); + } + + [SetUp] + public void SetUp() + { + isBreakTime.Value = false; + lightenDuringBreaks.Value = false; + } + + [TestCase(0.6f, 0.3f)] + [TestCase(0.2f, 0.0f)] + [TestCase(0.0f, 0.0f)] + public void TestBreakLightening(float userDim, float expectedBreakDim) + { + AddStep($"set dim level {userDim}", () => container.UserDimLevel.Value = userDim); + + AddStep("set break", () => isBreakTime.Value = true); + AddWaitStep("wait for dim", 3); + AddAssert($"is current dim {userDim}", () => container.DimEqual(userDim)); + + AddStep("set lighten during break", () => lightenDuringBreaks.Value = true); + AddWaitStep("wait for dim", 3); + AddAssert($"is current dim {expectedBreakDim}", () => container.DimEqual(expectedBreakDim)); + + AddStep("clear lighten during break", () => lightenDuringBreaks.Value = false); + AddWaitStep("wait for dim", 3); + AddAssert($"is current dim {userDim}", () => container.DimEqual(userDim)); + + AddStep("clear break", () => isBreakTime.Value = false); + AddStep("set lighten during break", () => lightenDuringBreaks.Value = true); + AddWaitStep("wait for dim", 3); + AddAssert($"is current dim {userDim}", () => container.DimEqual(userDim)); + } + + private class TestUserDimContainer : UserDimContainer + { + public bool DimEqual(float expectedDimLevel) => Content.Colour == OsuColour.Gray(1f - expectedDimLevel); + + public new Bindable UserDimLevel => base.UserDimLevel; + } + } +} diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index 20f5a4fd83..dcc8a52e9d 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -16,6 +16,9 @@ namespace osu.Game.Graphics.Containers /// public abstract class UserDimContainer : Container { + /// + /// Amount of lightening to apply to current dim level during break times. + /// private const float break_lighten_amount = 0.3f; protected const double BACKGROUND_FADE_DURATION = 800; @@ -34,7 +37,7 @@ namespace osu.Game.Graphics.Containers /// Whether player is in break time. /// Must be bound to to allow for dim adjustments in gameplay. /// - internal readonly IBindable IsBreakTime = new Bindable(); + public readonly IBindable IsBreakTime = new Bindable(); /// /// Whether the content of this container is currently being displayed. From 61265ed452d14b2353bc5957ae5af6b25e442a2b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 12 Dec 2019 00:52:33 +0300 Subject: [PATCH 74/84] Increase the waiting steps --- .../Visual/Background/TestSceneUserDimContainer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index 0aad9bed75..bdbfacaea2 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -55,20 +55,20 @@ namespace osu.Game.Tests.Visual.Background AddStep($"set dim level {userDim}", () => container.UserDimLevel.Value = userDim); AddStep("set break", () => isBreakTime.Value = true); - AddWaitStep("wait for dim", 3); + AddWaitStep("wait for dim", 5); AddAssert($"is current dim {userDim}", () => container.DimEqual(userDim)); AddStep("set lighten during break", () => lightenDuringBreaks.Value = true); - AddWaitStep("wait for dim", 3); + AddWaitStep("wait for dim", 5); AddAssert($"is current dim {expectedBreakDim}", () => container.DimEqual(expectedBreakDim)); AddStep("clear lighten during break", () => lightenDuringBreaks.Value = false); - AddWaitStep("wait for dim", 3); + AddWaitStep("wait for dim", 5); AddAssert($"is current dim {userDim}", () => container.DimEqual(userDim)); AddStep("clear break", () => isBreakTime.Value = false); AddStep("set lighten during break", () => lightenDuringBreaks.Value = true); - AddWaitStep("wait for dim", 3); + AddWaitStep("wait for dim", 5); AddAssert($"is current dim {userDim}", () => container.DimEqual(userDim)); } From 035a53cb9e2edb0f232369aa933989b2c0b3fdb8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 12 Dec 2019 01:10:43 +0300 Subject: [PATCH 75/84] Schedule SetUp() --- osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index bdbfacaea2..25c11d2292 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -41,11 +41,11 @@ namespace osu.Game.Tests.Visual.Background } [SetUp] - public void SetUp() + public void SetUp() => Schedule(() => { isBreakTime.Value = false; lightenDuringBreaks.Value = false; - } + }); [TestCase(0.6f, 0.3f)] [TestCase(0.2f, 0.0f)] From 606bd33aa664fa4d50d15c0b23340ddb52c64274 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 13:04:32 +0900 Subject: [PATCH 76/84] Use beatmap background in editor --- osu.Game/Screens/Edit/Editor.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 33a4c48d28..1b4964c068 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -4,7 +4,6 @@ using System; using osuTK.Graphics; using osu.Framework.Screens; -using osu.Game.Screens.Backgrounds; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -29,14 +28,13 @@ using osu.Game.Input.Bindings; using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Edit.Timing; +using osu.Game.Screens.Play; using osu.Game.Users; namespace osu.Game.Screens.Edit { - public class Editor : OsuScreen, IKeyBindingHandler + public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler { - protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4"); - public override float BackgroundParallaxAmount => 0.1f; public override bool AllowBackButton => false; @@ -250,8 +248,12 @@ namespace osu.Game.Screens.Edit { base.OnEntering(last); + // todo: temporary. we want to be applying dim using the UserDimContainer eventually. Background.FadeColour(Color4.DarkGray, 500); + Background.EnableUserDim.Value = false; + Background.BlurAmount.Value = 0; + resetTrack(true); } From b6c86d512a5c7c5ffcb87b362e956c6457b8fd1d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 13:28:27 +0900 Subject: [PATCH 77/84] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 3cd4dc48bf..252570a150 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -54,6 +54,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 530d62f583..a07348b57c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index fb753b8c6f..544bba3963 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -74,7 +74,7 @@ - + @@ -82,7 +82,7 @@ - + From 76a7e9cde82d8a37670802b6ea2d1583b79b7c1c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 14:04:57 +0900 Subject: [PATCH 78/84] Catch file exception in test reset --- osu.Game/Database/DatabaseContextFactory.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs index bb6bef1c50..1ed5fb3268 100644 --- a/osu.Game/Database/DatabaseContextFactory.cs +++ b/osu.Game/Database/DatabaseContextFactory.cs @@ -149,7 +149,15 @@ namespace osu.Game.Database lock (writeLock) { recycleThreadContexts(); - storage.DeleteDatabase(database_name); + + try + { + storage.DeleteDatabase(database_name); + } + catch + { + // for now we are not sure why file handles are kept open by EF, but this is generally only used in testing + } } } } From 6a539e307a2bdab3517ee840ee542dcf462c162c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 12 Dec 2019 09:20:56 +0300 Subject: [PATCH 79/84] Split into small tests and add more cases --- .../Background/TestSceneUserDimContainer.cs | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index 25c11d2292..03206402c3 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -53,23 +53,37 @@ namespace osu.Game.Tests.Visual.Background public void TestBreakLightening(float userDim, float expectedBreakDim) { AddStep($"set dim level {userDim}", () => container.UserDimLevel.Value = userDim); + AddStep("set lighten during break", () => lightenDuringBreaks.Value = true); AddStep("set break", () => isBreakTime.Value = true); - AddWaitStep("wait for dim", 5); - AddAssert($"is current dim {userDim}", () => container.DimEqual(userDim)); - - AddStep("set lighten during break", () => lightenDuringBreaks.Value = true); - AddWaitStep("wait for dim", 5); - AddAssert($"is current dim {expectedBreakDim}", () => container.DimEqual(expectedBreakDim)); - - AddStep("clear lighten during break", () => lightenDuringBreaks.Value = false); - AddWaitStep("wait for dim", 5); - AddAssert($"is current dim {userDim}", () => container.DimEqual(userDim)); - + AddUntilStep("has lightened", () => container.DimEqual(expectedBreakDim)); AddStep("clear break", () => isBreakTime.Value = false); + AddUntilStep("not lightened", () => container.DimEqual(userDim)); + } + + [Test] + public void TestEnableSettingDuringBreak() + { + AddStep("set dim level 0.6", () => container.UserDimLevel.Value = 0.6f); + + AddStep("set break", () => isBreakTime.Value = true); + AddUntilStep("not lightened", () => container.DimEqual(0.6f)); AddStep("set lighten during break", () => lightenDuringBreaks.Value = true); - AddWaitStep("wait for dim", 5); - AddAssert($"is current dim {userDim}", () => container.DimEqual(userDim)); + AddUntilStep("has lightened", () => container.DimEqual(0.3f)); + } + + [Test] + public void TestDisableSettingDuringBreak() + { + AddStep("set dim level 0.6", () => container.UserDimLevel.Value = 0.6f); + AddStep("set lighten during break", () => lightenDuringBreaks.Value = true); + + AddStep("set break", () => isBreakTime.Value = true); + AddUntilStep("has lightened", () => container.DimEqual(0.3f)); + AddStep("clear lighten during break", () => lightenDuringBreaks.Value = false); + AddUntilStep("not lightened", () => container.DimEqual(0.6f)); + AddStep("clear break", () => isBreakTime.Value = false); + AddUntilStep("not lightened", () => container.DimEqual(0.6f)); } private class TestUserDimContainer : UserDimContainer From 5861eca80d291bbfd6261ca732d4101012a86713 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 15:58:11 +0900 Subject: [PATCH 80/84] Make DrawableRuleset take a converted beatmap --- .../TestSceneDrawableHitObjects.cs | 2 +- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 +- .../UI/DrawableCatchRuleset.cs | 4 +- .../Edit/DrawableManiaEditRuleset.cs | 2 +- .../Edit/ManiaHitObjectComposer.cs | 2 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- .../UI/DrawableManiaRuleset.cs | 2 +- .../Edit/DrawableOsuEditRuleset.cs | 2 +- .../Edit/OsuHitObjectComposer.cs | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- .../UI/DrawableOsuRuleset.cs | 2 +- .../TestSceneTaikoPlayfield.cs | 3 +- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- .../UI/DrawableTaikoRuleset.cs | 2 +- .../TestSceneDrawableScrollingRuleset.cs | 6 +-- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/IWorkingBeatmap.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmap.cs | 4 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 8 ++-- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Rulesets/UI/DrawableRuleset.cs | 18 ++++---- .../UI/Scrolling/DrawableScrollingRuleset.cs | 2 +- osu.Game/Screens/Play/Player.cs | 41 ++++++++++--------- 23 files changed, 59 insertions(+), 57 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs index 0369b6db4e..02a017ce45 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Catch.Tests RelativeSizeAxes = Axes.Both, Children = new[] { - drawableRuleset = new DrawableCatchRuleset(new CatchRuleset(), beatmap, Array.Empty()) + drawableRuleset = new DrawableCatchRuleset(new CatchRuleset(), beatmap.GetPlayableBeatmap(new CatchRuleset().RulesetInfo)) } }); diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 71d68ace94..506fa23fa9 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch { public class CatchRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableCatchRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableCatchRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap); diff --git a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs index f5bddeb2cb..278ff97195 100644 --- a/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/UI/DrawableCatchRuleset.cs @@ -25,11 +25,11 @@ namespace osu.Game.Rulesets.Catch.UI protected override bool UserScrollSpeedAdjustment => false; - public DrawableCatchRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) + public DrawableCatchRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) { Direction.Value = ScrollingDirection.Down; - TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); + TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450); } public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(Beatmap); diff --git a/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs b/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs index 97d8aaa052..445df79f6f 100644 --- a/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs +++ b/osu.Game.Rulesets.Mania/Edit/DrawableManiaEditRuleset.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Edit { public new IScrollingInfo ScrollingInfo => base.ScrollingInfo; - public DrawableManiaEditRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) + public DrawableManiaEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 0bfe6f9517..1632b6a583 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Edit public int TotalColumns => ((ManiaPlayfield)drawableRuleset.Playfield).TotalColumns; - protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) + protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) { drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index c74a292331..a96c79b40b 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania { public class ManiaRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableManiaRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableManiaRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap); public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score); diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 0607bf0abd..cf1970c28b 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Mania.UI private readonly Bindable configDirection = new Bindable(); - public DrawableManiaRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) + public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) { BarLines = new BarLineGenerator(Beatmap).BarLines; diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs index 3437af8c1e..22b4c3e82e 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Edit /// private const double editor_hit_object_fade_out_extension = 500; - public DrawableOsuEditRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) + public DrawableOsuEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 812afaaa24..675b09fc6d 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Edit { } - protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) + protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableOsuEditRuleset(ruleset, beatmap, mods); protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[] diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index fa69cec78d..2f43909332 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu { public class OsuRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableOsuRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableOsuRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap); diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index 5bb728a9b0..039d38e4fd 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.UI { protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config; - public DrawableOsuRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) + public DrawableOsuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) { } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs index 8522a42739..b2c8c7feda 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoPlayfield.cs @@ -11,7 +11,6 @@ using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; @@ -91,7 +90,7 @@ namespace osu.Game.Rulesets.Taiko.Tests Origin = Anchor.Centre, RelativeSizeAxes = Axes.X, Height = 768, - Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap, Array.Empty()) } + Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap.GetPlayableBeatmap(new TaikoRuleset().RulesetInfo)) } }); } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index b2655f592c..ab9c95159c 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko { public class TaikoRuleset : Ruleset { - public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new DrawableTaikoRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableTaikoRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap); public const string SHORT_NAME = "taiko"; diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index d4ea9a043a..2233658428 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.UI protected override bool UserScrollSpeedAdjustment => false; - public DrawableTaikoRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) + public DrawableTaikoRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) { Direction.Value = ScrollingDirection.Left; diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 684e79b3f5..071dc381e0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -172,7 +172,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var ruleset = new TestScrollingRuleset(); - drawableRuleset = (TestDrawableScrollingRuleset)ruleset.CreateDrawableRulesetWith(CreateWorkingBeatmap(beatmap), Array.Empty()); + drawableRuleset = (TestDrawableScrollingRuleset)ruleset.CreateDrawableRulesetWith(beatmap); drawableRuleset.FrameStablePlayback = false; overrideAction?.Invoke(drawableRuleset); @@ -201,7 +201,7 @@ namespace osu.Game.Tests.Visual.Gameplay public override IEnumerable GetModsFor(ModType type) => throw new NotImplementedException(); - public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => new TestDrawableScrollingRuleset(this, beatmap, mods); + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new TestDrawableScrollingRuleset(this, beatmap, mods); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TestBeatmapConverter(beatmap); @@ -222,7 +222,7 @@ namespace osu.Game.Tests.Visual.Gameplay public new Bindable TimeRange => base.TimeRange; - public TestDrawableScrollingRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) + public TestDrawableScrollingRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) { TimeRange.Value = time_range; diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index a3ab01c886..59a27e3fde 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -57,7 +57,7 @@ namespace osu.Game.Beatmaps { public override IEnumerable GetModsFor(ModType type) => new Mod[] { }; - public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) { throw new NotImplementedException(); } diff --git a/osu.Game/Beatmaps/IWorkingBeatmap.cs b/osu.Game/Beatmaps/IWorkingBeatmap.cs index a087a52ada..5f1f0d1e40 100644 --- a/osu.Game/Beatmaps/IWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/IWorkingBeatmap.cs @@ -62,6 +62,6 @@ namespace osu.Game.Beatmaps /// The s to apply to the . /// The converted . /// If could not be converted to . - IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods); + IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null); } } diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 4452d26fcd..1255665cf0 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -99,8 +99,10 @@ namespace osu.Game.Beatmaps /// The applicable . protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap); - public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods) + public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods = null) { + mods ??= Array.Empty(); + var rulesetInstance = ruleset.CreateInstance(); IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 9ac967ef74..22d94abcb9 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -48,7 +48,6 @@ namespace osu.Game.Rulesets.Edit [Resolved] private BindableBeatDivisor beatDivisor { get; set; } - private IWorkingBeatmap workingBeatmap; private Beatmap playableBeatmap; private IBeatmapProcessor beatmapProcessor; @@ -71,7 +70,7 @@ namespace osu.Game.Rulesets.Edit { try { - drawableRulesetWrapper = new DrawableEditRulesetWrapper(CreateDrawableRuleset(Ruleset, workingBeatmap, Array.Empty())) + drawableRulesetWrapper = new DrawableEditRulesetWrapper(CreateDrawableRuleset(Ruleset, playableBeatmap)) { Clock = framedClock, ProcessCustomClock = false @@ -145,8 +144,7 @@ namespace osu.Game.Rulesets.Edit { var parentWorkingBeatmap = parent.Get>().Value; - playableBeatmap = (Beatmap)parentWorkingBeatmap.GetPlayableBeatmap(Ruleset.RulesetInfo, Array.Empty()); - workingBeatmap = new EditorWorkingBeatmap(playableBeatmap, parentWorkingBeatmap); + playableBeatmap = (Beatmap)parentWorkingBeatmap.GetPlayableBeatmap(Ruleset.RulesetInfo); beatmapProcessor = Ruleset.CreateBeatmapProcessor(playableBeatmap); @@ -250,7 +248,7 @@ namespace osu.Game.Rulesets.Edit protected abstract IReadOnlyList CompositionTools { get; } - protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods); + protected abstract DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null); public void BeginPlacement(HitObject hitObject) { diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index dd1b3615c7..45aa904b98 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets /// The s to apply. /// Unable to successfully load the beatmap to be usable with this ruleset. /// - public abstract DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods); + public abstract DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null); /// /// Creates a to convert a to one that is applicable for this . diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 10657f6b39..5033fd0686 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.UI /// /// The beatmap. /// - public Beatmap Beatmap; + public readonly Beatmap Beatmap; public override IEnumerable Objects => Beatmap.HitObjects; @@ -118,20 +118,22 @@ namespace osu.Game.Rulesets.UI /// Creates a ruleset visualisation for the provided ruleset and beatmap. /// /// The ruleset being represented. - /// The beatmap to create the hit renderer for. + /// The beatmap to create the hit renderer for. /// The s to apply. - protected DrawableRuleset(Ruleset ruleset, IWorkingBeatmap workingBeatmap, IReadOnlyList mods) + protected DrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset) { - if (workingBeatmap == null) - throw new ArgumentException("Beatmap cannot be null.", nameof(workingBeatmap)); + if (beatmap == null) + throw new ArgumentNullException(nameof(beatmap), "Beatmap cannot be null."); - this.mods = mods.ToArray(); + if (!(beatmap is Beatmap tBeatmap)) + throw new ArgumentException($"{GetType()} expected the beatmap to contain hitobjects of type {typeof(TObject)}.", nameof(beatmap)); + + Beatmap = tBeatmap; + this.mods = mods?.ToArray() ?? Array.Empty(); RelativeSizeAxes = Axes.Both; - Beatmap = (Beatmap)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods); - KeyBindingInputManager = CreateInputManager(); playfield = new Lazy(CreatePlayfield); diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index cf714b5d46..fda1d7c723 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.UI.Scrolling [Cached(Type = typeof(IScrollingInfo))] private readonly LocalScrollingInfo scrollingInfo; - protected DrawableScrollingRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList mods) + protected DrawableScrollingRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) { scrollingInfo = new LocalScrollingInfo(); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 9f4ca7d817..b65d20dcbb 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -114,26 +114,31 @@ namespace osu.Game.Screens.Play Mods.Value = base.Mods.Value.Select(m => m.CreateCopy()).ToArray(); - WorkingBeatmap working = loadBeatmap(); + if (Beatmap.Value is DummyWorkingBeatmap) + return; - if (working == null) + IBeatmap playableBeatmap = loadPlayableBeatmap(); + + if (playableBeatmap == null) return; sampleRestart = audio.Samples.Get(@"Gameplay/restart"); mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); + DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value); + ScoreProcessor = DrawableRuleset.CreateScoreProcessor(); ScoreProcessor.Mods.BindTo(Mods); if (!ScoreProcessor.Mode.Disabled) config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode); - InternalChild = GameplayClockContainer = new GameplayClockContainer(working, Mods.Value, DrawableRuleset.GameplayStartTime); + InternalChild = GameplayClockContainer = new GameplayClockContainer(Beatmap.Value, Mods.Value, DrawableRuleset.GameplayStartTime); addUnderlayComponents(GameplayClockContainer); - addGameplayComponents(GameplayClockContainer, working); - addOverlayComponents(GameplayClockContainer, working); + addGameplayComponents(GameplayClockContainer, Beatmap.Value); + addOverlayComponents(GameplayClockContainer, Beatmap.Value); DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true); @@ -250,36 +255,32 @@ namespace osu.Game.Screens.Play && !DrawableRuleset.HasReplayLoaded.Value && !breakOverlay.IsBreakTime.Value; - private WorkingBeatmap loadBeatmap() + private IBeatmap loadPlayableBeatmap() { - WorkingBeatmap working = Beatmap.Value; - if (working is DummyWorkingBeatmap) - return null; + IBeatmap playable; try { - var beatmap = working.Beatmap; - - if (beatmap == null) + if (Beatmap.Value.Beatmap == null) throw new InvalidOperationException("Beatmap was not loaded"); - rulesetInfo = Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset; + rulesetInfo = Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset; ruleset = rulesetInfo.CreateInstance(); try { - DrawableRuleset = ruleset.CreateDrawableRulesetWith(working, Mods.Value); + playable = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Mods.Value); } catch (BeatmapInvalidForRulesetException) { - // we may fail to create a DrawableRuleset if the beatmap cannot be loaded with the user's preferred ruleset - // let's try again forcing the beatmap's ruleset. - rulesetInfo = beatmap.BeatmapInfo.Ruleset; + // A playable beatmap may not be creatable with the user's preferred ruleset, so try using the beatmap's default ruleset + rulesetInfo = Beatmap.Value.BeatmapInfo.Ruleset; ruleset = rulesetInfo.CreateInstance(); - DrawableRuleset = ruleset.CreateDrawableRulesetWith(Beatmap.Value, Mods.Value); + + playable = Beatmap.Value.GetPlayableBeatmap(rulesetInfo, Mods.Value); } - if (!DrawableRuleset.Objects.Any()) + if (playable.HitObjects.Count == 0) { Logger.Log("Beatmap contains no hit objects!", level: LogLevel.Error); return null; @@ -292,7 +293,7 @@ namespace osu.Game.Screens.Play return null; } - return working; + return playable; } private void performImmediateExit() From 59345c97e4d3ec412d4d1b519f3021eb3988d70c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 15:58:31 +0900 Subject: [PATCH 81/84] Remove now unnecessary editor working beatmap --- osu.Game/Screens/Edit/EditorWorkingBeatmap.cs | 49 ------------------- 1 file changed, 49 deletions(-) delete mode 100644 osu.Game/Screens/Edit/EditorWorkingBeatmap.cs diff --git a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs b/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs deleted file mode 100644 index 4b8720fe1c..0000000000 --- a/osu.Game/Screens/Edit/EditorWorkingBeatmap.cs +++ /dev/null @@ -1,49 +0,0 @@ -// 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.Audio.Track; -using osu.Framework.Graphics.Textures; -using osu.Framework.Graphics.Video; -using osu.Game.Beatmaps; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; -using osu.Game.Skinning; -using osu.Game.Storyboards; - -namespace osu.Game.Screens.Edit -{ - /// - /// Encapsulates a while providing an overridden . - /// - /// - public class EditorWorkingBeatmap : IWorkingBeatmap - where TObject : HitObject - { - private readonly Beatmap playableBeatmap; - private readonly WorkingBeatmap workingBeatmap; - - public EditorWorkingBeatmap(Beatmap playableBeatmap, WorkingBeatmap workingBeatmap) - { - this.playableBeatmap = playableBeatmap; - this.workingBeatmap = workingBeatmap; - } - - public IBeatmap Beatmap => workingBeatmap.Beatmap; - - public Texture Background => workingBeatmap.Background; - - public VideoSprite Video => workingBeatmap.Video; - - public Track Track => workingBeatmap.Track; - - public Waveform Waveform => workingBeatmap.Waveform; - - public Storyboard Storyboard => workingBeatmap.Storyboard; - - public ISkin Skin => workingBeatmap.Skin; - - public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods) => playableBeatmap; - } -} From 1807fc9b6153e7c02881bae15efd00c5545c4d88 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 16:48:33 +0900 Subject: [PATCH 82/84] Fix testcase not converting beatmap --- .../Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 071dc381e0..c958932730 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -172,7 +172,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var ruleset = new TestScrollingRuleset(); - drawableRuleset = (TestDrawableScrollingRuleset)ruleset.CreateDrawableRulesetWith(beatmap); + drawableRuleset = (TestDrawableScrollingRuleset)ruleset.CreateDrawableRulesetWith(CreateWorkingBeatmap(beatmap).GetPlayableBeatmap(ruleset.RulesetInfo)); drawableRuleset.FrameStablePlayback = false; overrideAction?.Invoke(drawableRuleset); From 5e634c118309cf56ccd14ce43d5681293aff529f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 20:51:58 +0900 Subject: [PATCH 83/84] Move test values to constants --- .../Background/TestSceneUserDimContainer.cs | 19 +++++++++++-------- .../Graphics/Containers/UserDimContainer.cs | 4 ++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index 03206402c3..8eb256538a 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -47,7 +47,10 @@ namespace osu.Game.Tests.Visual.Background lightenDuringBreaks.Value = false; }); - [TestCase(0.6f, 0.3f)] + private const float test_user_dim = 0.6f; + private const float test_user_dim_lightened = test_user_dim - UserDimContainer.BREAK_LIGHTEN_AMOUNT; + + [TestCase(test_user_dim, test_user_dim_lightened)] [TestCase(0.2f, 0.0f)] [TestCase(0.0f, 0.0f)] public void TestBreakLightening(float userDim, float expectedBreakDim) @@ -64,26 +67,26 @@ namespace osu.Game.Tests.Visual.Background [Test] public void TestEnableSettingDuringBreak() { - AddStep("set dim level 0.6", () => container.UserDimLevel.Value = 0.6f); + AddStep("set dim level 0.6", () => container.UserDimLevel.Value = test_user_dim); AddStep("set break", () => isBreakTime.Value = true); - AddUntilStep("not lightened", () => container.DimEqual(0.6f)); + AddUntilStep("not lightened", () => container.DimEqual(test_user_dim)); AddStep("set lighten during break", () => lightenDuringBreaks.Value = true); - AddUntilStep("has lightened", () => container.DimEqual(0.3f)); + AddUntilStep("has lightened", () => container.DimEqual(test_user_dim_lightened)); } [Test] public void TestDisableSettingDuringBreak() { - AddStep("set dim level 0.6", () => container.UserDimLevel.Value = 0.6f); + AddStep("set dim level 0.6", () => container.UserDimLevel.Value = test_user_dim); AddStep("set lighten during break", () => lightenDuringBreaks.Value = true); AddStep("set break", () => isBreakTime.Value = true); - AddUntilStep("has lightened", () => container.DimEqual(0.3f)); + AddUntilStep("has lightened", () => container.DimEqual(test_user_dim_lightened)); AddStep("clear lighten during break", () => lightenDuringBreaks.Value = false); - AddUntilStep("not lightened", () => container.DimEqual(0.6f)); + AddUntilStep("not lightened", () => container.DimEqual(test_user_dim)); AddStep("clear break", () => isBreakTime.Value = false); - AddUntilStep("not lightened", () => container.DimEqual(0.6f)); + AddUntilStep("not lightened", () => container.DimEqual(test_user_dim)); } private class TestUserDimContainer : UserDimContainer diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index dcc8a52e9d..e67cd94d5c 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -19,7 +19,7 @@ namespace osu.Game.Graphics.Containers /// /// Amount of lightening to apply to current dim level during break times. /// - private const float break_lighten_amount = 0.3f; + public const float BREAK_LIGHTEN_AMOUNT = 0.3f; protected const double BACKGROUND_FADE_DURATION = 800; @@ -52,7 +52,7 @@ namespace osu.Game.Graphics.Containers protected Bindable ShowVideo { get; private set; } - private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? break_lighten_amount : 0; + private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0; protected float DimLevel => Math.Max(EnableUserDim.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0); protected override Container Content => dimContent; From ecc7fdc561a8d98e064fd0b47cc6bc7605dff53e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 21:04:56 +0900 Subject: [PATCH 84/84] Ensure a clean run on each test method --- .../Background/TestSceneUserDimContainer.cs | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index 8eb256538a..472c43096f 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -15,13 +15,22 @@ namespace osu.Game.Tests.Visual.Background { public class TestSceneUserDimContainer : OsuTestScene { - private readonly TestUserDimContainer container; - private readonly BindableBool isBreakTime = new BindableBool(); - private readonly Bindable lightenDuringBreaks = new Bindable(); + private TestUserDimContainer userDimContainer; - public TestSceneUserDimContainer() + private readonly BindableBool isBreakTime = new BindableBool(); + + private Bindable lightenDuringBreaks = new Bindable(); + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) { - Add(container = new TestUserDimContainer + lightenDuringBreaks = config.GetBindable(OsuSetting.LightenDuringBreaks); + } + + [SetUp] + public void SetUp() => Schedule(() => + { + Child = userDimContainer = new TestUserDimContainer { RelativeSizeAxes = Axes.Both, Child = new Box @@ -29,21 +38,11 @@ namespace osu.Game.Tests.Visual.Background Colour = Color4.White, RelativeSizeAxes = Axes.Both, }, - }); + }; - container.IsBreakTime.BindTo(isBreakTime); - } - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - config.BindWith(OsuSetting.LightenDuringBreaks, lightenDuringBreaks); - } - - [SetUp] - public void SetUp() => Schedule(() => - { + userDimContainer.IsBreakTime.BindTo(isBreakTime); isBreakTime.Value = false; + lightenDuringBreaks.Value = false; }); @@ -55,38 +54,38 @@ namespace osu.Game.Tests.Visual.Background [TestCase(0.0f, 0.0f)] public void TestBreakLightening(float userDim, float expectedBreakDim) { - AddStep($"set dim level {userDim}", () => container.UserDimLevel.Value = userDim); + AddStep($"set dim level {userDim}", () => userDimContainer.UserDimLevel.Value = userDim); AddStep("set lighten during break", () => lightenDuringBreaks.Value = true); AddStep("set break", () => isBreakTime.Value = true); - AddUntilStep("has lightened", () => container.DimEqual(expectedBreakDim)); + AddUntilStep("has lightened", () => userDimContainer.DimEqual(expectedBreakDim)); AddStep("clear break", () => isBreakTime.Value = false); - AddUntilStep("not lightened", () => container.DimEqual(userDim)); + AddUntilStep("not lightened", () => userDimContainer.DimEqual(userDim)); } [Test] public void TestEnableSettingDuringBreak() { - AddStep("set dim level 0.6", () => container.UserDimLevel.Value = test_user_dim); + AddStep("set dim level 0.6", () => userDimContainer.UserDimLevel.Value = test_user_dim); AddStep("set break", () => isBreakTime.Value = true); - AddUntilStep("not lightened", () => container.DimEqual(test_user_dim)); + AddUntilStep("not lightened", () => userDimContainer.DimEqual(test_user_dim)); AddStep("set lighten during break", () => lightenDuringBreaks.Value = true); - AddUntilStep("has lightened", () => container.DimEqual(test_user_dim_lightened)); + AddUntilStep("has lightened", () => userDimContainer.DimEqual(test_user_dim_lightened)); } [Test] public void TestDisableSettingDuringBreak() { - AddStep("set dim level 0.6", () => container.UserDimLevel.Value = test_user_dim); + AddStep("set dim level 0.6", () => userDimContainer.UserDimLevel.Value = test_user_dim); AddStep("set lighten during break", () => lightenDuringBreaks.Value = true); AddStep("set break", () => isBreakTime.Value = true); - AddUntilStep("has lightened", () => container.DimEqual(test_user_dim_lightened)); + AddUntilStep("has lightened", () => userDimContainer.DimEqual(test_user_dim_lightened)); AddStep("clear lighten during break", () => lightenDuringBreaks.Value = false); - AddUntilStep("not lightened", () => container.DimEqual(test_user_dim)); + AddUntilStep("not lightened", () => userDimContainer.DimEqual(test_user_dim)); AddStep("clear break", () => isBreakTime.Value = false); - AddUntilStep("not lightened", () => container.DimEqual(test_user_dim)); + AddUntilStep("not lightened", () => userDimContainer.DimEqual(test_user_dim)); } private class TestUserDimContainer : UserDimContainer