From 2071cba944050ce4da997b1e8111d96650d12c22 Mon Sep 17 00:00:00 2001 From: Joehu Date: Fri, 13 Nov 2020 12:32:23 -0800 Subject: [PATCH 01/66] Add music bindings to on screen display --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- .../Overlays/Music/MusicKeyBindingHandler.cs | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index a4b99bb6e6..89a6ee8b07 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -196,7 +196,7 @@ namespace osu.Game.Configuration public Func LookupSkinName { private get; set; } - public Func LookupKeyBindings { private get; set; } + public Func LookupKeyBindings { get; set; } } public enum OsuSetting diff --git a/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs b/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs index e6edfb1e3e..0d6158d46f 100644 --- a/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs +++ b/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Input.Bindings; using osu.Game.Overlays.OSD; @@ -25,6 +26,9 @@ namespace osu.Game.Overlays.Music [Resolved(canBeNull: true)] private OnScreenDisplay onScreenDisplay { get; set; } + [Resolved] + private OsuConfigManager config { get; set; } + public bool OnPressed(GlobalAction action) { if (beatmap.Disabled) @@ -37,11 +41,11 @@ namespace osu.Game.Overlays.Music bool wasPlaying = musicController.IsPlaying; if (musicController.TogglePause()) - onScreenDisplay?.Display(new MusicActionToast(wasPlaying ? "Pause track" : "Play track")); + onScreenDisplay?.Display(new MusicActionToast(wasPlaying ? "Pause track" : "Play track", config.LookupKeyBindings(action))); return true; case GlobalAction.MusicNext: - musicController.NextTrack(() => onScreenDisplay?.Display(new MusicActionToast("Next track"))); + musicController.NextTrack(() => onScreenDisplay?.Display(new MusicActionToast("Next track", config.LookupKeyBindings(action)))); return true; @@ -51,11 +55,11 @@ namespace osu.Game.Overlays.Music switch (res) { case PreviousTrackResult.Restart: - onScreenDisplay?.Display(new MusicActionToast("Restart track")); + onScreenDisplay?.Display(new MusicActionToast("Restart track", config.LookupKeyBindings(action))); break; case PreviousTrackResult.Previous: - onScreenDisplay?.Display(new MusicActionToast("Previous track")); + onScreenDisplay?.Display(new MusicActionToast("Previous track", config.LookupKeyBindings(action))); break; } }); @@ -72,8 +76,8 @@ namespace osu.Game.Overlays.Music private class MusicActionToast : Toast { - public MusicActionToast(string action) - : base("Music Playback", action, string.Empty) + public MusicActionToast(string action, string shortcut) + : base("Music Playback", action, shortcut) { } } From 9d8e7e895442c29df40a3ebf2fb0df5cf020bbb1 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Nov 2020 04:46:26 +0300 Subject: [PATCH 02/66] ProfileLineChart layout implementation --- .../Online/TestSceneProfileLineChart.cs | 39 +++++++++++ .../Sections/Historical/ProfileLineChart.cs | 64 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs create mode 100644 osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs new file mode 100644 index 0000000000..34359baab5 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs @@ -0,0 +1,39 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Overlays.Profile.Sections.Historical; +using osu.Framework.Graphics; +using static osu.Game.Users.User; +using System; +using osu.Game.Overlays; +using osu.Framework.Allocation; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneProfileLineChart : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); + + public TestSceneProfileLineChart() + { + var values = new[] + { + new UserHistoryCount { Date = new DateTime(2010, 5, 1), Count = 1000 }, + new UserHistoryCount { Date = new DateTime(2010, 6, 1), Count = 20 }, + new UserHistoryCount { Date = new DateTime(2010, 7, 1), Count = 20000 }, + new UserHistoryCount { Date = new DateTime(2010, 8, 1), Count = 30 }, + new UserHistoryCount { Date = new DateTime(2010, 9, 1), Count = 50 }, + new UserHistoryCount { Date = new DateTime(2010, 10, 1), Count = 2000 }, + new UserHistoryCount { Date = new DateTime(2010, 11, 1), Count = 2100 } + }; + + Add(new ProfileLineChart + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Values = values + }); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs new file mode 100644 index 0000000000..10a03aa012 --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -0,0 +1,64 @@ +// 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.Containers; +using osu.Framework.Graphics; +using JetBrains.Annotations; +using static osu.Game.Users.User; + +namespace osu.Game.Overlays.Profile.Sections.Historical +{ + public class ProfileLineChart : CompositeDrawable + { + private UserHistoryCount[] values; + + [CanBeNull] + public UserHistoryCount[] Values + { + get => values; + set + { + values = value; + graph.Values = values; + } + } + + private readonly UserHistoryGraph graph; + + public ProfileLineChart() + { + RelativeSizeAxes = Axes.X; + Height = 250; + InternalChild = new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension() + }, + RowDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new Drawable[] + { + Empty(), + graph = new UserHistoryGraph + { + RelativeSizeAxes = Axes.Both + } + }, + new Drawable[] + { + Empty(), + Empty() + } + } + }; + } + } +} From d98c59f2a4b399da80b5c511f062c60c6d5d536e Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Nov 2020 06:38:02 +0300 Subject: [PATCH 03/66] Implement horizontal ticks creation --- .../Sections/Historical/ProfileLineChart.cs | 126 +++++++++++++++++- 1 file changed, 123 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index 10a03aa012..a02f869f51 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -5,6 +5,13 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using JetBrains.Annotations; using static osu.Game.Users.User; +using System; +using System.Linq; +using osu.Game.Graphics.Sprites; +using osu.Framework.Utils; +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Framework.Graphics.Shapes; namespace osu.Game.Overlays.Profile.Sections.Historical { @@ -20,10 +27,14 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { values = value; graph.Values = values; + + createRowTicks(); } } private readonly UserHistoryGraph graph; + private readonly Container rowTicksContainer; + private readonly Container rowLinesContainer; public ProfileLineChart() { @@ -46,10 +57,25 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { new Drawable[] { - Empty(), - graph = new UserHistoryGraph + rowTicksContainer = new Container { - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + rowLinesContainer = new Container + { + RelativeSizeAxes = Axes.Both + }, + graph = new UserHistoryGraph + { + RelativeSizeAxes = Axes.Both + } + } } }, new Drawable[] @@ -60,5 +86,99 @@ namespace osu.Game.Overlays.Profile.Sections.Historical } }; } + + private void createRowTicks() + { + rowTicksContainer.Clear(); + rowLinesContainer.Clear(); + + var min = values.Select(v => v.Count).Min(); + var max = values.Select(v => v.Count).Max(); + + var niceRange = niceNumber(max - min, false); + var niceTick = niceNumber(niceRange / (6 - 1), true); + var axisStart = Math.Floor(min / niceTick) * niceTick; + var axisEnd = Math.Ceiling(max / niceTick) * niceTick; + + var rollingRow = axisStart; + + while (rollingRow <= axisEnd) + { + var y = -Interpolation.ValueAt(rollingRow, 0, 1f, min, max); + + rowTicksContainer.Add(new TickText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.CentreRight, + RelativePositionAxes = Axes.Y, + Text = rollingRow.ToString("N0"), + Y = y + }); + + rowLinesContainer.Add(new TickLine + { + Anchor = Anchor.BottomRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.X, + RelativePositionAxes = Axes.Y, + Height = 1, + Y = y + }); + + rollingRow += niceTick; + } + } + + private double niceNumber(double value, bool round) + { + var exponent = (int)Math.Floor(Math.Log10(value)); + var fraction = value / Math.Pow(10, exponent); + + double niceFraction; + + if (round) + { + if (fraction < 1.5) + niceFraction = 1.0; + else if (fraction < 3) + niceFraction = 2.0; + else if (fraction < 7) + niceFraction = 5.0; + else + niceFraction = 10.0; + } + else + { + if (fraction <= 1.0) + niceFraction = 1.0; + else if (fraction <= 2.0) + niceFraction = 2.0; + else if (fraction <= 5.0) + niceFraction = 5.0; + else + niceFraction = 10.0; + } + + return niceFraction * Math.Pow(10, exponent); + } + + private class TickText : OsuSpriteText + { + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Colour = colourProvider.Foreground1; + Font = OsuFont.GetFont(size: 12); + } + } + + private class TickLine : Box + { + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Colour = colourProvider.Background6; + } + } } } From 00e974794076c241779694f6ec988a8ccf204044 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Nov 2020 06:44:29 +0300 Subject: [PATCH 04/66] Test scene visual improvements --- .../Online/TestSceneProfileLineChart.cs | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs index 34359baab5..0be835c07d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs @@ -7,6 +7,8 @@ using static osu.Game.Users.User; using System; using osu.Game.Overlays; using osu.Framework.Allocation; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Containers; namespace osu.Game.Tests.Visual.Online { @@ -28,11 +30,25 @@ namespace osu.Game.Tests.Visual.Online new UserHistoryCount { Date = new DateTime(2010, 11, 1), Count = 2100 } }; - Add(new ProfileLineChart + AddRange(new Drawable[] { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Values = values + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4 + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Padding = new MarginPadding { Horizontal = 50 }, + Child = new ProfileLineChart + { + Values = values + } + } }); } } From 01f28a35c3269316e5c380d14c8c41c3a65eff6b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Nov 2020 07:28:01 +0300 Subject: [PATCH 05/66] Implement vertical ticks creation --- .../Sections/Historical/ProfileLineChart.cs | 70 +++++++++++++++++-- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index a02f869f51..2908b50a6e 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -29,12 +29,15 @@ namespace osu.Game.Overlays.Profile.Sections.Historical graph.Values = values; createRowTicks(); + createColumnTicks(); } } private readonly UserHistoryGraph graph; private readonly Container rowTicksContainer; + private readonly Container columnTicksContainer; private readonly Container rowLinesContainer; + private readonly Container columnLinesContainer; public ProfileLineChart() { @@ -67,9 +70,20 @@ namespace osu.Game.Overlays.Profile.Sections.Historical RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - rowLinesContainer = new Container + new Container { - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, + Children = new[] + { + rowLinesContainer = new Container + { + RelativeSizeAxes = Axes.Both + }, + columnLinesContainer = new Container + { + RelativeSizeAxes = Axes.Both + } + } }, graph = new UserHistoryGraph { @@ -81,7 +95,12 @@ namespace osu.Game.Overlays.Profile.Sections.Historical new Drawable[] { Empty(), - Empty() + columnTicksContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Top = 10 } + } } } }; @@ -104,7 +123,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical while (rollingRow <= axisEnd) { - var y = -Interpolation.ValueAt(rollingRow, 0, 1f, min, max); + var y = -Interpolation.ValueAt(rollingRow, 0, 1f, axisStart, axisEnd); rowTicksContainer.Add(new TickText { @@ -129,9 +148,50 @@ namespace osu.Game.Overlays.Profile.Sections.Historical } } + private void createColumnTicks() + { + columnTicksContainer.Clear(); + columnLinesContainer.Clear(); + + var min = values.Select(v => v.Date).Min().Ticks; + var max = values.Select(v => v.Date).Max().Ticks; + + var niceRange = niceNumber(max - min, false); + var niceTick = niceNumber(niceRange / (Math.Min(values.Length, 15) - 1), true); + var axisStart = Math.Floor(min / niceTick) * niceTick; + var axisEnd = Math.Ceiling(max / niceTick) * niceTick; + + var rollingRow = axisStart; + + while (rollingRow <= axisEnd) + { + var x = Interpolation.ValueAt(rollingRow, 0, 1f, axisStart, axisEnd); + + columnTicksContainer.Add(new TickText + { + Origin = Anchor.CentreLeft, + RelativePositionAxes = Axes.X, + Text = new DateTime((long)rollingRow).ToString("MMM yyyy"), + Rotation = 45, + X = x + }); + + columnLinesContainer.Add(new TickLine + { + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Y, + RelativePositionAxes = Axes.X, + Width = 1, + X = x + }); + + rollingRow += niceTick; + } + } + private double niceNumber(double value, bool round) { - var exponent = (int)Math.Floor(Math.Log10(value)); + var exponent = Math.Floor(Math.Log10(value)); var fraction = value / Math.Pow(10, exponent); double niceFraction; From ae4a2e74faab435156cfebe6f6da131b5b83896b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Nov 2020 18:21:10 +0300 Subject: [PATCH 06/66] Implement ProfileSubsection --- ...cs => TestSceneProfileSubsectionHeader.cs} | 6 +- .../Profile/Sections/PaginatedContainer.cs | 57 +++----------- .../Profile/Sections/ProfileSubsection.cs | 78 +++++++++++++++++++ ...erHeader.cs => ProfileSubsectionHeader.cs} | 4 +- 4 files changed, 95 insertions(+), 50 deletions(-) rename osu.Game.Tests/Visual/UserInterface/{TestScenePaginatedContainerHeader.cs => TestSceneProfileSubsectionHeader.cs} (95%) create mode 100644 osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs rename osu.Game/Overlays/Profile/Sections/{PaginatedContainerHeader.cs => ProfileSubsectionHeader.cs} (95%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePaginatedContainerHeader.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneProfileSubsectionHeader.cs similarity index 95% rename from osu.Game.Tests/Visual/UserInterface/TestScenePaginatedContainerHeader.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneProfileSubsectionHeader.cs index 2e9f919cfd..cd226662d7 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePaginatedContainerHeader.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneProfileSubsectionHeader.cs @@ -11,12 +11,12 @@ using osu.Framework.Allocation; namespace osu.Game.Tests.Visual.UserInterface { - public class TestScenePaginatedContainerHeader : OsuTestScene + public class TestSceneProfileSubsectionHeader : OsuTestScene { [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); - private PaginatedContainerHeader header; + private ProfileSubsectionHeader header; [Test] public void TestHiddenCounter() @@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.UserInterface private void createHeader(string text, CounterVisibilityState state, int initialValue = 0) { Clear(); - Add(header = new PaginatedContainerHeader(text, state) + Add(header = new ProfileSubsectionHeader(text, state) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index c1107ce907..7b66c3f51e 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -6,10 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Game.Online.API; -using osu.Game.Rulesets; using osu.Game.Users; using System.Collections.Generic; using System.Linq; @@ -18,7 +15,7 @@ using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Profile.Sections { - public abstract class PaginatedContainer : FillFlowContainer + public abstract class PaginatedContainer : ProfileSubsection { [Resolved] private IAPIProvider api { get; set; } @@ -26,42 +23,25 @@ namespace osu.Game.Overlays.Profile.Sections protected int VisiblePages; protected int ItemsPerPage; - protected readonly Bindable User = new Bindable(); protected FillFlowContainer ItemsContainer; - protected RulesetStore Rulesets; private APIRequest> retrievalRequest; private CancellationTokenSource loadCancellation; - private readonly string missingText; private ShowMoreButton moreButton; - private OsuSpriteText missing; - private PaginatedContainerHeader header; - - private readonly string headerText; - private readonly CounterVisibilityState counterVisibilityState; protected PaginatedContainer(Bindable user, string headerText = "", string missingText = "", CounterVisibilityState counterVisibilityState = CounterVisibilityState.AlwaysHidden) + : base(user, headerText, missingText, counterVisibilityState) { - this.headerText = headerText; - this.missingText = missingText; - this.counterVisibilityState = counterVisibilityState; - User.BindTo(user); } - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) + protected override Drawable CreateContent() => new FillFlowContainer { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Direction = FillDirection.Vertical; - + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, Children = new Drawable[] { - header = new PaginatedContainerHeader(headerText, counterVisibilityState) - { - Alpha = string.IsNullOrEmpty(headerText) ? 0 : 1 - }, ItemsContainer = new FillFlowContainer { AutoSizeAxes = Axes.Y, @@ -76,21 +56,10 @@ namespace osu.Game.Overlays.Profile.Sections Margin = new MarginPadding { Top = 10 }, Action = showMore, }, - missing = new OsuSpriteText - { - Font = OsuFont.GetFont(size: 15), - Text = missingText, - Alpha = 0, - }, - }; + } + }; - Rulesets = rulesets; - - User.ValueChanged += onUserChanged; - User.TriggerChange(); - } - - private void onUserChanged(ValueChangedEvent e) + protected override void OnUserChanged(ValueChangedEvent e) { loadCancellation?.Cancel(); retrievalRequest?.Cancel(); @@ -124,15 +93,15 @@ namespace osu.Game.Overlays.Profile.Sections moreButton.Hide(); moreButton.IsLoading = false; - if (!string.IsNullOrEmpty(missing.Text)) - missing.Show(); + if (!string.IsNullOrEmpty(Missing.Text)) + Missing.Show(); return; } LoadComponentsAsync(items.Select(CreateDrawableItem).Where(d => d != null), drawables => { - missing.Hide(); + Missing.Hide(); moreButton.FadeTo(items.Count == ItemsPerPage ? 1 : 0); moreButton.IsLoading = false; @@ -142,8 +111,6 @@ namespace osu.Game.Overlays.Profile.Sections protected virtual int GetCount(User user) => 0; - protected void SetCount(int value) => header.Current.Value = value; - protected virtual void OnItemsReceived(List items) { } diff --git a/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs new file mode 100644 index 0000000000..9583759693 --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs @@ -0,0 +1,78 @@ +// 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.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets; +using osu.Game.Users; +using JetBrains.Annotations; + +namespace osu.Game.Overlays.Profile.Sections +{ + public abstract class ProfileSubsection : FillFlowContainer + { + protected readonly Bindable User = new Bindable(); + + protected RulesetStore Rulesets { get; private set; } + + protected OsuSpriteText Missing { get; private set; } + + private readonly string headerText; + private readonly string missingText; + private readonly CounterVisibilityState counterVisibilityState; + + private ProfileSubsectionHeader header; + + protected ProfileSubsection(Bindable user, string headerText = "", string missingText = "", CounterVisibilityState counterVisibilityState = CounterVisibilityState.AlwaysHidden) + { + this.headerText = headerText; + this.missingText = missingText; + this.counterVisibilityState = counterVisibilityState; + User.BindTo(user); + } + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + + Children = new Drawable[] + { + header = new ProfileSubsectionHeader(headerText, counterVisibilityState) + { + Alpha = string.IsNullOrEmpty(headerText) ? 0 : 1 + }, + CreateContent(), + Missing = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 15), + Text = missingText, + Alpha = 0, + }, + }; + + Rulesets = rulesets; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + User.BindValueChanged(OnUserChanged, true); + } + + [NotNull] + protected abstract Drawable CreateContent(); + + protected virtual void OnUserChanged(ValueChangedEvent e) + { + } + + protected void SetCount(int value) => header.Current.Value = value; + } +} diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainerHeader.cs b/osu.Game/Overlays/Profile/Sections/ProfileSubsectionHeader.cs similarity index 95% rename from osu.Game/Overlays/Profile/Sections/PaginatedContainerHeader.cs rename to osu.Game/Overlays/Profile/Sections/ProfileSubsectionHeader.cs index 8c617e5fbd..5858cebe89 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainerHeader.cs +++ b/osu.Game/Overlays/Profile/Sections/ProfileSubsectionHeader.cs @@ -14,7 +14,7 @@ using osu.Game.Graphics; namespace osu.Game.Overlays.Profile.Sections { - public class PaginatedContainerHeader : CompositeDrawable, IHasCurrentValue + public class ProfileSubsectionHeader : CompositeDrawable, IHasCurrentValue { private readonly BindableWithCurrent current = new BindableWithCurrent(); @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Sections private CounterPill counterPill; - public PaginatedContainerHeader(string text, CounterVisibilityState counterState) + public ProfileSubsectionHeader(string text, CounterVisibilityState counterState) { this.text = text; this.counterState = counterState; From af174aa653206d3ae3dbb42368b2c4ff5285a488 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Nov 2020 18:48:47 +0300 Subject: [PATCH 07/66] Implement chart subsections --- .../Historical/ChartProfileSubsection.cs | 51 +++++++++++++++++++ .../Historical/PlayHistorySubsection.cs | 19 +++++++ .../Sections/Historical/ProfileLineChart.cs | 4 +- .../Sections/Historical/ReplaysSubsection.cs | 19 +++++++ .../Profile/Sections/HistoricalSection.cs | 2 + 5 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs create mode 100644 osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs create mode 100644 osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs new file mode 100644 index 0000000000..24083c9a79 --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs @@ -0,0 +1,51 @@ +// 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.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Users; +using static osu.Game.Users.User; + +namespace osu.Game.Overlays.Profile.Sections.Historical +{ + public abstract class ChartProfileSubsection : ProfileSubsection + { + private ProfileLineChart chart; + + protected ChartProfileSubsection(Bindable user, string headerText) + : base(user, headerText) + { + + } + + protected override Drawable CreateContent() => new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Top = 10, + Left = 20, + Right = 40 + }, + Child = chart = new ProfileLineChart() + }; + + protected override void OnUserChanged(ValueChangedEvent e) + { + var values = GetValues(e.NewValue); + + if (values?.Length > 1) + { + chart.Values = values; + Show(); + return; + } + + Hide(); + } + + protected abstract UserHistoryCount[] GetValues(User user); + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs new file mode 100644 index 0000000000..3e35f80b49 --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs @@ -0,0 +1,19 @@ +// 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.Bindables; +using osu.Game.Users; +using static osu.Game.Users.User; + +namespace osu.Game.Overlays.Profile.Sections.Historical +{ + public class PlayHistorySubsection : ChartProfileSubsection + { + public PlayHistorySubsection(Bindable user) + : base(user, "Play History") + { + } + + protected override UserHistoryCount[] GetValues(User user) => user.MonthlyPlaycounts; + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index 2908b50a6e..55fa6c5400 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -130,7 +130,9 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Anchor = Anchor.BottomRight, Origin = Anchor.CentreRight, RelativePositionAxes = Axes.Y, + Margin = new MarginPadding { Right = 3 }, Text = rollingRow.ToString("N0"), + Font = OsuFont.GetFont(size: 12), Y = y }); @@ -172,6 +174,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Origin = Anchor.CentreLeft, RelativePositionAxes = Axes.X, Text = new DateTime((long)rollingRow).ToString("MMM yyyy"), + Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), Rotation = 45, X = x }); @@ -228,7 +231,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private void load(OverlayColourProvider colourProvider) { Colour = colourProvider.Foreground1; - Font = OsuFont.GetFont(size: 12); } } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs new file mode 100644 index 0000000000..f6abd1c4fc --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs @@ -0,0 +1,19 @@ +// 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.Bindables; +using osu.Game.Users; +using static osu.Game.Users.User; + +namespace osu.Game.Overlays.Profile.Sections.Historical +{ + public class ReplaysSubsection : ChartProfileSubsection + { + public ReplaysSubsection(Bindable user) + : base(user, "Replays Watched History") + { + } + + protected override UserHistoryCount[] GetValues(User user) => user.ReplaysWatchedCounts; + } +} diff --git a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs index bfc47bd88c..6e2b9873cf 100644 --- a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs +++ b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs @@ -18,8 +18,10 @@ namespace osu.Game.Overlays.Profile.Sections { Children = new Drawable[] { + new PlayHistorySubsection(User), new PaginatedMostPlayedBeatmapContainer(User), new PaginatedScoreContainer(ScoreType.Recent, User, "Recent Plays (24h)", CounterVisibilityState.VisibleWhenZero), + new ReplaysSubsection(User) }; } } From 02168c6c2fb7f31907c5d02797fbc1508075dd87 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Nov 2020 19:17:01 +0300 Subject: [PATCH 08/66] Implement dates with zero count fill --- .../Historical/ChartProfileSubsection.cs | 28 ++++++++++++++++++- .../Sections/Historical/ProfileLineChart.cs | 2 +- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs index 24083c9a79..38224dd177 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -38,7 +39,32 @@ namespace osu.Game.Overlays.Profile.Sections.Historical if (values?.Length > 1) { - chart.Values = values; + // Fill dates with 0 count + + var newValues = new List { values[0] }; + var newLast = values[0]; + + for (int i = 1; i < values.Length; i++) + { + while (hasMissingDates(newLast, values[i])) + { + newValues.Add(newLast = new UserHistoryCount + { + Count = 0, + Date = newLast.Date.AddMonths(1) + }); + } + + newValues.Add(newLast = values[i]); + } + + static bool hasMissingDates(UserHistoryCount prev, UserHistoryCount current) + { + var possibleCurrent = prev.Date.AddMonths(1); + return possibleCurrent != current.Date; + } + + chart.Values = newValues.ToArray(); Show(); return; } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index 55fa6c5400..7cd529a726 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { private UserHistoryCount[] values; - [CanBeNull] + [NotNull] public UserHistoryCount[] Values { get => values; From 5354bf1fa593bcbfb1d6cafac3e4143af035e45e Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Nov 2020 20:07:52 +0300 Subject: [PATCH 09/66] Ticks distribution improvements --- .../Sections/Historical/ProfileLineChart.cs | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index 7cd529a726..fe4677037b 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -116,14 +116,12 @@ namespace osu.Game.Overlays.Profile.Sections.Historical var niceRange = niceNumber(max - min, false); var niceTick = niceNumber(niceRange / (6 - 1), true); - var axisStart = Math.Floor(min / niceTick) * niceTick; - var axisEnd = Math.Ceiling(max / niceTick) * niceTick; - var rollingRow = axisStart; + double rollingRow = min; - while (rollingRow <= axisEnd) + while (rollingRow <= max) { - var y = -Interpolation.ValueAt(rollingRow, 0, 1f, axisStart, axisEnd); + var y = -Interpolation.ValueAt(rollingRow, 0, 1f, min, max); rowTicksContainer.Add(new TickText { @@ -155,25 +153,23 @@ namespace osu.Game.Overlays.Profile.Sections.Historical columnTicksContainer.Clear(); columnLinesContainer.Clear(); - var min = values.Select(v => v.Date).Min().Ticks; - var max = values.Select(v => v.Date).Max().Ticks; + var min = values.Select(v => v.Date).Min().ToOADate(); + var max = values.Select(v => v.Date).Max().ToOADate(); var niceRange = niceNumber(max - min, false); var niceTick = niceNumber(niceRange / (Math.Min(values.Length, 15) - 1), true); - var axisStart = Math.Floor(min / niceTick) * niceTick; - var axisEnd = Math.Ceiling(max / niceTick) * niceTick; - var rollingRow = axisStart; + double rollingRow = min; - while (rollingRow <= axisEnd) + while (rollingRow <= max) { - var x = Interpolation.ValueAt(rollingRow, 0, 1f, axisStart, axisEnd); + var x = Interpolation.ValueAt(rollingRow, 0, 1f, min, max); columnTicksContainer.Add(new TickText { Origin = Anchor.CentreLeft, RelativePositionAxes = Axes.X, - Text = new DateTime((long)rollingRow).ToString("MMM yyyy"), + Text = DateTime.FromOADate(rollingRow).ToString("MMM yyyy"), Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), Rotation = 45, X = x From a94546f905ca8ca42e9b4386c148bf362bf612a4 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Nov 2020 20:17:32 +0300 Subject: [PATCH 10/66] CI fixes --- .../Profile/Sections/Historical/ChartProfileSubsection.cs | 1 - .../Overlays/Profile/Sections/Historical/ProfileLineChart.cs | 2 +- osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs index 38224dd177..4445cdce51 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs @@ -17,7 +17,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected ChartProfileSubsection(Bindable user, string headerText) : base(user, headerText) { - } protected override Drawable CreateContent() => new Container diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index fe4677037b..b7983fd356 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -92,7 +92,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical } } }, - new Drawable[] + new[] { Empty(), columnTicksContainer = new Container diff --git a/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs index 9583759693..751b35e342 100644 --- a/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/ProfileSubsection.cs @@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Profile.Sections AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; - Children = new Drawable[] + Children = new[] { header = new ProfileSubsectionHeader(headerText, counterVisibilityState) { From fe9d17fc568cf757991573fb55d12f36875ed908 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Nov 2020 20:31:03 +0300 Subject: [PATCH 11/66] Fix CodeFactor issues --- osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs | 2 +- .../Overlays/Profile/Sections/Historical/ProfileLineChart.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs index 0be835c07d..3d342b0d76 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs @@ -3,12 +3,12 @@ using osu.Game.Overlays.Profile.Sections.Historical; using osu.Framework.Graphics; -using static osu.Game.Users.User; using System; using osu.Game.Overlays; using osu.Framework.Allocation; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Containers; +using static osu.Game.Users.User; namespace osu.Game.Tests.Visual.Online { diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index b7983fd356..5a9c42d7e0 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -4,7 +4,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using JetBrains.Annotations; -using static osu.Game.Users.User; using System; using System.Linq; using osu.Game.Graphics.Sprites; @@ -12,6 +11,7 @@ using osu.Framework.Utils; using osu.Framework.Allocation; using osu.Game.Graphics; using osu.Framework.Graphics.Shapes; +using static osu.Game.Users.User; namespace osu.Game.Overlays.Profile.Sections.Historical { From a52c98b55cc0be3e190610a9214cca09eb8288a0 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 14 Nov 2020 21:20:37 +0300 Subject: [PATCH 12/66] Fix broken test scene --- .../Profile/Sections/Historical/PlayHistorySubsection.cs | 2 +- .../Overlays/Profile/Sections/Historical/ReplaysSubsection.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs index 3e35f80b49..2f15886c3a 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs @@ -14,6 +14,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { } - protected override UserHistoryCount[] GetValues(User user) => user.MonthlyPlaycounts; + protected override UserHistoryCount[] GetValues(User user) => user?.MonthlyPlaycounts; } } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs index f6abd1c4fc..e594e8d020 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs @@ -14,6 +14,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { } - protected override UserHistoryCount[] GetValues(User user) => user.ReplaysWatchedCounts; + protected override UserHistoryCount[] GetValues(User user) => user?.ReplaysWatchedCounts; } } From c013cd11c9ac330aa18217ce3480e7994d78c3fe Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 20 Nov 2020 17:24:09 +0900 Subject: [PATCH 13/66] Add DrawableHitObjectAdded event --- osu.Game/Rulesets/UI/HitObjectContainer.cs | 17 +++++++++++++++++ osu.Game/Rulesets/UI/Playfield.cs | 6 ++++++ 2 files changed, 23 insertions(+) diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index 5fbda305c8..5dc653395b 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -42,6 +42,11 @@ namespace osu.Game.Rulesets.UI /// public event Action RevertResult; + /// + /// Invoked when a is added. + /// + public event Action DrawableHitObjectAdded; + /// /// Invoked when a becomes used by a . /// @@ -115,6 +120,8 @@ namespace osu.Game.Rulesets.UI bindStartTime(drawable); AddInternal(drawableMap[entry] = drawable, false); + DrawableHitObjectAdded?.Invoke(drawable); + HitObjectUsageBegan?.Invoke(entry.HitObject); } @@ -147,6 +154,16 @@ namespace osu.Game.Rulesets.UI hitObject.OnRevertResult += onRevertResult; AddInternal(hitObject); + + onDrawableHitObjectAddedRecursive(hitObject); + } + + private void onDrawableHitObjectAddedRecursive(DrawableHitObject hitObject) + { + DrawableHitObjectAdded?.Invoke(hitObject); + + foreach (var nested in hitObject.NestedHitObjects) + onDrawableHitObjectAddedRecursive(nested); } public virtual bool Remove(DrawableHitObject hitObject) diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 82ec653f31..245fdd59e5 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -32,6 +32,11 @@ namespace osu.Game.Rulesets.UI /// public event Action RevertResult; + /// + /// Invoked when a is added. + /// + public event Action DrawableHitObjectAdded; + /// /// The contained in this Playfield. /// @@ -91,6 +96,7 @@ namespace osu.Game.Rulesets.UI { h.NewResult += (d, r) => NewResult?.Invoke(d, r); h.RevertResult += (d, r) => RevertResult?.Invoke(d, r); + h.DrawableHitObjectAdded += d => DrawableHitObjectAdded?.Invoke(d); h.HitObjectUsageBegan += o => HitObjectUsageBegan?.Invoke(o); h.HitObjectUsageFinished += o => HitObjectUsageFinished?.Invoke(o); })); From 468b2a97cbcf40f71a8df3e3c067617c7a197cff Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 20 Nov 2020 17:25:57 +0900 Subject: [PATCH 14/66] Use events instead of overriding Add (catch) --- .../Objects/Drawables/DrawableBananaShower.cs | 2 +- .../Objects/Drawables/DrawableJuiceStream.cs | 3 +-- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 15 ++++----------- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs index 4ce80aceb8..b46933d0c2 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables switch (hitObject) { case Banana banana: - return createDrawableRepresentation?.Invoke(banana)?.With(o => ((DrawableCatchHitObject)o).CheckPosition = p => CheckPosition?.Invoke(p) ?? false); + return createDrawableRepresentation?.Invoke(banana); } return base.CreateNestedHitObject(hitObject); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs index 7bc016d94f..cc8accbb2b 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs @@ -47,8 +47,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables switch (hitObject) { case CatchHitObject catchObject: - return createDrawableRepresentation?.Invoke(catchObject)?.With(o => - ((DrawableCatchHitObject)o).CheckPosition = p => CheckPosition?.Invoke(p) ?? false); + return createDrawableRepresentation?.Invoke(catchObject); } throw new ArgumentException($"{nameof(hitObject)} must be of type {nameof(CatchHitObject)}."); diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 735d7fc300..0b379bbe95 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -55,21 +55,14 @@ namespace osu.Game.Rulesets.Catch.UI HitObjectContainer, CatcherArea, }; + + NewResult += onNewResult; + RevertResult += onRevertResult; + DrawableHitObjectAdded += d => ((DrawableCatchHitObject)d).CheckPosition = CheckIfWeCanCatch; } public bool CheckIfWeCanCatch(CatchHitObject obj) => CatcherArea.AttemptCatch(obj); - public override void Add(DrawableHitObject h) - { - h.OnNewResult += onNewResult; - h.OnRevertResult += onRevertResult; - - base.Add(h); - - var fruit = (DrawableCatchHitObject)h; - fruit.CheckPosition = CheckIfWeCanCatch; - } - private void onNewResult(DrawableHitObject judgedObject, JudgementResult result) => CatcherArea.OnNewResult((DrawableCatchHitObject)judgedObject, result); From cd16a3fa614443cc150c74d407081447942ea58b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 20 Nov 2020 17:28:06 +0900 Subject: [PATCH 15/66] Use event instead of using custom pools (osu) --- .../Edit/DrawableOsuEditPool.cs | 63 ------------------- .../Edit/DrawableOsuEditRuleset.cs | 48 +++++++++++++- .../Objects/Drawables/DrawableOsuPool.cs | 32 ---------- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 38 +++++------ 4 files changed, 64 insertions(+), 117 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Edit/DrawableOsuEditPool.cs delete mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuPool.cs diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditPool.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditPool.cs deleted file mode 100644 index 776aacd143..0000000000 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditPool.cs +++ /dev/null @@ -1,63 +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; -using System.Linq; -using osu.Framework.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables; - -namespace osu.Game.Rulesets.Osu.Edit -{ - public class DrawableOsuEditPool : DrawableOsuPool - where T : DrawableHitObject, new() - { - /// - /// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay. - /// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points. - /// - private const double editor_hit_object_fade_out_extension = 700; - - public DrawableOsuEditPool(Func checkHittable, Action onLoaded, int initialSize, int? maximumSize = null) - : base(checkHittable, onLoaded, initialSize, maximumSize) - { - } - - protected override T CreateNewDrawable() => base.CreateNewDrawable().With(d => d.ApplyCustomUpdateState += updateState); - - private void updateState(DrawableHitObject hitObject, ArmedState state) - { - if (state == ArmedState.Idle) - return; - - // adjust the visuals of certain object types to make them stay on screen for longer than usual. - switch (hitObject) - { - default: - // there are quite a few drawable hit types we don't want to extend (spinners, ticks etc.) - return; - - case DrawableSlider _: - // no specifics to sliders but let them fade slower below. - break; - - case DrawableHitCircle circle: // also handles slider heads - circle.ApproachCircle - .FadeOutFromOne(editor_hit_object_fade_out_extension) - .Expire(); - break; - } - - // Get the existing fade out transform - var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha)); - - if (existing == null) - return; - - hitObject.RemoveTransform(existing); - - using (hitObject.BeginAbsoluteSequence(existing.StartTime)) - hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire(); - } - } -} diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs index 547dff88b5..a03389bfe9 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs @@ -2,9 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Framework.Graphics.Pooling; +using System.Linq; +using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; using osuTK; @@ -26,8 +29,47 @@ namespace osu.Game.Rulesets.Osu.Edit { protected override GameplayCursorContainer CreateCursor() => null; - protected override DrawablePool CreatePool(int initialSize, int? maximumSize = null) - => new DrawableOsuEditPool(CheckHittable, OnHitObjectLoaded, initialSize, maximumSize); + public OsuEditPlayfield() + { + DrawableHitObjectAdded += d => d.ApplyCustomUpdateState += updateState; + } + + private const double editor_hit_object_fade_out_extension = 700; + + private void updateState(DrawableHitObject hitObject, ArmedState state) + { + if (state == ArmedState.Idle) + return; + + // adjust the visuals of certain object types to make them stay on screen for longer than usual. + switch (hitObject) + { + default: + // there are quite a few drawable hit types we don't want to extend (spinners, ticks etc.) + return; + + case DrawableSlider _: + // no specifics to sliders but let them fade slower below. + break; + + case DrawableHitCircle circle: // also handles slider heads + circle.ApproachCircle + .FadeOutFromOne(editor_hit_object_fade_out_extension) + .Expire(); + break; + } + + // Get the existing fade out transform + var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha)); + + if (existing == null) + return; + + hitObject.RemoveTransform(existing); + + using (hitObject.BeginAbsoluteSequence(existing.StartTime)) + hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire(); + } } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuPool.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuPool.cs deleted file mode 100644 index 1b5fd50022..0000000000 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuPool.cs +++ /dev/null @@ -1,32 +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; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Pooling; -using osu.Game.Rulesets.Objects.Drawables; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - public class DrawableOsuPool : DrawablePool - where T : DrawableHitObject, new() - { - private readonly Func checkHittable; - private readonly Action onLoaded; - - public DrawableOsuPool(Func checkHittable, Action onLoaded, int initialSize, int? maximumSize = null) - : base(initialSize, maximumSize) - { - this.checkHittable = checkHittable; - this.onLoaded = onLoaded; - } - - protected override T CreateNewDrawable() => base.CreateNewDrawable().With(o => - { - var osuObject = (DrawableOsuHitObject)(object)o; - - osuObject.CheckHittable = checkHittable; - osuObject.OnLoadComplete += onLoaded; - }); - } -} diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index c816502d61..1b0d50b4f3 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -91,6 +91,15 @@ namespace osu.Game.Rulesets.Osu.UI AddRangeInternal(poolDictionary.Values); NewResult += onNewResult; + DrawableHitObjectAdded += onDrawableHitObjectAdded; + } + + private void onDrawableHitObjectAdded(DrawableHitObject drawable) + { + if (!drawable.IsLoaded) + drawable.OnLoadComplete += onDrawableHitObjectLoaded; + + ((DrawableOsuHitObject)drawable).CheckHittable = CheckHittable; } [BackgroundDependencyLoader(true)] @@ -98,28 +107,19 @@ namespace osu.Game.Rulesets.Osu.UI { config?.BindWith(OsuRulesetSetting.PlayfieldBorderStyle, playfieldBorder.PlayfieldBorderStyle); - registerPool(10, 100); + RegisterPool(10, 100); - registerPool(10, 100); - registerPool(10, 100); - registerPool(10, 100); - registerPool(10, 100); - registerPool(5, 50); + RegisterPool(10, 100); + RegisterPool(10, 100); + RegisterPool(10, 100); + RegisterPool(10, 100); + RegisterPool(5, 50); - registerPool(2, 20); - registerPool(10, 100); - registerPool(10, 100); + RegisterPool(2, 20); + RegisterPool(10, 100); + RegisterPool(10, 100); } - private void registerPool(int initialSize, int? maximumSize = null) - where TObject : HitObject - where TDrawable : DrawableHitObject, new() - => RegisterPool(CreatePool(initialSize, maximumSize)); - - protected virtual DrawablePool CreatePool(int initialSize, int? maximumSize = null) - where TDrawable : DrawableHitObject, new() - => new DrawableOsuPool(CheckHittable, OnHitObjectLoaded, initialSize, maximumSize); - protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new OsuHitObjectLifetimeEntry(hitObject); protected override void OnHitObjectAdded(HitObject hitObject) @@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Osu.UI followPoints.RemoveFollowPoints((OsuHitObject)hitObject); } - public void OnHitObjectLoaded(Drawable drawable) + private void onDrawableHitObjectLoaded(Drawable drawable) { switch (drawable) { From 772f6df668e7d95c14bb5ba9e6e84a69ba693ba3 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 20 Nov 2020 18:00:00 +0900 Subject: [PATCH 16/66] Add a remark for DrawableHitObjectAdded --- osu.Game/Rulesets/UI/HitObjectContainer.cs | 3 +++ osu.Game/Rulesets/UI/Playfield.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index 5dc653395b..da7f5a0ee5 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -45,6 +45,9 @@ namespace osu.Game.Rulesets.UI /// /// Invoked when a is added. /// + /// + /// This event is also called for nested s. + /// public event Action DrawableHitObjectAdded; /// diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 245fdd59e5..be56be91d5 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -35,6 +35,9 @@ namespace osu.Game.Rulesets.UI /// /// Invoked when a is added. /// + /// + /// This event is also called for nested s. + /// public event Action DrawableHitObjectAdded; /// From 27f5a99726c238effb4311e0324dc589f30a1d08 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 20 Nov 2020 18:42:48 +0900 Subject: [PATCH 17/66] Fix more than one proxy is created --- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 37 +++++++++--------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 1b0d50b4f3..d453b9cd53 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -96,10 +96,20 @@ namespace osu.Game.Rulesets.Osu.UI private void onDrawableHitObjectAdded(DrawableHitObject drawable) { - if (!drawable.IsLoaded) - drawable.OnLoadComplete += onDrawableHitObjectLoaded; - ((DrawableOsuHitObject)drawable).CheckHittable = CheckHittable; + + switch (drawable) + { + case DrawableSpinner _: + if (!drawable.HasProxy) + spinnerProxies.Add(drawable.CreateProxy()); + break; + + case IDrawableHitObjectWithProxiedApproach approach: + if (!approach.ProxiedLayer.HasProxy) + approachCircles.Add(approach.ProxiedLayer.CreateProxy()); + break; + } } [BackgroundDependencyLoader(true)] @@ -134,27 +144,6 @@ namespace osu.Game.Rulesets.Osu.UI followPoints.RemoveFollowPoints((OsuHitObject)hitObject); } - private void onDrawableHitObjectLoaded(Drawable drawable) - { - switch (drawable) - { - case DrawableSliderHead _: - case DrawableSliderTail _: - case DrawableSliderTick _: - case DrawableSliderRepeat _: - case DrawableSpinnerTick _: - break; - - case DrawableSpinner _: - spinnerProxies.Add(drawable.CreateProxy()); - break; - - case IDrawableHitObjectWithProxiedApproach approach: - approachCircles.Add(approach.ProxiedLayer.CreateProxy()); - break; - } - } - private void onNewResult(DrawableHitObject judgedObject, JudgementResult result) { // Hitobjects that block future hits should miss previous hitobjects if they're hit out-of-order. From 82aefa3868030e0fd323710a1153b30e890899ab Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sat, 21 Nov 2020 00:27:19 +0900 Subject: [PATCH 18/66] Rework and rename to OnNewDrawableHitObject. The semantics is changed and hopefully more clear. --- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 2 +- .../Edit/DrawableOsuEditRuleset.cs | 2 +- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 18 +++++++++++------ .../Objects/Drawables/DrawableHitObject.cs | 12 ++++++++++- osu.Game/Rulesets/UI/HitObjectContainer.cs | 20 ------------------- osu.Game/Rulesets/UI/Playfield.cs | 14 ++++++++++--- 6 files changed, 36 insertions(+), 32 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 0b379bbe95..cd246e78d5 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Catch.UI NewResult += onNewResult; RevertResult += onRevertResult; - DrawableHitObjectAdded += d => ((DrawableCatchHitObject)d).CheckPosition = CheckIfWeCanCatch; + OnNewDrawableHitObject += d => ((DrawableCatchHitObject)d).CheckPosition = CheckIfWeCanCatch; } public bool CheckIfWeCanCatch(CatchHitObject obj) => CatcherArea.AttemptCatch(obj); diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs index a03389bfe9..1a71a88c71 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Edit public OsuEditPlayfield() { - DrawableHitObjectAdded += d => d.ApplyCustomUpdateState += updateState; + OnNewDrawableHitObject += d => d.ApplyCustomUpdateState += updateState; } private const double editor_hit_object_fade_out_extension = 700; diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index d453b9cd53..d7336050eb 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -91,23 +92,28 @@ namespace osu.Game.Rulesets.Osu.UI AddRangeInternal(poolDictionary.Values); NewResult += onNewResult; - DrawableHitObjectAdded += onDrawableHitObjectAdded; + OnNewDrawableHitObject += onDrawableHitObjectAdded; } private void onDrawableHitObjectAdded(DrawableHitObject drawable) { ((DrawableOsuHitObject)drawable).CheckHittable = CheckHittable; + Debug.Assert(!drawable.IsLoaded, $"Already loaded {nameof(DrawableHitObject)} is added to {nameof(OsuPlayfield)}"); + drawable.OnLoadComplete += onDrawableHitObjectLoaded; + } + + private void onDrawableHitObjectLoaded(Drawable drawable) + { + // note: `Slider`'s `ProxiedLayer` is added when its nested `DrawableHitCircle` is loaded. switch (drawable) { case DrawableSpinner _: - if (!drawable.HasProxy) - spinnerProxies.Add(drawable.CreateProxy()); + spinnerProxies.Add(drawable.CreateProxy()); break; - case IDrawableHitObjectWithProxiedApproach approach: - if (!approach.ProxiedLayer.HasProxy) - approachCircles.Add(approach.ProxiedLayer.CreateProxy()); + case DrawableHitCircle hitCircle: + approachCircles.Add(hitCircle.ProxiedLayer.CreateProxy()); break; } } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index ca49ed9e75..312fbaa2d1 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -74,6 +74,11 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public event Action OnRevertResult; + /// + /// Invoked when a new nested hit object is created by . + /// + internal event Action OnNestedDrawableCreated; + /// /// Whether a visual indicator should be displayed when a scoring result occurs. /// @@ -214,10 +219,15 @@ namespace osu.Game.Rulesets.Objects.Drawables foreach (var h in HitObject.NestedHitObjects) { - var drawableNested = pooledObjectProvider?.GetPooledDrawableRepresentation(h) + var pooledDrawableNested = pooledObjectProvider?.GetPooledDrawableRepresentation(h); + var drawableNested = pooledDrawableNested ?? CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}."); + // Invoke the event only if this nested object is just created by `CreateNestedHitObject`. + if (pooledDrawableNested == null) + OnNestedDrawableCreated?.Invoke(drawableNested); + drawableNested.OnNewResult += onNewResult; drawableNested.OnRevertResult += onRevertResult; drawableNested.ApplyCustomUpdateState += onApplyCustomUpdateState; diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index da7f5a0ee5..5fbda305c8 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -42,14 +42,6 @@ namespace osu.Game.Rulesets.UI /// public event Action RevertResult; - /// - /// Invoked when a is added. - /// - /// - /// This event is also called for nested s. - /// - public event Action DrawableHitObjectAdded; - /// /// Invoked when a becomes used by a . /// @@ -123,8 +115,6 @@ namespace osu.Game.Rulesets.UI bindStartTime(drawable); AddInternal(drawableMap[entry] = drawable, false); - DrawableHitObjectAdded?.Invoke(drawable); - HitObjectUsageBegan?.Invoke(entry.HitObject); } @@ -157,16 +147,6 @@ namespace osu.Game.Rulesets.UI hitObject.OnRevertResult += onRevertResult; AddInternal(hitObject); - - onDrawableHitObjectAddedRecursive(hitObject); - } - - private void onDrawableHitObjectAddedRecursive(DrawableHitObject hitObject) - { - DrawableHitObjectAdded?.Invoke(hitObject); - - foreach (var nested in hitObject.NestedHitObjects) - onDrawableHitObjectAddedRecursive(nested); } public virtual bool Remove(DrawableHitObject hitObject) diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index be56be91d5..8723a8c531 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -33,12 +33,14 @@ namespace osu.Game.Rulesets.UI public event Action RevertResult; /// - /// Invoked when a is added. + /// Invoked before a new is added. + /// This event is invoked only once for each + /// even the drawable is pooled and used multiple times for different s. /// /// /// This event is also called for nested s. /// - public event Action DrawableHitObjectAdded; + public event Action OnNewDrawableHitObject; /// /// The contained in this Playfield. @@ -93,13 +95,15 @@ namespace osu.Game.Rulesets.UI /// protected Playfield() { + OnNewDrawableHitObject += d => + d.OnNestedDrawableCreated += nested => OnNewDrawableHitObject?.Invoke(nested); + RelativeSizeAxes = Axes.Both; hitObjectContainerLazy = new Lazy(() => CreateHitObjectContainer().With(h => { h.NewResult += (d, r) => NewResult?.Invoke(d, r); h.RevertResult += (d, r) => RevertResult?.Invoke(d, r); - h.DrawableHitObjectAdded += d => DrawableHitObjectAdded?.Invoke(d); h.HitObjectUsageBegan += o => HitObjectUsageBegan?.Invoke(o); h.HitObjectUsageFinished += o => HitObjectUsageFinished?.Invoke(o); })); @@ -133,6 +137,8 @@ namespace osu.Game.Rulesets.UI /// The DrawableHitObject to add. public virtual void Add(DrawableHitObject h) { + OnNewDrawableHitObject?.Invoke(h); + HitObjectContainer.Add(h); OnHitObjectAdded(h.HitObject); } @@ -334,6 +340,8 @@ namespace osu.Game.Rulesets.UI // This is done before Apply() so that the state is updated once when the hitobject is applied. if (!dho.IsLoaded) { + OnNewDrawableHitObject?.Invoke(dho); + foreach (var m in mods.OfType()) m.ApplyToDrawableHitObjects(dho.Yield()); } From 281ed49332c3a4f795eac9619bab786a5c1ee977 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sat, 21 Nov 2020 11:19:52 +0900 Subject: [PATCH 19/66] Add `HasInitialized` to DHO As it turned out, `IsLoaded` is not a reliable way. --- .../Objects/Drawables/DrawableHitObject.cs | 5 ++++ osu.Game/Rulesets/UI/Playfield.cs | 27 +++++++++++++------ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 312fbaa2d1..84b2dd7957 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -146,6 +146,11 @@ namespace osu.Game.Rulesets.Objects.Drawables private Container samplesContainer; + /// + /// Whether the initialization logic in has applied. + /// + internal bool HasInitialized; + /// /// Creates a new . /// diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 8723a8c531..f0b63fd347 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -16,6 +16,7 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osuTK; +using System.Diagnostics; namespace osu.Game.Rulesets.UI { @@ -95,9 +96,6 @@ namespace osu.Game.Rulesets.UI /// protected Playfield() { - OnNewDrawableHitObject += d => - d.OnNestedDrawableCreated += nested => OnNewDrawableHitObject?.Invoke(nested); - RelativeSizeAxes = Axes.Both; hitObjectContainerLazy = new Lazy(() => CreateHitObjectContainer().With(h => @@ -126,6 +124,16 @@ namespace osu.Game.Rulesets.UI } } + private void onNewDrawableHitObject(DrawableHitObject d) + { + d.OnNestedDrawableCreated += onNewDrawableHitObject; + + OnNewDrawableHitObject?.Invoke(d); + + Debug.Assert(!d.HasInitialized); + d.HasInitialized = true; + } + /// /// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield. /// @@ -137,7 +145,10 @@ namespace osu.Game.Rulesets.UI /// The DrawableHitObject to add. public virtual void Add(DrawableHitObject h) { - OnNewDrawableHitObject?.Invoke(h); + if (h.HasInitialized) + throw new InvalidOperationException($"{nameof(Playfield.Add)} doesn't support {nameof(DrawableHitObject)} reuse. Use pooling instead."); + + onNewDrawableHitObject(h); HitObjectContainer.Add(h); OnHitObjectAdded(h.HitObject); @@ -336,12 +347,12 @@ namespace osu.Game.Rulesets.UI { var dho = (DrawableHitObject)d; - // If this is the first time this DHO is being used (not loaded), then apply the DHO mods. - // This is done before Apply() so that the state is updated once when the hitobject is applied. - if (!dho.IsLoaded) + if (!dho.HasInitialized) { - OnNewDrawableHitObject?.Invoke(dho); + onNewDrawableHitObject(dho); + // If this is the first time this DHO is being used, then apply the DHO mods. + // This is done before Apply() so that the state is updated once when the hitobject is applied. foreach (var m in mods.OfType()) m.ApplyToDrawableHitObjects(dho.Yield()); } From 4345d8dcb641f5838c707e0556dfbaa49dcd821a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sat, 21 Nov 2020 15:20:33 +0900 Subject: [PATCH 20/66] Event -> virtual method --- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 6 ++++- .../Edit/DrawableOsuEditRuleset.cs | 4 ++-- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 3 +-- osu.Game/Rulesets/UI/Playfield.cs | 23 ++++++++++--------- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index cd246e78d5..7d8f18ee0b 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -58,7 +58,11 @@ namespace osu.Game.Rulesets.Catch.UI NewResult += onNewResult; RevertResult += onRevertResult; - OnNewDrawableHitObject += d => ((DrawableCatchHitObject)d).CheckPosition = CheckIfWeCanCatch; + } + + protected override void OnNewDrawableHitObject(DrawableHitObject d) + { + ((DrawableCatchHitObject)d).CheckPosition = CheckIfWeCanCatch; } public bool CheckIfWeCanCatch(CatchHitObject obj) => CatcherArea.AttemptCatch(obj); diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs index 1a71a88c71..dafde0b927 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs @@ -29,9 +29,9 @@ namespace osu.Game.Rulesets.Osu.Edit { protected override GameplayCursorContainer CreateCursor() => null; - public OsuEditPlayfield() + protected override void OnNewDrawableHitObject(DrawableHitObject d) { - OnNewDrawableHitObject += d => d.ApplyCustomUpdateState += updateState; + d.ApplyCustomUpdateState += updateState; } private const double editor_hit_object_fade_out_extension = 700; diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index d7336050eb..f70229fc1b 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -92,10 +92,9 @@ namespace osu.Game.Rulesets.Osu.UI AddRangeInternal(poolDictionary.Values); NewResult += onNewResult; - OnNewDrawableHitObject += onDrawableHitObjectAdded; } - private void onDrawableHitObjectAdded(DrawableHitObject drawable) + protected override void OnNewDrawableHitObject(DrawableHitObject drawable) { ((DrawableOsuHitObject)drawable).CheckHittable = CheckHittable; diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index f0b63fd347..fcdd5ff53d 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -33,16 +33,6 @@ namespace osu.Game.Rulesets.UI /// public event Action RevertResult; - /// - /// Invoked before a new is added. - /// This event is invoked only once for each - /// even the drawable is pooled and used multiple times for different s. - /// - /// - /// This event is also called for nested s. - /// - public event Action OnNewDrawableHitObject; - /// /// The contained in this Playfield. /// @@ -128,7 +118,7 @@ namespace osu.Game.Rulesets.UI { d.OnNestedDrawableCreated += onNewDrawableHitObject; - OnNewDrawableHitObject?.Invoke(d); + OnNewDrawableHitObject(d); Debug.Assert(!d.HasInitialized); d.HasInitialized = true; @@ -183,6 +173,17 @@ namespace osu.Game.Rulesets.UI { } + /// + /// Invoked before a new is added to this . + /// It is invoked only once even the drawable is pooled and used multiple times for different s. + /// + /// + /// This is also invoked for nested s. + /// + protected virtual void OnNewDrawableHitObject(DrawableHitObject drawableHitObject) + { + } + /// /// The cursor currently being used by this . May be null if no cursor is provided. /// From d4b56aac8403565e2c5f5629c0abe550f76500ad Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 22 Nov 2020 02:17:54 +0300 Subject: [PATCH 21/66] Add missing whitespace --- osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index 75c3ee8290..51e5622f68 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -28,6 +28,7 @@ namespace osu.Game.Overlays.Profile.Sections protected int VisiblePages; protected int ItemsPerPage; + protected FillFlowContainer ItemsContainer { get; private set; } private APIRequest> retrievalRequest; From 3cb1d0466734b0b2c07554f14a4c6f7e59f242c9 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 22 Nov 2020 02:25:12 +0300 Subject: [PATCH 22/66] Move dates fill into it's own method --- .../Historical/ChartProfileSubsection.cs | 55 ++++++++++--------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs index 9413d241fa..783ecec190 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs @@ -44,32 +44,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical if (values?.Length > 1) { - // Fill dates with 0 count - - var newValues = new List { values[0] }; - var newLast = values[0]; - - for (int i = 1; i < values.Length; i++) - { - while (hasMissingDates(newLast, values[i])) - { - newValues.Add(newLast = new UserHistoryCount - { - Count = 0, - Date = newLast.Date.AddMonths(1) - }); - } - - newValues.Add(newLast = values[i]); - } - - static bool hasMissingDates(UserHistoryCount prev, UserHistoryCount current) - { - var possibleCurrent = prev.Date.AddMonths(1); - return possibleCurrent != current.Date; - } - - chart.Values = newValues.ToArray(); + chart.Values = fillZeroValues(values); Show(); return; } @@ -77,6 +52,34 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Hide(); } + private UserHistoryCount[] fillZeroValues(UserHistoryCount[] values) + { + var newValues = new List { values[0] }; + var newLast = values[0]; + + for (int i = 1; i < values.Length; i++) + { + while (hasMissingDates(newLast, values[i])) + { + newValues.Add(newLast = new UserHistoryCount + { + Count = 0, + Date = newLast.Date.AddMonths(1) + }); + } + + newValues.Add(newLast = values[i]); + } + + return newValues.ToArray(); + + static bool hasMissingDates(UserHistoryCount prev, UserHistoryCount current) + { + var possibleCurrent = prev.Date.AddMonths(1); + return possibleCurrent != current.Date; + } + } + protected abstract UserHistoryCount[] GetValues(User user); } } From 453f0ba675de81682ba13a72225e9ad0211b5e2e Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 22 Nov 2020 02:34:29 +0300 Subject: [PATCH 23/66] Make tick lines thicker --- .../Profile/Sections/Historical/ProfileLineChart.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index 5a9c42d7e0..c658ac1aa7 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -12,6 +12,7 @@ using osu.Framework.Allocation; using osu.Game.Graphics; using osu.Framework.Graphics.Shapes; using static osu.Game.Users.User; +using osuTK; namespace osu.Game.Overlays.Profile.Sections.Historical { @@ -140,7 +141,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Origin = Anchor.CentreRight, RelativeSizeAxes = Axes.X, RelativePositionAxes = Axes.Y, - Height = 1, + Height = 0.1f, + EdgeSmoothness = Vector2.One, Y = y }); @@ -180,7 +182,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.Y, RelativePositionAxes = Axes.X, - Width = 1, + Width = 0.1f, + EdgeSmoothness = Vector2.One, X = x }); From 6e581902cdd49c05b513518df1b4f84bfc5b71b2 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 22 Nov 2020 03:11:38 +0300 Subject: [PATCH 24/66] Simplify column ticks creation --- .../Sections/Historical/ProfileLineChart.cs | 109 ++++++++++-------- 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index c658ac1aa7..31c14d3b19 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -123,28 +123,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical while (rollingRow <= max) { var y = -Interpolation.ValueAt(rollingRow, 0, 1f, min, max); - - rowTicksContainer.Add(new TickText - { - Anchor = Anchor.BottomRight, - Origin = Anchor.CentreRight, - RelativePositionAxes = Axes.Y, - Margin = new MarginPadding { Right = 3 }, - Text = rollingRow.ToString("N0"), - Font = OsuFont.GetFont(size: 12), - Y = y - }); - - rowLinesContainer.Add(new TickLine - { - Anchor = Anchor.BottomRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.X, - RelativePositionAxes = Axes.Y, - Height = 0.1f, - EdgeSmoothness = Vector2.One, - Y = y - }); + addRowTick(y, (long)rollingRow); rollingRow += niceTick; } @@ -155,42 +134,70 @@ namespace osu.Game.Overlays.Profile.Sections.Historical columnTicksContainer.Clear(); columnLinesContainer.Clear(); - var min = values.Select(v => v.Date).Min().ToOADate(); - var max = values.Select(v => v.Date).Max().ToOADate(); + var totalMonths = values.Length - 1; - var niceRange = niceNumber(max - min, false); - var niceTick = niceNumber(niceRange / (Math.Min(values.Length, 15) - 1), true); + int monthsPerTick = 1; - double rollingRow = min; + if (totalMonths >= 45) + monthsPerTick = 3; + else if (totalMonths >= 20) + monthsPerTick = 2; - while (rollingRow <= max) + for (int i = 0; i < totalMonths; i += monthsPerTick) { - var x = Interpolation.ValueAt(rollingRow, 0, 1f, min, max); - - columnTicksContainer.Add(new TickText - { - Origin = Anchor.CentreLeft, - RelativePositionAxes = Axes.X, - Text = DateTime.FromOADate(rollingRow).ToString("MMM yyyy"), - Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), - Rotation = 45, - X = x - }); - - columnLinesContainer.Add(new TickLine - { - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.Y, - RelativePositionAxes = Axes.X, - Width = 0.1f, - EdgeSmoothness = Vector2.One, - X = x - }); - - rollingRow += niceTick; + var x = (float)i / totalMonths; + addColumnTick(x, values[i].Date); } } + private void addRowTick(float y, long value) + { + rowTicksContainer.Add(new TickText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.CentreRight, + RelativePositionAxes = Axes.Y, + Margin = new MarginPadding { Right = 3 }, + Text = value.ToString("N0"), + Font = OsuFont.GetFont(size: 12), + Y = y + }); + + rowLinesContainer.Add(new TickLine + { + Anchor = Anchor.BottomRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.X, + RelativePositionAxes = Axes.Y, + Height = 0.1f, + EdgeSmoothness = Vector2.One, + Y = y + }); + } + + private void addColumnTick(float x, DateTime value) + { + columnTicksContainer.Add(new TickText + { + Origin = Anchor.CentreLeft, + RelativePositionAxes = Axes.X, + Text = value.ToString("MMM yyyy"), + Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), + Rotation = 45, + X = x + }); + + columnLinesContainer.Add(new TickLine + { + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Y, + RelativePositionAxes = Axes.X, + Width = 0.1f, + EdgeSmoothness = Vector2.One, + X = x + }); + } + private double niceNumber(double value, bool round) { var exponent = Math.Floor(Math.Log10(value)); From e6c116f0ab35b181e1d353a0640ec1cd1bcb23c5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 22 Nov 2020 03:49:00 +0300 Subject: [PATCH 25/66] Rework horizontal ticks creation --- .../Sections/Historical/ProfileLineChart.cs | 73 ++++++++++--------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index 31c14d3b19..770da21657 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -115,17 +115,19 @@ namespace osu.Game.Overlays.Profile.Sections.Historical var min = values.Select(v => v.Count).Min(); var max = values.Select(v => v.Count).Max(); - var niceRange = niceNumber(max - min, false); - var niceTick = niceNumber(niceRange / (6 - 1), true); + var tick = getTick(getRange(max - min), 6); - double rollingRow = min; + double rollingRow = 0; while (rollingRow <= max) { - var y = -Interpolation.ValueAt(rollingRow, 0, 1f, min, max); - addRowTick(y, (long)rollingRow); + if (rollingRow >= min) + { + var y = -Interpolation.ValueAt(rollingRow, 0, 1f, min, max); + addRowTick(y, (long)rollingRow); + } - rollingRow += niceTick; + rollingRow += tick; } } @@ -138,10 +140,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical int monthsPerTick = 1; - if (totalMonths >= 45) - monthsPerTick = 3; - else if (totalMonths >= 20) - monthsPerTick = 2; + if (totalMonths > 20) + monthsPerTick = totalMonths / 10; for (int i = 0; i < totalMonths; i += monthsPerTick) { @@ -198,37 +198,44 @@ namespace osu.Game.Overlays.Profile.Sections.Historical }); } - private double niceNumber(double value, bool round) + private long getRange(double initialRange) { + var exponent = Math.Floor(Math.Log10(initialRange)); + var fraction = initialRange / Math.Pow(10, exponent); + + double niceFraction; + + if (fraction <= 1.0) + niceFraction = 1.0; + else if (fraction <= 2.0) + niceFraction = 2.0; + else if (fraction <= 5.0) + niceFraction = 5.0; + else + niceFraction = 10.0; + + return (long)(niceFraction * Math.Pow(10, exponent)); + } + + private long getTick(long range, int maxTicksCount) + { + var value = range / (maxTicksCount - 1); + var exponent = Math.Floor(Math.Log10(value)); var fraction = value / Math.Pow(10, exponent); double niceFraction; - if (round) - { - if (fraction < 1.5) - niceFraction = 1.0; - else if (fraction < 3) - niceFraction = 2.0; - else if (fraction < 7) - niceFraction = 5.0; - else - niceFraction = 10.0; - } + if (fraction < 1.5) + niceFraction = 1.0; + else if (fraction < 3) + niceFraction = 2.0; + else if (fraction < 7) + niceFraction = 5.0; else - { - if (fraction <= 1.0) - niceFraction = 1.0; - else if (fraction <= 2.0) - niceFraction = 2.0; - else if (fraction <= 5.0) - niceFraction = 5.0; - else - niceFraction = 10.0; - } + niceFraction = 10.0; - return niceFraction * Math.Pow(10, exponent); + return (long)(niceFraction * Math.Pow(10, exponent)); } private class TickText : OsuSpriteText From f07f8089d69062f44077349945a7680a2ea20c21 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 22 Nov 2020 03:58:56 +0300 Subject: [PATCH 26/66] Adjust monthsPerTick value --- .../Profile/Sections/Historical/ProfileLineChart.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index 770da21657..932005a52f 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -140,8 +140,12 @@ namespace osu.Game.Overlays.Profile.Sections.Historical int monthsPerTick = 1; - if (totalMonths > 20) - monthsPerTick = totalMonths / 10; + if (totalMonths > 80) + monthsPerTick = 12; + else if (totalMonths >= 45) + monthsPerTick = 3; + else if (totalMonths > 20) + monthsPerTick = 2; for (int i = 0; i < totalMonths; i += monthsPerTick) { From 48871329471daf2a5c531460f5490250492529cb Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 22 Nov 2020 04:28:17 +0300 Subject: [PATCH 27/66] Adjustments for edge cases support --- .../Sections/Historical/ProfileLineChart.cs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index 932005a52f..688930d76a 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -117,6 +117,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical var tick = getTick(getRange(max - min), 6); + bool tickIsDecimal = tick < 1.0; + double rollingRow = 0; while (rollingRow <= max) @@ -124,7 +126,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical if (rollingRow >= min) { var y = -Interpolation.ValueAt(rollingRow, 0, 1f, min, max); - addRowTick(y, (long)rollingRow); + addRowTick(y, rollingRow, tickIsDecimal); } rollingRow += tick; @@ -136,7 +138,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical columnTicksContainer.Clear(); columnLinesContainer.Clear(); - var totalMonths = values.Length - 1; + var totalMonths = values.Length; int monthsPerTick = 1; @@ -149,12 +151,12 @@ namespace osu.Game.Overlays.Profile.Sections.Historical for (int i = 0; i < totalMonths; i += monthsPerTick) { - var x = (float)i / totalMonths; + var x = (float)i / (totalMonths - 1); addColumnTick(x, values[i].Date); } } - private void addRowTick(float y, long value) + private void addRowTick(float y, double value, bool tickIsDecimal) { rowTicksContainer.Add(new TickText { @@ -162,7 +164,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Origin = Anchor.CentreRight, RelativePositionAxes = Axes.Y, Margin = new MarginPadding { Right = 3 }, - Text = value.ToString("N0"), + Text = tickIsDecimal ? value.ToString("F1") : value.ToString("N0"), Font = OsuFont.GetFont(size: 12), Y = y }); @@ -202,7 +204,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical }); } - private long getRange(double initialRange) + private double getRange(double initialRange) { var exponent = Math.Floor(Math.Log10(initialRange)); var fraction = initialRange / Math.Pow(10, exponent); @@ -218,10 +220,10 @@ namespace osu.Game.Overlays.Profile.Sections.Historical else niceFraction = 10.0; - return (long)(niceFraction * Math.Pow(10, exponent)); + return niceFraction * Math.Pow(10, exponent); } - private long getTick(long range, int maxTicksCount) + private double getTick(double range, int maxTicksCount) { var value = range / (maxTicksCount - 1); @@ -239,7 +241,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical else niceFraction = 10.0; - return (long)(niceFraction * Math.Pow(10, exponent)); + return niceFraction * Math.Pow(10, exponent); } private class TickText : OsuSpriteText From b745fb681a8bfba02e01317f684fa1a3a8b699f0 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 22 Nov 2020 04:40:55 +0300 Subject: [PATCH 28/66] Fix incorrect static using placement --- .../Overlays/Profile/Sections/Historical/ProfileLineChart.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index 688930d76a..1ce14fa245 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -11,8 +11,8 @@ using osu.Framework.Utils; using osu.Framework.Allocation; using osu.Game.Graphics; using osu.Framework.Graphics.Shapes; -using static osu.Game.Users.User; using osuTK; +using static osu.Game.Users.User; namespace osu.Game.Overlays.Profile.Sections.Historical { From 5247ebaf53d32a830dc652915d2f6641afe60399 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 22 Nov 2020 18:30:51 +0900 Subject: [PATCH 29/66] Restore accidently removed comment --- osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs index dafde0b927..5fdb79cbbd 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs @@ -34,6 +34,10 @@ namespace osu.Game.Rulesets.Osu.Edit d.ApplyCustomUpdateState += updateState; } + /// + /// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay. + /// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points. + /// private const double editor_hit_object_fade_out_extension = 700; private void updateState(DrawableHitObject hitObject, ArmedState state) From c506b438bf465947fe0cff2f9bb25044f6bd35f9 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 22 Nov 2020 18:36:10 +0900 Subject: [PATCH 30/66] Remove more code and make some methods private --- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 4 ++-- .../Objects/Drawables/DrawableHitCircle.cs | 2 +- .../Objects/Drawables/DrawableSlider.cs | 2 +- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 5 +---- .../IDrawableHitObjectWithProxiedApproach.cs | 12 ------------ 5 files changed, 5 insertions(+), 20 deletions(-) delete mode 100644 osu.Game/Rulesets/Objects/Drawables/IDrawableHitObjectWithProxiedApproach.cs diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 7d8f18ee0b..abbdeacd9a 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -62,10 +62,10 @@ namespace osu.Game.Rulesets.Catch.UI protected override void OnNewDrawableHitObject(DrawableHitObject d) { - ((DrawableCatchHitObject)d).CheckPosition = CheckIfWeCanCatch; + ((DrawableCatchHitObject)d).CheckPosition = checkIfWeCanCatch; } - public bool CheckIfWeCanCatch(CatchHitObject obj) => CatcherArea.AttemptCatch(obj); + private bool checkIfWeCanCatch(CatchHitObject obj) => CatcherArea.AttemptCatch(obj); private void onNewResult(DrawableHitObject judgedObject, JudgementResult result) => CatcherArea.OnNewResult((DrawableCatchHitObject)judgedObject, result); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index d1ceca6d8f..abb51ae420 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -19,7 +19,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { - public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach + public class DrawableHitCircle : DrawableOsuHitObject { public OsuAction? HitAction => HitArea.HitAction; protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 14c494d909..6340367593 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -19,7 +19,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables { - public class DrawableSlider : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach + public class DrawableSlider : DrawableOsuHitObject { public new Slider HitObject => (Slider)base.HitObject; diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 93302c6046..8ff752952c 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -26,8 +26,6 @@ namespace osu.Game.Rulesets.Osu.UI { public class OsuPlayfield : Playfield { - public readonly Func CheckHittable; - private readonly PlayfieldBorder playfieldBorder; private readonly ProxyContainer approachCircles; private readonly ProxyContainer spinnerProxies; @@ -57,7 +55,6 @@ namespace osu.Game.Rulesets.Osu.UI }; hitPolicy = new OrderedHitPolicy(HitObjectContainer); - CheckHittable = hitPolicy.IsHittable; var hitWindows = new OsuHitWindows(); @@ -71,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.UI protected override void OnNewDrawableHitObject(DrawableHitObject drawable) { - ((DrawableOsuHitObject)drawable).CheckHittable = CheckHittable; + ((DrawableOsuHitObject)drawable).CheckHittable = hitPolicy.IsHittable; Debug.Assert(!drawable.IsLoaded, $"Already loaded {nameof(DrawableHitObject)} is added to {nameof(OsuPlayfield)}"); drawable.OnLoadComplete += onDrawableHitObjectLoaded; diff --git a/osu.Game/Rulesets/Objects/Drawables/IDrawableHitObjectWithProxiedApproach.cs b/osu.Game/Rulesets/Objects/Drawables/IDrawableHitObjectWithProxiedApproach.cs deleted file mode 100644 index 8f4c95c634..0000000000 --- a/osu.Game/Rulesets/Objects/Drawables/IDrawableHitObjectWithProxiedApproach.cs +++ /dev/null @@ -1,12 +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.Objects.Drawables -{ - public interface IDrawableHitObjectWithProxiedApproach - { - Drawable ProxiedLayer { get; } - } -} From 666112cb5a7faa7060c8131f9a20e87a4f24dd07 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Sun, 22 Nov 2020 18:47:35 +0900 Subject: [PATCH 31/66] Address @bdach's minor suggestions --- .../Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- osu.Game/Rulesets/UI/Playfield.cs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 84b2dd7957..6ed6c6412e 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// Whether the initialization logic in has applied. /// - internal bool HasInitialized; + internal bool IsInitialized; /// /// Creates a new . diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index fcdd5ff53d..2f589f4ce9 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -120,8 +120,8 @@ namespace osu.Game.Rulesets.UI OnNewDrawableHitObject(d); - Debug.Assert(!d.HasInitialized); - d.HasInitialized = true; + Debug.Assert(!d.IsInitialized); + d.IsInitialized = true; } /// @@ -135,8 +135,8 @@ namespace osu.Game.Rulesets.UI /// The DrawableHitObject to add. public virtual void Add(DrawableHitObject h) { - if (h.HasInitialized) - throw new InvalidOperationException($"{nameof(Playfield.Add)} doesn't support {nameof(DrawableHitObject)} reuse. Use pooling instead."); + if (h.IsInitialized) + throw new InvalidOperationException($"{nameof(Add)} doesn't support {nameof(DrawableHitObject)} reuse. Use pooling instead."); onNewDrawableHitObject(h); @@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.UI /// /// Invoked before a new is added to this . - /// It is invoked only once even the drawable is pooled and used multiple times for different s. + /// It is invoked only once even if the drawable is pooled and used multiple times for different s. /// /// /// This is also invoked for nested s. @@ -348,7 +348,7 @@ namespace osu.Game.Rulesets.UI { var dho = (DrawableHitObject)d; - if (!dho.HasInitialized) + if (!dho.IsInitialized) { onNewDrawableHitObject(dho); From 3ed78688012d711fd374f5350b742625cda6f192 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 23 Nov 2020 13:49:14 +0900 Subject: [PATCH 32/66] Scroll editor setup screen to file selector on display Previously the file selector would potentially display off-screen, making for confusing UX. Closes #10942. --- osu.Game/Graphics/Containers/SectionsContainer.cs | 2 ++ .../Edit/Setup/FileChooserLabelledTextBox.cs | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index f32f8e0c67..81968de304 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using JetBrains.Annotations; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -14,6 +15,7 @@ namespace osu.Game.Graphics.Containers /// /// A container that can scroll to each section inside it. /// + [Cached] public class SectionsContainer : Container where T : Drawable { diff --git a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs index b802b3405a..5de6842b50 100644 --- a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs +++ b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs @@ -3,10 +3,12 @@ using System; using System.IO; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; @@ -21,6 +23,9 @@ namespace osu.Game.Screens.Edit.Setup private readonly IBindable currentFile = new Bindable(); + [Resolved] + private SectionsContainer sectionsContainer { get; set; } + public FileChooserLabelledTextBox() { currentFile.BindValueChanged(onFileSelected); @@ -47,14 +52,16 @@ namespace osu.Game.Screens.Edit.Setup public void DisplayFileChooser() { - Target.Child = new FileSelector(validFileExtensions: ResourcesSection.AudioExtensions) + FileSelector fileSelector; + + Target.Child = fileSelector = new FileSelector(validFileExtensions: ResourcesSection.AudioExtensions) { RelativeSizeAxes = Axes.X, Height = 400, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, CurrentFile = { BindTarget = currentFile } }; + + sectionsContainer?.ScrollTo(fileSelector); } internal class FileChooserOsuTextBox : OsuTextBox From 1b33d3003924daed2d2995a4889725728fce4d46 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 23 Nov 2020 08:52:29 +0300 Subject: [PATCH 33/66] Simplify horizontal ticks creation --- .../Sections/Historical/ProfileLineChart.cs | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index 1ce14fa245..7dbcb9ba16 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -115,9 +115,11 @@ namespace osu.Game.Overlays.Profile.Sections.Historical var min = values.Select(v => v.Count).Min(); var max = values.Select(v => v.Count).Max(); - var tick = getTick(getRange(max - min), 6); + var tick = getTick(max - min, 6); - bool tickIsDecimal = tick < 1.0; + // Prevent infinite loop in case if tick is zero + if (tick == 0) + tick = 1; double rollingRow = 0; @@ -126,7 +128,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical if (rollingRow >= min) { var y = -Interpolation.ValueAt(rollingRow, 0, 1f, min, max); - addRowTick(y, rollingRow, tickIsDecimal); + addRowTick(y, rollingRow); } rollingRow += tick; @@ -156,7 +158,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical } } - private void addRowTick(float y, double value, bool tickIsDecimal) + private void addRowTick(float y, double value) { rowTicksContainer.Add(new TickText { @@ -164,7 +166,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Origin = Anchor.CentreRight, RelativePositionAxes = Axes.Y, Margin = new MarginPadding { Right = 3 }, - Text = tickIsDecimal ? value.ToString("F1") : value.ToString("N0"), + Text = value.ToString("N0"), Font = OsuFont.GetFont(size: 12), Y = y }); @@ -204,28 +206,9 @@ namespace osu.Game.Overlays.Profile.Sections.Historical }); } - private double getRange(double initialRange) + private long getTick(long range, int maxTicksCount) { - var exponent = Math.Floor(Math.Log10(initialRange)); - var fraction = initialRange / Math.Pow(10, exponent); - - double niceFraction; - - if (fraction <= 1.0) - niceFraction = 1.0; - else if (fraction <= 2.0) - niceFraction = 2.0; - else if (fraction <= 5.0) - niceFraction = 5.0; - else - niceFraction = 10.0; - - return niceFraction * Math.Pow(10, exponent); - } - - private double getTick(double range, int maxTicksCount) - { - var value = range / (maxTicksCount - 1); + var value = (float)range / (maxTicksCount - 1); var exponent = Math.Floor(Math.Log10(value)); var fraction = value / Math.Pow(10, exponent); @@ -241,7 +224,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical else niceFraction = 10.0; - return niceFraction * Math.Pow(10, exponent); + return (long)(niceFraction * Math.Pow(10, exponent)); } private class TickText : OsuSpriteText From 3c0ee7de9b12f17924e1b94e3bdc9d58f9e6850d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 23 Nov 2020 09:51:50 +0300 Subject: [PATCH 34/66] Add proper tests --- .../Online/TestSceneChartProfileSubsection.cs | 145 ++++++++++++++++++ .../Online/TestSceneProfileLineChart.cs | 55 ------- 2 files changed, 145 insertions(+), 55 deletions(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneChartProfileSubsection.cs delete mode 100644 osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneChartProfileSubsection.cs b/osu.Game.Tests/Visual/Online/TestSceneChartProfileSubsection.cs new file mode 100644 index 0000000000..4983dfba12 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneChartProfileSubsection.cs @@ -0,0 +1,145 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Overlays.Profile.Sections.Historical; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Users; +using NUnit.Framework; +using osu.Game.Overlays; +using osu.Framework.Allocation; +using System; +using System.Linq; +using osu.Framework.Testing; +using osu.Framework.Graphics.Shapes; +using static osu.Game.Users.User; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneChartProfileSubsection : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Red); + + private readonly Bindable user = new Bindable(); + private readonly PlayHistorySubsection section; + + public TestSceneChartProfileSubsection() + { + AddRange(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4 + }, + section = new PlayHistorySubsection(user) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + } + }); + } + + [Test] + public void TestNullValues() + { + AddStep("Load user", () => user.Value = user_with_null_values); + AddAssert("Section is hidden", () => section.Alpha == 0); + } + + [Test] + public void TestEmptyValues() + { + AddStep("Load user", () => user.Value = user_with_empty_values); + AddAssert("Section is hidden", () => section.Alpha == 0); + } + + [Test] + public void TestOveValue() + { + AddStep("Load user", () => user.Value = user_with_one_value); + AddAssert("Section is hidden", () => section.Alpha == 0); + } + + [Test] + public void TestTwoValues() + { + AddStep("Load user", () => user.Value = user_with_two_values); + AddAssert("Section is visible", () => section.Alpha == 1); + } + + [Test] + public void TestFilledValues() + { + AddStep("Load user", () => user.Value = user_with_filled_values); + AddAssert("Section is visible", () => section.Alpha == 1); + AddAssert("Array length is the same", () => user_with_filled_values.MonthlyPlaycounts.Length == getChartValuesLength()); + } + + [Test] + public void TestMissingValues() + { + AddStep("Load user", () => user.Value = user_with_missing_values); + AddAssert("Section is visible", () => section.Alpha == 1); + AddAssert("Array length is 7", () => getChartValuesLength() == 7); + } + + private int getChartValuesLength() => this.ChildrenOfType().Single().Values.Length; + + private static readonly User user_with_null_values = new User + { + Id = 1 + }; + + private static readonly User user_with_empty_values = new User + { + Id = 2, + MonthlyPlaycounts = Array.Empty() + }; + + private static readonly User user_with_one_value = new User + { + Id = 3, + MonthlyPlaycounts = new[] + { + new UserHistoryCount { Date = new DateTime(2010, 5, 1), Count = 100 } + } + }; + + private static readonly User user_with_two_values = new User + { + Id = 4, + MonthlyPlaycounts = new[] + { + new UserHistoryCount { Date = new DateTime(2010, 5, 1), Count = 1 }, + new UserHistoryCount { Date = new DateTime(2010, 6, 1), Count = 2 } + } + }; + + private static readonly User user_with_filled_values = new User + { + Id = 5, + MonthlyPlaycounts = new[] + { + new UserHistoryCount { Date = new DateTime(2010, 5, 1), Count = 1000 }, + new UserHistoryCount { Date = new DateTime(2010, 6, 1), Count = 20 }, + new UserHistoryCount { Date = new DateTime(2010, 7, 1), Count = 20000 }, + new UserHistoryCount { Date = new DateTime(2010, 8, 1), Count = 30 }, + new UserHistoryCount { Date = new DateTime(2010, 9, 1), Count = 50 }, + new UserHistoryCount { Date = new DateTime(2010, 10, 1), Count = 2000 }, + new UserHistoryCount { Date = new DateTime(2010, 11, 1), Count = 2100 } + } + }; + + private static readonly User user_with_missing_values = new User + { + Id = 6, + MonthlyPlaycounts = new[] + { + new UserHistoryCount { Date = new DateTime(2020, 1, 1), Count = 100 }, + new UserHistoryCount { Date = new DateTime(2020, 7, 1), Count = 200 } + } + }; + } +} diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs deleted file mode 100644 index 3d342b0d76..0000000000 --- a/osu.Game.Tests/Visual/Online/TestSceneProfileLineChart.cs +++ /dev/null @@ -1,55 +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.Game.Overlays.Profile.Sections.Historical; -using osu.Framework.Graphics; -using System; -using osu.Game.Overlays; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Containers; -using static osu.Game.Users.User; - -namespace osu.Game.Tests.Visual.Online -{ - public class TestSceneProfileLineChart : OsuTestScene - { - [Cached] - private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); - - public TestSceneProfileLineChart() - { - var values = new[] - { - new UserHistoryCount { Date = new DateTime(2010, 5, 1), Count = 1000 }, - new UserHistoryCount { Date = new DateTime(2010, 6, 1), Count = 20 }, - new UserHistoryCount { Date = new DateTime(2010, 7, 1), Count = 20000 }, - new UserHistoryCount { Date = new DateTime(2010, 8, 1), Count = 30 }, - new UserHistoryCount { Date = new DateTime(2010, 9, 1), Count = 50 }, - new UserHistoryCount { Date = new DateTime(2010, 10, 1), Count = 2000 }, - new UserHistoryCount { Date = new DateTime(2010, 11, 1), Count = 2100 } - }; - - AddRange(new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background4 - }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Padding = new MarginPadding { Horizontal = 50 }, - Child = new ProfileLineChart - { - Values = values - } - } - }); - } - } -} From 087ea9c9a5366c3848a56ea479259065913929d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 23 Nov 2020 20:51:38 +0100 Subject: [PATCH 35/66] Fix typo in test name --- osu.Game.Tests/Visual/Online/TestSceneChartProfileSubsection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChartProfileSubsection.cs b/osu.Game.Tests/Visual/Online/TestSceneChartProfileSubsection.cs index 4983dfba12..0c8ee5e0c5 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChartProfileSubsection.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChartProfileSubsection.cs @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.Online } [Test] - public void TestOveValue() + public void TestOneValue() { AddStep("Load user", () => user.Value = user_with_one_value); AddAssert("Section is hidden", () => section.Alpha == 0); From 20f1775ddb11ff2e88c22475788b5784892f0621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 23 Nov 2020 20:59:04 +0100 Subject: [PATCH 36/66] Rename test scene to match tested class --- ...ProfileSubsection.cs => TestScenePlayHistorySubsection.cs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename osu.Game.Tests/Visual/Online/{TestSceneChartProfileSubsection.cs => TestScenePlayHistorySubsection.cs} (97%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChartProfileSubsection.cs b/osu.Game.Tests/Visual/Online/TestScenePlayHistorySubsection.cs similarity index 97% rename from osu.Game.Tests/Visual/Online/TestSceneChartProfileSubsection.cs rename to osu.Game.Tests/Visual/Online/TestScenePlayHistorySubsection.cs index 0c8ee5e0c5..c9dbc9dc24 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChartProfileSubsection.cs +++ b/osu.Game.Tests/Visual/Online/TestScenePlayHistorySubsection.cs @@ -16,7 +16,7 @@ using static osu.Game.Users.User; namespace osu.Game.Tests.Visual.Online { - public class TestSceneChartProfileSubsection : OsuTestScene + public class TestScenePlayHistorySubsection : OsuTestScene { [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Red); @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Online private readonly Bindable user = new Bindable(); private readonly PlayHistorySubsection section; - public TestSceneChartProfileSubsection() + public TestScenePlayHistorySubsection() { AddRange(new Drawable[] { From e9ffeb8b5d1471e59e91cce116e316e54ffe3db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 23 Nov 2020 21:09:42 +0100 Subject: [PATCH 37/66] Make missing date check more robust --- .../Profile/Sections/Historical/ChartProfileSubsection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs index 783ecec190..3fd334005f 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs @@ -76,7 +76,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical static bool hasMissingDates(UserHistoryCount prev, UserHistoryCount current) { var possibleCurrent = prev.Date.AddMonths(1); - return possibleCurrent != current.Date; + return possibleCurrent < current.Date; } } From bb5aa9a9c914d2fa0bd57431dfa7d737b00831ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 23 Nov 2020 21:24:37 +0100 Subject: [PATCH 38/66] Guard against empty values early --- .../Profile/Sections/Historical/ProfileLineChart.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index 7dbcb9ba16..af2ef5a75c 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -26,8 +26,10 @@ namespace osu.Game.Overlays.Profile.Sections.Historical get => values; set { - values = value; - graph.Values = values; + if (value.Length == 0) + throw new ArgumentException("At least one value expected!", nameof(value)); + + graph.Values = values = value; createRowTicks(); createColumnTicks(); From 7b0d3dfe0cee8696f7251465ba63a7755d161ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 23 Nov 2020 21:38:04 +0100 Subject: [PATCH 39/66] Refactor tick calculation code for readability --- .../Sections/Historical/ProfileLineChart.cs | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index af2ef5a75c..ecc85fb48d 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -117,7 +117,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical var min = values.Select(v => v.Count).Min(); var max = values.Select(v => v.Count).Max(); - var tick = getTick(max - min, 6); + var tick = getTickInterval(max - min, 6); // Prevent infinite loop in case if tick is zero if (tick == 0) @@ -208,25 +208,32 @@ namespace osu.Game.Overlays.Profile.Sections.Historical }); } - private long getTick(long range, int maxTicksCount) + private long getTickInterval(long range, int maxTicksCount) { - var value = (float)range / (maxTicksCount - 1); + // this interval is what would be achieved if the interval was divided perfectly evenly into maxTicksCount ticks. + // can contain ugly fractional parts. + var exactTickInterval = (float)range / (maxTicksCount - 1); - var exponent = Math.Floor(Math.Log10(value)); - var fraction = value / Math.Pow(10, exponent); + // the ideal ticks start with a 1, 2 or 5, and are multipliers of powers of 10. + // first off, use log10 to calculate the number of digits in the "exact" interval. + var numberOfDigits = Math.Floor(Math.Log10(exactTickInterval)); + var tickBase = Math.Pow(10, numberOfDigits); + // then see how the exact tick relates to the power of 10. + var exactTickMultiplier = exactTickInterval / tickBase; - double niceFraction; + double tickMultiplier; - if (fraction < 1.5) - niceFraction = 1.0; - else if (fraction < 3) - niceFraction = 2.0; - else if (fraction < 7) - niceFraction = 5.0; + // round up the fraction to start with a 1, 2 or 5. closest match wins. + if (exactTickMultiplier < 1.5) + tickMultiplier = 1.0; + else if (exactTickMultiplier < 3) + tickMultiplier = 2.0; + else if (exactTickMultiplier < 7) + tickMultiplier = 5.0; else - niceFraction = 10.0; + tickMultiplier = 10.0; - return (long)(niceFraction * Math.Pow(10, exponent)); + return Math.Max((long)(tickMultiplier * tickBase), 1); } private class TickText : OsuSpriteText From 8347ecf494f6824d1fe0ee36b6094e434cc31196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 23 Nov 2020 21:52:47 +0100 Subject: [PATCH 40/66] Simplify row tick creation code --- .../Sections/Historical/ProfileLineChart.cs | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index ecc85fb48d..c1eb0811fb 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -117,23 +117,15 @@ namespace osu.Game.Overlays.Profile.Sections.Historical var min = values.Select(v => v.Count).Min(); var max = values.Select(v => v.Count).Max(); - var tick = getTickInterval(max - min, 6); + var tickInterval = getTickInterval(max - min, 6); - // Prevent infinite loop in case if tick is zero - if (tick == 0) - tick = 1; - - double rollingRow = 0; - - while (rollingRow <= max) + for (long currentTick = 0; currentTick <= max; currentTick += tickInterval) { - if (rollingRow >= min) - { - var y = -Interpolation.ValueAt(rollingRow, 0, 1f, min, max); - addRowTick(y, rollingRow); - } + if (currentTick < min) + continue; - rollingRow += tick; + float y = -Interpolation.ValueAt(currentTick, 0, 1f, min, max); + addRowTick(y, currentTick); } } From 5701b32bae6c6d7dd5e4f7d5cf450415d2d5cc40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 23 Nov 2020 22:12:32 +0100 Subject: [PATCH 41/66] Handle constant graphs better --- .../Online/TestScenePlayHistorySubsection.cs | 40 ++++++++++++++++++- osu.Game/Graphics/UserInterface/LineGraph.cs | 6 ++- .../Sections/Historical/ProfileLineChart.cs | 12 +++++- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestScenePlayHistorySubsection.cs b/osu.Game.Tests/Visual/Online/TestScenePlayHistorySubsection.cs index c9dbc9dc24..cf5ecf5bf2 100644 --- a/osu.Game.Tests/Visual/Online/TestScenePlayHistorySubsection.cs +++ b/osu.Game.Tests/Visual/Online/TestScenePlayHistorySubsection.cs @@ -69,6 +69,20 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Section is visible", () => section.Alpha == 1); } + [Test] + public void TestConstantValues() + { + AddStep("Load user", () => user.Value = user_with_constant_values); + AddAssert("Section is visible", () => section.Alpha == 1); + } + + [Test] + public void TestConstantZeroValues() + { + AddStep("Load user", () => user.Value = user_with_zero_values); + AddAssert("Section is visible", () => section.Alpha == 1); + } + [Test] public void TestFilledValues() { @@ -117,10 +131,32 @@ namespace osu.Game.Tests.Visual.Online } }; - private static readonly User user_with_filled_values = new User + private static readonly User user_with_constant_values = new User { Id = 5, MonthlyPlaycounts = new[] + { + new UserHistoryCount { Date = new DateTime(2010, 5, 1), Count = 5 }, + new UserHistoryCount { Date = new DateTime(2010, 6, 1), Count = 5 }, + new UserHistoryCount { Date = new DateTime(2010, 7, 1), Count = 5 } + } + }; + + private static readonly User user_with_zero_values = new User + { + Id = 6, + MonthlyPlaycounts = new[] + { + new UserHistoryCount { Date = new DateTime(2010, 5, 1), Count = 0 }, + new UserHistoryCount { Date = new DateTime(2010, 6, 1), Count = 0 }, + new UserHistoryCount { Date = new DateTime(2010, 7, 1), Count = 0 } + } + }; + + private static readonly User user_with_filled_values = new User + { + Id = 7, + MonthlyPlaycounts = new[] { new UserHistoryCount { Date = new DateTime(2010, 5, 1), Count = 1000 }, new UserHistoryCount { Date = new DateTime(2010, 6, 1), Count = 20 }, @@ -134,7 +170,7 @@ namespace osu.Game.Tests.Visual.Online private static readonly User user_with_missing_values = new User { - Id = 6, + Id = 8, MonthlyPlaycounts = new[] { new UserHistoryCount { Date = new DateTime(2020, 1, 1), Count = 100 }, diff --git a/osu.Game/Graphics/UserInterface/LineGraph.cs b/osu.Game/Graphics/UserInterface/LineGraph.cs index 42b523fc5c..70db26c817 100644 --- a/osu.Game/Graphics/UserInterface/LineGraph.cs +++ b/osu.Game/Graphics/UserInterface/LineGraph.cs @@ -119,7 +119,11 @@ namespace osu.Game.Graphics.UserInterface protected float GetYPosition(float value) { - if (ActualMaxValue == ActualMinValue) return 0; + if (ActualMaxValue == ActualMinValue) + // show line at top if the only value on the graph is positive, + // and at bottom if the only value on the graph is zero or negative. + // just kind of makes most sense intuitively. + return value > 1 ? 0 : 1; return (ActualMaxValue - value) / (ActualMaxValue - ActualMinValue); } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index c1eb0811fb..989871745d 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -124,8 +124,16 @@ namespace osu.Game.Overlays.Profile.Sections.Historical if (currentTick < min) continue; - float y = -Interpolation.ValueAt(currentTick, 0, 1f, min, max); - addRowTick(y, currentTick); + float y; + // special-case the min == max case to match LineGraph. + // lerp isn't really well-defined over a zero interval anyway. + if (min == max) + y = currentTick > 1 ? 1 : 0; + else + y = Interpolation.ValueAt(currentTick, 0, 1f, min, max); + + // y axis is inverted in graph-like coordinates. + addRowTick(-y, currentTick); } } From 52f5473cc043eb4ed12f3e9e395c51eff9a1a57a Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 23 Nov 2020 15:13:58 -0800 Subject: [PATCH 42/66] Set global action as a parameter in toast --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- osu.Game/Input/KeyBindingStore.cs | 2 +- .../Overlays/Music/MusicKeyBindingHandler.cs | 16 ++++----- osu.Game/Overlays/OSD/Toast.cs | 33 +++++++++++++++++-- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 89a6ee8b07..44ac48d83a 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -196,7 +196,7 @@ namespace osu.Game.Configuration public Func LookupSkinName { private get; set; } - public Func LookupKeyBindings { get; set; } + public Func LookupKeyBindings { get; set; } } public enum OsuSetting diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index bc73d74d74..f4b7c873d5 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -37,7 +37,7 @@ namespace osu.Game.Input /// /// The action to lookup. /// A set of display strings for all the user's key configuration for the action. - public IEnumerable GetReadableKeyCombinationsFor(GlobalAction globalAction) + public IEnumerable GetReadableKeyCombinationsFor(GlobalAction? globalAction) { foreach (var action in Query().Where(b => (GlobalAction)b.Action == globalAction)) { diff --git a/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs b/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs index 0d6158d46f..fa8180b7c0 100644 --- a/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs +++ b/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs @@ -6,7 +6,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; -using osu.Game.Configuration; using osu.Game.Input.Bindings; using osu.Game.Overlays.OSD; @@ -26,9 +25,6 @@ namespace osu.Game.Overlays.Music [Resolved(canBeNull: true)] private OnScreenDisplay onScreenDisplay { get; set; } - [Resolved] - private OsuConfigManager config { get; set; } - public bool OnPressed(GlobalAction action) { if (beatmap.Disabled) @@ -41,11 +37,11 @@ namespace osu.Game.Overlays.Music bool wasPlaying = musicController.IsPlaying; if (musicController.TogglePause()) - onScreenDisplay?.Display(new MusicActionToast(wasPlaying ? "Pause track" : "Play track", config.LookupKeyBindings(action))); + onScreenDisplay?.Display(new MusicActionToast(wasPlaying ? "Pause track" : "Play track", action)); return true; case GlobalAction.MusicNext: - musicController.NextTrack(() => onScreenDisplay?.Display(new MusicActionToast("Next track", config.LookupKeyBindings(action)))); + musicController.NextTrack(() => onScreenDisplay?.Display(new MusicActionToast("Next track", action))); return true; @@ -55,11 +51,11 @@ namespace osu.Game.Overlays.Music switch (res) { case PreviousTrackResult.Restart: - onScreenDisplay?.Display(new MusicActionToast("Restart track", config.LookupKeyBindings(action))); + onScreenDisplay?.Display(new MusicActionToast("Restart track", action)); break; case PreviousTrackResult.Previous: - onScreenDisplay?.Display(new MusicActionToast("Previous track", config.LookupKeyBindings(action))); + onScreenDisplay?.Display(new MusicActionToast("Previous track", action)); break; } }); @@ -76,8 +72,8 @@ namespace osu.Game.Overlays.Music private class MusicActionToast : Toast { - public MusicActionToast(string action, string shortcut) - : base("Music Playback", action, shortcut) + public MusicActionToast(string value, GlobalAction action) + : base("Music Playback", value, action: action) { } } diff --git a/osu.Game/Overlays/OSD/Toast.cs b/osu.Game/Overlays/OSD/Toast.cs index 1497ca8fa8..d32d63055f 100644 --- a/osu.Game/Overlays/OSD/Toast.cs +++ b/osu.Game/Overlays/OSD/Toast.cs @@ -1,11 +1,14 @@ // 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.Graphics.Shapes; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; @@ -16,12 +19,22 @@ namespace osu.Game.Overlays.OSD private const int toast_minimum_width = 240; private readonly Container content; + + private readonly OsuSpriteText spriteText; + + private readonly string shortcut; + + private readonly GlobalAction? action; + protected override Container Content => content; protected readonly OsuSpriteText ValueText; - protected Toast(string description, string value, string shortcut) + protected Toast(string description, string value, string shortcut = null, GlobalAction? action = null) { + this.shortcut = shortcut; + this.action = action; + Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -68,7 +81,7 @@ namespace osu.Game.Overlays.OSD Origin = Anchor.Centre, Text = value }, - new OsuSpriteText + spriteText = new OsuSpriteText { Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, @@ -76,9 +89,23 @@ namespace osu.Game.Overlays.OSD Alpha = 0.3f, Margin = new MarginPadding { Bottom = 15 }, Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), - Text = string.IsNullOrEmpty(shortcut) ? "NO KEY BOUND" : shortcut.ToUpperInvariant() }, }; } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + string text; + + if (action != null) + text = config.LookupKeyBindings(action); + else if (!string.IsNullOrEmpty(shortcut)) + text = shortcut; + else + text = "no key bound"; + + spriteText.Text = text.ToUpperInvariant(); + } } } From 44ca67c534ab40635f632e8b4fb6b2b1e124f4bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Nov 2020 13:10:11 +0900 Subject: [PATCH 43/66] Simplify fill logic and add xmldoc --- .../Historical/ChartProfileSubsection.cs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs index 3fd334005f..885b12ca6d 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -52,32 +53,30 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Hide(); } - private UserHistoryCount[] fillZeroValues(UserHistoryCount[] values) + /// + /// Add entries for any missing months (filled with zero values). + /// + private UserHistoryCount[] fillZeroValues(UserHistoryCount[] historyEntries) { - var newValues = new List { values[0] }; - var newLast = values[0]; + var filledHistoryEntries = new List(); - for (int i = 1; i < values.Length; i++) + foreach (var entry in historyEntries) { - while (hasMissingDates(newLast, values[i])) + var lastFilled = filledHistoryEntries.LastOrDefault(); + + while (lastFilled?.Date.AddMonths(1) < entry.Date) { - newValues.Add(newLast = new UserHistoryCount + filledHistoryEntries.Add(lastFilled = new UserHistoryCount { Count = 0, - Date = newLast.Date.AddMonths(1) + Date = lastFilled.Date.AddMonths(1) }); } - newValues.Add(newLast = values[i]); + filledHistoryEntries.Add(entry); } - return newValues.ToArray(); - - static bool hasMissingDates(UserHistoryCount prev, UserHistoryCount current) - { - var possibleCurrent = prev.Date.AddMonths(1); - return possibleCurrent < current.Date; - } + return filledHistoryEntries.ToArray(); } protected abstract UserHistoryCount[] GetValues(User user); From 82640418ba1e140d7d4d4d109a15a29e155fe24f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Nov 2020 13:12:04 +0900 Subject: [PATCH 44/66] Invert hide logic for readability --- .../Profile/Sections/Historical/ChartProfileSubsection.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs index 885b12ca6d..b82773155d 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs @@ -43,14 +43,14 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { var values = GetValues(e.NewValue); - if (values?.Length > 1) + if (values == null || values.Length <= 1) { - chart.Values = fillZeroValues(values); - Show(); + Hide(); return; } - Hide(); + chart.Values = fillZeroValues(values); + Show(); } /// From e36b1051c18ba0fc4d2210cea84b269c4547e2b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Nov 2020 13:15:59 +0900 Subject: [PATCH 45/66] Add spacing between inline comments --- .../Overlays/Profile/Sections/Historical/ProfileLineChart.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index 989871745d..f02aa36b6c 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -125,6 +125,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical continue; float y; + // special-case the min == max case to match LineGraph. // lerp isn't really well-defined over a zero interval anyway. if (min == max) @@ -218,6 +219,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical // first off, use log10 to calculate the number of digits in the "exact" interval. var numberOfDigits = Math.Floor(Math.Log10(exactTickInterval)); var tickBase = Math.Pow(10, numberOfDigits); + // then see how the exact tick relates to the power of 10. var exactTickMultiplier = exactTickInterval / tickBase; From 1fd4b04767bffde0750165ac1064ef13376bfacb Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 23 Nov 2020 20:43:46 -0800 Subject: [PATCH 46/66] Just set music shortcut text locally --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- osu.Game/Input/KeyBindingStore.cs | 2 +- .../Overlays/Music/MusicKeyBindingHandler.cs | 12 ++++++- osu.Game/Overlays/OSD/Toast.cs | 34 +++---------------- 4 files changed, 18 insertions(+), 32 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 44ac48d83a..89a6ee8b07 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -196,7 +196,7 @@ namespace osu.Game.Configuration public Func LookupSkinName { private get; set; } - public Func LookupKeyBindings { get; set; } + public Func LookupKeyBindings { get; set; } } public enum OsuSetting diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index f4b7c873d5..bc73d74d74 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -37,7 +37,7 @@ namespace osu.Game.Input /// /// The action to lookup. /// A set of display strings for all the user's key configuration for the action. - public IEnumerable GetReadableKeyCombinationsFor(GlobalAction? globalAction) + public IEnumerable GetReadableKeyCombinationsFor(GlobalAction globalAction) { foreach (var action in Query().Where(b => (GlobalAction)b.Action == globalAction)) { diff --git a/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs b/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs index fa8180b7c0..f06e02e5e1 100644 --- a/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs +++ b/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Input.Bindings; using osu.Game.Overlays.OSD; @@ -72,9 +73,18 @@ namespace osu.Game.Overlays.Music private class MusicActionToast : Toast { + private readonly GlobalAction action; + public MusicActionToast(string value, GlobalAction action) - : base("Music Playback", value, action: action) + : base("Music Playback", value, string.Empty) { + this.action = action; + } + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + ShortcutText.Text = config.LookupKeyBindings(action).ToUpperInvariant(); } } } diff --git a/osu.Game/Overlays/OSD/Toast.cs b/osu.Game/Overlays/OSD/Toast.cs index d32d63055f..4a6316df3f 100644 --- a/osu.Game/Overlays/OSD/Toast.cs +++ b/osu.Game/Overlays/OSD/Toast.cs @@ -1,14 +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.Graphics.Shapes; -using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; @@ -20,21 +17,14 @@ namespace osu.Game.Overlays.OSD private readonly Container content; - private readonly OsuSpriteText spriteText; - - private readonly string shortcut; - - private readonly GlobalAction? action; - protected override Container Content => content; protected readonly OsuSpriteText ValueText; - protected Toast(string description, string value, string shortcut = null, GlobalAction? action = null) - { - this.shortcut = shortcut; - this.action = action; + protected readonly OsuSpriteText ShortcutText; + protected Toast(string description, string value, string shortcut) + { Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -81,7 +71,7 @@ namespace osu.Game.Overlays.OSD Origin = Anchor.Centre, Text = value }, - spriteText = new OsuSpriteText + ShortcutText = new OsuSpriteText { Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, @@ -89,23 +79,9 @@ namespace osu.Game.Overlays.OSD Alpha = 0.3f, Margin = new MarginPadding { Bottom = 15 }, Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), + Text = string.IsNullOrEmpty(shortcut) ? "NO KEY BOUND" : shortcut.ToUpperInvariant() }, }; } - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - string text; - - if (action != null) - text = config.LookupKeyBindings(action); - else if (!string.IsNullOrEmpty(shortcut)) - text = shortcut; - else - text = "no key bound"; - - spriteText.Text = text.ToUpperInvariant(); - } } } From bd1dad5477f46c041b176d6644714e5f7f126291 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Nov 2020 15:54:27 +0900 Subject: [PATCH 47/66] Remove null allowance for now --- osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs index 5de6842b50..6e2737256a 100644 --- a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs +++ b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs @@ -61,7 +61,7 @@ namespace osu.Game.Screens.Edit.Setup CurrentFile = { BindTarget = currentFile } }; - sectionsContainer?.ScrollTo(fileSelector); + sectionsContainer.ScrollTo(fileSelector); } internal class FileChooserOsuTextBox : OsuTextBox From b9c1f782fa00b52beb0b29772ec9767d45b3e2db Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 24 Nov 2020 17:03:26 +0900 Subject: [PATCH 48/66] Remove type parameter from DrawableCatchHitObject --- .../Objects/Drawables/DrawableBananaShower.cs | 2 +- .../Drawables/DrawableCatchHitObject.cs | 19 +++---------------- .../Objects/Drawables/DrawableDroplet.cs | 4 ++-- .../Objects/Drawables/DrawableFruit.cs | 4 ++-- .../Objects/Drawables/DrawableJuiceStream.cs | 2 +- 5 files changed, 9 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs index 4ce80aceb8..2215e1d983 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs @@ -9,7 +9,7 @@ using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableBananaShower : DrawableCatchHitObject + public class DrawableBananaShower : DrawableCatchHitObject { private readonly Func> createDrawableRepresentation; private readonly Container bananaContainer; diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs index 7922510a49..07f1f79243 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs @@ -13,12 +13,11 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public abstract class PalpableDrawableCatchHitObject : DrawableCatchHitObject - where TObject : PalpableCatchHitObject + public abstract class PalpableDrawableCatchHitObject : DrawableCatchHitObject { protected Container ScaleContainer { get; private set; } - protected PalpableDrawableCatchHitObject(TObject hitObject) + protected PalpableDrawableCatchHitObject(CatchHitObject hitObject) : base(hitObject) { Origin = Anchor.Centre; @@ -46,19 +45,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables comboColours[(HitObject.IndexInBeatmap + 1) % comboColours.Count]; } - public abstract class DrawableCatchHitObject : DrawableCatchHitObject - where TObject : CatchHitObject - { - public new TObject HitObject; - - protected DrawableCatchHitObject(TObject hitObject) - : base(hitObject) - { - HitObject = hitObject; - Anchor = Anchor.BottomLeft; - } - } - public abstract class DrawableCatchHitObject : DrawableHitObject { protected override double InitialLifetimeOffset => HitObject.TimePreempt; @@ -73,6 +59,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables : base(hitObject) { X = hitObject.X; + Anchor = Anchor.BottomLeft; } public Func CheckPosition; diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs index 688240fd86..9db64eba6e 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs @@ -8,11 +8,11 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableDroplet : PalpableDrawableCatchHitObject + public class DrawableDroplet : PalpableDrawableCatchHitObject { public override bool StaysOnPlate => false; - public DrawableDroplet(Droplet h) + public DrawableDroplet(CatchHitObject h) : base(h) { } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index c1c34e4157..f87c8866b1 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -8,9 +8,9 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableFruit : PalpableDrawableCatchHitObject + public class DrawableFruit : PalpableDrawableCatchHitObject { - public DrawableFruit(Fruit h) + public DrawableFruit(CatchHitObject h) : base(h) { } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs index 7bc016d94f..af4a269404 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs @@ -10,7 +10,7 @@ using osuTK; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableJuiceStream : DrawableCatchHitObject + public class DrawableJuiceStream : DrawableCatchHitObject { private readonly Func> createDrawableRepresentation; private readonly Container dropletContainer; From c9a41f9dae9ece4d7de354f1137b521d02d8add0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Nov 2020 17:14:39 +0900 Subject: [PATCH 49/66] Make all objects in selection candidates for spatial snapping Closes #10898. --- .../Editor/TestSceneManiaBeatSnapGrid.cs | 5 +++ .../Editor/TestSceneOsuDistanceSnapGrid.cs | 3 ++ .../Edit/OsuHitObjectComposer.cs | 11 +++++- .../Editing/TestSceneDistanceSnapGrid.cs | 3 ++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 3 ++ .../Rulesets/Edit/IPositionSnapProvider.cs | 12 ++++++- .../Compose/Components/BlueprintContainer.cs | 34 ++++++++++++++----- .../Compose/Components/Timeline/Timeline.cs | 3 ++ 8 files changed, 64 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs index 654b752001..538a51db5f 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaBeatSnapGrid.cs @@ -96,6 +96,11 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor throw new System.NotImplementedException(); } + public override SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) + { + throw new System.NotImplementedException(); + } + public override float GetBeatSnapDistanceAt(double referenceTime) { throw new System.NotImplementedException(); diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index 1232369a0b..9af2a99470 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -174,6 +174,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private class SnapProvider : IPositionSnapProvider { + public SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) => + new SnapResult(screenSpacePosition, null); + public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); public float GetBeatSnapDistanceAt(double referenceTime) => (float)beat_length; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index bfa8ab4431..0490e8b8ce 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -105,11 +105,20 @@ namespace osu.Game.Rulesets.Osu.Edit } } - public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) + public override SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) { if (snapToVisibleBlueprints(screenSpacePosition, out var snapResult)) return snapResult; + return new SnapResult(screenSpacePosition, null); + } + + public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) + { + var positionSnap = SnapScreenSpacePositionToValidPosition(screenSpacePosition); + if (positionSnap.ScreenSpacePosition != screenSpacePosition) + return positionSnap; + // will be null if distance snap is disabled or not feasible for the current time value. if (distanceSnapGrid == null) return base.SnapScreenSpacePositionToValidTime(screenSpacePosition); diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index 8190cf5f89..11830ebe35 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -153,6 +153,9 @@ namespace osu.Game.Tests.Visual.Editing private class SnapProvider : IPositionSnapProvider { + public SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) => + new SnapResult(screenSpacePosition, null); + public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, 0); public float GetBeatSnapDistanceAt(double referenceTime) => 10; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index b90aa6863a..35852f60ea 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -442,6 +442,9 @@ namespace osu.Game.Rulesets.Edit public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); + public virtual SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) => + new SnapResult(screenSpacePosition, null); + public abstract float GetBeatSnapDistanceAt(double referenceTime); public abstract float DurationToDistance(double referenceTime, double duration); diff --git a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs index cce631464f..4664f3808c 100644 --- a/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/IPositionSnapProvider.cs @@ -8,12 +8,22 @@ namespace osu.Game.Rulesets.Edit public interface IPositionSnapProvider { /// - /// Given a position, find a valid time snap. + /// Given a position, find a valid time and position snap. /// + /// + /// This call should be equivalent to running with any additional logic that can be performed without the time immutability restriction. + /// /// The screen-space position to be snapped. /// The time and position post-snapping. SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition); + /// + /// Given a position, find a value position snap, restricting time to its input value. + /// + /// The screen-space position to be snapped. + /// The position post-snapping. Time will always be null. + SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition); + /// /// Retrieves the distance between two points within a timing point that are one beat length apart. /// diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index df9cadebfc..e9f5238980 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -424,7 +424,7 @@ namespace osu.Game.Screens.Edit.Compose.Components #region Selection Movement - private Vector2? movementBlueprintOriginalPosition; + private Vector2[] movementBlueprintOriginalPositions; private SelectionBlueprint movementBlueprint; private bool isDraggingBlueprint; @@ -442,8 +442,9 @@ namespace osu.Game.Screens.Edit.Compose.Components return; // Movement is tracked from the blueprint of the earliest hitobject, since it only makes sense to distance snap from that hitobject - movementBlueprint = SelectionHandler.SelectedBlueprints.OrderBy(b => b.HitObject.StartTime).First(); - movementBlueprintOriginalPosition = movementBlueprint.ScreenSpaceSelectionPoint; // todo: unsure if correct + var orderedSelection = SelectionHandler.SelectedBlueprints.OrderBy(b => b.HitObject.StartTime); + movementBlueprint = orderedSelection.First(); + movementBlueprintOriginalPositions = orderedSelection.Select(m => m.ScreenSpaceSelectionPoint).ToArray(); } /// @@ -459,12 +460,29 @@ namespace osu.Game.Screens.Edit.Compose.Components if (snapProvider == null) return true; - Debug.Assert(movementBlueprintOriginalPosition != null); + Debug.Assert(movementBlueprintOriginalPositions != null); - HitObject draggedObject = movementBlueprint.HitObject; + Vector2 distanceTravelled = e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; + + // check for positional snap for every object in selection (for things like object-object snapping) + for (var i = 0; i < movementBlueprintOriginalPositions.Length; i++) + { + var testPosition = movementBlueprintOriginalPositions[i] + distanceTravelled; + + var positionalResult = snapProvider.SnapScreenSpacePositionToValidPosition(testPosition); + + if (positionalResult.ScreenSpacePosition == testPosition) continue; + + // attempt to move the objects, and abort any time based snapping if we can. + if (SelectionHandler.HandleMovement(new MoveSelectionEvent(SelectionHandler.SelectedBlueprints.ElementAt(i), positionalResult.ScreenSpacePosition))) + return true; + } + + // if no positional snapping could be performed, try unrestricted snapping from the earliest + // hitobject in the selection. // The final movement position, relative to movementBlueprintOriginalPosition. - Vector2 movePosition = movementBlueprintOriginalPosition.Value + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition; + Vector2 movePosition = movementBlueprintOriginalPositions.First() + distanceTravelled; // Retrieve a snapped position. var result = snapProvider.SnapScreenSpacePositionToValidTime(movePosition); @@ -476,7 +494,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (result.Time.HasValue) { // Apply the start time at the newly snapped-to position - double offset = result.Time.Value - draggedObject.StartTime; + double offset = result.Time.Value - movementBlueprint.HitObject.StartTime; foreach (HitObject obj in Beatmap.SelectedHitObjects) { @@ -497,7 +515,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (movementBlueprint == null) return false; - movementBlueprintOriginalPosition = null; + movementBlueprintOriginalPositions = null; movementBlueprint = null; return true; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index f6675902fc..20836c0e68 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -224,6 +224,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline /// public double VisibleRange => track.Length / Zoom; + public SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) => + new SnapResult(screenSpacePosition, null); + public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(screenSpacePosition)))); From 09f2a85d71bcca9753e6ed622574ac6ff29cd663 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Nov 2020 17:40:00 +0900 Subject: [PATCH 50/66] Fix potential test failure due to precision check missing --- osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs index 1ca94df26b..8212a16540 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor var first = (OsuHitObject)objects.First(); var second = (OsuHitObject)objects.Last(); - return first.Position == second.Position; + return Precision.AlmostEquals(first.EndPosition, second.Position); }); } From 4eef6c0d4072ef8a2670b0a40b4fcfd07fad1303 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 24 Nov 2020 17:59:18 +0900 Subject: [PATCH 51/66] Add test coverage --- .../Editor/TestSceneObjectObjectSnap.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs index 8212a16540..d20be90001 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs @@ -86,5 +86,55 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor return Precision.AlmostEquals(first.EndPosition, second.Position); }); } + + [Test] + public void TestSecondCircleInSelectionAlsoSnaps() + { + AddStep("move mouse to centre", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre)); + + AddStep("disable distance snap", () => InputManager.Key(Key.Q)); + + AddStep("enter placement mode", () => InputManager.Key(Key.Number2)); + + AddStep("place first object", () => InputManager.Click(MouseButton.Left)); + + AddStep("move mouse right", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.2f, 0))); + AddStep("place second object", () => InputManager.Click(MouseButton.Left)); + + AddStep("move mouse down", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(0, playfield.ScreenSpaceDrawQuad.Width * 0.2f))); + AddStep("place third object", () => InputManager.Click(MouseButton.Left)); + + AddStep("enter selection mode", () => InputManager.Key(Key.Number1)); + + AddStep("select objects 2 and 3", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects.Skip(1))); + + AddStep("begin drag", () => InputManager.PressButton(MouseButton.Left)); + + AddStep("move mouse slightly off centre", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.02f, 0))); + + AddAssert("object 3 snapped to 1", () => + { + var objects = EditorBeatmap.HitObjects; + + var first = (OsuHitObject)objects.First(); + var third = (OsuHitObject)objects.Last(); + + return Precision.AlmostEquals(first.EndPosition, third.Position); + }); + + AddStep("move mouse slightly off centre", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * -0.22f, playfield.ScreenSpaceDrawQuad.Width * 0.21f))); + + AddAssert("object 2 snapped to 1", () => + { + var objects = EditorBeatmap.HitObjects; + + var first = (OsuHitObject)objects.First(); + var second = (OsuHitObject)objects.ElementAt(1); + + return Precision.AlmostEquals(first.EndPosition, second.Position); + }); + + AddStep("end drag", () => InputManager.ReleaseButton(MouseButton.Left)); + } } } From 916a313f1965ae52872001bd6b3e511854d3368d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 24 Nov 2020 19:13:46 +0900 Subject: [PATCH 52/66] Rename PalpableDrawable -> DrawablePalpable --- .../Objects/Drawables/DrawableCatchHitObject.cs | 4 ++-- osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs | 2 +- osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs index 07f1f79243..7fbc79e4b5 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs @@ -13,11 +13,11 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public abstract class PalpableDrawableCatchHitObject : DrawableCatchHitObject + public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject { protected Container ScaleContainer { get; private set; } - protected PalpableDrawableCatchHitObject(CatchHitObject hitObject) + protected DrawablePalpableCatchHitObject(CatchHitObject hitObject) : base(hitObject) { Origin = Anchor.Centre; diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs index 9db64eba6e..37a05b5d76 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs @@ -8,7 +8,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableDroplet : PalpableDrawableCatchHitObject + public class DrawableDroplet : DrawablePalpableCatchHitObject { public override bool StaysOnPlate => false; diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index f87c8866b1..b000a728c1 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -8,7 +8,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableFruit : PalpableDrawableCatchHitObject + public class DrawableFruit : DrawablePalpableCatchHitObject { public DrawableFruit(CatchHitObject h) : base(h) From 4f7aa7e54187fa5b6371499b7d963862a37fb678 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 24 Nov 2020 19:16:03 +0900 Subject: [PATCH 53/66] Move Palpable* to separate files --- .../Objects/CatchHitObject.cs | 8 ---- .../Drawables/DrawableCatchHitObject.cs | 37 ---------------- .../DrawablePalpableCatchHitObject.cs | 44 +++++++++++++++++++ .../Objects/PalpableCatchHitObject.cs | 13 ++++++ 4 files changed, 57 insertions(+), 45 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs create mode 100644 osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 5985ec9b68..21bb026bae 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -105,14 +105,6 @@ namespace osu.Game.Rulesets.Catch.Objects protected override HitWindows CreateHitWindows() => HitWindows.Empty; } - /// - /// Represents a single object that can be caught by the catcher. - /// - public abstract class PalpableCatchHitObject : CatchHitObject - { - public override bool CanBePlated => true; - } - public enum FruitVisualRepresentation { Pear, diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs index 7fbc79e4b5..4cbc998447 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs @@ -2,49 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; -using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Drawables; -using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject - { - protected Container ScaleContainer { get; private set; } - - protected DrawablePalpableCatchHitObject(CatchHitObject hitObject) - : base(hitObject) - { - Origin = Anchor.Centre; - Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); - Masking = false; - } - - [BackgroundDependencyLoader] - private void load() - { - AddRangeInternal(new Drawable[] - { - ScaleContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - } - }); - - ScaleContainer.Scale = new Vector2(HitObject.Scale); - } - - protected override Color4 GetComboColour(IReadOnlyList comboColours) => - comboColours[(HitObject.IndexInBeatmap + 1) % comboColours.Count]; - } - public abstract class DrawableCatchHitObject : DrawableHitObject { protected override double InitialLifetimeOffset => HitObject.TimePreempt; diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs new file mode 100644 index 0000000000..3e843d60c1 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -0,0 +1,44 @@ +// 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.Graphics.Containers; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables +{ + public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject + { + protected Container ScaleContainer { get; private set; } + + protected DrawablePalpableCatchHitObject(CatchHitObject hitObject) + : base(hitObject) + { + Origin = Anchor.Centre; + Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); + Masking = false; + } + + [BackgroundDependencyLoader] + private void load() + { + AddRangeInternal(new Drawable[] + { + ScaleContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + } + }); + + ScaleContainer.Scale = new Vector2(HitObject.Scale); + } + + protected override Color4 GetComboColour(IReadOnlyList comboColours) => + comboColours[(HitObject.IndexInBeatmap + 1) % comboColours.Count]; + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs new file mode 100644 index 0000000000..3a0cbe3ecd --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.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. + +namespace osu.Game.Rulesets.Catch.Objects +{ + /// + /// Represents a single object that can be caught by the catcher. + /// + public abstract class PalpableCatchHitObject : CatchHitObject + { + public override bool CanBePlated => true; + } +} From ab7251d742ec03ae9a9feb082ae2867209e0b955 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 24 Nov 2020 19:57:37 +0900 Subject: [PATCH 54/66] Move members to `PalpableCatchHitObject` --- .../TestSceneFruitObjects.cs | 2 +- .../Beatmaps/CatchBeatmapProcessor.cs | 22 ++++++++-------- .../Preprocessing/CatchDifficultyHitObject.cs | 4 +-- .../Objects/CatchHitObject.cs | 22 ---------------- .../Drawables/DrawableCatchHitObject.cs | 2 -- .../Objects/Drawables/DrawableFruit.cs | 2 ++ .../DrawablePalpableCatchHitObject.cs | 26 +++++++++---------- .../Objects/Drawables/DropletPiece.cs | 2 +- .../Objects/Drawables/FruitPiece.cs | 4 +-- .../Objects/PalpableCatchHitObject.cs | 20 +++++++++++++- .../Replays/CatchAutoGenerator.cs | 17 ++++-------- .../Skinning/LegacyFruitPiece.cs | 3 ++- osu.Game.Rulesets.Catch/UI/Catcher.cs | 6 ++--- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 12 ++++----- 14 files changed, 66 insertions(+), 78 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs index 89063319d6..e8ecd2ca1b 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitObjects.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Catch.Tests hitObject.Scale = 1.5f; if (hyperdash) - hitObject.HyperDashTarget = new Banana(); + ((PalpableCatchHitObject)hitObject).HyperDashTarget = new Banana(); d.Anchor = Anchor.Centre; d.RelativePositionAxes = Axes.None; diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index a08c5b6fb1..00ce9ea8c2 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -5,11 +5,11 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.MathUtils; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Catch.MathUtils; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Catch.Beatmaps { @@ -192,24 +192,24 @@ namespace osu.Game.Rulesets.Catch.Beatmaps private static void initialiseHyperDash(IBeatmap beatmap) { - List objectWithDroplets = new List(); + List palpableObjects = new List(); foreach (var currentObject in beatmap.HitObjects) { if (currentObject is Fruit fruitObject) - objectWithDroplets.Add(fruitObject); + palpableObjects.Add(fruitObject); if (currentObject is JuiceStream) { - foreach (var currentJuiceElement in currentObject.NestedHitObjects) + foreach (var juice in currentObject.NestedHitObjects) { - if (!(currentJuiceElement is TinyDroplet)) - objectWithDroplets.Add((CatchHitObject)currentJuiceElement); + if (juice is PalpableCatchHitObject palpableObject && !(juice is TinyDroplet)) + palpableObjects.Add(palpableObject); } } } - objectWithDroplets.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)); + palpableObjects.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)); double halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) / 2; @@ -221,10 +221,10 @@ namespace osu.Game.Rulesets.Catch.Beatmaps int lastDirection = 0; double lastExcess = halfCatcherWidth; - for (int i = 0; i < objectWithDroplets.Count - 1; i++) + for (int i = 0; i < palpableObjects.Count - 1; i++) { - CatchHitObject currentObject = objectWithDroplets[i]; - CatchHitObject nextObject = objectWithDroplets[i + 1]; + var currentObject = palpableObjects[i]; + var nextObject = palpableObjects[i + 1]; // Reset variables in-case values have changed (e.g. after applying HR) currentObject.HyperDashTarget = null; diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index 3e21b8fbaf..dcd410e08f 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -12,9 +12,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing { private const float normalized_hitobject_radius = 41.0f; - public new CatchHitObject BaseObject => (CatchHitObject)base.BaseObject; + public new PalpableCatchHitObject BaseObject => (PalpableCatchHitObject)base.BaseObject; - public new CatchHitObject LastObject => (CatchHitObject)base.LastObject; + public new PalpableCatchHitObject LastObject => (PalpableCatchHitObject)base.LastObject; public readonly float NormalizedPosition; public readonly float LastNormalizedPosition; diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 21bb026bae..ccd2422381 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -27,11 +27,6 @@ namespace osu.Game.Rulesets.Catch.Objects set => x = value; } - /// - /// Whether this object can be placed on the catcher's plate. - /// - public virtual bool CanBePlated => false; - /// /// A random offset applied to , set by the . /// @@ -63,13 +58,6 @@ namespace osu.Game.Rulesets.Catch.Objects set => ComboIndexBindable.Value = value; } - /// - /// Difference between the distance to the next object - /// and the distance that would have triggered a hyper dash. - /// A value close to 0 indicates a difficult jump (for difficulty calculation). - /// - public float DistanceToHyperDash { get; set; } - public Bindable LastInComboBindable { get; } = new Bindable(); /// @@ -83,16 +71,6 @@ namespace osu.Game.Rulesets.Catch.Objects public float Scale { get; set; } = 1; - /// - /// Whether this fruit can initiate a hyperdash. - /// - public bool HyperDash => HyperDashTarget != null; - - /// - /// The target fruit if we are to initiate a hyperdash. - /// - public CatchHitObject HyperDashTarget; - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs index 4cbc998447..f9f534f9ab 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs @@ -12,8 +12,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { protected override double InitialLifetimeOffset => HitObject.TimePreempt; - public virtual bool StaysOnPlate => HitObject.CanBePlated; - public float DisplayRadius => DrawSize.X / 2 * Scale.X * HitObject.Scale; protected override float SamplePlaybackPosition => HitObject.X / CatchPlayfield.WIDTH; diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index b000a728c1..a2fa79965f 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -10,6 +10,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { public class DrawableFruit : DrawablePalpableCatchHitObject { + public override bool StaysOnPlate => true; + public DrawableFruit(CatchHitObject h) : base(h) { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index 3e843d60c1..539dbb52d1 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -12,29 +12,27 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject { - protected Container ScaleContainer { get; private set; } + public virtual bool StaysOnPlate => true; - protected DrawablePalpableCatchHitObject(CatchHitObject hitObject) - : base(hitObject) + protected readonly Container ScaleContainer; + + protected DrawablePalpableCatchHitObject(CatchHitObject h) + : base(h) { Origin = Anchor.Centre; Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); - Masking = false; + + AddInternal(ScaleContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + }); } [BackgroundDependencyLoader] private void load() { - AddRangeInternal(new Drawable[] - { - ScaleContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - } - }); - ScaleContainer.Scale = new Vector2(HitObject.Scale); } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DropletPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DropletPiece.cs index c2499446fa..61e6187611 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DropletPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DropletPiece.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables private void load(DrawableHitObject drawableObject) { DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject; - var hitObject = drawableCatchObject.HitObject; + var hitObject = (PalpableCatchHitObject)drawableCatchObject.HitObject; InternalChild = new Pulp { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs index 4bffdab3d8..b14e37d138 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public const float RADIUS_ADJUST = 1.1f; private Circle border; - private CatchHitObject hitObject; + private PalpableCatchHitObject hitObject; public FruitPiece() { @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables private void load(DrawableHitObject drawableObject) { DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject; - hitObject = drawableCatchObject.HitObject; + hitObject = (PalpableCatchHitObject)drawableCatchObject.HitObject; AddRangeInternal(new[] { diff --git a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs index 3a0cbe3ecd..5e35b9ea12 100644 --- a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs @@ -5,9 +5,27 @@ namespace osu.Game.Rulesets.Catch.Objects { /// /// Represents a single object that can be caught by the catcher. + /// This includes normal fruits, droplets, and bananas but excludes objects that acts only as a container of nested hit objects. /// public abstract class PalpableCatchHitObject : CatchHitObject { - public override bool CanBePlated => true; + /// + /// Difference between the distance to the next object + /// and the distance that would have triggered a hyper dash. + /// A value close to 0 indicates a difficult jump (for difficulty calculation). + /// + public float DistanceToHyperDash { get; set; } + + /// + /// Whether this fruit can initiate a hyperdash. + /// + public bool HyperDash => HyperDashTarget != null; + + /// + /// The target fruit if we are to initiate a hyperdash. + /// + public CatchHitObject HyperDashTarget; + + public virtual bool StaysOnPlate => true; } } diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index a4f54bfe82..dfc81ee8d9 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Catch.Replays float lastPosition = CatchPlayfield.CENTER_X; double lastTime = 0; - void moveToNext(CatchHitObject h) + void moveToNext(PalpableCatchHitObject h) { float positionChange = Math.Abs(lastPosition - h.X); double timeAvailable = h.StartTime - lastTime; @@ -101,23 +101,16 @@ namespace osu.Game.Rulesets.Catch.Replays foreach (var obj in Beatmap.HitObjects) { - switch (obj) + if (obj is PalpableCatchHitObject palpableObject) { - case Fruit _: - moveToNext(obj); - break; + moveToNext(palpableObject); } foreach (var nestedObj in obj.NestedHitObjects.Cast()) { - switch (nestedObj) + if (nestedObj is PalpableCatchHitObject palpableNestedObject) { - case Banana _: - case TinyDroplet _: - case Droplet _: - case Fruit _: - moveToNext(nestedObj); - break; + moveToNext(palpableNestedObject); } } } diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs index 381d066750..00ab20152a 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Drawables; @@ -51,7 +52,7 @@ namespace osu.Game.Rulesets.Catch.Skinning }, }; - if (drawableCatchObject.HitObject.HyperDash) + if (((PalpableCatchHitObject)drawableCatchObject.HitObject).HyperDash) { var hyperDash = new Sprite { diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index a221ca7966..0f0b9df76e 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -220,11 +220,11 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Let the catcher attempt to catch a fruit. /// - /// The fruit to catch. + /// The fruit to catch. /// Whether the catch is possible. - public bool AttemptCatch(CatchHitObject fruit) + public bool AttemptCatch(CatchHitObject hitObject) { - if (!fruit.CanBePlated) + if (!(hitObject is PalpableCatchHitObject fruit)) return false; var halfCatchWidth = catchWidth * 0.5f; diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 5e794a76aa..70739673a9 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.UI }; } - public void OnNewResult(DrawableCatchHitObject fruit, JudgementResult result) + public void OnNewResult(DrawableCatchHitObject hitObject, JudgementResult result) { if (!result.Type.IsScorable()) return; @@ -69,15 +69,15 @@ namespace osu.Game.Rulesets.Catch.UI lastPlateableFruit.OnLoadComplete += _ => action(); } - if (result.IsHit && fruit.HitObject.CanBePlated) + if (result.IsHit && hitObject.HitObject is PalpableCatchHitObject fruit) { // create a new (cloned) fruit to stay on the plate. the original is faded out immediately. - var caughtFruit = (DrawableCatchHitObject)CreateDrawableRepresentation?.Invoke(fruit.HitObject); + var caughtFruit = (DrawableCatchHitObject)CreateDrawableRepresentation?.Invoke(fruit); if (caughtFruit == null) return; caughtFruit.RelativePositionAxes = Axes.None; - caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0); + caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(hitObject.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0); caughtFruit.IsOnPlate = true; caughtFruit.Anchor = Anchor.TopCentre; @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Catch.UI runAfterLoaded(() => MovableCatcher.Explode(caughtFruit)); } - if (fruit.HitObject.LastInCombo) + if (hitObject.HitObject.LastInCombo) { if (result.Judgement is CatchJudgement catchJudgement && catchJudgement.ShouldExplodeFor(result)) runAfterLoaded(() => MovableCatcher.Explode()); @@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Catch.UI MovableCatcher.Drop(); } - comboDisplay.OnNewResult(fruit, result); + comboDisplay.OnNewResult(hitObject, result); } public void OnRevertResult(DrawableCatchHitObject fruit, JudgementResult result) From 3c3229ac4b262ec4856d8ae0c43ed561f2b77a6a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 25 Nov 2020 07:59:45 +0900 Subject: [PATCH 55/66] Remove redundant `StaysOnPlate` --- osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs | 2 -- osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs | 4 +--- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 4 ++-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index a2fa79965f..b000a728c1 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -10,8 +10,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { public class DrawableFruit : DrawablePalpableCatchHitObject { - public override bool StaysOnPlate => true; - public DrawableFruit(CatchHitObject h) : base(h) { diff --git a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs index 5e35b9ea12..bdc7dcc1fe 100644 --- a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. namespace osu.Game.Rulesets.Catch.Objects @@ -25,7 +25,5 @@ namespace osu.Game.Rulesets.Catch.Objects /// The target fruit if we are to initiate a hyperdash. /// public CatchHitObject HyperDashTarget; - - public virtual bool StaysOnPlate => true; } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 70739673a9..ad79a23279 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -69,10 +69,10 @@ namespace osu.Game.Rulesets.Catch.UI lastPlateableFruit.OnLoadComplete += _ => action(); } - if (result.IsHit && hitObject.HitObject is PalpableCatchHitObject fruit) + if (result.IsHit && hitObject is DrawablePalpableCatchHitObject fruit) { // create a new (cloned) fruit to stay on the plate. the original is faded out immediately. - var caughtFruit = (DrawableCatchHitObject)CreateDrawableRepresentation?.Invoke(fruit); + var caughtFruit = (DrawableCatchHitObject)CreateDrawableRepresentation?.Invoke(fruit.HitObject); if (caughtFruit == null) return; From 6e55eb2090c82bd8f1efce579aeebeb17eaadfe0 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 25 Nov 2020 08:00:11 +0900 Subject: [PATCH 56/66] Fix and add comments --- .../Objects/Drawables/DrawablePalpableCatchHitObject.cs | 3 +++ osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index 539dbb52d1..c096ea2814 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -12,6 +12,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject { + /// + /// Whether this hit object should stay on the catcher plate when the object is caught by the catcher. + /// public virtual bool StaysOnPlate => true; protected readonly Container ScaleContainer; diff --git a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs index bdc7dcc1fe..361b338b2c 100644 --- a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs @@ -1,11 +1,11 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. namespace osu.Game.Rulesets.Catch.Objects { /// /// Represents a single object that can be caught by the catcher. - /// This includes normal fruits, droplets, and bananas but excludes objects that acts only as a container of nested hit objects. + /// This includes normal fruits, droplets, and bananas but excludes objects that act only as a container of nested hit objects. /// public abstract class PalpableCatchHitObject : CatchHitObject { From 323533d94574451fb801645b3f2535c89b94ed3d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 25 Nov 2020 08:07:59 +0900 Subject: [PATCH 57/66] Add hiding Palpable HitObject property --- .../Objects/Drawables/DrawablePalpableCatchHitObject.cs | 2 ++ osu.Game.Rulesets.Catch/Objects/Drawables/DropletPiece.cs | 5 ++--- osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs | 6 +++--- osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs | 5 ++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index c096ea2814..935aad914e 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -12,6 +12,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject { + public new PalpableCatchHitObject HitObject => (PalpableCatchHitObject)base.HitObject; + /// /// Whether this hit object should stay on the catcher plate when the object is caught by the catcher. /// diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DropletPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DropletPiece.cs index 61e6187611..dd0723c744 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DropletPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DropletPiece.cs @@ -22,8 +22,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject) { - DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject; - var hitObject = (PalpableCatchHitObject)drawableCatchObject.HitObject; + var drawableCatchObject = (DrawablePalpableCatchHitObject)drawableObject; InternalChild = new Pulp { @@ -31,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables AccentColour = { BindTarget = drawableObject.AccentColour } }; - if (hitObject.HyperDash) + if (drawableCatchObject.HitObject.HyperDash) { AddInternal(new Container { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs index b14e37d138..f98050ae91 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs @@ -30,12 +30,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject) { - DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject; - hitObject = (PalpableCatchHitObject)drawableCatchObject.HitObject; + var drawableCatchObject = (DrawablePalpableCatchHitObject)drawableObject; + hitObject = drawableCatchObject.HitObject; AddRangeInternal(new[] { - getFruitFor(drawableCatchObject.HitObject.VisualRepresentation), + getFruitFor(hitObject.VisualRepresentation), border = new Circle { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs index 00ab20152a..1494ef3888 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/LegacyFruitPiece.cs @@ -6,7 +6,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Drawables; @@ -32,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Skinning [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject, ISkinSource skin) { - DrawableCatchHitObject drawableCatchObject = (DrawableCatchHitObject)drawableObject; + var drawableCatchObject = (DrawablePalpableCatchHitObject)drawableObject; accentColour.BindTo(drawableCatchObject.AccentColour); @@ -52,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.Skinning }, }; - if (((PalpableCatchHitObject)drawableCatchObject.HitObject).HyperDash) + if (drawableCatchObject.HitObject.HyperDash) { var hyperDash = new Sprite { From d4c6d6275e4197d81d91531d9f41e26085b8df0c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Nov 2020 16:46:19 +0900 Subject: [PATCH 58/66] Fix volume not being adjustable in the editor using alt-scroll We do this in other places so I think it's fine to handle like this for now (until we come up with a better global solution). Closes #10958. --- osu.Game/Screens/Edit/Editor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 85467d3bbb..3d5c0ddad4 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -375,6 +375,9 @@ namespace osu.Game.Screens.Edit protected override bool OnScroll(ScrollEvent e) { + if (e.ControlPressed || e.AltPressed || e.SuperPressed) + return false; + const double precision = 1; double scrollComponent = e.ScrollDelta.X + e.ScrollDelta.Y; From 0ddeff648d2581a64613346543ce1c5dcb202f8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Nov 2020 17:25:54 +0900 Subject: [PATCH 59/66] Fix incorrect index lookup on non-ordered selections --- .../Compose/Components/BlueprintContainer.cs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index e9f5238980..def5f396f1 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -187,7 +187,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (e.Button == MouseButton.Right) return false; - if (movementBlueprint != null) + if (movementBlueprints != null) { isDraggingBlueprint = true; changeHandler?.BeginChange(); @@ -299,7 +299,7 @@ namespace osu.Game.Screens.Edit.Compose.Components SelectionBlueprints.Remove(blueprint); - if (movementBlueprint == blueprint) + if (movementBlueprints?.Contains(blueprint) == true) finishSelectionMovement(); OnBlueprintRemoved(hitObject); @@ -425,7 +425,7 @@ namespace osu.Game.Screens.Edit.Compose.Components #region Selection Movement private Vector2[] movementBlueprintOriginalPositions; - private SelectionBlueprint movementBlueprint; + private SelectionBlueprint[] movementBlueprints; private bool isDraggingBlueprint; /// @@ -442,9 +442,8 @@ namespace osu.Game.Screens.Edit.Compose.Components return; // Movement is tracked from the blueprint of the earliest hitobject, since it only makes sense to distance snap from that hitobject - var orderedSelection = SelectionHandler.SelectedBlueprints.OrderBy(b => b.HitObject.StartTime); - movementBlueprint = orderedSelection.First(); - movementBlueprintOriginalPositions = orderedSelection.Select(m => m.ScreenSpaceSelectionPoint).ToArray(); + movementBlueprints = SelectionHandler.SelectedBlueprints.OrderBy(b => b.HitObject.StartTime).ToArray(); + movementBlueprintOriginalPositions = movementBlueprints.Select(m => m.ScreenSpaceSelectionPoint).ToArray(); } /// @@ -454,7 +453,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Whether a movement was active. private bool moveCurrentSelection(DragEvent e) { - if (movementBlueprint == null) + if (movementBlueprints == null) return false; if (snapProvider == null) @@ -474,7 +473,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (positionalResult.ScreenSpacePosition == testPosition) continue; // attempt to move the objects, and abort any time based snapping if we can. - if (SelectionHandler.HandleMovement(new MoveSelectionEvent(SelectionHandler.SelectedBlueprints.ElementAt(i), positionalResult.ScreenSpacePosition))) + if (SelectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprints[i], positionalResult.ScreenSpacePosition))) return true; } @@ -488,13 +487,13 @@ namespace osu.Game.Screens.Edit.Compose.Components var result = snapProvider.SnapScreenSpacePositionToValidTime(movePosition); // Move the hitobjects. - if (!SelectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, result.ScreenSpacePosition))) + if (!SelectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprints.First(), result.ScreenSpacePosition))) return true; if (result.Time.HasValue) { // Apply the start time at the newly snapped-to position - double offset = result.Time.Value - movementBlueprint.HitObject.StartTime; + double offset = result.Time.Value - movementBlueprints.First().HitObject.StartTime; foreach (HitObject obj in Beatmap.SelectedHitObjects) { @@ -512,11 +511,11 @@ namespace osu.Game.Screens.Edit.Compose.Components /// Whether a movement was active. private bool finishSelectionMovement() { - if (movementBlueprint == null) + if (movementBlueprints == null) return false; movementBlueprintOriginalPositions = null; - movementBlueprint = null; + movementBlueprints = null; return true; } From 740b9fb3a08bdffc40a74a1b963ac8864f7d6a75 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 25 Nov 2020 17:33:21 +0900 Subject: [PATCH 60/66] Update test to cover non-ordered selection --- .../Editor/TestSceneObjectObjectSnap.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs index d20be90001..6b532e5014 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs @@ -98,15 +98,24 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("place first object", () => InputManager.Click(MouseButton.Left)); + AddStep("increment time", () => EditorClock.SeekForward(true)); + AddStep("move mouse right", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.2f, 0))); AddStep("place second object", () => InputManager.Click(MouseButton.Left)); + AddStep("increment time", () => EditorClock.SeekForward(true)); + AddStep("move mouse down", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(0, playfield.ScreenSpaceDrawQuad.Width * 0.2f))); AddStep("place third object", () => InputManager.Click(MouseButton.Left)); AddStep("enter selection mode", () => InputManager.Key(Key.Number1)); - AddStep("select objects 2 and 3", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects.Skip(1))); + AddStep("select objects 2 and 3", () => + { + // add selection backwards to test non-sequential time ordering + EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[2]); + EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects[1]); + }); AddStep("begin drag", () => InputManager.PressButton(MouseButton.Left)); From 9131546876b33e7a6cf56e18daddea2bbfca089e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 26 Nov 2020 13:04:19 +0900 Subject: [PATCH 61/66] Workaround TestSceneCatchModRelax failure --- .../Mods/TestSceneCatchModRelax.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs index 1eb0975010..c01aff0aa0 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs @@ -38,17 +38,17 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods new Fruit { X = 0, - StartTime = 250 + StartTime = 1000 }, new Fruit { X = CatchPlayfield.WIDTH, - StartTime = 500 + StartTime = 2000 }, new JuiceStream { X = CatchPlayfield.CENTER_X, - StartTime = 750, + StartTime = 3000, Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, Vector2.UnitY * 200 }) } } From 8a73b335f3b59bf84e2b71c7d82ba673a4e78375 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 26 Nov 2020 14:26:38 +0900 Subject: [PATCH 62/66] Move catch piece files --- osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs | 1 + osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs | 1 + .../Objects/Drawables/{ => Pieces}/BananaPiece.cs | 3 +-- .../Objects/Drawables/{ => Pieces}/DropletPiece.cs | 3 +-- .../Objects/Drawables/{ => Pieces}/FruitPiece.cs | 2 +- .../Objects/Drawables/{ => Pieces}/GrapePiece.cs | 3 +-- .../Objects/Drawables/{ => Pieces}/PearPiece.cs | 3 +-- .../Objects/Drawables/{ => Pieces}/PineapplePiece.cs | 3 +-- .../Objects/Drawables/{ => Pieces}/PulpFormation.cs | 2 +- .../Objects/Drawables/{ => Pieces}/RaspberryPiece.cs | 3 +-- 10 files changed, 10 insertions(+), 14 deletions(-) rename osu.Game.Rulesets.Catch/Objects/Drawables/{ => Pieces}/BananaPiece.cs (88%) rename osu.Game.Rulesets.Catch/Objects/Drawables/{ => Pieces}/DropletPiece.cs (95%) rename osu.Game.Rulesets.Catch/Objects/Drawables/{ => Pieces}/FruitPiece.cs (98%) rename osu.Game.Rulesets.Catch/Objects/Drawables/{ => Pieces}/GrapePiece.cs (92%) rename osu.Game.Rulesets.Catch/Objects/Drawables/{ => Pieces}/PearPiece.cs (92%) rename osu.Game.Rulesets.Catch/Objects/Drawables/{ => Pieces}/PineapplePiece.cs (93%) rename osu.Game.Rulesets.Catch/Objects/Drawables/{ => Pieces}/PulpFormation.cs (96%) rename osu.Game.Rulesets.Catch/Objects/Drawables/{ => Pieces}/RaspberryPiece.cs (93%) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs index 37a05b5d76..74cd240aa3 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Utils; +using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces; using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index b000a728c1..96e24bf76c 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Utils; +using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces; using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/BananaPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/BananaPiece.cs similarity index 88% rename from osu.Game.Rulesets.Catch/Objects/Drawables/BananaPiece.cs rename to osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/BananaPiece.cs index ebb0bf0f2c..fa8837dec5 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/BananaPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/BananaPiece.cs @@ -2,10 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces; using osuTK; -namespace osu.Game.Rulesets.Catch.Objects.Drawables +namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces { public class BananaPiece : PulpFormation { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DropletPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/DropletPiece.cs similarity index 95% rename from osu.Game.Rulesets.Catch/Objects/Drawables/DropletPiece.cs rename to osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/DropletPiece.cs index dd0723c744..6de3167958 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DropletPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/DropletPiece.cs @@ -5,12 +5,11 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Drawables; using osuTK; -namespace osu.Game.Rulesets.Catch.Objects.Drawables +namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces { public class DropletPiece : CompositeDrawable { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/FruitPiece.cs similarity index 98% rename from osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs rename to osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/FruitPiece.cs index f98050ae91..632c8e6f3a 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/FruitPiece.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Drawables; using osuTK.Graphics; -namespace osu.Game.Rulesets.Catch.Objects.Drawables +namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces { internal class FruitPiece : CompositeDrawable { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/GrapePiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/GrapePiece.cs similarity index 92% rename from osu.Game.Rulesets.Catch/Objects/Drawables/GrapePiece.cs rename to osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/GrapePiece.cs index 1d1faf893b..15349c18d5 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/GrapePiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/GrapePiece.cs @@ -2,10 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces; using osuTK; -namespace osu.Game.Rulesets.Catch.Objects.Drawables +namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces { public class GrapePiece : PulpFormation { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/PearPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PearPiece.cs similarity index 92% rename from osu.Game.Rulesets.Catch/Objects/Drawables/PearPiece.cs rename to osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PearPiece.cs index 7f14217cda..3372a06996 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/PearPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PearPiece.cs @@ -2,10 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces; using osuTK; -namespace osu.Game.Rulesets.Catch.Objects.Drawables +namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces { public class PearPiece : PulpFormation { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/PineapplePiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PineapplePiece.cs similarity index 93% rename from osu.Game.Rulesets.Catch/Objects/Drawables/PineapplePiece.cs rename to osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PineapplePiece.cs index c328ba1837..7f80c58178 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/PineapplePiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PineapplePiece.cs @@ -2,10 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces; using osuTK; -namespace osu.Game.Rulesets.Catch.Objects.Drawables +namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces { public class PineapplePiece : PulpFormation { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/PulpFormation.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PulpFormation.cs similarity index 96% rename from osu.Game.Rulesets.Catch/Objects/Drawables/PulpFormation.cs rename to osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PulpFormation.cs index be70c3400c..1df548e70a 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/PulpFormation.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/PulpFormation.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Objects.Drawables; using osuTK; using osuTK.Graphics; -namespace osu.Game.Rulesets.Catch.Objects.Drawables +namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces { public abstract class PulpFormation : CompositeDrawable { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/RaspberryPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/RaspberryPiece.cs similarity index 93% rename from osu.Game.Rulesets.Catch/Objects/Drawables/RaspberryPiece.cs rename to osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/RaspberryPiece.cs index 22ce3ba5b3..288ece95b2 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/RaspberryPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/RaspberryPiece.cs @@ -2,10 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces; using osuTK; -namespace osu.Game.Rulesets.Catch.Objects.Drawables +namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces { public class RaspberryPiece : PulpFormation { From cafe8cf7fad552ed741afacd751f39147ee2da51 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 26 Nov 2020 14:36:42 +0900 Subject: [PATCH 63/66] Refactor border of fruits to classes --- .../Objects/Drawables/Pieces/BorderPiece.cs | 31 +++++++++++++ .../Objects/Drawables/Pieces/DropletPiece.cs | 33 +------------- .../Objects/Drawables/Pieces/FruitPiece.cs | 43 ++----------------- .../Drawables/Pieces/HyperBorderPiece.cs | 22 ++++++++++ .../Pieces/HyperDropletBorderPiece.cs | 14 ++++++ 5 files changed, 71 insertions(+), 72 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/BorderPiece.cs create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/HyperBorderPiece.cs create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/HyperDropletBorderPiece.cs diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/BorderPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/BorderPiece.cs new file mode 100644 index 0000000000..1e7a0b0685 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/BorderPiece.cs @@ -0,0 +1,31 @@ +// 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.Shapes; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces +{ + public class BorderPiece : Circle + { + public BorderPiece() + { + Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + BorderColour = Color4.White; + BorderThickness = 6f * FruitPiece.RADIUS_ADJUST; + + // Border is drawn only when there is a child drawable. + Child = new Box + { + AlwaysPresent = true, + Alpha = 0, + RelativeSizeAxes = Axes.Both, + }; + } + } +} + diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/DropletPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/DropletPiece.cs index 6de3167958..bcef30fda8 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/DropletPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/DropletPiece.cs @@ -4,8 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Drawables; using osuTK; @@ -31,36 +29,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces if (drawableCatchObject.HitObject.HyperDash) { - AddInternal(new Container - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(2f), - Depth = 1, - Children = new Drawable[] - { - new Circle - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - BorderColour = Catcher.DEFAULT_HYPER_DASH_COLOUR, - BorderThickness = 6, - Children = new Drawable[] - { - new Box - { - AlwaysPresent = true, - Alpha = 0.3f, - Blending = BlendingParameters.Additive, - RelativeSizeAxes = Axes.Both, - Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR, - } - } - } - } - }); + AddInternal(new HyperDropletBorderPiece()); } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/FruitPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/FruitPiece.cs index 632c8e6f3a..208c9f8316 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/FruitPiece.cs @@ -5,10 +5,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Objects.Drawables; -using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces { @@ -19,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces /// public const float RADIUS_ADJUST = 1.1f; - private Circle border; + private BorderPiece border; private PalpableCatchHitObject hitObject; public FruitPiece() @@ -36,46 +33,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces AddRangeInternal(new[] { getFruitFor(hitObject.VisualRepresentation), - border = new Circle - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - BorderColour = Color4.White, - BorderThickness = 6f * RADIUS_ADJUST, - Children = new Drawable[] - { - new Box - { - AlwaysPresent = true, - Alpha = 0, - RelativeSizeAxes = Axes.Both - } - } - }, + border = new BorderPiece(), }); if (hitObject.HyperDash) { - AddInternal(new Circle - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - BorderColour = Catcher.DEFAULT_HYPER_DASH_COLOUR, - BorderThickness = 12f * RADIUS_ADJUST, - Children = new Drawable[] - { - new Box - { - AlwaysPresent = true, - Alpha = 0.3f, - Blending = BlendingParameters.Additive, - RelativeSizeAxes = Axes.Both, - Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR, - } - } - }); + AddInternal(new HyperBorderPiece()); } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/HyperBorderPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/HyperBorderPiece.cs new file mode 100644 index 0000000000..60bb07e89d --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/HyperBorderPiece.cs @@ -0,0 +1,22 @@ +// 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.Game.Rulesets.Catch.UI; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces +{ + public class HyperBorderPiece : BorderPiece + { + public HyperBorderPiece() + { + BorderColour = Catcher.DEFAULT_HYPER_DASH_COLOUR; + BorderThickness = 12f * FruitPiece.RADIUS_ADJUST; + + Child.Alpha = 0.3f; + Child.Blending = BlendingParameters.Additive; + Child.Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR; + } + } +} + diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/HyperDropletBorderPiece.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/HyperDropletBorderPiece.cs new file mode 100644 index 0000000000..1bd9fd6bb2 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/Pieces/HyperDropletBorderPiece.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Catch.Objects.Drawables.Pieces +{ + public class HyperDropletBorderPiece : HyperBorderPiece + { + public HyperDropletBorderPiece() + { + Size /= 2; + BorderThickness = 6f; + } + } +} From f562854feb72d51a4b198db2b69e5ea5132c6131 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 26 Nov 2020 17:22:22 +0900 Subject: [PATCH 64/66] Fix timeline objects sometimes not receiving combo colours --- .../Timeline/TimelineHitObjectBlueprint.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 975433d407..d534fb3e13 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -55,6 +55,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private const float circle_size = 24; + [Resolved(CanBeNull = true)] + private HitObjectComposer composer { get; set; } + public TimelineHitObjectBlueprint(HitObject hitObject) : base(hitObject) { @@ -152,19 +155,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline updateShadows(); } - [BackgroundDependencyLoader(true)] - private void load(HitObjectComposer composer) + protected override void LoadComplete() { + base.LoadComplete(); + if (composer != null) { // best effort to get the drawable representation for grabbing colour and what not. drawableHitObject = composer.HitObjects.FirstOrDefault(d => d.HitObject == HitObject); } - } - - protected override void LoadComplete() - { - base.LoadComplete(); if (HitObject is IHasComboInformation comboInfo) { From e53f849aa056ac40f5dc9d745a0ad1bc94541e87 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 26 Nov 2020 18:14:25 +0900 Subject: [PATCH 65/66] Completely separate combo colours from DHOs --- osu.Game.Rulesets.Catch/Objects/Banana.cs | 28 ++++++- .../Objects/Drawables/DrawableBanana.cs | 25 ------ .../DrawablePalpableCatchHitObject.cs | 5 -- .../Objects/PalpableCatchHitObject.cs | 8 +- .../Objects/Drawables/DrawableHitObject.cs | 8 +- .../Objects/Types/IHasComboInformation.cs | 11 +++ .../Timeline/TimelineHitObjectBlueprint.cs | 84 ++++++++----------- .../Screens/Edit/Compose/ComposeScreen.cs | 20 ++++- 8 files changed, 103 insertions(+), 86 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Banana.cs b/osu.Game.Rulesets.Catch/Objects/Banana.cs index 4ecfb7b16d..d1033f7801 100644 --- a/osu.Game.Rulesets.Catch/Objects/Banana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Banana.cs @@ -2,13 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Types; +using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects { - public class Banana : Fruit + public class Banana : Fruit, IHasComboInformation { /// /// Index of banana in current shower. @@ -26,6 +29,29 @@ namespace osu.Game.Rulesets.Catch.Objects Samples = samples; } + private Color4? colour; + + Color4 IHasComboInformation.GetComboColour(IReadOnlyList comboColours) + { + // override any external colour changes with banananana + return colour ??= getBananaColour(); + } + + private Color4 getBananaColour() + { + switch (RNG.Next(0, 3)) + { + default: + return new Color4(255, 240, 0, 255); + + case 1: + return new Color4(255, 192, 0, 255); + + case 2: + return new Color4(214, 221, 28, 255); + } + } + private class BananaHitSampleInfo : HitSampleInfo { private static string[] lookupNames { get; } = { "metronomelow", "catch-banana" }; diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs index a865984d45..7748b1c565 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs @@ -1,10 +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 System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Utils; -using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { @@ -15,14 +13,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { } - private Color4? colour; - - protected override Color4 GetComboColour(IReadOnlyList comboColours) - { - // override any external colour changes with banananana - return colour ??= getBananaColour(); - } - protected override void UpdateInitialTransforms() { base.UpdateInitialTransforms(); @@ -46,20 +36,5 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables if (Samples != null) Samples.Frequency.Value = 0.77f + ((Banana)HitObject).BananaIndex * 0.006f; } - - private Color4 getBananaColour() - { - switch (RNG.Next(0, 3)) - { - default: - return new Color4(255, 240, 0, 255); - - case 1: - return new Color4(255, 192, 0, 255); - - case 2: - return new Color4(214, 221, 28, 255); - } - } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index 935aad914e..9339a1c420 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -1,12 +1,10 @@ // 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.Graphics.Containers; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { @@ -40,8 +38,5 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { ScaleContainer.Scale = new Vector2(HitObject.Scale); } - - protected override Color4 GetComboColour(IReadOnlyList comboColours) => - comboColours[(HitObject.IndexInBeatmap + 1) % comboColours.Count]; } } diff --git a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs index 361b338b2c..995f61c386 100644 --- a/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/PalpableCatchHitObject.cs @@ -1,13 +1,17 @@ // 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.Game.Rulesets.Objects.Types; +using osuTK.Graphics; + namespace osu.Game.Rulesets.Catch.Objects { /// /// Represents a single object that can be caught by the catcher. /// This includes normal fruits, droplets, and bananas but excludes objects that act only as a container of nested hit objects. /// - public abstract class PalpableCatchHitObject : CatchHitObject + public abstract class PalpableCatchHitObject : CatchHitObject, IHasComboInformation { /// /// Difference between the distance to the next object @@ -25,5 +29,7 @@ namespace osu.Game.Rulesets.Catch.Objects /// The target fruit if we are to initiate a hyperdash. /// public CatchHitObject HyperDashTarget; + + Color4 IHasComboInformation.GetComboColour(IReadOnlyList comboColours) => comboColours[(IndexInBeatmap + 1) % comboColours.Count]; } } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 537da24e01..571b05cc05 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -529,11 +529,10 @@ namespace osu.Game.Rulesets.Objects.Drawables private void updateComboColour() { - if (!(HitObject is IHasComboInformation)) return; + if (!(HitObject is IHasComboInformation combo)) return; - var comboColours = CurrentSkin.GetConfig>(GlobalSkinColours.ComboColours)?.Value; - - AccentColour.Value = GetComboColour(comboColours); + var comboColours = CurrentSkin.GetConfig>(GlobalSkinColours.ComboColours)?.Value ?? Array.Empty(); + AccentColour.Value = combo.GetComboColour(comboColours); } /// @@ -544,6 +543,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// This will only be called if the implements . /// /// A list of combo colours provided by the beatmap or skin. Can be null if not available. + [Obsolete("Unused. Implement IHasComboInformation and IHasComboInformation.GetComboColour() on the HitObject model instead.")] // Can be removed 20210527 protected virtual Color4 GetComboColour(IReadOnlyList comboColours) { if (!(HitObject is IHasComboInformation combo)) diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index 211c077d4f..4f66802079 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -1,7 +1,10 @@ // 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 JetBrains.Annotations; using osu.Framework.Bindables; +using osuTK.Graphics; namespace osu.Game.Rulesets.Objects.Types { @@ -35,5 +38,13 @@ namespace osu.Game.Rulesets.Objects.Types /// Whether this is the last object in the current combo. /// bool LastInCombo { get; set; } + + /// + /// Retrieves the colour of the combo described by this object from a set of possible combo colours. + /// Defaults to using to decide the colour. + /// + /// A list of possible combo colours provided by the beatmap or skin. + /// The colour of the combo described by this object. + Color4 GetComboColour([NotNull] IReadOnlyList comboColours) => comboColours.Count > 0 ? comboColours[ComboIndex % comboColours.Count] : Color4.White; } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index d534fb3e13..657c5834b2 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -19,8 +18,8 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -28,35 +27,26 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public class TimelineHitObjectBlueprint : SelectionBlueprint { - private readonly Circle circle; + private const float thickness = 5; + private const float shadow_radius = 5; + private const float circle_size = 24; + + public Action OnDragHandled; [UsedImplicitly] private readonly Bindable startTime; - public Action OnDragHandled; + private Bindable indexInCurrentComboBindable; + private Bindable comboIndexBindable; + private readonly Circle circle; private readonly DragBar dragBar; - private readonly List shadowComponents = new List(); - - private DrawableHitObject drawableHitObject; - - private Bindable comboColour; - private readonly Container mainComponents; - private readonly OsuSpriteText comboIndexText; - private Bindable comboIndex; - - private const float thickness = 5; - - private const float shadow_radius = 5; - - private const float circle_size = 24; - - [Resolved(CanBeNull = true)] - private HitObjectComposer composer { get; set; } + [Resolved] + private ISkinSource skin { get; set; } public TimelineHitObjectBlueprint(HitObject hitObject) : base(hitObject) @@ -159,38 +149,38 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { base.LoadComplete(); - if (composer != null) - { - // best effort to get the drawable representation for grabbing colour and what not. - drawableHitObject = composer.HitObjects.FirstOrDefault(d => d.HitObject == HitObject); - } - if (HitObject is IHasComboInformation comboInfo) { - comboIndex = comboInfo.IndexInCurrentComboBindable.GetBoundCopy(); - comboIndex.BindValueChanged(combo => - { - comboIndexText.Text = (combo.NewValue + 1).ToString(); - }, true); + indexInCurrentComboBindable = comboInfo.IndexInCurrentComboBindable.GetBoundCopy(); + indexInCurrentComboBindable.BindValueChanged(_ => updateComboIndex(), true); + + comboIndexBindable = comboInfo.ComboIndexBindable.GetBoundCopy(); + comboIndexBindable.BindValueChanged(_ => updateComboColour(), true); + + skin.SourceChanged += updateComboColour; } + } - if (drawableHitObject != null) - { - comboColour = drawableHitObject.AccentColour.GetBoundCopy(); - comboColour.BindValueChanged(colour => - { - if (HitObject is IHasDuration) - mainComponents.Colour = ColourInfo.GradientHorizontal(drawableHitObject.AccentColour.Value, Color4.White); - else - mainComponents.Colour = drawableHitObject.AccentColour.Value; + private void updateComboIndex() => comboIndexText.Text = (indexInCurrentComboBindable.Value + 1).ToString(); - var col = mainComponents.Colour.TopLeft.Linear; - float brightness = col.R + col.G + col.B; + private void updateComboColour() + { + if (!(HitObject is IHasComboInformation combo)) + return; - // decide the combo index colour based on brightness? - comboIndexText.Colour = brightness > 0.5f ? Color4.Black : Color4.White; - }, true); - } + var comboColours = skin.GetConfig>(GlobalSkinColours.ComboColours)?.Value ?? Array.Empty(); + var comboColour = combo.GetComboColour(comboColours); + + if (HitObject is IHasDuration) + mainComponents.Colour = ColourInfo.GradientHorizontal(comboColour, Color4.White); + else + mainComponents.Colour = comboColour; + + var col = mainComponents.Colour.TopLeft.Linear; + float brightness = col.R + col.G + col.B; + + // decide the combo index colour based on brightness? + comboIndexText.Colour = brightness > 0.5f ? Color4.Black : Color4.White; } protected override void Update() diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index 46d5eb40b4..c297a03dbf 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.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.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -43,6 +44,21 @@ namespace osu.Game.Screens.Edit.Compose if (ruleset == null || composer == null) return new ScreenWhiteBox.UnderConstructionMessage(ruleset == null ? "This beatmap" : $"{ruleset.Description}'s composer"); + return wrapSkinnableContent(composer); + } + + protected override Drawable CreateTimelineContent() + { + if (ruleset == null || composer == null) + return base.CreateTimelineContent(); + + return wrapSkinnableContent(new TimelineBlueprintContainer(composer)); + } + + private Drawable wrapSkinnableContent(Drawable content) + { + Debug.Assert(ruleset != null); + var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin); // the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation @@ -51,9 +67,7 @@ namespace osu.Game.Screens.Edit.Compose // load the skinning hierarchy first. // this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources. - return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(composer)); + return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(content)); } - - protected override Drawable CreateTimelineContent() => composer == null ? base.CreateTimelineContent() : new TimelineBlueprintContainer(composer); } } From 1e79cb498b8454d2a6349bae36d7ff0fe788f484 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 26 Nov 2020 20:07:16 +0900 Subject: [PATCH 66/66] Standardise binding description case to sentence casing --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index e5d3a89a88..9af1b49ef9 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -163,10 +163,10 @@ namespace osu.Game.Input.Bindings [Description("Toggle now playing overlay")] ToggleNowPlaying, - [Description("Previous Selection")] + [Description("Previous selection")] SelectPrevious, - [Description("Next Selection")] + [Description("Next selection")] SelectNext, [Description("Home")] @@ -175,26 +175,26 @@ namespace osu.Game.Input.Bindings [Description("Toggle notifications")] ToggleNotifications, - [Description("Pause")] + [Description("Pause gameplay")] PauseGameplay, // Editor - [Description("Setup Mode")] + [Description("Setup mode")] EditorSetupMode, - [Description("Compose Mode")] + [Description("Compose mode")] EditorComposeMode, - [Description("Design Mode")] + [Description("Design mode")] EditorDesignMode, - [Description("Timing Mode")] + [Description("Timing mode")] EditorTimingMode, [Description("Hold for HUD")] HoldForHUD, - [Description("Random Skin")] + [Description("Random skin")] RandomSkin, } }